@@ -9,8 +9,9 @@ import asyncRetry from 'async-retry';
9
9
import { Client } from '@temporalio/client' ;
10
10
import { Worker } from './helpers' ;
11
11
import * as activities from './activities' ;
12
- import { WorkerDeploymentVersion } from '@temporalio/common' ;
12
+ import { toCanonicalString , WorkerDeploymentVersion } from '@temporalio/common' ;
13
13
import { makeTestFunction } from './helpers-integration' ;
14
+ import { unblockSignal , versionQuery } from './workflows/' ;
14
15
15
16
const test = makeTestFunction ( { workflowsPath : __filename } ) ;
16
17
@@ -33,7 +34,7 @@ test('Worker deployment based versioning', async (t) => {
33
34
} ;
34
35
35
36
const worker1 = await Worker . create ( {
36
- workflowsPath : require . resolve ( './workflows ' ) ,
37
+ workflowsPath : require . resolve ( './deployment-versioning-v1 ' ) ,
37
38
activities,
38
39
taskQueue,
39
40
workerDeploymentOptions : {
@@ -47,7 +48,7 @@ test('Worker deployment based versioning', async (t) => {
47
48
} ) ;
48
49
49
50
const worker2 = await Worker . create ( {
50
- workflowsPath : require . resolve ( './workflows ' ) ,
51
+ workflowsPath : require . resolve ( './deployment-versioning-v2 ' ) ,
51
52
activities,
52
53
taskQueue,
53
54
workerDeploymentOptions : {
@@ -61,7 +62,7 @@ test('Worker deployment based versioning', async (t) => {
61
62
} ) ;
62
63
63
64
const worker3 = await Worker . create ( {
64
- workflowsPath : require . resolve ( './workflows ' ) ,
65
+ workflowsPath : require . resolve ( './deployment-versioning-v3 ' ) ,
65
66
activities,
66
67
taskQueue,
67
68
workerDeploymentOptions : {
@@ -79,41 +80,41 @@ test('Worker deployment based versioning', async (t) => {
79
80
await setCurrentDeploymentVersion ( client , describeResp1 . conflictToken , w1DeploymentVersion ) ;
80
81
81
82
// Start workflow 1 which will use the 1.0 worker on auto-upgrade
82
- const wf1 = await client . workflow . start ( 'autoUpgradeWorkflow ' , {
83
+ const wf1 = await client . workflow . start ( 'deploymentVersioning ' , {
83
84
taskQueue,
84
- workflowId : 'basic -versioning-v1-' + randomUUID ( ) ,
85
+ workflowId : 'deployment -versioning-v1-' + randomUUID ( ) ,
85
86
} ) ;
86
- const state1 = await wf1 . query ( 'state' ) ;
87
+ const state1 = await wf1 . query ( versionQuery ) ;
87
88
assert . equal ( state1 , 'v1' ) ;
88
89
89
90
// Wait for worker 2 to be visible and set as current version
90
91
const describeResp2 = await waitUntilWorkerDeploymentVisible ( client , w2DeploymentVersion ) ;
91
92
await setCurrentDeploymentVersion ( client , describeResp2 . conflictToken , w2DeploymentVersion ) ;
92
93
93
94
// Start workflow 2 which will use the 2.0 worker pinned
94
- const wf2 = await client . workflow . start ( 'pinnedWorkflow ' , {
95
+ const wf2 = await client . workflow . start ( 'deploymentVersioning ' , {
95
96
taskQueue,
96
- workflowId : 'basic -versioning-v2-' + randomUUID ( ) ,
97
+ workflowId : 'deployment -versioning-v2-' + randomUUID ( ) ,
97
98
} ) ;
98
- const state2 = await wf2 . query ( 'state' ) ;
99
+ const state2 = await wf2 . query ( versionQuery ) ;
99
100
assert . equal ( state2 , 'v2' ) ;
100
101
101
102
// Wait for worker 3 to be visible and set as current version
102
103
const describeResp3 = await waitUntilWorkerDeploymentVisible ( client , w3DeploymentVersion ) ;
103
104
await setCurrentDeploymentVersion ( client , describeResp3 . conflictToken , w3DeploymentVersion ) ;
104
105
105
106
// Start workflow 3 which will use the 3.0 worker on auto-upgrade
106
- const wf3 = await client . workflow . start ( 'autoUpgradeWorkflow ' , {
107
+ const wf3 = await client . workflow . start ( 'deploymentVersioning ' , {
107
108
taskQueue,
108
- workflowId : 'basic -versioning-v3-' + randomUUID ( ) ,
109
+ workflowId : 'deployment -versioning-v3-' + randomUUID ( ) ,
109
110
} ) ;
110
- const state3 = await wf3 . query ( 'state' ) ;
111
+ const state3 = await wf3 . query ( versionQuery ) ;
111
112
assert . equal ( state3 , 'v3' ) ;
112
113
113
114
// Signal all workflows to finish
114
- await wf1 . signal ( 'doFinish' ) ;
115
- await wf2 . signal ( 'doFinish' ) ;
116
- await wf3 . signal ( 'doFinish' ) ;
115
+ await wf1 . signal ( unblockSignal ) ;
116
+ await wf2 . signal ( unblockSignal ) ;
117
+ await wf3 . signal ( unblockSignal ) ;
117
118
118
119
const res1 = await wf1 . result ( ) ;
119
120
const res2 = await wf2 . result ( ) ;
@@ -132,27 +133,139 @@ test('Worker deployment based versioning', async (t) => {
132
133
t . pass ( ) ;
133
134
} ) ;
134
135
136
+ test ( 'Worker deployment based versioning with ramping' , async ( t ) => {
137
+ const taskQueue = 'worker-deployment-based-ramping-' + randomUUID ( ) ;
138
+ const deploymentName = 'deployment-ramping-' + randomUUID ( ) ;
139
+ const client = t . context . env . client ;
140
+
141
+ const v1 = {
142
+ buildId : '1.0' ,
143
+ deploymentName : deploymentName ,
144
+ } ;
145
+ const v2 = {
146
+ buildId : '2.0' ,
147
+ deploymentName : deploymentName ,
148
+ } ;
149
+
150
+ const worker1 = await Worker . create ( {
151
+ workflowsPath : require . resolve ( './deployment-versioning-v1' ) ,
152
+ activities,
153
+ taskQueue,
154
+ workerDeploymentOptions : {
155
+ useWorkerVersioning : true ,
156
+ version : v1 ,
157
+ } ,
158
+ } ) ;
159
+ const worker1Promise = worker1 . run ( ) ;
160
+ worker1Promise . catch ( ( err ) => {
161
+ t . fail ( 'Worker 1.0 run error: ' + JSON . stringify ( err ) ) ;
162
+ } ) ;
163
+
164
+ const worker2 = await Worker . create ( {
165
+ workflowsPath : require . resolve ( './deployment-versioning-v2' ) ,
166
+ activities,
167
+ taskQueue,
168
+ workerDeploymentOptions : {
169
+ useWorkerVersioning : true ,
170
+ version : v2 ,
171
+ } ,
172
+ } ) ;
173
+ const worker2Promise = worker2 . run ( ) ;
174
+ worker2Promise . catch ( ( err ) => {
175
+ t . fail ( 'Worker 2.0 run error: ' + JSON . stringify ( err ) ) ;
176
+ } ) ;
177
+
178
+ // Wait for worker deployments to be visible
179
+ await waitUntilWorkerDeploymentVisible ( client , v1 ) ;
180
+ const describeResp = await waitUntilWorkerDeploymentVisible ( client , v2 ) ;
181
+
182
+ // Set current version to v1 and ramp v2 to 100%
183
+ let conflictToken = ( await setCurrentDeploymentVersion ( client , describeResp . conflictToken , v1 ) ) . conflictToken ;
184
+ conflictToken = ( await setRampingVersion ( client , conflictToken , v2 , 100 ) ) . conflictToken ;
185
+
186
+ // Run workflows and verify they run on v2
187
+ for ( let i = 0 ; i < 3 ; i ++ ) {
188
+ const wf = await client . workflow . start ( 'deploymentVersioning' , {
189
+ taskQueue,
190
+ workflowId : `versioning-ramp-100-${ i } -${ randomUUID ( ) } ` ,
191
+ } ) ;
192
+ await wf . signal ( unblockSignal ) ;
193
+ const res = await wf . result ( ) ;
194
+ assert . equal ( res , 'version-v2' ) ;
195
+ }
196
+
197
+ // Set ramp to 0, expecting workflows to run on v1
198
+ conflictToken = ( await setRampingVersion ( client , conflictToken , v2 , 0 ) ) . conflictToken ;
199
+ for ( let i = 0 ; i < 3 ; i ++ ) {
200
+ const wf = await client . workflow . start ( 'deploymentVersioning' , {
201
+ taskQueue,
202
+ workflowId : `versioning-ramp-0-${ i } -${ randomUUID ( ) } ` ,
203
+ } ) ;
204
+ await wf . signal ( unblockSignal ) ;
205
+ const res = await wf . result ( ) ;
206
+ assert . equal ( res , 'version-v1' ) ;
207
+ }
208
+
209
+ // Set ramp to 50 and eventually verify workflows run on both versions
210
+ await setRampingVersion ( client , conflictToken , v2 , 50 ) ;
211
+ const seenResults = new Set < string > ( ) ;
212
+
213
+ const runAndRecord = async ( ) => {
214
+ const wf = await client . workflow . start ( 'deploymentVersioning' , {
215
+ taskQueue,
216
+ workflowId : `versioning-ramp-50-${ randomUUID ( ) } ` ,
217
+ } ) ;
218
+ await wf . signal ( unblockSignal ) ;
219
+ return await wf . result ( ) ;
220
+ } ;
221
+
222
+ await asyncRetry (
223
+ async ( ) => {
224
+ const res = await runAndRecord ( ) ;
225
+ seenResults . add ( res ) ;
226
+ if ( ! seenResults . has ( 'version-v1' ) || ! seenResults . has ( 'version-v2' ) ) {
227
+ throw new Error ( 'Not all versions seen yet' ) ;
228
+ }
229
+ } ,
230
+ { maxTimeout : 1000 , retries : 20 }
231
+ ) ;
232
+
233
+ worker1 . shutdown ( ) ;
234
+ worker2 . shutdown ( ) ;
235
+ await worker1Promise ;
236
+ await worker2Promise ;
237
+ t . pass ( ) ;
238
+ } ) ;
239
+
240
+ async function setRampingVersion (
241
+ client : Client ,
242
+ conflictToken : Uint8Array ,
243
+ version : WorkerDeploymentVersion ,
244
+ percentage : number
245
+ ) {
246
+ return await client . workflowService . setWorkerDeploymentRampingVersion ( {
247
+ namespace : client . options . namespace ,
248
+ deploymentName : version . deploymentName ,
249
+ version : toCanonicalString ( version ) ,
250
+ conflictToken,
251
+ percentage,
252
+ } ) ;
253
+ }
254
+
135
255
async function waitUntilWorkerDeploymentVisible ( client : Client , version : WorkerDeploymentVersion ) {
136
256
return await asyncRetry (
137
257
async ( ) => {
138
- try {
139
- const resp = await client . workflowService . describeWorkerDeployment ( {
140
- namespace : client . options . namespace ,
141
- deploymentName : version . deploymentName ,
142
- } ) ;
143
-
144
- const isVersionVisible = resp . workerDeploymentInfo ! . versionSummaries ! . some (
145
- ( vs ) => vs . version === version . buildId
146
- ) ;
147
-
148
- if ( ! isVersionVisible ) {
149
- throw new Error ( 'Version not visible yet' ) ;
150
- }
151
-
152
- return resp ;
153
- } catch ( error ) {
154
- throw error ;
258
+ const resp = await client . workflowService . describeWorkerDeployment ( {
259
+ namespace : client . options . namespace ,
260
+ deploymentName : version . deploymentName ,
261
+ } ) ;
262
+ const isVersionVisible = resp . workerDeploymentInfo ! . versionSummaries ! . some (
263
+ ( vs ) => vs . version === toCanonicalString ( version )
264
+ ) ;
265
+ if ( ! isVersionVisible ) {
266
+ throw new Error ( 'Version not visible yet' ) ;
155
267
}
268
+ return resp ;
156
269
} ,
157
270
{ maxTimeout : 1000 , retries : 10 }
158
271
) ;
@@ -166,7 +279,7 @@ async function setCurrentDeploymentVersion(
166
279
return await client . workflowService . setWorkerDeploymentCurrentVersion ( {
167
280
namespace : client . options . namespace ,
168
281
deploymentName : version . deploymentName ,
169
- version : version . buildId ,
282
+ version : toCanonicalString ( version ) ,
170
283
conflictToken,
171
284
} ) ;
172
285
}
0 commit comments