Skip to content

Commit 9aae0d2

Browse files
Worker Deployment Versioning (#1679)
Co-authored-by: James Watkins-Harvey <james.watkinsharvey@temporal.io>
1 parent ea28266 commit 9aae0d2

40 files changed

+1335
-277
lines changed

package-lock.json

Lines changed: 18 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/client/src/workflow-client.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1227,6 +1227,7 @@ export class WorkflowClient extends BaseClient {
12271227
cronSchedule: options.cronSchedule,
12281228
header: { fields: headers },
12291229
priority: options.priority ? compilePriority(options.priority) : undefined,
1230+
versioningOverride: options.versioningOverride ?? undefined,
12301231
};
12311232
try {
12321233
return (await this.workflowService.signalWithStartWorkflowExecution(req)).runId;
@@ -1296,6 +1297,7 @@ export class WorkflowClient extends BaseClient {
12961297
cronSchedule: opts.cronSchedule,
12971298
header: { fields: headers },
12981299
priority: opts.priority ? compilePriority(opts.priority) : undefined,
1300+
versioningOverride: opts.versioningOverride ?? undefined,
12991301
};
13001302
}
13011303

packages/client/src/workflow-options.ts

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1-
import { CommonWorkflowOptions, SignalDefinition, WithWorkflowArgs, Workflow } from '@temporalio/common';
1+
import {
2+
CommonWorkflowOptions,
3+
SignalDefinition,
4+
WithWorkflowArgs,
5+
Workflow,
6+
VersioningOverride,
7+
toCanonicalString,
8+
} from '@temporalio/common';
29
import { Duration, msOptionalToTs } from '@temporalio/common/lib/time';
310
import { Replace } from '@temporalio/common/lib/type-helpers';
4-
import { google } from '@temporalio/proto';
11+
import { google, temporal } from '@temporalio/proto';
512

613
export * from '@temporalio/common/lib/workflow-options';
714

@@ -38,9 +45,15 @@ export interface WorkflowOptions extends CommonWorkflowOptions {
3845

3946
/**
4047
* Amount of time to wait before starting the workflow.
41-
*
4248
*/
4349
startDelay?: Duration;
50+
51+
/**
52+
* Override the versioning behavior of the Workflow that is about to be started.
53+
*
54+
* @experimental Deployment based versioning is experimental and may change in the future.
55+
*/
56+
versioningOverride?: VersioningOverride;
4457
}
4558

4659
export type WithCompiledWorkflowOptions<T extends WorkflowOptions> = Replace<
@@ -50,18 +63,21 @@ export type WithCompiledWorkflowOptions<T extends WorkflowOptions> = Replace<
5063
workflowRunTimeout?: google.protobuf.IDuration;
5164
workflowTaskTimeout?: google.protobuf.IDuration;
5265
startDelay?: google.protobuf.IDuration;
66+
versioningOverride?: temporal.api.workflow.v1.IVersioningOverride;
5367
}
5468
>;
5569

5670
export function compileWorkflowOptions<T extends WorkflowOptions>(options: T): WithCompiledWorkflowOptions<T> {
57-
const { workflowExecutionTimeout, workflowRunTimeout, workflowTaskTimeout, startDelay, ...rest } = options;
71+
const { workflowExecutionTimeout, workflowRunTimeout, workflowTaskTimeout, startDelay, versioningOverride, ...rest } =
72+
options;
5873

5974
return {
6075
...rest,
6176
workflowExecutionTimeout: msOptionalToTs(workflowExecutionTimeout),
6277
workflowRunTimeout: msOptionalToTs(workflowRunTimeout),
6378
workflowTaskTimeout: msOptionalToTs(workflowTaskTimeout),
6479
startDelay: msOptionalToTs(startDelay),
80+
versioningOverride: versioningOverrideToProto(versioningOverride),
6581
};
6682
}
6783

@@ -109,3 +125,25 @@ export interface WorkflowSignalWithStartOptionsWithArgs<SignalArgs extends any[]
109125
* Options for starting a Workflow
110126
*/
111127
export type WorkflowStartOptions<T extends Workflow = Workflow> = WithWorkflowArgs<T, WorkflowOptions>;
128+
129+
function versioningOverrideToProto(
130+
vo: VersioningOverride | undefined
131+
): temporal.api.workflow.v1.IVersioningOverride | undefined {
132+
if (!vo) return undefined;
133+
134+
// TODO: Remove deprecated field assignments when versioning is non-experimental
135+
if (vo === 'AUTO_UPGRADE') {
136+
return {
137+
autoUpgrade: true,
138+
behavior: temporal.api.enums.v1.VersioningBehavior.VERSIONING_BEHAVIOR_AUTO_UPGRADE,
139+
};
140+
}
141+
142+
return {
143+
pinned: {
144+
version: vo.pinnedTo,
145+
},
146+
behavior: temporal.api.enums.v1.VersioningBehavior.VERSIONING_BEHAVIOR_PINNED,
147+
pinnedVersion: toCanonicalString(vo.pinnedTo),
148+
};
149+
}

packages/common/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export * from './logger';
2222
export * from './priority';
2323
export * from './retry-policy';
2424
export type { Timestamp, Duration, StringValue } from './time';
25+
export * from './worker-deployments';
26+
export * from './workflow-definition-options';
2527
export * from './workflow-handle';
2628
export * from './workflow-options';
2729
export * from './versioning-intent';
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import type { temporal } from '@temporalio/proto';
2+
import { makeProtoEnumConverters } from './internal-workflow';
3+
4+
/**
5+
* Represents the version of a specific worker deployment.
6+
*
7+
* @experimental Deployment based versioning is experimental and may change in the future.
8+
*/
9+
export interface WorkerDeploymentVersion {
10+
readonly buildId: string;
11+
readonly deploymentName: string;
12+
}
13+
14+
/**
15+
* @returns The canonical representation of a deployment version, which is a string in the format
16+
* `deploymentName.buildId`.
17+
*/
18+
export function toCanonicalString(version: WorkerDeploymentVersion): string {
19+
return `${version.deploymentName}.${version.buildId}`;
20+
}
21+
22+
/**
23+
* Specifies when a workflow might move from a worker of one Build Id to another.
24+
*
25+
* * 'PINNED' - The workflow will be pinned to the current Build ID unless manually moved.
26+
* * 'AUTO_UPGRADE' - The workflow will automatically move to the latest version (default Build ID
27+
* of the task queue) when the next task is dispatched.
28+
*
29+
* @experimental Deployment based versioning is experimental and may change in the future.
30+
*/
31+
export const VersioningBehavior = {
32+
PINNED: 'PINNED',
33+
AUTO_UPGRADE: 'AUTO_UPGRADE',
34+
} as const;
35+
export type VersioningBehavior = (typeof VersioningBehavior)[keyof typeof VersioningBehavior];
36+
37+
export const [encodeVersioningBehavior, decodeVersioningBehavior] = makeProtoEnumConverters<
38+
temporal.api.enums.v1.VersioningBehavior,
39+
typeof temporal.api.enums.v1.VersioningBehavior,
40+
keyof typeof temporal.api.enums.v1.VersioningBehavior,
41+
typeof VersioningBehavior,
42+
'VERSIONING_BEHAVIOR_'
43+
>(
44+
{
45+
[VersioningBehavior.PINNED]: 1,
46+
[VersioningBehavior.AUTO_UPGRADE]: 2,
47+
UNSPECIFIED: 0,
48+
} as const,
49+
'VERSIONING_BEHAVIOR_'
50+
);
51+
52+
/**
53+
* Represents versioning overrides. For example, when starting workflows.
54+
*/
55+
export type VersioningOverride = PinnedVersioningOverride | 'AUTO_UPGRADE';
56+
57+
/**
58+
* Workflow will be pinned to a specific deployment version.
59+
*/
60+
export interface PinnedVersioningOverride {
61+
/**
62+
* The worker deployment version to pin the workflow to.
63+
*/
64+
pinnedTo: WorkerDeploymentVersion;
65+
}
66+
67+
/**
68+
* The workflow will auto-upgrade to the current deployment version on the next workflow task.
69+
*/
70+
export type AutoUpgradeVersioningOverride = 'AUTO_UPGRADE';
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { VersioningBehavior } from './worker-deployments';
2+
3+
/**
4+
* Options that can be used when defining a workflow via {@link setWorkflowOptions}.
5+
*/
6+
export interface WorkflowDefinitionOptions {
7+
versioningBehavior?: VersioningBehavior;
8+
}
9+
10+
type AsyncFunction<Args extends any[], ReturnType> = (...args: Args) => Promise<ReturnType>;
11+
export type WorkflowDefinitionOptionsOrGetter = WorkflowDefinitionOptions | (() => WorkflowDefinitionOptions);
12+
13+
/**
14+
* @internal
15+
* @hidden
16+
* A workflow function that has been defined with options from {@link WorkflowDefinitionOptions}.
17+
*/
18+
export interface WorkflowFunctionWithOptions<Args extends any[], ReturnType> extends AsyncFunction<Args, ReturnType> {
19+
workflowDefinitionOptions: WorkflowDefinitionOptionsOrGetter;
20+
}

packages/common/src/workflow-options.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Duration } from './time';
55
import { makeProtoEnumConverters } from './internal-workflow';
66
import { SearchAttributePair, SearchAttributes, TypedSearchAttributes } from './search-attributes';
77
import { Priority } from './priority';
8+
import { WorkflowFunctionWithOptions } from './workflow-definition-options';
89

910
/**
1011
* Defines what happens when trying to start a Workflow with the same ID as a *Closed* Workflow.
@@ -243,7 +244,9 @@ export interface WorkflowDurationOptions {
243244

244245
export type CommonWorkflowOptions = BaseWorkflowOptions & WorkflowDurationOptions;
245246

246-
export function extractWorkflowType<T extends Workflow>(workflowTypeOrFunc: string | T): string {
247+
export function extractWorkflowType<T extends Workflow>(
248+
workflowTypeOrFunc: string | T | WorkflowFunctionWithOptions<any[], any>
249+
): string {
247250
if (typeof workflowTypeOrFunc === 'string') return workflowTypeOrFunc as string;
248251
if (typeof workflowTypeOrFunc === 'function') {
249252
if (workflowTypeOrFunc?.name) return workflowTypeOrFunc.name;

0 commit comments

Comments
 (0)