@temporalio/workflow 1.11.8 → 1.12.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/alea.js +5 -3
- package/lib/alea.js.map +1 -1
- package/lib/cancellation-scope.js +36 -18
- package/lib/cancellation-scope.js.map +1 -1
- package/lib/errors.js +1 -0
- package/lib/errors.js.map +1 -1
- package/lib/flags.js +1 -1
- package/lib/flags.js.map +1 -1
- package/lib/index.d.ts +6 -3
- package/lib/index.js +3 -1
- package/lib/index.js.map +1 -1
- package/lib/interceptors.d.ts +11 -1
- package/lib/interfaces.d.ts +68 -5
- package/lib/interfaces.js +1 -0
- package/lib/interfaces.js.map +1 -1
- package/lib/internals.d.ts +55 -7
- package/lib/internals.js +323 -201
- package/lib/internals.js.map +1 -1
- package/lib/logs.js +11 -1
- package/lib/logs.js.map +1 -1
- package/lib/metrics.d.ts +32 -0
- package/lib/metrics.js +118 -0
- package/lib/metrics.js.map +1 -0
- package/lib/trigger.js +8 -0
- package/lib/trigger.js.map +1 -1
- package/lib/update-scope.js +8 -0
- package/lib/update-scope.js.map +1 -1
- package/lib/worker-interface.d.ts +6 -1
- package/lib/worker-interface.js +165 -103
- package/lib/worker-interface.js.map +1 -1
- package/lib/workflow.d.ts +75 -17
- package/lib/workflow.js +204 -31
- package/lib/workflow.js.map +1 -1
- package/package.json +7 -4
- package/src/flags.ts +1 -1
- package/src/index.ts +4 -2
- package/src/interceptors.ts +21 -1
- package/src/interfaces.ts +81 -7
- package/src/internals.ts +133 -33
- package/src/logs.ts +11 -1
- package/src/metrics.ts +191 -0
- package/src/worker-interface.ts +164 -107
- package/src/workflow.ts +229 -35
package/src/interfaces.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { RawSourceMap } from 'source-map';
|
|
2
2
|
import {
|
|
3
3
|
RetryPolicy,
|
|
4
|
-
TemporalFailure,
|
|
5
4
|
CommonWorkflowOptions,
|
|
6
5
|
HandlerUnfinishedPolicy,
|
|
7
6
|
SearchAttributes,
|
|
@@ -10,6 +9,11 @@ import {
|
|
|
10
9
|
QueryDefinition,
|
|
11
10
|
Duration,
|
|
12
11
|
VersioningIntent,
|
|
12
|
+
TypedSearchAttributes,
|
|
13
|
+
SearchAttributePair,
|
|
14
|
+
Priority,
|
|
15
|
+
WorkerDeploymentVersion,
|
|
16
|
+
VersioningBehavior,
|
|
13
17
|
} from '@temporalio/common';
|
|
14
18
|
import { SymbolBasedInstanceOfError } from '@temporalio/common/lib/type-helpers';
|
|
15
19
|
import { makeProtoEnumConverters } from '@temporalio/common/lib/internal-workflow/enums-helpers';
|
|
@@ -39,8 +43,16 @@ export interface WorkflowInfo {
|
|
|
39
43
|
* Indexed information attached to the Workflow Execution
|
|
40
44
|
*
|
|
41
45
|
* This value may change during the lifetime of an Execution.
|
|
46
|
+
* @deprecated Use {@link typedSearchAttributes} instead.
|
|
42
47
|
*/
|
|
43
|
-
readonly searchAttributes: SearchAttributes;
|
|
48
|
+
readonly searchAttributes: SearchAttributes; // eslint-disable-line deprecation/deprecation
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Indexed information attached to the Workflow Execution, exposed through an interface.
|
|
52
|
+
*
|
|
53
|
+
* This value may change during the lifetime of an Execution.
|
|
54
|
+
*/
|
|
55
|
+
readonly typedSearchAttributes: TypedSearchAttributes;
|
|
44
56
|
|
|
45
57
|
/**
|
|
46
58
|
* Non-indexed information attached to the Workflow Execution
|
|
@@ -52,6 +64,22 @@ export interface WorkflowInfo {
|
|
|
52
64
|
*/
|
|
53
65
|
readonly parent?: ParentWorkflowInfo;
|
|
54
66
|
|
|
67
|
+
/**
|
|
68
|
+
* The root workflow execution, defined as follows:
|
|
69
|
+
* 1. A workflow without a parent workflow is its own root workflow.
|
|
70
|
+
* 2. A workflow with a parent workflow has the same root workflow as
|
|
71
|
+
* its parent.
|
|
72
|
+
*
|
|
73
|
+
* When there is no parent workflow, i.e., the workflow is its own root workflow,
|
|
74
|
+
* this field is `undefined`.
|
|
75
|
+
*
|
|
76
|
+
* Note that Continue-as-New (or reset) propagates the workflow parentage relationship,
|
|
77
|
+
* and therefore, whether the new workflow has the same root workflow as the original one
|
|
78
|
+
* depends on whether it had a parent.
|
|
79
|
+
*
|
|
80
|
+
*/
|
|
81
|
+
readonly root?: RootWorkflowInfo;
|
|
82
|
+
|
|
55
83
|
/**
|
|
56
84
|
* Result from the previous Run (present if this is a Cron Workflow or was Continued As New).
|
|
57
85
|
*
|
|
@@ -62,7 +90,7 @@ export interface WorkflowInfo {
|
|
|
62
90
|
/**
|
|
63
91
|
* Failure from the previous Run (present when this Run is a retry, or the last Run of a Cron Workflow failed)
|
|
64
92
|
*/
|
|
65
|
-
readonly lastFailure?:
|
|
93
|
+
readonly lastFailure?: Error;
|
|
66
94
|
|
|
67
95
|
/**
|
|
68
96
|
* Length of Workflow history up until the current Workflow Task.
|
|
@@ -169,10 +197,28 @@ export interface WorkflowInfo {
|
|
|
169
197
|
* task was completed by a worker without a Build ID. If this worker is the one executing this
|
|
170
198
|
* task for the first time and has a Build ID set, then its ID will be used. This value may change
|
|
171
199
|
* over the lifetime of the workflow run, but is deterministic and safe to use for branching.
|
|
200
|
+
*
|
|
201
|
+
* @deprecated Use `currentDeploymentVersion` instead
|
|
172
202
|
*/
|
|
173
203
|
readonly currentBuildId?: string;
|
|
174
204
|
|
|
205
|
+
/**
|
|
206
|
+
* The Deployment Version of the worker which executed the current Workflow Task. May be undefined
|
|
207
|
+
* if the task was completed by a worker without a Deployment Version. If this worker is the one
|
|
208
|
+
* executing this task for the first time and has a Deployment Version set, then its ID will be
|
|
209
|
+
* used. This value may change over the lifetime of the workflow run, but is deterministic and
|
|
210
|
+
* safe to use for branching.
|
|
211
|
+
*
|
|
212
|
+
* @experimental Deployment based versioning is experimental and may change in the future.
|
|
213
|
+
*/
|
|
214
|
+
readonly currentDeploymentVersion?: WorkerDeploymentVersion;
|
|
215
|
+
|
|
175
216
|
readonly unsafe: UnsafeWorkflowInfo;
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Priority of this workflow
|
|
220
|
+
*/
|
|
221
|
+
readonly priority?: Priority;
|
|
176
222
|
}
|
|
177
223
|
|
|
178
224
|
/**
|
|
@@ -213,6 +259,11 @@ export interface ParentWorkflowInfo {
|
|
|
213
259
|
namespace: string;
|
|
214
260
|
}
|
|
215
261
|
|
|
262
|
+
export interface RootWorkflowInfo {
|
|
263
|
+
workflowId: string;
|
|
264
|
+
runId: string;
|
|
265
|
+
}
|
|
266
|
+
|
|
216
267
|
/**
|
|
217
268
|
* Not an actual error, used by the Workflow runtime to abort execution when {@link continueAsNew} is called
|
|
218
269
|
*/
|
|
@@ -251,15 +302,27 @@ export interface ContinueAsNewOptions {
|
|
|
251
302
|
memo?: Record<string, unknown>;
|
|
252
303
|
/**
|
|
253
304
|
* Searchable attributes to attach to next Workflow run
|
|
305
|
+
* @deprecated Use {@link typedSearchAttributes} instead.
|
|
306
|
+
*/
|
|
307
|
+
searchAttributes?: SearchAttributes; // eslint-disable-line deprecation/deprecation
|
|
308
|
+
/**
|
|
309
|
+
* Specifies additional indexed information to attach to the Workflow Execution. More info:
|
|
310
|
+
* https://docs.temporal.io/docs/typescript/search-attributes
|
|
311
|
+
*
|
|
312
|
+
* Values are always converted using {@link JsonPayloadConverter}, even when a custom data converter is provided.
|
|
313
|
+
* Note that search attributes are not encoded, as such, do not include any sensitive information.
|
|
314
|
+
*
|
|
315
|
+
* If both {@link searchAttributes} and {@link typedSearchAttributes} are provided, conflicting keys will be overwritten
|
|
316
|
+
* by {@link typedSearchAttributes}.
|
|
254
317
|
*/
|
|
255
|
-
|
|
318
|
+
typedSearchAttributes?: SearchAttributePair[] | TypedSearchAttributes;
|
|
256
319
|
/**
|
|
257
320
|
* When using the Worker Versioning feature, specifies whether this Workflow should
|
|
258
321
|
* Continue-as-New onto a worker with a compatible Build Id or not. See {@link VersioningIntent}.
|
|
259
322
|
*
|
|
260
323
|
* @default 'COMPATIBLE'
|
|
261
324
|
*
|
|
262
|
-
* @experimental
|
|
325
|
+
* @experimental The Worker Versioning API is still being designed. Major changes are expected.
|
|
263
326
|
*/
|
|
264
327
|
versioningIntent?: VersioningIntent;
|
|
265
328
|
}
|
|
@@ -390,7 +453,7 @@ export const [encodeParentClosePolicy, decodeParentClosePolicy] = makeProtoEnumC
|
|
|
390
453
|
'PARENT_CLOSE_POLICY_'
|
|
391
454
|
);
|
|
392
455
|
|
|
393
|
-
export interface ChildWorkflowOptions extends CommonWorkflowOptions {
|
|
456
|
+
export interface ChildWorkflowOptions extends Omit<CommonWorkflowOptions, 'workflowIdConflictPolicy'> {
|
|
394
457
|
/**
|
|
395
458
|
* Workflow id to use when starting. If not specified a UUID is generated. Note that it is
|
|
396
459
|
* dangerous as in case of client side retries no deduplication will happen based on the
|
|
@@ -429,7 +492,7 @@ export interface ChildWorkflowOptions extends CommonWorkflowOptions {
|
|
|
429
492
|
*
|
|
430
493
|
* @default 'COMPATIBLE'
|
|
431
494
|
*
|
|
432
|
-
* @experimental
|
|
495
|
+
* @experimental The Worker Versioning API is still being designed. Major changes are expected.
|
|
433
496
|
*/
|
|
434
497
|
versioningIntent?: VersioningIntent;
|
|
435
498
|
}
|
|
@@ -538,6 +601,16 @@ export type Handler<
|
|
|
538
601
|
*/
|
|
539
602
|
export type DefaultSignalHandler = (signalName: string, ...args: unknown[]) => void | Promise<void>;
|
|
540
603
|
|
|
604
|
+
/**
|
|
605
|
+
* A handler function accepting update calls for non-registered update names.
|
|
606
|
+
*/
|
|
607
|
+
export type DefaultUpdateHandler = (updateName: string, ...args: unknown[]) => Promise<unknown> | unknown;
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* A handler function accepting query calls for non-registered query names.
|
|
611
|
+
*/
|
|
612
|
+
export type DefaultQueryHandler = (queryName: string, ...args: unknown[]) => unknown;
|
|
613
|
+
|
|
541
614
|
/**
|
|
542
615
|
* A validation function capable of accepting the arguments for a given UpdateDefinition.
|
|
543
616
|
*/
|
|
@@ -565,4 +638,5 @@ export type UpdateHandlerOptions<Args extends any[]> = {
|
|
|
565
638
|
export interface ActivationCompletion {
|
|
566
639
|
commands: coresdk.workflow_commands.IWorkflowCommand[];
|
|
567
640
|
usedInternalFlags: number[];
|
|
641
|
+
versioningBehavior?: VersioningBehavior;
|
|
568
642
|
}
|
package/src/internals.ts
CHANGED
|
@@ -19,10 +19,15 @@ import {
|
|
|
19
19
|
WorkflowUpdateType,
|
|
20
20
|
WorkflowUpdateValidatorType,
|
|
21
21
|
mapFromPayloads,
|
|
22
|
-
searchAttributePayloadConverter,
|
|
23
22
|
fromPayloadsAtIndex,
|
|
24
|
-
|
|
23
|
+
WorkflowFunctionWithOptions,
|
|
24
|
+
VersioningBehavior,
|
|
25
|
+
WorkflowDefinitionOptions,
|
|
25
26
|
} from '@temporalio/common';
|
|
27
|
+
import {
|
|
28
|
+
decodeSearchAttributes,
|
|
29
|
+
decodeTypedSearchAttributes,
|
|
30
|
+
} from '@temporalio/common/lib/converter/payload-search-attributes';
|
|
26
31
|
import { composeInterceptors } from '@temporalio/common/lib/interceptors';
|
|
27
32
|
import { makeProtoEnumConverters } from '@temporalio/common/lib/internal-workflow';
|
|
28
33
|
import type { coresdk, temporal } from '@temporalio/proto';
|
|
@@ -41,6 +46,8 @@ import {
|
|
|
41
46
|
WorkflowInfo,
|
|
42
47
|
WorkflowCreateOptionsInternal,
|
|
43
48
|
ActivationCompletion,
|
|
49
|
+
DefaultUpdateHandler,
|
|
50
|
+
DefaultQueryHandler,
|
|
44
51
|
} from './interfaces';
|
|
45
52
|
import { type SinkCall } from './sinks';
|
|
46
53
|
import { untrackPromise } from './stack-helpers';
|
|
@@ -189,6 +196,16 @@ export class Activator implements ActivationHandler {
|
|
|
189
196
|
*/
|
|
190
197
|
defaultSignalHandler?: DefaultSignalHandler;
|
|
191
198
|
|
|
199
|
+
/**
|
|
200
|
+
* A update handler that catches calls for non-registered update names.
|
|
201
|
+
*/
|
|
202
|
+
defaultUpdateHandler?: DefaultUpdateHandler;
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* A query handler that catches calls for non-registered query names.
|
|
206
|
+
*/
|
|
207
|
+
defaultQueryHandler?: DefaultQueryHandler;
|
|
208
|
+
|
|
192
209
|
/**
|
|
193
210
|
* Source map file for looking up the source files in response to __enhanced_stack_trace
|
|
194
211
|
*/
|
|
@@ -204,6 +221,38 @@ export class Activator implements ActivationHandler {
|
|
|
204
221
|
childToParent: new Map(),
|
|
205
222
|
};
|
|
206
223
|
|
|
224
|
+
/**
|
|
225
|
+
* The error that caused the current Workflow Task to fail. Sets if a non-`TemporalFailure`
|
|
226
|
+
* error bubbles up out of the Workflow function, or out of a Signal or Update handler. We
|
|
227
|
+
* capture errors this way because those functions are not technically awaited when started,
|
|
228
|
+
* but left to run asynchronously. There is therefore no real "parent" function that can
|
|
229
|
+
* directly handle those errors, and not capturing it would result in an Unhandled Promise
|
|
230
|
+
* Rejection. So instead, we buffer the error here, to then be processed in the context
|
|
231
|
+
* of our own synchronous Activation handling event loop.
|
|
232
|
+
*
|
|
233
|
+
* Our code does a best effort to stop processing the current activation as soon as possible
|
|
234
|
+
* after this field is set:
|
|
235
|
+
* - If an error is thrown while executing code synchronously (e.g. anything before the
|
|
236
|
+
* first `await` statement in a Workflow function or a signal/update handler), the error
|
|
237
|
+
* will be _immediately_ rethrown, which will prevent execution of further jobs in the
|
|
238
|
+
* current activation. We know we're currently running code synchronously thanks to the
|
|
239
|
+
* `rethrowSynchronously` flag below.
|
|
240
|
+
* - It an error is thrown while executing microtasks, then the error will be rethrown on
|
|
241
|
+
* the next call to `tryUnblockConditions()`.
|
|
242
|
+
*
|
|
243
|
+
* Unfortunately, there's no way for us to prevent further execution of microtasks that have
|
|
244
|
+
* already been scheduled, nor those that will be recursively scheduled from those microtasks.
|
|
245
|
+
* Should more errors get thrown while settling microtasks, those will be ignored (i.e. only
|
|
246
|
+
* the first captured error is preserved).
|
|
247
|
+
*/
|
|
248
|
+
public workflowTaskError: unknown;
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Set to true when running synchronous code (e.g. while processing activation jobs and when calling
|
|
252
|
+
* `tryUnblockConditions()`). While this flag is set, it is safe to let errors bubble up.
|
|
253
|
+
*/
|
|
254
|
+
public rethrowSynchronously = false;
|
|
255
|
+
|
|
207
256
|
public readonly rootScope = new RootCancellationScope();
|
|
208
257
|
|
|
209
258
|
/**
|
|
@@ -336,7 +385,7 @@ export class Activator implements ActivationHandler {
|
|
|
336
385
|
/**
|
|
337
386
|
* Reference to the current Workflow, initialized when a Workflow is started
|
|
338
387
|
*/
|
|
339
|
-
public workflow?: Workflow
|
|
388
|
+
public workflow?: Workflow | WorkflowFunctionWithOptions<any[], any>;
|
|
340
389
|
|
|
341
390
|
/**
|
|
342
391
|
* Information about the current Workflow
|
|
@@ -370,12 +419,17 @@ export class Activator implements ActivationHandler {
|
|
|
370
419
|
sinkCalls = Array<SinkCall>();
|
|
371
420
|
|
|
372
421
|
/**
|
|
373
|
-
* A nanosecond resolution time function, externally injected
|
|
422
|
+
* A nanosecond resolution time function, externally injected. This is used to
|
|
423
|
+
* precisely sort logs entries emitted from the Workflow Context vs those emitted
|
|
424
|
+
* from other sources (e.g. main thread, Core, etc).
|
|
374
425
|
*/
|
|
375
426
|
public readonly getTimeOfDay: () => bigint;
|
|
376
427
|
|
|
377
428
|
public readonly registeredActivityNames: Set<string>;
|
|
378
429
|
|
|
430
|
+
public versioningBehavior?: VersioningBehavior;
|
|
431
|
+
public workflowDefinitionOptionsGetter?: () => WorkflowDefinitionOptions;
|
|
432
|
+
|
|
379
433
|
constructor({
|
|
380
434
|
info,
|
|
381
435
|
now,
|
|
@@ -448,6 +502,7 @@ export class Activator implements ActivationHandler {
|
|
|
448
502
|
return {
|
|
449
503
|
commands: this.commands.splice(0),
|
|
450
504
|
usedInternalFlags: [...this.knownFlags],
|
|
505
|
+
versioningBehavior: this.versioningBehavior,
|
|
451
506
|
};
|
|
452
507
|
}
|
|
453
508
|
|
|
@@ -478,8 +533,10 @@ export class Activator implements ActivationHandler {
|
|
|
478
533
|
// Most things related to initialization have already been handled in the constructor
|
|
479
534
|
this.mutateWorkflowInfo((info) => ({
|
|
480
535
|
...info,
|
|
481
|
-
|
|
482
|
-
|
|
536
|
+
|
|
537
|
+
searchAttributes: decodeSearchAttributes(searchAttributes?.indexedFields),
|
|
538
|
+
typedSearchAttributes: decodeTypedSearchAttributes(searchAttributes?.indexedFields),
|
|
539
|
+
|
|
483
540
|
memo: mapFromPayloads(this.payloadConverter, memo?.fields),
|
|
484
541
|
lastResult: fromPayloadsAtIndex(this.payloadConverter, 0, lastCompletionResult?.payloads),
|
|
485
542
|
lastFailure:
|
|
@@ -487,6 +544,9 @@ export class Activator implements ActivationHandler {
|
|
|
487
544
|
? this.failureConverter.failureToError(continuedFailure, this.payloadConverter)
|
|
488
545
|
: undefined,
|
|
489
546
|
}));
|
|
547
|
+
if (this.workflowDefinitionOptionsGetter) {
|
|
548
|
+
this.versioningBehavior = this.workflowDefinitionOptionsGetter().versioningBehavior;
|
|
549
|
+
}
|
|
490
550
|
}
|
|
491
551
|
|
|
492
552
|
public cancelWorkflow(_activation: coresdk.workflow_activation.ICancelWorkflow): void {
|
|
@@ -523,16 +583,6 @@ export class Activator implements ActivationHandler {
|
|
|
523
583
|
}
|
|
524
584
|
}
|
|
525
585
|
|
|
526
|
-
public resolveNexusOperationStart(_activation: coresdk.workflow_activation.IResolveNexusOperationStart): void {
|
|
527
|
-
// This will never be called as we can't produce Nexus-related commands anyway
|
|
528
|
-
throw new Error('Nexus is not yet implemented');
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
public resolveNexusOperation(_activation: coresdk.workflow_activation.IResolveNexusOperation): void {
|
|
532
|
-
// This will never be called as we can't produce Nexus-related commands anyway
|
|
533
|
-
throw new Error('Nexus is not yet implemented');
|
|
534
|
-
}
|
|
535
|
-
|
|
536
586
|
public resolveChildWorkflowExecutionStart(
|
|
537
587
|
activation: coresdk.workflow_activation.IResolveChildWorkflowExecutionStart
|
|
538
588
|
): void {
|
|
@@ -587,9 +637,21 @@ export class Activator implements ActivationHandler {
|
|
|
587
637
|
}
|
|
588
638
|
}
|
|
589
639
|
|
|
640
|
+
public resolveNexusOperationStart(_: coresdk.workflow_activation.IResolveNexusOperationStart): void {
|
|
641
|
+
throw new Error('TODO');
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
public resolveNexusOperation(_: coresdk.workflow_activation.IResolveNexusOperation): void {
|
|
645
|
+
throw new Error('TODO');
|
|
646
|
+
}
|
|
647
|
+
|
|
590
648
|
// Intentionally non-async function so this handler doesn't show up in the stack trace
|
|
591
649
|
protected queryWorkflowNextHandler({ queryName, args }: QueryInput): Promise<unknown> {
|
|
592
|
-
|
|
650
|
+
let fn = this.queryHandlers.get(queryName)?.handler;
|
|
651
|
+
if (fn === undefined && this.defaultQueryHandler !== undefined) {
|
|
652
|
+
fn = this.defaultQueryHandler.bind(undefined, queryName);
|
|
653
|
+
}
|
|
654
|
+
// No handler or default registered, fail.
|
|
593
655
|
if (fn === undefined) {
|
|
594
656
|
const knownQueryTypes = [...this.queryHandlers.keys()].join(' ');
|
|
595
657
|
// Fail the query
|
|
@@ -599,6 +661,7 @@ export class Activator implements ActivationHandler {
|
|
|
599
661
|
)
|
|
600
662
|
);
|
|
601
663
|
}
|
|
664
|
+
// Execute handler.
|
|
602
665
|
try {
|
|
603
666
|
const ret = fn(...args);
|
|
604
667
|
if (ret instanceof Promise) {
|
|
@@ -643,8 +706,20 @@ export class Activator implements ActivationHandler {
|
|
|
643
706
|
if (!protocolInstanceId) {
|
|
644
707
|
throw new TypeError('Missing activation update protocolInstanceId');
|
|
645
708
|
}
|
|
646
|
-
|
|
647
|
-
|
|
709
|
+
|
|
710
|
+
const entry =
|
|
711
|
+
this.updateHandlers.get(name) ??
|
|
712
|
+
(this.defaultUpdateHandler
|
|
713
|
+
? {
|
|
714
|
+
handler: this.defaultUpdateHandler.bind(undefined, name),
|
|
715
|
+
validator: undefined,
|
|
716
|
+
// Default to a warning policy.
|
|
717
|
+
unfinishedPolicy: HandlerUnfinishedPolicy.WARN_AND_ABANDON,
|
|
718
|
+
}
|
|
719
|
+
: null);
|
|
720
|
+
|
|
721
|
+
// If we don't have an entry from either source, buffer and return
|
|
722
|
+
if (entry === null) {
|
|
648
723
|
this.bufferedUpdates.push(activation);
|
|
649
724
|
return;
|
|
650
725
|
}
|
|
@@ -713,7 +788,7 @@ export class Activator implements ActivationHandler {
|
|
|
713
788
|
if (error instanceof TemporalFailure) {
|
|
714
789
|
this.rejectUpdate(protocolInstanceId, error);
|
|
715
790
|
} else {
|
|
716
|
-
|
|
791
|
+
this.handleWorkflowFailure(error);
|
|
717
792
|
}
|
|
718
793
|
})
|
|
719
794
|
.finally(() => this.inProgressUpdates.delete(updateId));
|
|
@@ -736,13 +811,21 @@ export class Activator implements ActivationHandler {
|
|
|
736
811
|
public dispatchBufferedUpdates(): void {
|
|
737
812
|
const bufferedUpdates = this.bufferedUpdates;
|
|
738
813
|
while (bufferedUpdates.length) {
|
|
739
|
-
|
|
740
|
-
if (
|
|
741
|
-
|
|
742
|
-
|
|
814
|
+
// We have a default update handler, so all updates are dispatchable.
|
|
815
|
+
if (this.defaultUpdateHandler) {
|
|
816
|
+
const update = bufferedUpdates.shift();
|
|
817
|
+
// Logically, this must be defined as we're in the loop.
|
|
818
|
+
// But Typescript doesn't know that so we use a non-null assertion (!).
|
|
819
|
+
this.doUpdate(update!);
|
|
820
|
+
} else {
|
|
821
|
+
const foundIndex = bufferedUpdates.findIndex((update) => this.updateHandlers.has(update.name as string));
|
|
822
|
+
if (foundIndex === -1) {
|
|
823
|
+
// No buffered Updates have a handler yet.
|
|
824
|
+
break;
|
|
825
|
+
}
|
|
826
|
+
const [update] = bufferedUpdates.splice(foundIndex, 1);
|
|
827
|
+
this.doUpdate(update);
|
|
743
828
|
}
|
|
744
|
-
const [update] = bufferedUpdates.splice(foundIndex, 1);
|
|
745
|
-
this.doUpdate(update);
|
|
746
829
|
}
|
|
747
830
|
}
|
|
748
831
|
|
|
@@ -840,6 +923,8 @@ export class Activator implements ActivationHandler {
|
|
|
840
923
|
}
|
|
841
924
|
|
|
842
925
|
public warnIfUnfinishedHandlers(): void {
|
|
926
|
+
if (this.workflowTaskError) return;
|
|
927
|
+
|
|
843
928
|
const getWarnable = (handlerExecutions: Iterable<MessageHandlerExecution>): MessageHandlerExecution[] => {
|
|
844
929
|
return Array.from(handlerExecutions).filter(
|
|
845
930
|
(ex) => ex.unfinishedPolicy === HandlerUnfinishedPolicy.WARN_AND_ABANDON
|
|
@@ -951,17 +1036,12 @@ export class Activator implements ActivationHandler {
|
|
|
951
1036
|
* Transforms failures into a command to be sent to the server.
|
|
952
1037
|
* Used to handle any failure emitted by the Workflow.
|
|
953
1038
|
*/
|
|
954
|
-
|
|
1039
|
+
handleWorkflowFailure(error: unknown): void {
|
|
955
1040
|
if (this.cancelled && isCancellation(error)) {
|
|
956
1041
|
this.pushCommand({ cancelWorkflowExecution: {} }, true);
|
|
957
1042
|
} else if (error instanceof ContinueAsNew) {
|
|
958
1043
|
this.pushCommand({ continueAsNewWorkflowExecution: error.command }, true);
|
|
959
|
-
} else {
|
|
960
|
-
if (!(error instanceof TemporalFailure)) {
|
|
961
|
-
// This results in an unhandled rejection which will fail the activation
|
|
962
|
-
// preventing it from completing.
|
|
963
|
-
throw error;
|
|
964
|
-
}
|
|
1044
|
+
} else if (error instanceof TemporalFailure) {
|
|
965
1045
|
// Fail the workflow. We do not want to issue unfinishedHandlers warnings. To achieve that, we
|
|
966
1046
|
// mark all handlers as completed now.
|
|
967
1047
|
this.inProgressSignals.clear();
|
|
@@ -974,9 +1054,29 @@ export class Activator implements ActivationHandler {
|
|
|
974
1054
|
},
|
|
975
1055
|
true
|
|
976
1056
|
);
|
|
1057
|
+
} else {
|
|
1058
|
+
this.recordWorkflowTaskError(error);
|
|
977
1059
|
}
|
|
978
1060
|
}
|
|
979
1061
|
|
|
1062
|
+
recordWorkflowTaskError(error: unknown): void {
|
|
1063
|
+
// Only keep the first error that bubbles up; subsequent errors will be ignored.
|
|
1064
|
+
if (this.workflowTaskError === undefined) this.workflowTaskError = error;
|
|
1065
|
+
|
|
1066
|
+
// Immediately rethrow the error if we know it is safe to do so (i.e. we are not running async
|
|
1067
|
+
// microtasks). Otherwise, the error will be rethrown whenever we get an opportunity to do so,
|
|
1068
|
+
// e.g. the next time `tryUnblockConditions()` is called.
|
|
1069
|
+
if (this.rethrowSynchronously) this.maybeRethrowWorkflowTaskError();
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/**
|
|
1073
|
+
* If a Workflow Task error was captured, and we are running in synchronous mode,
|
|
1074
|
+
* then bubble it up now. This is safe to call even if there is no error to rethrow.
|
|
1075
|
+
*/
|
|
1076
|
+
maybeRethrowWorkflowTaskError(): void {
|
|
1077
|
+
if (this.workflowTaskError) throw this.workflowTaskError;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
980
1080
|
private completeQuery(queryId: string, result: unknown): void {
|
|
981
1081
|
this.pushCommand({
|
|
982
1082
|
respondToQuery: { queryId, succeeded: { response: this.payloadConverter.toPayload(result) } },
|
package/src/logs.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { type Sink, type Sinks, proxySinks } from './sinks';
|
|
|
5
5
|
import { isCancellation } from './errors';
|
|
6
6
|
import { WorkflowInfo, ContinueAsNew } from './interfaces';
|
|
7
7
|
import { assertInWorkflowContext } from './global-attributes';
|
|
8
|
+
import { currentUpdateInfo, inWorkflowContext } from './workflow';
|
|
8
9
|
|
|
9
10
|
export interface WorkflowLogger extends Sink {
|
|
10
11
|
trace(message: string, attrs?: Record<string, unknown>): void;
|
|
@@ -117,11 +118,20 @@ export function executeWithLifecycleLogging(fn: () => Promise<unknown>): Promise
|
|
|
117
118
|
* Note that this function may be called from outside of the Workflow context (eg. by the worker itself).
|
|
118
119
|
*/
|
|
119
120
|
export function workflowLogAttributes(info: WorkflowInfo): Record<string, unknown> {
|
|
120
|
-
|
|
121
|
+
const attributes: { [key: string]: string } = {
|
|
121
122
|
namespace: info.namespace,
|
|
122
123
|
taskQueue: info.taskQueue,
|
|
123
124
|
workflowId: info.workflowId,
|
|
124
125
|
runId: info.runId,
|
|
125
126
|
workflowType: info.workflowType,
|
|
126
127
|
};
|
|
128
|
+
if (inWorkflowContext()) {
|
|
129
|
+
const updateInfo = currentUpdateInfo();
|
|
130
|
+
if (updateInfo) {
|
|
131
|
+
// Add update info if it exists
|
|
132
|
+
attributes['updateId'] = updateInfo.id;
|
|
133
|
+
attributes['updateName'] = updateInfo.name;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return attributes;
|
|
127
137
|
}
|