@proompteng/temporal-bun-sdk 0.3.0 → 0.4.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/README.md +46 -10
- package/dist/src/bin/replay-command.d.ts.map +1 -1
- package/dist/src/bin/replay-command.js +6 -2
- package/dist/src/bin/replay-command.js.map +1 -1
- package/dist/src/bin/temporal-bun.d.ts +1 -1
- package/dist/src/bin/temporal-bun.d.ts.map +1 -1
- package/dist/src/bin/temporal-bun.js +74 -0
- package/dist/src/bin/temporal-bun.js.map +1 -1
- package/dist/src/client/layer.d.ts +2 -2
- package/dist/src/client/layer.d.ts.map +1 -1
- package/dist/src/client/retries.d.ts.map +1 -1
- package/dist/src/client/retries.js +27 -3
- package/dist/src/client/retries.js.map +1 -1
- package/dist/src/client/serialization.d.ts +4 -1
- package/dist/src/client/serialization.d.ts.map +1 -1
- package/dist/src/client/serialization.js +2 -2
- package/dist/src/client/serialization.js.map +1 -1
- package/dist/src/client.d.ts +12 -4
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/client.js +270 -63
- package/dist/src/client.js.map +1 -1
- package/dist/src/common/payloads/codecs.d.ts +38 -0
- package/dist/src/common/payloads/codecs.d.ts.map +1 -0
- package/dist/src/common/payloads/codecs.js +174 -0
- package/dist/src/common/payloads/codecs.js.map +1 -0
- package/dist/src/common/payloads/converter.d.ts +52 -3
- package/dist/src/common/payloads/converter.d.ts.map +1 -1
- package/dist/src/common/payloads/converter.js +340 -2
- package/dist/src/common/payloads/converter.js.map +1 -1
- package/dist/src/common/payloads/failure.d.ts +5 -6
- package/dist/src/common/payloads/failure.d.ts.map +1 -1
- package/dist/src/common/payloads/failure.js +3 -52
- package/dist/src/common/payloads/failure.js.map +1 -1
- package/dist/src/common/payloads/index.d.ts +1 -0
- package/dist/src/common/payloads/index.d.ts.map +1 -1
- package/dist/src/common/payloads/index.js +1 -0
- package/dist/src/common/payloads/index.js.map +1 -1
- package/dist/src/config.d.ts +9 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +62 -1
- package/dist/src/config.js.map +1 -1
- package/dist/src/interceptors/client.d.ts +28 -0
- package/dist/src/interceptors/client.d.ts.map +1 -0
- package/dist/src/interceptors/client.js +169 -0
- package/dist/src/interceptors/client.js.map +1 -0
- package/dist/src/interceptors/types.d.ts +33 -0
- package/dist/src/interceptors/types.d.ts.map +1 -0
- package/dist/src/interceptors/types.js +44 -0
- package/dist/src/interceptors/types.js.map +1 -0
- package/dist/src/interceptors/worker.d.ts +22 -0
- package/dist/src/interceptors/worker.d.ts.map +1 -0
- package/dist/src/interceptors/worker.js +156 -0
- package/dist/src/interceptors/worker.js.map +1 -0
- package/dist/src/runtime/cli-layer.d.ts +4 -3
- package/dist/src/runtime/cli-layer.d.ts.map +1 -1
- package/dist/src/runtime/cli-layer.js +5 -2
- package/dist/src/runtime/cli-layer.js.map +1 -1
- package/dist/src/runtime/effect-layers.d.ts +9 -0
- package/dist/src/runtime/effect-layers.d.ts.map +1 -1
- package/dist/src/runtime/effect-layers.js +15 -0
- package/dist/src/runtime/effect-layers.js.map +1 -1
- package/dist/src/worker/runtime.d.ts +5 -0
- package/dist/src/worker/runtime.d.ts.map +1 -1
- package/dist/src/worker/runtime.js +207 -12
- package/dist/src/worker/runtime.js.map +1 -1
- package/dist/src/worker/sticky-cache.d.ts +5 -0
- package/dist/src/worker/sticky-cache.d.ts.map +1 -1
- package/dist/src/worker/sticky-cache.js +26 -0
- package/dist/src/worker/sticky-cache.js.map +1 -1
- package/dist/src/worker/update-protocol.d.ts.map +1 -1
- package/dist/src/worker/update-protocol.js +15 -0
- package/dist/src/worker/update-protocol.js.map +1 -1
- package/dist/src/worker.js +1 -0
- package/dist/src/worker.js.map +1 -1
- package/dist/src/workflow/commands.d.ts +38 -2
- package/dist/src/workflow/commands.d.ts.map +1 -1
- package/dist/src/workflow/commands.js +153 -1
- package/dist/src/workflow/commands.js.map +1 -1
- package/dist/src/workflow/context.d.ts +59 -2
- package/dist/src/workflow/context.d.ts.map +1 -1
- package/dist/src/workflow/context.js +317 -7
- package/dist/src/workflow/context.js.map +1 -1
- package/dist/src/workflow/determinism.d.ts +5 -0
- package/dist/src/workflow/determinism.d.ts.map +1 -1
- package/dist/src/workflow/determinism.js +45 -3
- package/dist/src/workflow/determinism.js.map +1 -1
- package/dist/src/workflow/errors.d.ts +3 -0
- package/dist/src/workflow/errors.d.ts.map +1 -1
- package/dist/src/workflow/errors.js +6 -0
- package/dist/src/workflow/errors.js.map +1 -1
- package/dist/src/workflow/executor.d.ts +3 -0
- package/dist/src/workflow/executor.d.ts.map +1 -1
- package/dist/src/workflow/executor.js +70 -10
- package/dist/src/workflow/executor.js.map +1 -1
- package/dist/src/workflow/replay.d.ts.map +1 -1
- package/dist/src/workflow/replay.js +220 -1
- package/dist/src/workflow/replay.js.map +1 -1
- package/package.json +1 -1
|
@@ -6,10 +6,11 @@ import { Cause, Effect, Exit, Fiber } from 'effect';
|
|
|
6
6
|
import { makeActivityLifecycle, } from '../activities/lifecycle';
|
|
7
7
|
import { buildTransportOptions, normalizeTemporalAddress } from '../client';
|
|
8
8
|
import { durationFromMillis, durationToMillis } from '../common/duration';
|
|
9
|
-
import { createDefaultDataConverter, decodePayloadsToValues, encodeValuesToPayloads, } from '../common/payloads/converter';
|
|
9
|
+
import { buildCodecsFromConfig, createDefaultDataConverter, decodePayloadsToValues, encodeValuesToPayloads, } from '../common/payloads/converter';
|
|
10
10
|
import { encodeErrorToFailure, encodeFailurePayloads, failureToError } from '../common/payloads/failure';
|
|
11
11
|
import { sleep } from '../common/sleep';
|
|
12
12
|
import { loadTemporalConfig } from '../config';
|
|
13
|
+
import { makeDefaultWorkerInterceptors, runWorkerInterceptors, } from '../interceptors/worker';
|
|
13
14
|
import { createObservabilityServices } from '../observability';
|
|
14
15
|
import { CommandSchema, RecordMarkerCommandAttributesSchema, } from '../proto/temporal/api/command/v1/message_pb';
|
|
15
16
|
import { PayloadsSchema, WorkflowExecutionSchema, } from '../proto/temporal/api/common/v1/message_pb';
|
|
@@ -66,7 +67,6 @@ const COMPLETION_COMMAND_TYPES = new Set([
|
|
|
66
67
|
export class WorkerRuntime {
|
|
67
68
|
static async create(options = {}) {
|
|
68
69
|
const config = options.config ?? (await loadTemporalConfig());
|
|
69
|
-
const dataConverter = options.dataConverter ?? createDefaultDataConverter();
|
|
70
70
|
const namespace = options.namespace ?? config.namespace;
|
|
71
71
|
if (!namespace) {
|
|
72
72
|
throw new Error('Temporal namespace must be provided');
|
|
@@ -80,12 +80,6 @@ export class WorkerRuntime {
|
|
|
80
80
|
if (workflows.length === 0) {
|
|
81
81
|
throw new Error('No workflow definitions were registered; provide workflows or workflowsPath');
|
|
82
82
|
}
|
|
83
|
-
const registry = new WorkflowRegistry();
|
|
84
|
-
registry.registerMany(workflows);
|
|
85
|
-
const executor = new WorkflowExecutor({
|
|
86
|
-
registry,
|
|
87
|
-
dataConverter,
|
|
88
|
-
});
|
|
89
83
|
const activities = options.activities ?? {};
|
|
90
84
|
const observability = await Effect.runPromise(createObservabilityServices({
|
|
91
85
|
logLevel: config.logLevel,
|
|
@@ -97,6 +91,18 @@ export class WorkerRuntime {
|
|
|
97
91
|
metricsExporter: options.metricsExporter,
|
|
98
92
|
}));
|
|
99
93
|
const { logger, metricsRegistry, metricsExporter } = observability;
|
|
94
|
+
const dataConverter = options.dataConverter ??
|
|
95
|
+
createDefaultDataConverter({
|
|
96
|
+
payloadCodecs: buildCodecsFromConfig(config.payloadCodecs),
|
|
97
|
+
logger,
|
|
98
|
+
metricsRegistry,
|
|
99
|
+
});
|
|
100
|
+
const registry = new WorkflowRegistry();
|
|
101
|
+
registry.registerMany(workflows);
|
|
102
|
+
const executor = new WorkflowExecutor({
|
|
103
|
+
registry,
|
|
104
|
+
dataConverter,
|
|
105
|
+
});
|
|
100
106
|
const runtimeMetrics = await WorkerRuntime.#initMetrics(metricsRegistry);
|
|
101
107
|
let workflowService;
|
|
102
108
|
if (options.workflowService) {
|
|
@@ -181,6 +187,22 @@ export class WorkerRuntime {
|
|
|
181
187
|
}));
|
|
182
188
|
}
|
|
183
189
|
}
|
|
190
|
+
const tracingEnabled = options.tracingEnabled ?? config.tracingInterceptorsEnabled ?? false;
|
|
191
|
+
const workerInterceptorBuilder = options.interceptorBuilder ?? {
|
|
192
|
+
build: (input) => makeDefaultWorkerInterceptors(input),
|
|
193
|
+
};
|
|
194
|
+
const defaultWorkerInterceptors = await Effect.runPromise(workerInterceptorBuilder.build({
|
|
195
|
+
namespace,
|
|
196
|
+
taskQueue,
|
|
197
|
+
identity,
|
|
198
|
+
buildId,
|
|
199
|
+
logger,
|
|
200
|
+
metricsRegistry,
|
|
201
|
+
metricsExporter,
|
|
202
|
+
dataConverter,
|
|
203
|
+
tracingEnabled,
|
|
204
|
+
}));
|
|
205
|
+
const workerInterceptors = [...defaultWorkerInterceptors, ...(options.interceptors ?? [])];
|
|
184
206
|
const activityLifecycle = await Effect.runPromise(makeActivityLifecycle({
|
|
185
207
|
heartbeatIntervalMs: config.activityHeartbeatIntervalMs,
|
|
186
208
|
heartbeatRpcTimeoutMs: config.activityHeartbeatRpcTimeoutMs,
|
|
@@ -233,6 +255,7 @@ export class WorkerRuntime {
|
|
|
233
255
|
versioningBehavior,
|
|
234
256
|
stickySchedulingEnabled,
|
|
235
257
|
workflowPollerCount,
|
|
258
|
+
interceptors: workerInterceptors,
|
|
236
259
|
});
|
|
237
260
|
}
|
|
238
261
|
#config;
|
|
@@ -248,6 +271,7 @@ export class WorkerRuntime {
|
|
|
248
271
|
#namespace;
|
|
249
272
|
#taskQueue;
|
|
250
273
|
#identity;
|
|
274
|
+
#interceptors;
|
|
251
275
|
#activityLifecycle;
|
|
252
276
|
#scheduler;
|
|
253
277
|
#stickyCache;
|
|
@@ -275,6 +299,7 @@ export class WorkerRuntime {
|
|
|
275
299
|
this.#namespace = params.namespace;
|
|
276
300
|
this.#taskQueue = params.taskQueue;
|
|
277
301
|
this.#identity = params.identity;
|
|
302
|
+
this.#interceptors = params.interceptors;
|
|
278
303
|
this.#activityLifecycle = params.activityLifecycle;
|
|
279
304
|
this.#scheduler = params.scheduler;
|
|
280
305
|
this.#stickyCache = params.stickyCache;
|
|
@@ -353,6 +378,10 @@ export class WorkerRuntime {
|
|
|
353
378
|
workflowTaskCompleted: await makeCounter('temporal_worker_workflow_tasks_completed_total', 'Workflow tasks completed by the scheduler'),
|
|
354
379
|
activityTaskStarted: await makeCounter('temporal_worker_activity_tasks_started_total', 'Activity tasks dispatched to the scheduler'),
|
|
355
380
|
activityTaskCompleted: await makeCounter('temporal_worker_activity_tasks_completed_total', 'Activity tasks completed by the scheduler'),
|
|
381
|
+
queryTaskStarted: await makeCounter('temporal_worker_query_started_total', 'Query-only workflow tasks dispatched to the scheduler'),
|
|
382
|
+
queryTaskCompleted: await makeCounter('temporal_worker_query_completed_total', 'Query-only workflow tasks completed successfully'),
|
|
383
|
+
queryTaskFailed: await makeCounter('temporal_worker_query_failed_total', 'Query-only workflow tasks responded with failure'),
|
|
384
|
+
queryTaskLatency: await makeHistogram('temporal_worker_query_latency_ms', 'End-to-end workflow query latency (ms)'),
|
|
356
385
|
};
|
|
357
386
|
}
|
|
358
387
|
async run() {
|
|
@@ -514,17 +543,38 @@ export class WorkerRuntime {
|
|
|
514
543
|
#isRpcAbortError(error) {
|
|
515
544
|
return isAbortError(error) || (error instanceof ConnectError && error.code === Code.Canceled);
|
|
516
545
|
}
|
|
546
|
+
#isBenignPollTimeout(error) {
|
|
547
|
+
if (error instanceof ConnectError) {
|
|
548
|
+
return error.code === Code.DeadlineExceeded || error.code === Code.Canceled;
|
|
549
|
+
}
|
|
550
|
+
if (error instanceof Error) {
|
|
551
|
+
const msg = error.message.toLowerCase();
|
|
552
|
+
return msg.includes('deadline') && msg.includes('exceeded');
|
|
553
|
+
}
|
|
554
|
+
if (typeof error === 'string') {
|
|
555
|
+
const msg = error.toLowerCase();
|
|
556
|
+
return msg.includes('deadline') && msg.includes('exceeded');
|
|
557
|
+
}
|
|
558
|
+
return false;
|
|
559
|
+
}
|
|
517
560
|
async #enqueueActivityTask(response) {
|
|
518
561
|
const taskToken = response.taskToken ?? new Uint8Array();
|
|
519
562
|
const envelope = {
|
|
520
563
|
taskToken,
|
|
521
|
-
handler: () => this.#
|
|
564
|
+
handler: () => this.#runActivityTask(response),
|
|
522
565
|
args: [],
|
|
523
566
|
};
|
|
524
567
|
await Effect.runPromise(this.#scheduler.enqueueActivity(envelope));
|
|
525
568
|
}
|
|
526
569
|
#handleWorkflowPollerError(queueName, error) {
|
|
527
570
|
return Effect.promise(async () => {
|
|
571
|
+
if (this.#isBenignPollTimeout(error)) {
|
|
572
|
+
this.#log('debug', 'workflow poll timeout (no tasks)', {
|
|
573
|
+
queueName,
|
|
574
|
+
namespace: this.#namespace,
|
|
575
|
+
});
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
528
578
|
this.#incrementCounter(this.#metrics.workflowPollErrors);
|
|
529
579
|
this.#log('warn', 'workflow polling failed', {
|
|
530
580
|
queueName,
|
|
@@ -536,6 +586,13 @@ export class WorkerRuntime {
|
|
|
536
586
|
}
|
|
537
587
|
#handleActivityPollerError(error) {
|
|
538
588
|
return Effect.promise(async () => {
|
|
589
|
+
if (this.#isBenignPollTimeout(error)) {
|
|
590
|
+
this.#log('debug', 'activity poll timeout (no tasks)', {
|
|
591
|
+
namespace: this.#namespace,
|
|
592
|
+
taskQueue: this.#taskQueue,
|
|
593
|
+
});
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
539
596
|
this.#incrementCounter(this.#metrics.activityPollErrors);
|
|
540
597
|
this.#log('warn', 'activity polling failed', {
|
|
541
598
|
namespace: this.#namespace,
|
|
@@ -547,7 +604,40 @@ export class WorkerRuntime {
|
|
|
547
604
|
}
|
|
548
605
|
async #handleWorkflowTask(response, nondeterminismRetry = 0) {
|
|
549
606
|
const execution = this.#resolveWorkflowExecution(response);
|
|
607
|
+
const queryCount = response.queries && typeof response.queries === 'object' && !Array.isArray(response.queries)
|
|
608
|
+
? Object.keys(response.queries).length
|
|
609
|
+
: Array.isArray(response.queries)
|
|
610
|
+
? response.queries.length
|
|
611
|
+
: 0;
|
|
612
|
+
const hasQueryRequests = Boolean(response.query) || queryCount > 0;
|
|
613
|
+
const hasUpdateMessages = (response.messages?.length ?? 0) > 0;
|
|
614
|
+
const kind = hasUpdateMessages
|
|
615
|
+
? 'worker.updateTask'
|
|
616
|
+
: hasQueryRequests
|
|
617
|
+
? 'worker.queryTask'
|
|
618
|
+
: 'worker.workflowTask';
|
|
619
|
+
const context = {
|
|
620
|
+
kind,
|
|
621
|
+
namespace: this.#namespace,
|
|
622
|
+
taskQueue: this.#taskQueue,
|
|
623
|
+
identity: this.#identity,
|
|
624
|
+
buildId: this.#deploymentOptions.buildId,
|
|
625
|
+
workflowId: execution.workflowId,
|
|
626
|
+
runId: execution.runId,
|
|
627
|
+
attempt: Number(response.attempt ?? 1),
|
|
628
|
+
metadata: { nondeterminismRetry },
|
|
629
|
+
};
|
|
630
|
+
const effect = runWorkerInterceptors(this.#interceptors, context, () => Effect.tryPromise(() => this.#processWorkflowTask(response, nondeterminismRetry, execution)));
|
|
631
|
+
await Effect.runPromise(effect);
|
|
632
|
+
}
|
|
633
|
+
async #processWorkflowTask(response, nondeterminismRetry = 0, executionOverride) {
|
|
634
|
+
const execution = executionOverride ?? this.#resolveWorkflowExecution(response);
|
|
550
635
|
const workflowTaskAttempt = Number(response.attempt ?? 1);
|
|
636
|
+
const isLegacyQueryTask = Boolean(response.query);
|
|
637
|
+
const queryStartTime = isLegacyQueryTask ? Date.now() : null;
|
|
638
|
+
if (isLegacyQueryTask) {
|
|
639
|
+
this.#incrementCounter(this.#metrics.queryTaskStarted);
|
|
640
|
+
}
|
|
551
641
|
const historyEvents = await this.#collectWorkflowHistory(execution, response);
|
|
552
642
|
const workflowType = this.#resolveWorkflowType(response, historyEvents);
|
|
553
643
|
const args = await this.#decodeWorkflowArgs(historyEvents);
|
|
@@ -637,7 +727,8 @@ export class WorkerRuntime {
|
|
|
637
727
|
}
|
|
638
728
|
const expectedDeterminismState = previousState;
|
|
639
729
|
try {
|
|
640
|
-
const activityResults = await this.#extractActivityResolutions(historyEvents);
|
|
730
|
+
const { results: activityResults, scheduledEventIds: activityScheduleEventIds } = await this.#extractActivityResolutions(historyEvents);
|
|
731
|
+
const timerResults = await this.#extractTimerResolutions(historyEvents);
|
|
641
732
|
const replayUpdates = historyReplay?.updates ?? [];
|
|
642
733
|
const mergedUpdates = mergeUpdateInvocations(replayUpdates, collectedUpdates.invocations);
|
|
643
734
|
const output = await this.#executor.execute({
|
|
@@ -649,9 +740,12 @@ export class WorkerRuntime {
|
|
|
649
740
|
arguments: args,
|
|
650
741
|
determinismState: previousState,
|
|
651
742
|
activityResults,
|
|
743
|
+
activityScheduleEventIds,
|
|
652
744
|
signalDeliveries,
|
|
745
|
+
timerResults,
|
|
653
746
|
queryRequests,
|
|
654
747
|
updates: mergedUpdates,
|
|
748
|
+
mode: isLegacyQueryTask ? 'query' : 'workflow',
|
|
655
749
|
});
|
|
656
750
|
this.#log('debug', 'workflow query evaluation summary', {
|
|
657
751
|
...baseLogFields,
|
|
@@ -681,10 +775,28 @@ export class WorkerRuntime {
|
|
|
681
775
|
legacyQueryResult = entry;
|
|
682
776
|
}
|
|
683
777
|
}
|
|
778
|
+
if (isLegacyQueryTask) {
|
|
779
|
+
const target = legacyQueryResult ?? output.queryResults.find((entry) => entry.request.source === 'legacy');
|
|
780
|
+
if (!target) {
|
|
781
|
+
throw new Error('Legacy query result missing from workflow execution');
|
|
782
|
+
}
|
|
783
|
+
await this.#respondLegacyQueryTask(response, target);
|
|
784
|
+
if (queryStartTime !== null) {
|
|
785
|
+
this.#observeHistogram(this.#metrics.queryTaskLatency, Date.now() - queryStartTime);
|
|
786
|
+
}
|
|
787
|
+
this.#incrementCounter(this.#metrics.queryTaskCompleted);
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
684
790
|
const cacheBaselineEventId = this.#resolveCurrentStartedEventId(response) ?? historyReplay?.lastEventId ?? null;
|
|
685
791
|
const shouldRecordMarker = output.completion === 'pending';
|
|
686
792
|
let commandsForResponse = output.commands;
|
|
687
|
-
const dispatchesForNewMessages = (output.updateDispatches ?? []).filter((dispatch) =>
|
|
793
|
+
const dispatchesForNewMessages = (output.updateDispatches ?? []).filter((dispatch) => {
|
|
794
|
+
if (dispatch.type === 'acceptance' || dispatch.type === 'rejection') {
|
|
795
|
+
return collectedUpdates.requestsByUpdateId.has(dispatch.updateId);
|
|
796
|
+
}
|
|
797
|
+
// Allow completion messages to be emitted even if the request metadata was seen on a prior task.
|
|
798
|
+
return true;
|
|
799
|
+
});
|
|
688
800
|
const updateProtocolMessages = await buildUpdateProtocolMessages({
|
|
689
801
|
dispatches: dispatchesForNewMessages,
|
|
690
802
|
collected: collectedUpdates,
|
|
@@ -702,6 +814,7 @@ export class WorkerRuntime {
|
|
|
702
814
|
}
|
|
703
815
|
else {
|
|
704
816
|
await this.#removeStickyEntry(stickyKey);
|
|
817
|
+
await this.#removeStickyEntriesForWorkflow(stickyKey.workflowId);
|
|
705
818
|
this.#log('debug', 'sticky cache entry cleared (workflow completed)', baseLogFields);
|
|
706
819
|
}
|
|
707
820
|
}
|
|
@@ -754,12 +867,29 @@ export class WorkerRuntime {
|
|
|
754
867
|
let stickyEntryCleared = false;
|
|
755
868
|
if (stickyKey) {
|
|
756
869
|
await this.#removeStickyEntry(stickyKey);
|
|
870
|
+
await this.#removeStickyEntriesForWorkflow(stickyKey.workflowId);
|
|
757
871
|
stickyEntryCleared = true;
|
|
758
872
|
}
|
|
759
873
|
if (this.#isTaskNotFoundError(error)) {
|
|
760
874
|
this.#logWorkflowTaskNotFound('respondWorkflowTaskCompleted', execution);
|
|
761
875
|
return;
|
|
762
876
|
}
|
|
877
|
+
if (isLegacyQueryTask) {
|
|
878
|
+
this.#incrementCounter(this.#metrics.queryTaskFailed);
|
|
879
|
+
if (queryStartTime !== null) {
|
|
880
|
+
this.#observeHistogram(this.#metrics.queryTaskLatency, Date.now() - queryStartTime);
|
|
881
|
+
}
|
|
882
|
+
if (error instanceof WorkflowNondeterminismError) {
|
|
883
|
+
const mismatches = await this.#computeNondeterminismMismatches(error, expectedDeterminismState);
|
|
884
|
+
this.#incrementCounter(this.#metrics.nondeterminism);
|
|
885
|
+
this.#log('error', 'workflow query nondeterminism detected', {
|
|
886
|
+
...baseLogFields,
|
|
887
|
+
mismatches,
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
await this.#respondLegacyQueryFailure(response, error);
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
763
893
|
if (error instanceof WorkflowNondeterminismError) {
|
|
764
894
|
const mismatches = await this.#computeNondeterminismMismatches(error, expectedDeterminismState);
|
|
765
895
|
if (stickyKey && stickyEntryCleared) {
|
|
@@ -838,6 +968,7 @@ export class WorkerRuntime {
|
|
|
838
968
|
async #extractActivityResolutions(events) {
|
|
839
969
|
const resolutions = new Map();
|
|
840
970
|
const scheduledActivityIds = new Map();
|
|
971
|
+
const activityScheduleById = new Map();
|
|
841
972
|
const normalizeEventId = (value) => {
|
|
842
973
|
if (value === undefined || value === null) {
|
|
843
974
|
return undefined;
|
|
@@ -868,6 +999,7 @@ export class WorkerRuntime {
|
|
|
868
999
|
const scheduledKey = normalizeEventId(event.eventId);
|
|
869
1000
|
if (activityId && scheduledKey) {
|
|
870
1001
|
scheduledActivityIds.set(scheduledKey, activityId);
|
|
1002
|
+
activityScheduleById.set(activityId, scheduledKey);
|
|
871
1003
|
}
|
|
872
1004
|
break;
|
|
873
1005
|
}
|
|
@@ -932,7 +1064,7 @@ export class WorkerRuntime {
|
|
|
932
1064
|
break;
|
|
933
1065
|
}
|
|
934
1066
|
}
|
|
935
|
-
return resolutions;
|
|
1067
|
+
return { results: resolutions, scheduledEventIds: activityScheduleById };
|
|
936
1068
|
}
|
|
937
1069
|
async #extractSignalDeliveries(events) {
|
|
938
1070
|
const deliveries = [];
|
|
@@ -970,6 +1102,22 @@ export class WorkerRuntime {
|
|
|
970
1102
|
}
|
|
971
1103
|
return deliveries;
|
|
972
1104
|
}
|
|
1105
|
+
async #extractTimerResolutions(events) {
|
|
1106
|
+
const fired = new Set();
|
|
1107
|
+
for (const event of events) {
|
|
1108
|
+
if (event.eventType !== EventType.TIMER_FIRED) {
|
|
1109
|
+
continue;
|
|
1110
|
+
}
|
|
1111
|
+
if (event.attributes?.case !== 'timerFiredEventAttributes') {
|
|
1112
|
+
continue;
|
|
1113
|
+
}
|
|
1114
|
+
const attrs = event.attributes.value;
|
|
1115
|
+
if (attrs.timerId) {
|
|
1116
|
+
fired.add(attrs.timerId);
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
return fired;
|
|
1120
|
+
}
|
|
973
1121
|
async #extractWorkflowQueryRequests(response) {
|
|
974
1122
|
const requests = [];
|
|
975
1123
|
const map = response.queries ?? {};
|
|
@@ -1035,6 +1183,34 @@ export class WorkerRuntime {
|
|
|
1035
1183
|
});
|
|
1036
1184
|
await this.#workflowService.respondQueryTaskCompleted(request, { timeoutMs: RESPOND_TIMEOUT_MS });
|
|
1037
1185
|
}
|
|
1186
|
+
async #respondLegacyQueryFailure(response, cause) {
|
|
1187
|
+
const failure = await encodeErrorToFailure(this.#dataConverter, cause);
|
|
1188
|
+
const message = cause instanceof Error ? cause.message : 'Workflow query failed';
|
|
1189
|
+
const request = create(RespondQueryTaskCompletedRequestSchema, {
|
|
1190
|
+
taskToken: response.taskToken ?? new Uint8Array(),
|
|
1191
|
+
completedType: QueryResultType.FAILED,
|
|
1192
|
+
errorMessage: message,
|
|
1193
|
+
namespace: this.#namespace,
|
|
1194
|
+
failure,
|
|
1195
|
+
cause: WorkflowTaskFailedCause.UNSPECIFIED,
|
|
1196
|
+
});
|
|
1197
|
+
try {
|
|
1198
|
+
await this.#workflowService.respondQueryTaskCompleted(request, { timeoutMs: RESPOND_TIMEOUT_MS });
|
|
1199
|
+
}
|
|
1200
|
+
catch (rpcError) {
|
|
1201
|
+
this.#log('error', 'respondQueryTaskCompleted failed for legacy query', {
|
|
1202
|
+
namespace: this.#namespace,
|
|
1203
|
+
workflowId: response.workflowExecution?.workflowId,
|
|
1204
|
+
runId: response.workflowExecution?.runId,
|
|
1205
|
+
error: rpcError instanceof Error ? rpcError.message : String(rpcError),
|
|
1206
|
+
});
|
|
1207
|
+
if (this.#isTaskNotFoundError(rpcError)) {
|
|
1208
|
+
this.#logWorkflowTaskNotFound('respondQueryTaskCompleted', this.#resolveWorkflowExecution(response));
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
throw rpcError;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1038
1214
|
async #fetchWorkflowHistoryPage(execution, nextPageToken) {
|
|
1039
1215
|
if (!execution.workflowId || !execution.runId) {
|
|
1040
1216
|
return { events: [], nextPageToken: new Uint8Array() };
|
|
@@ -1316,6 +1492,11 @@ export class WorkerRuntime {
|
|
|
1316
1492
|
async #removeStickyEntry(key) {
|
|
1317
1493
|
await Effect.runPromise(this.#stickyCache.remove(key));
|
|
1318
1494
|
}
|
|
1495
|
+
async #removeStickyEntriesForWorkflow(workflowId) {
|
|
1496
|
+
if (!workflowId)
|
|
1497
|
+
return;
|
|
1498
|
+
await Effect.runPromise(this.#stickyCache.removeByWorkflow({ namespace: this.#namespace, workflowId }));
|
|
1499
|
+
}
|
|
1319
1500
|
async #failWorkflowTask(response, execution, error, cause = WorkflowTaskFailedCause.UNSPECIFIED) {
|
|
1320
1501
|
const failure = await encodeErrorToFailure(this.#dataConverter, error);
|
|
1321
1502
|
const encoded = await encodeFailurePayloads(this.#dataConverter, failure);
|
|
@@ -1339,6 +1520,20 @@ export class WorkerRuntime {
|
|
|
1339
1520
|
throw rpcError;
|
|
1340
1521
|
}
|
|
1341
1522
|
}
|
|
1523
|
+
async #runActivityTask(response) {
|
|
1524
|
+
const context = {
|
|
1525
|
+
kind: 'worker.activityTask',
|
|
1526
|
+
namespace: this.#namespace,
|
|
1527
|
+
taskQueue: this.#taskQueue,
|
|
1528
|
+
identity: this.#identity,
|
|
1529
|
+
buildId: this.#deploymentOptions.buildId,
|
|
1530
|
+
workflowId: response.workflowExecution?.workflowId ?? undefined,
|
|
1531
|
+
runId: response.workflowExecution?.runId ?? undefined,
|
|
1532
|
+
attempt: Number(response.attempt ?? 1),
|
|
1533
|
+
};
|
|
1534
|
+
const effect = runWorkerInterceptors(this.#interceptors, context, () => Effect.tryPromise(() => this.#processActivityTask(response)));
|
|
1535
|
+
await Effect.runPromise(effect);
|
|
1536
|
+
}
|
|
1342
1537
|
async #processActivityTask(response) {
|
|
1343
1538
|
const cancelRequested = isActivityCancelRequested(response);
|
|
1344
1539
|
if (cancelRequested) {
|