@proompteng/temporal-bun-sdk 0.2.0 → 0.3.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 +95 -0
- package/dist/src/client/serialization.d.ts +30 -1
- package/dist/src/client/serialization.d.ts.map +1 -1
- package/dist/src/client/serialization.js +76 -3
- package/dist/src/client/serialization.js.map +1 -1
- package/dist/src/client/types.d.ts +26 -0
- package/dist/src/client/types.d.ts.map +1 -1
- package/dist/src/client.d.ts +10 -2
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/client.js +243 -1
- package/dist/src/client.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/worker/concurrency.d.ts +8 -0
- package/dist/src/worker/concurrency.d.ts.map +1 -1
- package/dist/src/worker/concurrency.js +5 -9
- package/dist/src/worker/concurrency.js.map +1 -1
- package/dist/src/worker/runtime.d.ts.map +1 -1
- package/dist/src/worker/runtime.js +303 -29
- package/dist/src/worker/runtime.js.map +1 -1
- package/dist/src/worker/update-protocol.d.ts +33 -0
- package/dist/src/worker/update-protocol.d.ts.map +1 -0
- package/dist/src/worker/update-protocol.js +228 -0
- package/dist/src/worker/update-protocol.js.map +1 -0
- package/dist/src/workflow/context.d.ts +52 -1
- package/dist/src/workflow/context.d.ts.map +1 -1
- package/dist/src/workflow/context.js +212 -2
- package/dist/src/workflow/context.js.map +1 -1
- package/dist/src/workflow/definition.d.ts +29 -3
- package/dist/src/workflow/definition.d.ts.map +1 -1
- package/dist/src/workflow/definition.js +30 -4
- package/dist/src/workflow/definition.js.map +1 -1
- package/dist/src/workflow/determinism.d.ts +59 -0
- package/dist/src/workflow/determinism.d.ts.map +1 -1
- package/dist/src/workflow/determinism.js +76 -1
- 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 +53 -0
- package/dist/src/workflow/executor.d.ts.map +1 -1
- package/dist/src/workflow/executor.js +237 -6
- package/dist/src/workflow/executor.js.map +1 -1
- package/dist/src/workflow/inbound.d.ts +84 -0
- package/dist/src/workflow/inbound.d.ts.map +1 -0
- package/dist/src/workflow/inbound.js +65 -0
- package/dist/src/workflow/inbound.js.map +1 -0
- package/dist/src/workflow/index.d.ts +1 -0
- package/dist/src/workflow/index.d.ts.map +1 -1
- package/dist/src/workflow/index.js +1 -0
- package/dist/src/workflow/index.js.map +1 -1
- package/dist/src/workflow/replay.d.ts +24 -2
- package/dist/src/workflow/replay.d.ts.map +1 -1
- package/dist/src/workflow/replay.js +459 -14
- package/dist/src/workflow/replay.js.map +1 -1
- package/dist/src/workflows/index.d.ts +1 -1
- package/dist/src/workflows/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -18,9 +18,10 @@ import { CommandType } from '../proto/temporal/api/enums/v1/command_type_pb';
|
|
|
18
18
|
import { WorkerVersioningMode } from '../proto/temporal/api/enums/v1/deployment_pb';
|
|
19
19
|
import { EventType } from '../proto/temporal/api/enums/v1/event_type_pb';
|
|
20
20
|
import { WorkflowTaskFailedCause } from '../proto/temporal/api/enums/v1/failed_cause_pb';
|
|
21
|
+
import { QueryResultType } from '../proto/temporal/api/enums/v1/query_pb';
|
|
21
22
|
import { HistoryEventFilterType, TimeoutType, VersioningBehavior } from '../proto/temporal/api/enums/v1/workflow_pb';
|
|
22
23
|
import { StickyExecutionAttributesSchema, TaskQueueSchema, } from '../proto/temporal/api/taskqueue/v1/message_pb';
|
|
23
|
-
import { GetWorkflowExecutionHistoryRequestSchema, PollActivityTaskQueueRequestSchema, PollWorkflowTaskQueueRequestSchema, RespondActivityTaskCanceledRequestSchema, RespondActivityTaskCompletedRequestSchema, RespondActivityTaskFailedRequestSchema, RespondWorkflowTaskCompletedRequestSchema, RespondWorkflowTaskFailedRequestSchema, } from '../proto/temporal/api/workflowservice/v1/request_response_pb';
|
|
24
|
+
import { GetWorkflowExecutionHistoryRequestSchema, PollActivityTaskQueueRequestSchema, PollWorkflowTaskQueueRequestSchema, RespondActivityTaskCanceledRequestSchema, RespondActivityTaskCompletedRequestSchema, RespondActivityTaskFailedRequestSchema, RespondQueryTaskCompletedRequestSchema, RespondWorkflowTaskCompletedRequestSchema, RespondWorkflowTaskFailedRequestSchema, } from '../proto/temporal/api/workflowservice/v1/request_response_pb';
|
|
24
25
|
import { WorkflowService } from '../proto/temporal/api/workflowservice/v1/service_pb';
|
|
25
26
|
import { WorkflowNondeterminismError } from '../workflow/errors';
|
|
26
27
|
import { WorkflowExecutor } from '../workflow/executor';
|
|
@@ -30,6 +31,24 @@ import { runWithActivityContext } from './activity-context';
|
|
|
30
31
|
import { checkWorkerVersioningCapability, registerWorkerBuildIdCompatibility } from './build-id';
|
|
31
32
|
import { makeWorkerScheduler, } from './concurrency';
|
|
32
33
|
import { makeStickyCache, } from './sticky-cache';
|
|
34
|
+
import { buildUpdateProtocolMessages, collectWorkflowUpdates } from './update-protocol';
|
|
35
|
+
const mergeUpdateInvocations = (historyInvocations, messageInvocations) => {
|
|
36
|
+
const merged = [];
|
|
37
|
+
const seen = new Set();
|
|
38
|
+
for (const invocation of historyInvocations ?? []) {
|
|
39
|
+
if (!seen.has(invocation.updateId)) {
|
|
40
|
+
merged.push(invocation);
|
|
41
|
+
seen.add(invocation.updateId);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
for (const invocation of messageInvocations ?? []) {
|
|
45
|
+
if (!seen.has(invocation.updateId)) {
|
|
46
|
+
merged.push(invocation);
|
|
47
|
+
seen.add(invocation.updateId);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return merged;
|
|
51
|
+
};
|
|
33
52
|
const POLL_TIMEOUT_MS = 60_000;
|
|
34
53
|
const RESPOND_TIMEOUT_MS = 15_000;
|
|
35
54
|
const HISTORY_FETCH_TIMEOUT_MS = 60_000;
|
|
@@ -98,6 +117,12 @@ export class WorkerRuntime {
|
|
|
98
117
|
activityConcurrency,
|
|
99
118
|
hooks: options.schedulerHooks,
|
|
100
119
|
logger,
|
|
120
|
+
metrics: {
|
|
121
|
+
workflowTaskStarted: runtimeMetrics.workflowTaskStarted,
|
|
122
|
+
workflowTaskCompleted: runtimeMetrics.workflowTaskCompleted,
|
|
123
|
+
activityTaskStarted: runtimeMetrics.activityTaskStarted,
|
|
124
|
+
activityTaskCompleted: runtimeMetrics.activityTaskCompleted,
|
|
125
|
+
},
|
|
101
126
|
}));
|
|
102
127
|
const stickyCacheCandidate = options.stickyCache;
|
|
103
128
|
const hasStickyCacheInstance = WorkerRuntime.#isStickyCacheInstance(stickyCacheCandidate);
|
|
@@ -267,7 +292,11 @@ export class WorkerRuntime {
|
|
|
267
292
|
if (!state) {
|
|
268
293
|
return false;
|
|
269
294
|
}
|
|
270
|
-
return state.commandHistory.length > 0 ||
|
|
295
|
+
return (state.commandHistory.length > 0 ||
|
|
296
|
+
state.randomValues.length > 0 ||
|
|
297
|
+
state.timeValues.length > 0 ||
|
|
298
|
+
(state.signals?.length ?? 0) > 0 ||
|
|
299
|
+
(state.queries?.length ?? 0) > 0);
|
|
271
300
|
}
|
|
272
301
|
#resolvePreviousHistoryEventId(response) {
|
|
273
302
|
const previous = response.previousStartedEventId;
|
|
@@ -320,6 +349,10 @@ export class WorkerRuntime {
|
|
|
320
349
|
heartbeatFailures: await makeCounter('temporal_worker_heartbeat_failures_total', 'Activity heartbeat failures'),
|
|
321
350
|
activityFailures: await makeCounter('temporal_worker_activity_failures_total', 'Activity failures delivered to Temporal'),
|
|
322
351
|
workflowFailures: await makeCounter('temporal_worker_workflow_failures_total', 'Workflow failure responses sent to Temporal'),
|
|
352
|
+
workflowTaskStarted: await makeCounter('temporal_worker_workflow_tasks_started_total', 'Workflow tasks dispatched to the scheduler'),
|
|
353
|
+
workflowTaskCompleted: await makeCounter('temporal_worker_workflow_tasks_completed_total', 'Workflow tasks completed by the scheduler'),
|
|
354
|
+
activityTaskStarted: await makeCounter('temporal_worker_activity_tasks_started_total', 'Activity tasks dispatched to the scheduler'),
|
|
355
|
+
activityTaskCompleted: await makeCounter('temporal_worker_activity_tasks_completed_total', 'Activity tasks completed by the scheduler'),
|
|
323
356
|
};
|
|
324
357
|
}
|
|
325
358
|
async run() {
|
|
@@ -519,15 +552,45 @@ export class WorkerRuntime {
|
|
|
519
552
|
const workflowType = this.#resolveWorkflowType(response, historyEvents);
|
|
520
553
|
const args = await this.#decodeWorkflowArgs(historyEvents);
|
|
521
554
|
const workflowInfo = this.#buildWorkflowInfo(workflowType, execution);
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
555
|
+
const collectedUpdates = await collectWorkflowUpdates({
|
|
556
|
+
messages: response.messages ?? [],
|
|
557
|
+
dataConverter: this.#dataConverter,
|
|
558
|
+
log: (level, message, fields) => this.#log(level, message, fields),
|
|
559
|
+
});
|
|
526
560
|
const baseLogFields = this.#workflowLogFields(execution, workflowType, {
|
|
527
561
|
workflowTaskAttempt,
|
|
528
562
|
stickyScheduling: this.#stickySchedulingEnabled,
|
|
529
563
|
nondeterminismRetry,
|
|
530
564
|
});
|
|
565
|
+
const signalDeliveries = await this.#extractSignalDeliveries(historyEvents);
|
|
566
|
+
if (signalDeliveries.length > 0) {
|
|
567
|
+
this.#log('debug', 'workflow signal deliveries buffered', {
|
|
568
|
+
...baseLogFields,
|
|
569
|
+
signalCount: signalDeliveries.length,
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
const queryRequests = await this.#extractWorkflowQueryRequests(response);
|
|
573
|
+
const hasQueryRequests = queryRequests.length > 0;
|
|
574
|
+
const hasMultiQueries = queryRequests.some((request) => request.source === 'multi');
|
|
575
|
+
const hasLegacyQueries = queryRequests.some((request) => request.source === 'legacy');
|
|
576
|
+
if (hasQueryRequests) {
|
|
577
|
+
this.#log('debug', 'workflow queries pending evaluation', {
|
|
578
|
+
...baseLogFields,
|
|
579
|
+
queryCount: queryRequests.length,
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
this.#log('info', 'debug: workflow task metadata', {
|
|
583
|
+
...baseLogFields,
|
|
584
|
+
taskTokenBytes: response.taskToken?.length ?? 0,
|
|
585
|
+
queryCount: queryRequests.length,
|
|
586
|
+
historyEventCount: response.history?.events?.length ?? 0,
|
|
587
|
+
});
|
|
588
|
+
const stickyKey = this.#buildStickyKey(execution.workflowId, execution.runId);
|
|
589
|
+
const stickyEntry = stickyKey ? await this.#getStickyEntry(stickyKey) : undefined;
|
|
590
|
+
const historyReplay = await this.#ingestDeterminismState(workflowInfo, historyEvents, {
|
|
591
|
+
queryRequests,
|
|
592
|
+
});
|
|
593
|
+
const hasHistorySnapshot = this.#isValidDeterminismSnapshot(historyReplay?.determinismState);
|
|
531
594
|
let previousState;
|
|
532
595
|
if (stickyEntry && this.#isValidDeterminismSnapshot(stickyEntry.determinismState)) {
|
|
533
596
|
const historyBaselineEventId = this.#resolvePreviousHistoryEventId(response) ?? historyReplay?.lastEventId ?? null;
|
|
@@ -575,6 +638,8 @@ export class WorkerRuntime {
|
|
|
575
638
|
const expectedDeterminismState = previousState;
|
|
576
639
|
try {
|
|
577
640
|
const activityResults = await this.#extractActivityResolutions(historyEvents);
|
|
641
|
+
const replayUpdates = historyReplay?.updates ?? [];
|
|
642
|
+
const mergedUpdates = mergeUpdateInvocations(replayUpdates, collectedUpdates.invocations);
|
|
578
643
|
const output = await this.#executor.execute({
|
|
579
644
|
workflowType,
|
|
580
645
|
workflowId: execution.workflowId,
|
|
@@ -584,10 +649,49 @@ export class WorkerRuntime {
|
|
|
584
649
|
arguments: args,
|
|
585
650
|
determinismState: previousState,
|
|
586
651
|
activityResults,
|
|
652
|
+
signalDeliveries,
|
|
653
|
+
queryRequests,
|
|
654
|
+
updates: mergedUpdates,
|
|
655
|
+
});
|
|
656
|
+
this.#log('debug', 'workflow query evaluation summary', {
|
|
657
|
+
...baseLogFields,
|
|
658
|
+
queryResultCount: output.queryResults.length,
|
|
659
|
+
});
|
|
660
|
+
this.#log('debug', 'workflow query results raw', {
|
|
661
|
+
...baseLogFields,
|
|
662
|
+
queryResults: output.queryResults.map((entry) => ({
|
|
663
|
+
name: entry.request.name,
|
|
664
|
+
source: entry.request.source,
|
|
665
|
+
id: entry.request.id ?? null,
|
|
666
|
+
})),
|
|
587
667
|
});
|
|
668
|
+
const multiQueryResults = {};
|
|
669
|
+
let legacyQueryResult;
|
|
670
|
+
for (const entry of output.queryResults) {
|
|
671
|
+
this.#log('debug', 'workflow query evaluation completed', {
|
|
672
|
+
...baseLogFields,
|
|
673
|
+
querySource: entry.request.source,
|
|
674
|
+
queryName: entry.request.name,
|
|
675
|
+
queryId: entry.request.id ?? null,
|
|
676
|
+
});
|
|
677
|
+
if (entry.request.source === 'multi' && entry.request.id) {
|
|
678
|
+
multiQueryResults[entry.request.id] = entry.result;
|
|
679
|
+
}
|
|
680
|
+
else if (entry.request.source === 'legacy') {
|
|
681
|
+
legacyQueryResult = entry;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
588
684
|
const cacheBaselineEventId = this.#resolveCurrentStartedEventId(response) ?? historyReplay?.lastEventId ?? null;
|
|
589
685
|
const shouldRecordMarker = output.completion === 'pending';
|
|
590
686
|
let commandsForResponse = output.commands;
|
|
687
|
+
const dispatchesForNewMessages = (output.updateDispatches ?? []).filter((dispatch) => collectedUpdates.requestsByUpdateId.has(dispatch.updateId));
|
|
688
|
+
const updateProtocolMessages = await buildUpdateProtocolMessages({
|
|
689
|
+
dispatches: dispatchesForNewMessages,
|
|
690
|
+
collected: collectedUpdates,
|
|
691
|
+
dataConverter: this.#dataConverter,
|
|
692
|
+
defaultIdentity: this.#identity,
|
|
693
|
+
log: (level, message, fields) => this.#log(level, message, fields),
|
|
694
|
+
});
|
|
591
695
|
if (stickyKey) {
|
|
592
696
|
if (output.completion === 'pending') {
|
|
593
697
|
await this.#upsertStickyEntry(stickyKey, output.determinismState, cacheBaselineEventId, workflowType);
|
|
@@ -614,24 +718,36 @@ export class WorkerRuntime {
|
|
|
614
718
|
const markerCommand = this.#buildDeterminismMarkerCommand(markerDetails);
|
|
615
719
|
commandsForResponse = this.#injectDeterminismMarker(commandsForResponse, markerCommand);
|
|
616
720
|
}
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
this.#
|
|
632
|
-
return;
|
|
721
|
+
const shouldRespondWorkflowTask = hasMultiQueries || !hasLegacyQueries;
|
|
722
|
+
if (shouldRespondWorkflowTask) {
|
|
723
|
+
const completion = create(RespondWorkflowTaskCompletedRequestSchema, {
|
|
724
|
+
taskToken: response.taskToken,
|
|
725
|
+
commands: commandsForResponse,
|
|
726
|
+
identity: this.#identity,
|
|
727
|
+
namespace: this.#namespace,
|
|
728
|
+
deploymentOptions: this.#deploymentOptions,
|
|
729
|
+
queryResults: multiQueryResults,
|
|
730
|
+
...(this.#stickySchedulingEnabled && !hasLegacyQueries ? { stickyAttributes: this.#stickyAttributes } : {}),
|
|
731
|
+
...(this.#versioningBehavior !== null ? { versioningBehavior: this.#versioningBehavior } : {}),
|
|
732
|
+
...(updateProtocolMessages.length > 0 ? { messages: updateProtocolMessages } : {}),
|
|
733
|
+
});
|
|
734
|
+
try {
|
|
735
|
+
await this.#workflowService.respondWorkflowTaskCompleted(completion, { timeoutMs: RESPOND_TIMEOUT_MS });
|
|
633
736
|
}
|
|
634
|
-
|
|
737
|
+
catch (rpcError) {
|
|
738
|
+
this.#log('error', 'debug: respondWorkflowTaskCompleted failed', {
|
|
739
|
+
...baseLogFields,
|
|
740
|
+
error: rpcError instanceof Error ? rpcError.message : String(rpcError),
|
|
741
|
+
});
|
|
742
|
+
if (this.#isTaskNotFoundError(rpcError)) {
|
|
743
|
+
this.#logWorkflowTaskNotFound('respondWorkflowTaskCompleted', execution);
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
throw rpcError;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
if (legacyQueryResult) {
|
|
750
|
+
await this.#respondLegacyQueryTask(response, legacyQueryResult);
|
|
635
751
|
}
|
|
636
752
|
}
|
|
637
753
|
catch (error) {
|
|
@@ -693,7 +809,7 @@ export class WorkerRuntime {
|
|
|
693
809
|
runId,
|
|
694
810
|
};
|
|
695
811
|
}
|
|
696
|
-
async #ingestDeterminismState(workflowInfo, historyEvents) {
|
|
812
|
+
async #ingestDeterminismState(workflowInfo, historyEvents, options) {
|
|
697
813
|
if (historyEvents.length === 0) {
|
|
698
814
|
return undefined;
|
|
699
815
|
}
|
|
@@ -701,6 +817,7 @@ export class WorkerRuntime {
|
|
|
701
817
|
info: workflowInfo,
|
|
702
818
|
history: historyEvents,
|
|
703
819
|
dataConverter: this.#dataConverter,
|
|
820
|
+
queries: options?.queryRequests,
|
|
704
821
|
}));
|
|
705
822
|
}
|
|
706
823
|
async #collectWorkflowHistory(execution, _response) {
|
|
@@ -817,6 +934,107 @@ export class WorkerRuntime {
|
|
|
817
934
|
}
|
|
818
935
|
return resolutions;
|
|
819
936
|
}
|
|
937
|
+
async #extractSignalDeliveries(events) {
|
|
938
|
+
const deliveries = [];
|
|
939
|
+
const normalizeEventId = (value) => {
|
|
940
|
+
if (value === undefined || value === null) {
|
|
941
|
+
return null;
|
|
942
|
+
}
|
|
943
|
+
if (typeof value === 'string') {
|
|
944
|
+
return value;
|
|
945
|
+
}
|
|
946
|
+
return value.toString();
|
|
947
|
+
};
|
|
948
|
+
for (const event of events) {
|
|
949
|
+
if (event.eventType !== EventType.WORKFLOW_EXECUTION_SIGNALED) {
|
|
950
|
+
continue;
|
|
951
|
+
}
|
|
952
|
+
if (event.attributes?.case !== 'workflowExecutionSignaledEventAttributes') {
|
|
953
|
+
continue;
|
|
954
|
+
}
|
|
955
|
+
const attrs = event.attributes.value;
|
|
956
|
+
const args = await decodePayloadsToValues(this.#dataConverter, attrs.input?.payloads ?? []);
|
|
957
|
+
const workflowTaskCompletedEventId = 'workflowTaskCompletedEventId' in attrs
|
|
958
|
+
? normalizeEventId(attrs
|
|
959
|
+
.workflowTaskCompletedEventId)
|
|
960
|
+
: null;
|
|
961
|
+
deliveries.push({
|
|
962
|
+
name: attrs.signalName ?? 'unknown',
|
|
963
|
+
args,
|
|
964
|
+
metadata: {
|
|
965
|
+
eventId: normalizeEventId(event.eventId),
|
|
966
|
+
workflowTaskCompletedEventId,
|
|
967
|
+
identity: attrs.identity ?? null,
|
|
968
|
+
},
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
return deliveries;
|
|
972
|
+
}
|
|
973
|
+
async #extractWorkflowQueryRequests(response) {
|
|
974
|
+
const requests = [];
|
|
975
|
+
const map = response.queries ?? {};
|
|
976
|
+
this.#log('info', 'debug: workflow query payloads detected', {
|
|
977
|
+
namespace: this.#namespace,
|
|
978
|
+
taskQueue: this.#taskQueue,
|
|
979
|
+
workflowId: response.workflowExecution?.workflowId,
|
|
980
|
+
runId: response.workflowExecution?.runId,
|
|
981
|
+
queryMapSize: Object.keys(map).length,
|
|
982
|
+
hasLegacyQuery: Boolean(response.query),
|
|
983
|
+
});
|
|
984
|
+
for (const [id, query] of Object.entries(map)) {
|
|
985
|
+
const args = await decodePayloadsToValues(this.#dataConverter, query.queryArgs?.payloads ?? []);
|
|
986
|
+
const header = await this.#decodeQueryHeader(query);
|
|
987
|
+
requests.push({
|
|
988
|
+
id,
|
|
989
|
+
name: query.queryType ?? 'query',
|
|
990
|
+
args,
|
|
991
|
+
metadata: header ? { header } : undefined,
|
|
992
|
+
source: 'multi',
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
if (response.query) {
|
|
996
|
+
const args = await decodePayloadsToValues(this.#dataConverter, response.query.queryArgs?.payloads ?? []);
|
|
997
|
+
const header = await this.#decodeQueryHeader(response.query);
|
|
998
|
+
requests.push({
|
|
999
|
+
name: response.query.queryType ?? 'query',
|
|
1000
|
+
args,
|
|
1001
|
+
metadata: header ? { header } : undefined,
|
|
1002
|
+
source: 'legacy',
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
return requests;
|
|
1006
|
+
}
|
|
1007
|
+
async #decodeQueryHeader(query) {
|
|
1008
|
+
const fields = query?.header?.fields;
|
|
1009
|
+
if (!fields || Object.keys(fields).length === 0) {
|
|
1010
|
+
return undefined;
|
|
1011
|
+
}
|
|
1012
|
+
const decoded = {};
|
|
1013
|
+
for (const [key, payload] of Object.entries(fields)) {
|
|
1014
|
+
const values = await decodePayloadsToValues(this.#dataConverter, payload ? [payload] : []);
|
|
1015
|
+
decoded[key] = values.length === 0 ? undefined : values.length === 1 ? values[0] : Object.freeze([...values]);
|
|
1016
|
+
}
|
|
1017
|
+
return Object.keys(decoded).length > 0 ? decoded : undefined;
|
|
1018
|
+
}
|
|
1019
|
+
async #respondLegacyQueryTask(response, entry) {
|
|
1020
|
+
const queryResult = entry.result;
|
|
1021
|
+
this.#log('debug', 'responding to legacy workflow query', {
|
|
1022
|
+
namespace: this.#namespace,
|
|
1023
|
+
workflowId: response.workflowExecution?.workflowId,
|
|
1024
|
+
runId: response.workflowExecution?.runId,
|
|
1025
|
+
queryName: entry.request.name,
|
|
1026
|
+
});
|
|
1027
|
+
const request = create(RespondQueryTaskCompletedRequestSchema, {
|
|
1028
|
+
taskToken: response.taskToken ?? new Uint8Array(),
|
|
1029
|
+
completedType: queryResult.resultType ?? QueryResultType.ANSWERED,
|
|
1030
|
+
queryResult: queryResult.answer,
|
|
1031
|
+
errorMessage: queryResult.errorMessage ?? '',
|
|
1032
|
+
namespace: this.#namespace,
|
|
1033
|
+
failure: queryResult.failure,
|
|
1034
|
+
cause: WorkflowTaskFailedCause.UNSPECIFIED,
|
|
1035
|
+
});
|
|
1036
|
+
await this.#workflowService.respondQueryTaskCompleted(request, { timeoutMs: RESPOND_TIMEOUT_MS });
|
|
1037
|
+
}
|
|
820
1038
|
async #fetchWorkflowHistoryPage(execution, nextPageToken) {
|
|
821
1039
|
if (!execution.workflowId || !execution.runId) {
|
|
822
1040
|
return { events: [], nextPageToken: new Uint8Array() };
|
|
@@ -931,11 +1149,13 @@ export class WorkerRuntime {
|
|
|
931
1149
|
};
|
|
932
1150
|
}
|
|
933
1151
|
async #computeNondeterminismMismatches(error, expectedState) {
|
|
934
|
-
const baseline =
|
|
935
|
-
commandHistory: [],
|
|
936
|
-
randomValues: [],
|
|
937
|
-
timeValues: [],
|
|
938
|
-
failureMetadata:
|
|
1152
|
+
const baseline = {
|
|
1153
|
+
commandHistory: expectedState?.commandHistory ?? [],
|
|
1154
|
+
randomValues: expectedState?.randomValues ?? [],
|
|
1155
|
+
timeValues: expectedState?.timeValues ?? [],
|
|
1156
|
+
failureMetadata: expectedState?.failureMetadata,
|
|
1157
|
+
signals: expectedState?.signals ?? [],
|
|
1158
|
+
queries: expectedState?.queries ?? [],
|
|
939
1159
|
};
|
|
940
1160
|
const hint = error.details?.hint;
|
|
941
1161
|
if (!hint) {
|
|
@@ -944,6 +1164,8 @@ export class WorkerRuntime {
|
|
|
944
1164
|
const commandIndex = this.#parseIndexFromHint(hint, 'commandIndex');
|
|
945
1165
|
const randomIndex = this.#parseIndexFromHint(hint, 'randomIndex');
|
|
946
1166
|
const timeIndex = this.#parseIndexFromHint(hint, 'timeIndex');
|
|
1167
|
+
const signalIndex = this.#parseIndexFromHint(hint, 'signalIndex');
|
|
1168
|
+
const queryIndex = this.#parseIndexFromHint(hint, 'queryIndex');
|
|
947
1169
|
const mutableActual = {
|
|
948
1170
|
commandHistory: baseline.commandHistory.map((entry) => ({
|
|
949
1171
|
intent: entry.intent,
|
|
@@ -952,6 +1174,8 @@ export class WorkerRuntime {
|
|
|
952
1174
|
randomValues: [...baseline.randomValues],
|
|
953
1175
|
timeValues: [...baseline.timeValues],
|
|
954
1176
|
failureMetadata: baseline.failureMetadata ? { ...baseline.failureMetadata } : undefined,
|
|
1177
|
+
signals: baseline.signals.map((record) => ({ ...record })),
|
|
1178
|
+
queries: baseline.queries.map((record) => ({ ...record })),
|
|
955
1179
|
};
|
|
956
1180
|
let mutated = false;
|
|
957
1181
|
if (commandIndex !== null) {
|
|
@@ -980,6 +1204,36 @@ export class WorkerRuntime {
|
|
|
980
1204
|
mutableActual.timeValues[timeIndex] = Number.NaN;
|
|
981
1205
|
mutated = true;
|
|
982
1206
|
}
|
|
1207
|
+
if (signalIndex !== null) {
|
|
1208
|
+
const receivedSignal = this.#asSignalRecord(error.details?.received);
|
|
1209
|
+
if (receivedSignal) {
|
|
1210
|
+
if (signalIndex < mutableActual.signals.length) {
|
|
1211
|
+
mutableActual.signals = mutableActual.signals.map((record, idx) => idx === signalIndex ? receivedSignal : record);
|
|
1212
|
+
}
|
|
1213
|
+
else {
|
|
1214
|
+
mutableActual.signals = [...mutableActual.signals, receivedSignal];
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
else if (signalIndex < mutableActual.signals.length) {
|
|
1218
|
+
mutableActual.signals = mutableActual.signals.filter((_, idx) => idx !== signalIndex);
|
|
1219
|
+
}
|
|
1220
|
+
mutated = true;
|
|
1221
|
+
}
|
|
1222
|
+
if (queryIndex !== null) {
|
|
1223
|
+
const receivedQuery = this.#asQueryRecord(error.details?.received);
|
|
1224
|
+
if (receivedQuery) {
|
|
1225
|
+
if (queryIndex < mutableActual.queries.length) {
|
|
1226
|
+
mutableActual.queries = mutableActual.queries.map((record, idx) => idx === queryIndex ? receivedQuery : record);
|
|
1227
|
+
}
|
|
1228
|
+
else {
|
|
1229
|
+
mutableActual.queries = [...mutableActual.queries, receivedQuery];
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
else if (queryIndex < mutableActual.queries.length) {
|
|
1233
|
+
mutableActual.queries = mutableActual.queries.filter((_, idx) => idx !== queryIndex);
|
|
1234
|
+
}
|
|
1235
|
+
mutated = true;
|
|
1236
|
+
}
|
|
983
1237
|
if (!mutated) {
|
|
984
1238
|
return [];
|
|
985
1239
|
}
|
|
@@ -988,6 +1242,8 @@ export class WorkerRuntime {
|
|
|
988
1242
|
randomValues: mutableActual.randomValues,
|
|
989
1243
|
timeValues: mutableActual.timeValues,
|
|
990
1244
|
failureMetadata: mutableActual.failureMetadata,
|
|
1245
|
+
signals: mutableActual.signals,
|
|
1246
|
+
queries: mutableActual.queries,
|
|
991
1247
|
};
|
|
992
1248
|
const diff = await Effect.runPromise(diffDeterminismState(baseline, actualState));
|
|
993
1249
|
return diff.mismatches;
|
|
@@ -1030,6 +1286,24 @@ export class WorkerRuntime {
|
|
|
1030
1286
|
array.push(Number.NaN);
|
|
1031
1287
|
}
|
|
1032
1288
|
}
|
|
1289
|
+
#asSignalRecord(value) {
|
|
1290
|
+
if (!value || typeof value !== 'object') {
|
|
1291
|
+
return undefined;
|
|
1292
|
+
}
|
|
1293
|
+
if ('signalName' in value && 'payloadHash' in value) {
|
|
1294
|
+
return value;
|
|
1295
|
+
}
|
|
1296
|
+
return undefined;
|
|
1297
|
+
}
|
|
1298
|
+
#asQueryRecord(value) {
|
|
1299
|
+
if (!value || typeof value !== 'object') {
|
|
1300
|
+
return undefined;
|
|
1301
|
+
}
|
|
1302
|
+
if ('queryName' in value && 'requestHash' in value) {
|
|
1303
|
+
return value;
|
|
1304
|
+
}
|
|
1305
|
+
return undefined;
|
|
1306
|
+
}
|
|
1033
1307
|
static #isStickyCacheInstance(value) {
|
|
1034
1308
|
if (!value || typeof value !== 'object') {
|
|
1035
1309
|
return false;
|