Skip to content

Commit 23f57a5

Browse files
authored
Merge pull request #645: New Worker Versioning API
2 parents d1bd93a + acdfa85 commit 23f57a5

28 files changed

+870
-119
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"roadrunner-php/roadrunner-api-dto": "^1.13.0",
3636
"roadrunner-php/version-checker": "^1.0.1",
3737
"spiral/attributes": "^3.1.8",
38-
"spiral/roadrunner": "^2025.1.2",
38+
"spiral/roadrunner": "^2025.1.3",
3939
"spiral/roadrunner-cli": "^2.6",
4040
"spiral/roadrunner-kv": "^4.3.1",
4141
"spiral/roadrunner-worker": "^3.6.2",

dload.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
temp-dir="./runtime"
55
>
66
<actions>
7-
<download software="rr" version="^2025.1.2"/>
8-
<download software="temporal" version="^1.3-priority"/>
7+
<download software="rr" version="^2025.1.3"/>
8+
<download software="temporal" version="^1.4.1"/>
99
<download software="temporal-tests-server"/>
1010
</actions>
1111
<registry>

psalm-baseline.xml

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,14 @@
268268
<code><![CDATA[non-empty-string]]></code>
269269
</MoreSpecificReturnType>
270270
</file>
271+
<file src="src/Common/Versioning/WorkerDeploymentVersion.php">
272+
<PossiblyNullArgument>
273+
<code><![CDATA[$parts[1]]]></code>
274+
</PossiblyNullArgument>
275+
<PossiblyUndefinedArrayOffset>
276+
<code><![CDATA[$parts[1]]]></code>
277+
</PossiblyUndefinedArrayOffset>
278+
</file>
271279
<file src="src/DataConverter/DataConverter.php">
272280
<MissingImmutableAnnotation>
273281
<code><![CDATA[DataConverter]]></code>
@@ -547,8 +555,10 @@
547555
</PropertyNotSetInConstructor>
548556
</file>
549557
<file src="src/Internal/Declaration/Graph/ClassNode.php">
558+
<ImplicitToStringCast>
559+
<code><![CDATA[list<array{class-string, \ReflectionMethod}>]]></code>
560+
</ImplicitToStringCast>
550561
<InvalidReturnType>
551-
<code><![CDATA[\Traversable<ClassNode, \ReflectionMethod>]]></code>
552562
<code><![CDATA[\Traversable<ClassNode>]]></code>
553563
</InvalidReturnType>
554564
<MissingClosureReturnType>
@@ -611,17 +621,16 @@
611621
<code><![CDATA[$name]]></code>
612622
<code><![CDATA[$retry]]></code>
613623
</ArgumentTypeCoercion>
614-
<RawObjectIteration>
615-
<code><![CDATA[$group]]></code>
616-
</RawObjectIteration>
617624
<RedundantConditionGivenDocblockType>
618625
<code><![CDATA[$retry !== null]]></code>
619626
</RedundantConditionGivenDocblockType>
627+
<UnnecessaryVarAnnotation>
628+
<code><![CDATA[\ReflectionMethod]]></code>
629+
</UnnecessaryVarAnnotation>
620630
</file>
621631
<file src="src/Internal/Declaration/Reader/WorkflowReader.php">
622632
<ArgumentTypeCoercion>
623633
<code><![CDATA[$contextualRetry]]></code>
624-
<code><![CDATA[$method]]></code>
625634
<code><![CDATA[$name]]></code>
626635
<code><![CDATA[$retry]]></code>
627636
</ArgumentTypeCoercion>
@@ -631,13 +640,12 @@
631640
<InvalidReturnType>
632641
<code><![CDATA[\Traversable<ActivityPrototype>]]></code>
633642
</InvalidReturnType>
634-
<RawObjectIteration>
635-
<code><![CDATA[$group]]></code>
636-
<code><![CDATA[$group]]></code>
637-
</RawObjectIteration>
638643
<RedundantConditionGivenDocblockType>
639644
<code><![CDATA[$retry !== null]]></code>
640645
</RedundantConditionGivenDocblockType>
646+
<UnnecessaryVarAnnotation>
647+
<code><![CDATA[\Traversable<class-string, \ReflectionMethod>]]></code>
648+
</UnnecessaryVarAnnotation>
641649
</file>
642650
<file src="src/Internal/Declaration/WorkflowInstance/QueryDispatcher.php">
643651
<PropertyNotSetInConstructor>
@@ -1451,6 +1459,15 @@
14511459
<code><![CDATA[run]]></code>
14521460
</MissingReturnType>
14531461
</file>
1462+
<file src="src/Worker/WorkerOptions.php">
1463+
<DeprecatedProperty>
1464+
<code><![CDATA[$self->buildID]]></code>
1465+
<code><![CDATA[$self->useBuildIDForVersioning]]></code>
1466+
</DeprecatedProperty>
1467+
<MissingConstructor>
1468+
<code><![CDATA[$deploymentOptions]]></code>
1469+
</MissingConstructor>
1470+
</file>
14541471
<file src="src/WorkerFactory.php">
14551472
<DeprecatedClass>
14561473
<code><![CDATA[new AnnotationReader()]]></code>
@@ -1557,4 +1574,9 @@
15571574
<code><![CDATA[NamedArgumentConstructor]]></code>
15581575
</DeprecatedClass>
15591576
</file>
1577+
<file src="src/Workflow/WorkflowVersioningBehavior.php">
1578+
<DeprecatedClass>
1579+
<code><![CDATA[NamedArgumentConstructor]]></code>
1580+
</DeprecatedClass>
1581+
</file>
15601582
</files>

src/Client/WorkflowOptions.php

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Temporal\Common\SearchAttributes\SearchAttributeKey;
2424
use Temporal\Common\TypedSearchAttributes;
2525
use Temporal\Common\Uuid;
26+
use Temporal\Common\Versioning\VersioningOverride;
2627
use Temporal\Common\WorkflowIdConflictPolicy;
2728
use Temporal\DataConverter\DataConverterInterface;
2829
use Temporal\Internal\Marshaller\Meta\Marshal;
@@ -32,8 +33,8 @@
3233
use Temporal\Internal\Marshaller\Type\NullableType;
3334
use Temporal\Internal\Support\DateInterval;
3435
use Temporal\Internal\Support\Options;
35-
use Temporal\Worker\WorkerFactoryInterface;
3636
use Temporal\Worker\Worker;
37+
use Temporal\Worker\WorkerFactoryInterface;
3738

3839
/**
3940
* WorkflowOptions configuration parameters for starting a workflow execution.
@@ -182,6 +183,16 @@ final class WorkflowOptions extends Options
182183
#[Marshal(name: 'Priority')]
183184
public Priority $priority;
184185

186+
/**
187+
* Override the version the Workflow will start on.
188+
*
189+
* @since SDK 2.16.0
190+
* @since RoadRunner 2025.1.3
191+
* @internal Experimental
192+
*/
193+
#[Marshal(name: 'VersioningOverride')]
194+
public ?VersioningOverride $versioningOverride = null;
195+
185196
/**
186197
* @throws \Exception
187198
*/
@@ -521,6 +532,20 @@ public function withStaticDetails(string $details): self
521532
return $self;
522533
}
523534

535+
/**
536+
* Sets the versioning override to use when starting this workflow.
537+
*
538+
* @since SDK 2.16.0
539+
* @since RoadRunner 2025.1.3
540+
* @internal Experimental
541+
*/
542+
public function withVersioningOverride(?VersioningOverride $override): self
543+
{
544+
$self = clone $this;
545+
$self->versioningOverride = $override;
546+
return $self;
547+
}
548+
524549
/**
525550
* @internal
526551
*/
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Temporal\Common\Versioning;
6+
7+
/**
8+
* Specifies when a workflow might move from a worker of one Build Id to another.
9+
*
10+
* Versioning Behavior specifies if and how a workflow execution moves between Worker Deployment
11+
* Versions. The Versioning Behavior of a workflow execution is typically specified by the worker
12+
* who completes the first task of the execution, but is also overridable manually for new and
13+
* existing workflows (see VersioningOverride).
14+
* Experimental. Worker Deployments are experimental and might significantly change in the future.
15+
*
16+
* @see \Temporal\Api\Enums\V1\VersioningBehavior
17+
*
18+
* @since SDK 2.16.0
19+
* @since RoadRunner 2025.1.3
20+
* @internal Experimental
21+
*/
22+
enum VersioningBehavior: int
23+
{
24+
/**
25+
* An unspecified versioning behavior. By default, workers opting into worker versioning will be
26+
* required to specify a behavior.
27+
*/
28+
case Unspecified = 0;
29+
30+
/**
31+
* The workflow will be pinned to the current Build ID unless manually moved.
32+
*
33+
* Workflow will start on the Current Deployment Version of its Task Queue, and then
34+
* will be pinned to that same Deployment Version until completion (the Version that
35+
* this Workflow is pinned to is specified in `versioning_info.version`).
36+
* This behavior eliminates most of compatibility concerns users face when changing their code.
37+
* Patching is not needed when pinned workflows code change.
38+
* Can be overridden explicitly via `UpdateWorkflowExecutionOptions` API to move the
39+
* execution to another Deployment Version.
40+
* Activities of `Pinned` workflows are sent to the same Deployment Version. Exception to this
41+
* would be when the activity Task Queue workers are not present in the workflow's Deployment
42+
* Version, in which case the activity will be sent to the Current Deployment Version of its own
43+
* task queue.
44+
*/
45+
case Pinned = 1;
46+
47+
/**
48+
* The workflow will automatically move to the latest version (default Build ID of the task queue)
49+
* when the next task is dispatched.
50+
*
51+
* Workflow will automatically move to the Current Deployment Version of its Task Queue when the
52+
* next workflow task is dispatched.
53+
* AutoUpgrade behavior is suitable for long-running workflows as it allows them to move to the
54+
* latest Deployment Version, but the user still needs to use Patching to keep the new code
55+
* compatible with prior versions for changed workflow types.
56+
* Activities of `AUTO_UPGRADE` workflows are sent to the Deployment Version of the workflow
57+
* execution (as specified in versioning_info.version based on the last completed
58+
* workflow task). Exception to this would be when the activity Task Queue workers are not
59+
* present in the workflow's Deployment Version, in which case, the activity will be sent to a
60+
* different Deployment Version according to the Current Deployment Version of its own task
61+
* queue.
62+
* Workflows stuck on a backlogged activity will still auto-upgrade if the Current Deployment
63+
* Version of their Task Queue changes, without having to wait for the backlogged activity to
64+
* complete on the old Version.
65+
*/
66+
case AutoUpgrade = 2;
67+
68+
public static function tryFromName(?string $name): ?self
69+
{
70+
return match ($name) {
71+
'Unspecified', null => self::Unspecified,
72+
'Pinned' => self::Pinned,
73+
'AutoUpgrade' => self::AutoUpgrade,
74+
default => null,
75+
};
76+
}
77+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
/**
4+
* This file is part of Temporal package.
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
declare(strict_types=1);
11+
12+
namespace Temporal\Common\Versioning;
13+
14+
/**
15+
* Represents the override of a worker's versioning behavior for a workflow execution.
16+
*
17+
* @since SDK 2.16.0
18+
* @since RoadRunner 2025.1.3
19+
* @internal Experimental
20+
*/
21+
final class VersioningOverride
22+
{
23+
private function __construct(
24+
public readonly VersioningBehavior $behavior,
25+
public readonly ?WorkerDeploymentVersion $version = null,
26+
) {}
27+
28+
/**
29+
* The Workflow will be pinned to a specific deployment version.
30+
*/
31+
public static function pinned(WorkerDeploymentVersion $version): self
32+
{
33+
return new self(
34+
VersioningBehavior::Pinned,
35+
$version,
36+
);
37+
}
38+
39+
/**
40+
* The Workflow will auto-upgrade to the current deployment version on the next workflow task.
41+
*/
42+
public static function autoUpgrade(): self
43+
{
44+
return new self(
45+
VersioningBehavior::AutoUpgrade,
46+
);
47+
}
48+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Temporal\Common\Versioning;
6+
7+
use Temporal\Exception\InvalidArgumentException;
8+
use Temporal\Internal\Marshaller\Meta\Marshal;
9+
use Temporal\Internal\Traits\CloneWith;
10+
11+
/**
12+
* Represents the version of a specific worker deployment.
13+
*
14+
* @see \Temporal\Api\Deployment\V1\WorkerDeploymentVersion
15+
*
16+
* @since SDK 2.16.0
17+
* @since RoadRunner 2025.1.3
18+
* @internal Experimental
19+
*/
20+
class WorkerDeploymentVersion implements \Stringable
21+
{
22+
use CloneWith;
23+
24+
private function __construct(
25+
/**
26+
* A unique identifier for this Version within the Deployment it is a part of.
27+
* Not necessarily unique within the namespace.
28+
* The combination of {@see $deployment_name} and {@see $buildId} uniquely identifies this
29+
* Version within the namespace, because Deployment names are unique within a namespace.
30+
*/
31+
#[Marshal('DeploymentName')]
32+
public readonly string $deploymentName,
33+
34+
/**
35+
* Identifies the Worker Deployment this Version is part of.
36+
*/
37+
#[Marshal('BuildId')]
38+
public readonly string $buildId,
39+
) {}
40+
41+
/**
42+
* Create a new worker deployment version with the given deployment name and build ID.
43+
*
44+
* @param non-empty-string $deploymentName The name of the worker deployment. Must not contain a ".".
45+
* @param non-empty-string $buildId The build ID of the worker deployment.
46+
*
47+
* @throws InvalidArgumentException if the deployment name or build ID is empty or invalid.
48+
*/
49+
public static function new(string $deploymentName, string $buildId): self
50+
{
51+
return new self($deploymentName, $buildId);
52+
}
53+
54+
/**
55+
* Build a worker deployment version from a canonical string representation.
56+
*
57+
* @param non-empty-string $canonicalString The canonical string representation of the worker deployment version,
58+
* formatted as "deploymentName.buildId". Deployment name must not have a "." in it.
59+
*
60+
* @throws InvalidArgumentException if the input string is not in the expected format.
61+
*/
62+
public static function fromString(string $canonicalString): self
63+
{
64+
$parts = \explode('.', $canonicalString, 2);
65+
\count($parts) === 2 or throw new InvalidArgumentException(
66+
"Invalid canonical string format. Expected 'deploymentName.buildId'",
67+
);
68+
69+
return new self($parts[0], $parts[1]);
70+
}
71+
72+
/**
73+
* @return non-empty-string canonical string representation of this worker deployment version.
74+
*/
75+
public function __toString()
76+
{
77+
return "{$this->deploymentName}.{$this->buildId}";
78+
}
79+
}

0 commit comments

Comments
 (0)