@@ -7,8 +7,7 @@ import type { WorkflowInfo, StackTraceFileLocation } from '@temporalio/workflow'
7
7
import { type SinkCall } from '@temporalio/workflow/lib/sinks' ;
8
8
import * as internals from '@temporalio/workflow/lib/worker-interface' ;
9
9
import { Activator } from '@temporalio/workflow/lib/internals' ;
10
- import { assertValidFlag , SdkFlag , SdkFlags } from '@temporalio/workflow/lib/flags' ;
11
- import { partition } from '../utils' ;
10
+ import { SdkFlags } from '@temporalio/workflow/lib/flags' ;
12
11
import { Workflow } from './interface' ;
13
12
import { WorkflowBundleWithSourceMapAndFilename } from './workflow-worker-thread/input' ;
14
13
@@ -274,7 +273,6 @@ export type WorkflowModule = typeof internals;
274
273
* A Workflow implementation using Node.js' built-in `vm` module
275
274
*/
276
275
export abstract class BaseVMWorkflow implements Workflow {
277
- private readonly knownFlags = new Set < number > ( ) ;
278
276
unhandledRejection : unknown ;
279
277
280
278
constructor (
@@ -289,7 +287,7 @@ export abstract class BaseVMWorkflow implements Workflow {
289
287
* Send request to the Workflow runtime's worker-interface
290
288
*/
291
289
async getAndResetSinkCalls ( ) : Promise < SinkCall [ ] > {
292
- return this . workflowModule . getAndResetSinkCalls ( ) ;
290
+ return this . activator . getAndResetSinkCalls ( ) ;
293
291
}
294
292
295
293
/**
@@ -301,48 +299,45 @@ export abstract class BaseVMWorkflow implements Workflow {
301
299
public async activate (
302
300
activation : coresdk . workflow_activation . IWorkflowActivation
303
301
) : Promise < coresdk . workflow_completion . IWorkflowActivationCompletion > {
304
- if ( this . context === undefined ) {
305
- throw new IllegalStateError ( 'Workflow isolate context uninitialized' ) ;
306
- }
307
- if ( ! activation . jobs ) {
308
- throw new Error ( 'Expected workflow activation jobs to be defined' ) ;
302
+ if ( this . context === undefined ) throw new IllegalStateError ( 'Workflow isolate context uninitialized' ) ;
303
+ if ( ! activation . jobs ) throw new Error ( 'Expected workflow activation jobs to be defined' ) ;
304
+
305
+ // Queries are particular in many ways; handle them separately
306
+ const [ queries , nonQueries ] = partition ( activation . jobs , ( { queryWorkflow } ) => queryWorkflow != null ) ;
307
+ if ( queries . length > 0 ) {
308
+ // Core guarantees that a single activation will not contain both queries and other jobs
309
+ if ( nonQueries . length > 0 ) throw new TypeError ( 'Got both queries and other jobs in a single activation' ) ;
310
+ return this . activateQueries ( activation ) ;
309
311
}
310
312
311
- this . addKnownFlags ( activation . availableInternalFlags ?? [ ] ) ;
313
+ // Let's prepare the activation
314
+ const [ patches , nonPatches ] = partition ( nonQueries , ( { notifyHasPatch } ) => notifyHasPatch != null ) ;
315
+ this . workflowModule . startActivation (
316
+ coresdk . workflow_activation . WorkflowActivation . fromObject ( { ...activation , jobs : patches } )
317
+ ) ;
312
318
319
+ // Extract signals and updates
313
320
const hasUpdates = activation . jobs . some ( ( { doUpdate } ) => doUpdate != null ) ;
314
- const groupUpdatesWithSignals =
315
- hasUpdates && this . hasFlag ( SdkFlags . GroupUpdatesJobsWithSignals , activation . isReplaying ?? false ) ;
316
-
317
- // Job processing order
318
- // 1. patch notifications
319
- // 2. signals and updates (updates )
320
- // 3. anything left except for queries
321
- // 4. queries
322
- const [ patches , nonPatches ] = partition ( activation . jobs , ( { notifyHasPatch } ) => notifyHasPatch != null ) ;
323
- // const [signals, nonSignals] = partition(nonPatches, ({ signalWorkflow }) => signalWorkflow != null);
324
- const [ signals , nonSignals ] = partition (
321
+ const groupUpdatesWithSignals = hasUpdates && this . activator . hasFlag ( SdkFlags . GroupUpdatesJobsWithSignals ) ;
322
+
323
+ const [ signals , rest ] = partition (
325
324
nonPatches ,
326
325
( { signalWorkflow, doUpdate } ) => signalWorkflow != null || ( doUpdate != null && groupUpdatesWithSignals )
327
326
) ;
328
- const [ queries , rest ] = partition ( nonSignals , ( { queryWorkflow } ) => queryWorkflow != null ) ;
329
- let batchIndex = 0 ;
330
-
331
- // Loop and invoke each batch and wait for microtasks to complete.
332
- // This is done outside of the isolate because when we used isolated-vm we couldn't wait for microtasks from inside the isolate, not relevant anymore.
333
- for ( const jobs of [ patches , signals , rest , queries ] ) {
334
- if ( jobs . length === 0 ) {
335
- continue ;
336
- }
327
+
328
+ // Loop and invoke each batch, waiting for microtasks to complete after each batch.
329
+ let batchIndex = 1 ;
330
+ for ( const jobs of [ signals , rest ] ) {
331
+ if ( jobs . length === 0 ) continue ;
337
332
this . workflowModule . activate (
338
333
coresdk . workflow_activation . WorkflowActivation . fromObject ( { ...activation , jobs } ) ,
339
334
batchIndex ++
340
335
) ;
341
- if ( internals . shouldUnblockConditions ( jobs [ 0 ] ) ) {
342
- this . tryUnblockConditions ( ) ;
343
- }
336
+ this . tryUnblockMicrotasks ( ) ;
344
337
}
338
+
345
339
const completion = this . workflowModule . concludeActivation ( ) ;
340
+
346
341
// Give unhandledRejection handler a chance to be triggered.
347
342
await new Promise ( setImmediate ) ;
348
343
if ( this . unhandledRejection ) {
@@ -354,6 +349,13 @@ export abstract class BaseVMWorkflow implements Workflow {
354
349
return completion ;
355
350
}
356
351
352
+ private activateQueries (
353
+ activation : coresdk . workflow_activation . IWorkflowActivation
354
+ ) : coresdk . workflow_completion . IWorkflowActivationCompletion {
355
+ this . workflowModule . activateQueries ( activation ) ;
356
+ return this . workflowModule . concludeActivation ( ) ;
357
+ }
358
+
357
359
/**
358
360
* If called (by an external unhandledRejection handler), activations will fail with provided error.
359
361
*/
@@ -362,12 +364,12 @@ export abstract class BaseVMWorkflow implements Workflow {
362
364
}
363
365
364
366
/**
365
- * Call into the Workflow context to attempt to unblock any blocked conditions.
367
+ * Call into the Workflow context to attempt to unblock any blocked conditions and microtasks .
366
368
*
367
369
* This is performed in a loop allowing microtasks to be processed between
368
370
* each iteration until there are no more conditions to unblock.
369
371
*/
370
- protected tryUnblockConditions ( ) : void {
372
+ protected tryUnblockMicrotasks ( ) : void {
371
373
for ( ; ; ) {
372
374
const numUnblocked = this . workflowModule . tryUnblockConditions ( ) ;
373
375
if ( numUnblocked === 0 ) break ;
@@ -378,34 +380,11 @@ export abstract class BaseVMWorkflow implements Workflow {
378
380
* Do not use this Workflow instance after this method has been called.
379
381
*/
380
382
public abstract dispose ( ) : Promise < void > ;
383
+ }
381
384
382
- /**
383
- * Called early while handling an activation to register known flags
384
- *
385
- * This duplicates the functionality of `Activator.addKnownFlags`, for use outside of the sandbox.
386
- */
387
- private addKnownFlags ( flags : number [ ] ) : void {
388
- for ( const flag of flags ) {
389
- assertValidFlag ( flag ) ;
390
- this . knownFlags . add ( flag ) ;
391
- }
392
- }
393
-
394
- /**
395
- * Check if a flag is known to the Workflow Execution; if not, enable the flag if workflow
396
- * is not replaying and the flag is configured to be enabled by default.
397
- *
398
- * This duplicates the functionality of `Activator.addKnownFlags`, for use outside of the sandbox.
399
- * Note that we can't rely on WorkflowInfo.isReplaying here.
400
- */
401
- protected hasFlag ( flag : SdkFlag , isReplaying : boolean ) : boolean {
402
- if ( this . knownFlags . has ( flag . id ) ) {
403
- return true ;
404
- }
405
- if ( ! isReplaying && flag . default ) {
406
- this . knownFlags . add ( flag . id ) ;
407
- return true ;
408
- }
409
- return false ;
410
- }
385
+ function partition < T > ( arr : T [ ] , predicate : ( x : T ) => boolean ) : [ T [ ] , T [ ] ] {
386
+ const truthy = Array < T > ( ) ;
387
+ const falsy = Array < T > ( ) ;
388
+ arr . forEach ( ( v ) => ( predicate ( v ) ? truthy : falsy ) . push ( v ) ) ;
389
+ return [ truthy , falsy ] ;
411
390
}
0 commit comments