@proompteng/temporal-bun-sdk 0.3.0 → 0.5.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 +54 -11
- 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 +75 -1
- 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/common/payloads/json-codec.d.ts.map +1 -1
- package/dist/src/common/payloads/json-codec.js +4 -6
- package/dist/src/common/payloads/json-codec.js.map +1 -1
- package/dist/src/config.d.ts +25 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +111 -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 +190 -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 +177 -0
- package/dist/src/interceptors/worker.js.map +1 -0
- package/dist/src/observability/index.d.ts +4 -0
- package/dist/src/observability/index.d.ts.map +1 -1
- package/dist/src/observability/index.js +4 -0
- package/dist/src/observability/index.js.map +1 -1
- package/dist/src/observability/metrics.d.ts.map +1 -1
- package/dist/src/observability/metrics.js +70 -10
- package/dist/src/observability/metrics.js.map +1 -1
- package/dist/src/observability/opentelemetry.d.ts +20 -0
- package/dist/src/observability/opentelemetry.d.ts.map +1 -0
- package/dist/src/observability/opentelemetry.js +334 -0
- package/dist/src/observability/opentelemetry.js.map +1 -0
- package/dist/src/proto/temporal/api/activity/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/batch/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/command/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/common/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/deployment/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/batch_operation_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/command_type_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/common_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/deployment_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/event_type_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/failed_cause_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/namespace_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/nexus_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/query_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/reset_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/schedule_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/task_queue_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/update_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/workflow_pb.js +1 -1
- package/dist/src/proto/temporal/api/errordetails/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/export/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/failure/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/filter/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/history/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/namespace/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/nexus/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/operatorservice/v1/request_response_pb.js +1 -1
- package/dist/src/proto/temporal/api/operatorservice/v1/service_pb.js +1 -1
- package/dist/src/proto/temporal/api/protocol/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/query/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/replication/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/rules/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/sdk/v1/enhanced_stack_trace_pb.js +1 -1
- package/dist/src/proto/temporal/api/sdk/v1/task_complete_metadata_pb.js +1 -1
- package/dist/src/proto/temporal/api/sdk/v1/user_metadata_pb.js +1 -1
- package/dist/src/proto/temporal/api/sdk/v1/worker_config_pb.js +1 -1
- package/dist/src/proto/temporal/api/sdk/v1/workflow_metadata_pb.js +1 -1
- package/dist/src/proto/temporal/api/taskqueue/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/update/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/version/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/worker/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/workflow/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/workflowservice/v1/request_response_pb.js +1 -1
- package/dist/src/proto/temporal/api/workflowservice/v1/service_pb.js +1 -1
- 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 +6 -0
- package/dist/src/worker/runtime.d.ts.map +1 -1
- package/dist/src/worker/runtime.js +853 -70
- package/dist/src/worker/runtime.js.map +1 -1
- package/dist/src/worker/sticky-cache.d.ts +15 -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 +40 -3
- package/dist/src/workflow/commands.d.ts.map +1 -1
- package/dist/src/workflow/commands.js +161 -7
- 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 +345 -16
- package/dist/src/workflow/context.js.map +1 -1
- package/dist/src/workflow/definition.js.map +1 -1
- package/dist/src/workflow/determinism.d.ts +9 -0
- package/dist/src/workflow/determinism.d.ts.map +1 -1
- package/dist/src/workflow/determinism.js +145 -16
- 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 +6 -0
- package/dist/src/workflow/executor.d.ts.map +1 -1
- package/dist/src/workflow/executor.js +147 -18
- package/dist/src/workflow/executor.js.map +1 -1
- package/dist/src/workflow/inbound.d.ts +1 -0
- package/dist/src/workflow/inbound.d.ts.map +1 -1
- package/dist/src/workflow/inbound.js +1 -0
- package/dist/src/workflow/inbound.js.map +1 -1
- package/dist/src/workflow/index.d.ts +5 -0
- package/dist/src/workflow/index.d.ts.map +1 -1
- package/dist/src/workflow/index.js +3 -0
- package/dist/src/workflow/index.js.map +1 -1
- package/dist/src/workflow/log.d.ts +18 -0
- package/dist/src/workflow/log.d.ts.map +1 -0
- package/dist/src/workflow/log.js +36 -0
- package/dist/src/workflow/log.js.map +1 -0
- package/dist/src/workflow/replay.d.ts +31 -3
- package/dist/src/workflow/replay.d.ts.map +1 -1
- package/dist/src/workflow/replay.js +763 -67
- package/dist/src/workflow/replay.js.map +1 -1
- package/package.json +4 -3
|
@@ -2,12 +2,13 @@ import { create } from '@bufbuild/protobuf';
|
|
|
2
2
|
import { Effect } from 'effect';
|
|
3
3
|
import { durationToMillis } from '../common/duration';
|
|
4
4
|
import { decodePayloadsToValues, encodeValuesToPayloads } from '../common/payloads';
|
|
5
|
-
import { PayloadsSchema } from '../proto/temporal/api/common/v1/message_pb';
|
|
5
|
+
import { PayloadsSchema, } from '../proto/temporal/api/common/v1/message_pb';
|
|
6
6
|
import { EventType } from '../proto/temporal/api/enums/v1/event_type_pb';
|
|
7
7
|
import { ParentClosePolicy, TimeoutType, WorkflowIdReusePolicy } from '../proto/temporal/api/enums/v1/workflow_pb';
|
|
8
8
|
import { intentsEqual, stableStringify } from './determinism';
|
|
9
9
|
export const DETERMINISM_MARKER_NAME = 'temporal-bun-sdk/determinism';
|
|
10
|
-
const
|
|
10
|
+
const DETERMINISM_MARKER_SCHEMA_VERSION_V1 = 1;
|
|
11
|
+
const DETERMINISM_MARKER_SCHEMA_VERSION_V2 = 2;
|
|
11
12
|
const DETERMINISM_MARKER_DETAIL_KEY = 'snapshot';
|
|
12
13
|
/**
|
|
13
14
|
* Encode a workflow determinism snapshot into a marker payload map suitable for
|
|
@@ -15,13 +16,25 @@ const DETERMINISM_MARKER_DETAIL_KEY = 'snapshot';
|
|
|
15
16
|
*/
|
|
16
17
|
export const encodeDeterminismMarkerDetails = (converter, input) => Effect.tryPromise(async () => {
|
|
17
18
|
const recordedAt = (input.recordedAt ?? new Date()).toISOString();
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
const markerType = input.markerType ?? 'full';
|
|
20
|
+
if (markerType === 'delta' && !input.determinismDelta) {
|
|
21
|
+
throw new Error('Determinism marker delta payload missing');
|
|
22
|
+
}
|
|
23
|
+
const envelope = markerType === 'delta'
|
|
24
|
+
? {
|
|
25
|
+
schemaVersion: DETERMINISM_MARKER_SCHEMA_VERSION_V2,
|
|
26
|
+
workflow: input.info,
|
|
27
|
+
determinismDelta: input.determinismDelta,
|
|
28
|
+
lastEventId: normalizeEventId(input.lastEventId),
|
|
29
|
+
recordedAtIso: recordedAt,
|
|
30
|
+
}
|
|
31
|
+
: {
|
|
32
|
+
schemaVersion: DETERMINISM_MARKER_SCHEMA_VERSION_V1,
|
|
33
|
+
workflow: input.info,
|
|
34
|
+
determinismState: input.determinismState,
|
|
35
|
+
lastEventId: normalizeEventId(input.lastEventId),
|
|
36
|
+
recordedAtIso: recordedAt,
|
|
37
|
+
};
|
|
25
38
|
const payloads = await encodeValuesToPayloads(converter, [envelope]);
|
|
26
39
|
if (!payloads || payloads.length === 0) {
|
|
27
40
|
throw new Error('Failed to encode determinism marker payloads');
|
|
@@ -30,6 +43,20 @@ export const encodeDeterminismMarkerDetails = (converter, input) => Effect.tryPr
|
|
|
30
43
|
[DETERMINISM_MARKER_DETAIL_KEY]: create(PayloadsSchema, { payloads }),
|
|
31
44
|
};
|
|
32
45
|
});
|
|
46
|
+
export const measurePayloadsByteSize = (details) => {
|
|
47
|
+
let total = 0;
|
|
48
|
+
for (const payloads of Object.values(details)) {
|
|
49
|
+
for (const payload of payloads.payloads ?? []) {
|
|
50
|
+
total += payload.data?.byteLength ?? 0;
|
|
51
|
+
const metadata = payload.metadata ?? {};
|
|
52
|
+
for (const value of Object.values(metadata)) {
|
|
53
|
+
total += value?.byteLength ?? 0;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return total;
|
|
58
|
+
};
|
|
59
|
+
export const encodeDeterminismMarkerDetailsWithSize = (converter, input) => encodeDeterminismMarkerDetails(converter, input).pipe(Effect.map((details) => ({ details, sizeBytes: measurePayloadsByteSize(details) })));
|
|
33
60
|
/**
|
|
34
61
|
* Decode determinism snapshot metadata from a marker payload map. Returns
|
|
35
62
|
* `undefined` when the supplied details do not contain a determinism snapshot.
|
|
@@ -57,10 +84,14 @@ export const decodeDeterminismMarkerEnvelope = (input) => Effect.tryPromise(asyn
|
|
|
57
84
|
export const ingestWorkflowHistory = (intake) => Effect.gen(function* () {
|
|
58
85
|
const events = sortHistoryEvents(intake.history ?? []);
|
|
59
86
|
const shouldUseDeterminismMarker = intake.ignoreDeterminismMarker !== true;
|
|
60
|
-
let
|
|
61
|
-
|
|
87
|
+
let markerState;
|
|
88
|
+
let markerLastEventId = null;
|
|
89
|
+
let markerEventId = null;
|
|
90
|
+
let markerEventIndex = null;
|
|
91
|
+
let markerInvalid = false;
|
|
92
|
+
let hasMarker = false;
|
|
62
93
|
if (shouldUseDeterminismMarker) {
|
|
63
|
-
for (const event of events) {
|
|
94
|
+
for (const [index, event] of events.entries()) {
|
|
64
95
|
if (event.eventType !== EventType.MARKER_RECORDED) {
|
|
65
96
|
continue;
|
|
66
97
|
}
|
|
@@ -72,36 +103,82 @@ export const ingestWorkflowHistory = (intake) => Effect.gen(function* () {
|
|
|
72
103
|
details: event.attributes.value.details,
|
|
73
104
|
}), () => Effect.succeed(undefined));
|
|
74
105
|
if (decoded) {
|
|
75
|
-
|
|
106
|
+
const applied = applyDeterminismMarker(markerState, decoded);
|
|
107
|
+
if (!applied) {
|
|
108
|
+
markerInvalid = true;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
markerState = applied;
|
|
112
|
+
markerLastEventId = decoded.lastEventId;
|
|
113
|
+
markerEventId = normalizeEventId(event.eventId);
|
|
114
|
+
markerEventIndex = index;
|
|
115
|
+
hasMarker = true;
|
|
76
116
|
}
|
|
77
117
|
}
|
|
78
118
|
}
|
|
79
119
|
const extractedFailureMetadata = extractFailureMetadata(events);
|
|
80
|
-
const
|
|
81
|
-
if (shouldUseDeterminismMarker &&
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
};
|
|
120
|
+
const historyLastEventId = resolveHistoryLastEventId(events) ?? null;
|
|
121
|
+
if (shouldUseDeterminismMarker && hasMarker && !markerInvalid && markerState && markerEventIndex !== null) {
|
|
122
|
+
const historyKinds = collectCommandKinds(events.slice(0, markerEventIndex + 1));
|
|
123
|
+
const markerKinds = markerState.commandHistory.map((entry) => entry.intent.kind);
|
|
124
|
+
if (!commandKindsMatch(markerKinds, historyKinds)) {
|
|
125
|
+
markerInvalid = true;
|
|
126
|
+
markerState = undefined;
|
|
88
127
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
128
|
+
}
|
|
129
|
+
const eventsAfterMarker = shouldUseDeterminismMarker && hasMarker && markerEventIndex !== null ? events.slice(markerEventIndex + 1) : events;
|
|
130
|
+
const replayUpdateInvocationsAfterMarker = yield* Effect.tryPromise(async () => collectWorkflowUpdateInvocations(eventsAfterMarker, intake.dataConverter));
|
|
131
|
+
const updateEntriesAfterMarker = collectWorkflowUpdateEntries(eventsAfterMarker);
|
|
132
|
+
const replayUpdateInvocationsFull = yield* Effect.tryPromise(async () => collectWorkflowUpdateInvocations(events, intake.dataConverter));
|
|
133
|
+
const updateEntriesFull = collectWorkflowUpdateEntries(events);
|
|
134
|
+
const markerCoversHistory = markerEventId !== null && markerEventId === historyLastEventId;
|
|
135
|
+
const historyCommandCount = countHistoryCommandEvents(events);
|
|
136
|
+
if (shouldUseDeterminismMarker && hasMarker && !markerInvalid && markerState) {
|
|
137
|
+
if (markerCoversHistory || eventsAfterMarker.length === 0) {
|
|
138
|
+
let determinismState = cloneDeterminismState(markerState);
|
|
139
|
+
if (!determinismState.failureMetadata && extractedFailureMetadata) {
|
|
140
|
+
determinismState = {
|
|
141
|
+
...determinismState,
|
|
142
|
+
failureMetadata: extractedFailureMetadata,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
if ((!determinismState.updates || determinismState.updates.length === 0) &&
|
|
146
|
+
updateEntriesAfterMarker.length > 0) {
|
|
147
|
+
determinismState = {
|
|
148
|
+
...determinismState,
|
|
149
|
+
updates: updateEntriesAfterMarker,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
determinismState = mergePendingQueryRequests(determinismState, intake.queries);
|
|
153
|
+
const latestEventId = historyLastEventId ?? markerLastEventId ?? null;
|
|
154
|
+
if (determinismState.commandHistory.length !== historyCommandCount) {
|
|
155
|
+
return yield* reconstructDeterminismState(events, intake, extractedFailureMetadata, updateEntriesFull, replayUpdateInvocationsFull);
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
determinismState,
|
|
159
|
+
lastEventId: latestEventId,
|
|
160
|
+
hasDeterminismMarker: true,
|
|
161
|
+
markerState: cloneDeterminismState(markerState),
|
|
162
|
+
...(replayUpdateInvocationsFull.length > 0 ? { updates: replayUpdateInvocationsFull } : {}),
|
|
93
163
|
};
|
|
94
164
|
}
|
|
95
|
-
|
|
96
|
-
|
|
165
|
+
const replayed = yield* reconstructDeterminismState(eventsAfterMarker, intake, extractedFailureMetadata, updateEntriesAfterMarker, replayUpdateInvocationsAfterMarker, {
|
|
166
|
+
seedState: markerState,
|
|
167
|
+
historyLastEventId,
|
|
168
|
+
seedHistoryEvents: markerEventIndex !== null ? events.slice(0, markerEventIndex + 1) : undefined,
|
|
169
|
+
});
|
|
170
|
+
if (replayed.determinismState.commandHistory.length !== historyCommandCount) {
|
|
171
|
+
return yield* reconstructDeterminismState(events, intake, extractedFailureMetadata, updateEntriesFull, replayUpdateInvocationsFull);
|
|
172
|
+
}
|
|
97
173
|
return {
|
|
98
|
-
|
|
99
|
-
lastEventId: latestEventId,
|
|
174
|
+
...replayed,
|
|
100
175
|
hasDeterminismMarker: true,
|
|
101
|
-
|
|
176
|
+
markerState: cloneDeterminismState(markerState),
|
|
177
|
+
lastEventId: historyLastEventId ?? markerLastEventId ?? null,
|
|
178
|
+
...(replayUpdateInvocationsFull.length > 0 ? { updates: replayUpdateInvocationsFull } : {}),
|
|
102
179
|
};
|
|
103
180
|
}
|
|
104
|
-
return yield* reconstructDeterminismState(events, intake, extractedFailureMetadata,
|
|
181
|
+
return yield* reconstructDeterminismState(events, intake, extractedFailureMetadata, updateEntriesFull, replayUpdateInvocationsFull);
|
|
105
182
|
});
|
|
106
183
|
/**
|
|
107
184
|
* Diff determinism state against freshly emitted intents to produce rich
|
|
@@ -220,6 +297,7 @@ export const cloneDeterminismState = (state) => ({
|
|
|
220
297
|
})),
|
|
221
298
|
randomValues: [...state.randomValues],
|
|
222
299
|
timeValues: [...state.timeValues],
|
|
300
|
+
...(state.logCount !== undefined ? { logCount: state.logCount } : {}),
|
|
223
301
|
failureMetadata: state.failureMetadata ? { ...state.failureMetadata } : undefined,
|
|
224
302
|
signals: state.signals ? state.signals.map((record) => ({ ...record })) : [],
|
|
225
303
|
queries: state.queries ? state.queries.map((record) => ({ ...record })) : [],
|
|
@@ -384,12 +462,295 @@ const resolveCommandMismatchMetadata = (expectedEntry, actualEntry) => {
|
|
|
384
462
|
eventType,
|
|
385
463
|
};
|
|
386
464
|
};
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
465
|
+
const seedDeterminismEventMaps = (state, seedEvents) => {
|
|
466
|
+
const scheduledActivities = new Map();
|
|
467
|
+
const timersByStartEventId = new Map();
|
|
468
|
+
if (!state) {
|
|
469
|
+
if (seedEvents) {
|
|
470
|
+
seedDeterminismEventMapsFromHistory(scheduledActivities, timersByStartEventId, seedEvents);
|
|
471
|
+
}
|
|
472
|
+
return { scheduledActivities, timersByStartEventId };
|
|
473
|
+
}
|
|
474
|
+
for (const entry of state.commandHistory) {
|
|
475
|
+
const eventId = entry.metadata?.eventId;
|
|
476
|
+
if (!eventId) {
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
if (entry.intent.kind === 'schedule-activity') {
|
|
480
|
+
scheduledActivities.set(eventId, entry.intent.activityId);
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
if (entry.intent.kind === 'start-timer') {
|
|
484
|
+
timersByStartEventId.set(eventId, entry.intent.timerId);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (seedEvents) {
|
|
488
|
+
seedDeterminismEventMapsFromHistory(scheduledActivities, timersByStartEventId, seedEvents);
|
|
489
|
+
}
|
|
490
|
+
return { scheduledActivities, timersByStartEventId };
|
|
491
|
+
};
|
|
492
|
+
const seedDeterminismEventMapsFromHistory = (scheduledActivities, timersByStartEventId, events) => {
|
|
493
|
+
for (const event of events) {
|
|
494
|
+
switch (event.eventType) {
|
|
495
|
+
case EventType.ACTIVITY_TASK_SCHEDULED: {
|
|
496
|
+
if (event.attributes?.case !== 'activityTaskScheduledEventAttributes') {
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
const activityId = event.attributes.value.activityId;
|
|
500
|
+
const eventId = normalizeEventId(event.eventId);
|
|
501
|
+
if (activityId && eventId && !scheduledActivities.has(eventId)) {
|
|
502
|
+
scheduledActivities.set(eventId, activityId);
|
|
503
|
+
}
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
case EventType.TIMER_STARTED: {
|
|
507
|
+
if (event.attributes?.case !== 'timerStartedEventAttributes') {
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
const timerId = event.attributes.value.timerId;
|
|
511
|
+
const eventId = normalizeEventId(event.eventId);
|
|
512
|
+
if (timerId && eventId && !timersByStartEventId.has(eventId)) {
|
|
513
|
+
timersByStartEventId.set(eventId, timerId);
|
|
514
|
+
}
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
default:
|
|
518
|
+
break;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
const countHistoryCommandEvents = (events) => {
|
|
523
|
+
const { scheduledActivities, timersByStartEventId } = seedDeterminismEventMaps(undefined, events);
|
|
524
|
+
let count = 0;
|
|
525
|
+
for (const event of events) {
|
|
526
|
+
switch (event.eventType) {
|
|
527
|
+
case EventType.ACTIVITY_TASK_SCHEDULED: {
|
|
528
|
+
if (event.attributes?.case !== 'activityTaskScheduledEventAttributes') {
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
if (event.attributes.value.activityType?.name) {
|
|
532
|
+
count += 1;
|
|
533
|
+
}
|
|
534
|
+
break;
|
|
535
|
+
}
|
|
536
|
+
case EventType.TIMER_STARTED: {
|
|
537
|
+
if (event.attributes?.case !== 'timerStartedEventAttributes') {
|
|
538
|
+
break;
|
|
539
|
+
}
|
|
540
|
+
if (durationToMillis(event.attributes.value.startToFireTimeout) !== undefined) {
|
|
541
|
+
count += 1;
|
|
542
|
+
}
|
|
543
|
+
break;
|
|
544
|
+
}
|
|
545
|
+
case EventType.START_CHILD_WORKFLOW_EXECUTION_INITIATED: {
|
|
546
|
+
if (event.attributes?.case !== 'startChildWorkflowExecutionInitiatedEventAttributes') {
|
|
547
|
+
break;
|
|
548
|
+
}
|
|
549
|
+
if (event.attributes.value.workflowType?.name) {
|
|
550
|
+
count += 1;
|
|
551
|
+
}
|
|
552
|
+
break;
|
|
553
|
+
}
|
|
554
|
+
case EventType.ACTIVITY_TASK_CANCEL_REQUESTED: {
|
|
555
|
+
if (event.attributes?.case !== 'activityTaskCancelRequestedEventAttributes') {
|
|
556
|
+
break;
|
|
557
|
+
}
|
|
558
|
+
const scheduledEventId = normalizeBigintIdentifier(event.attributes.value.scheduledEventId);
|
|
559
|
+
if (scheduledEventId && scheduledActivities.has(scheduledEventId)) {
|
|
560
|
+
count += 1;
|
|
561
|
+
}
|
|
562
|
+
break;
|
|
563
|
+
}
|
|
564
|
+
case EventType.TIMER_CANCELED: {
|
|
565
|
+
if (event.attributes?.case !== 'timerCanceledEventAttributes') {
|
|
566
|
+
break;
|
|
567
|
+
}
|
|
568
|
+
const startedEventId = normalizeBigintIdentifier(event.attributes.value.startedEventId);
|
|
569
|
+
if (startedEventId && timersByStartEventId.has(startedEventId)) {
|
|
570
|
+
count += 1;
|
|
571
|
+
}
|
|
572
|
+
break;
|
|
573
|
+
}
|
|
574
|
+
case EventType.SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: {
|
|
575
|
+
if (event.attributes?.case !== 'signalExternalWorkflowExecutionInitiatedEventAttributes') {
|
|
576
|
+
break;
|
|
577
|
+
}
|
|
578
|
+
if (event.attributes.value.signalName) {
|
|
579
|
+
count += 1;
|
|
580
|
+
}
|
|
581
|
+
break;
|
|
582
|
+
}
|
|
583
|
+
case EventType.REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: {
|
|
584
|
+
if (event.attributes?.case !== 'requestCancelExternalWorkflowExecutionInitiatedEventAttributes') {
|
|
585
|
+
break;
|
|
586
|
+
}
|
|
587
|
+
count += 1;
|
|
588
|
+
break;
|
|
589
|
+
}
|
|
590
|
+
case EventType.WORKFLOW_EXECUTION_CONTINUED_AS_NEW: {
|
|
591
|
+
if (event.attributes?.case !== 'workflowExecutionContinuedAsNewEventAttributes') {
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
594
|
+
count += 1;
|
|
595
|
+
break;
|
|
596
|
+
}
|
|
597
|
+
case EventType.MARKER_RECORDED: {
|
|
598
|
+
if (event.attributes?.case !== 'markerRecordedEventAttributes') {
|
|
599
|
+
break;
|
|
600
|
+
}
|
|
601
|
+
if (event.attributes.value.markerName !== DETERMINISM_MARKER_NAME) {
|
|
602
|
+
count += 1;
|
|
603
|
+
}
|
|
604
|
+
break;
|
|
605
|
+
}
|
|
606
|
+
case EventType.UPSERT_WORKFLOW_SEARCH_ATTRIBUTES: {
|
|
607
|
+
if (event.attributes?.case !== 'upsertWorkflowSearchAttributesEventAttributes') {
|
|
608
|
+
break;
|
|
609
|
+
}
|
|
610
|
+
count += 1;
|
|
611
|
+
break;
|
|
612
|
+
}
|
|
613
|
+
case EventType.WORKFLOW_PROPERTIES_MODIFIED: {
|
|
614
|
+
if (event.attributes?.case !== 'workflowPropertiesModifiedEventAttributes') {
|
|
615
|
+
break;
|
|
616
|
+
}
|
|
617
|
+
count += 1;
|
|
618
|
+
break;
|
|
619
|
+
}
|
|
620
|
+
default:
|
|
621
|
+
break;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
return count;
|
|
625
|
+
};
|
|
626
|
+
const commandKindsMatch = (expected, actual) => {
|
|
627
|
+
if (expected.length !== actual.length) {
|
|
628
|
+
return false;
|
|
629
|
+
}
|
|
630
|
+
for (let index = 0; index < expected.length; index += 1) {
|
|
631
|
+
if (expected[index] !== actual[index]) {
|
|
632
|
+
return false;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
return true;
|
|
636
|
+
};
|
|
637
|
+
const collectCommandKinds = (events) => {
|
|
638
|
+
const { scheduledActivities, timersByStartEventId } = seedDeterminismEventMaps(undefined, events);
|
|
639
|
+
const kinds = [];
|
|
640
|
+
for (const event of events) {
|
|
641
|
+
switch (event.eventType) {
|
|
642
|
+
case EventType.ACTIVITY_TASK_SCHEDULED: {
|
|
643
|
+
if (event.attributes?.case !== 'activityTaskScheduledEventAttributes') {
|
|
644
|
+
break;
|
|
645
|
+
}
|
|
646
|
+
if (event.attributes.value.activityType?.name) {
|
|
647
|
+
kinds.push('schedule-activity');
|
|
648
|
+
}
|
|
649
|
+
break;
|
|
650
|
+
}
|
|
651
|
+
case EventType.TIMER_STARTED: {
|
|
652
|
+
if (event.attributes?.case !== 'timerStartedEventAttributes') {
|
|
653
|
+
break;
|
|
654
|
+
}
|
|
655
|
+
if (event.attributes.value.timerId) {
|
|
656
|
+
kinds.push('start-timer');
|
|
657
|
+
}
|
|
658
|
+
break;
|
|
659
|
+
}
|
|
660
|
+
case EventType.START_CHILD_WORKFLOW_EXECUTION_INITIATED: {
|
|
661
|
+
if (event.attributes?.case !== 'startChildWorkflowExecutionInitiatedEventAttributes') {
|
|
662
|
+
break;
|
|
663
|
+
}
|
|
664
|
+
if (event.attributes.value.workflowType?.name) {
|
|
665
|
+
kinds.push('start-child-workflow');
|
|
666
|
+
}
|
|
667
|
+
break;
|
|
668
|
+
}
|
|
669
|
+
case EventType.ACTIVITY_TASK_CANCEL_REQUESTED: {
|
|
670
|
+
if (event.attributes?.case !== 'activityTaskCancelRequestedEventAttributes') {
|
|
671
|
+
break;
|
|
672
|
+
}
|
|
673
|
+
const scheduledKey = normalizeEventId(event.attributes.value.scheduledEventId);
|
|
674
|
+
if (scheduledKey && scheduledActivities.has(scheduledKey)) {
|
|
675
|
+
kinds.push('request-cancel-activity');
|
|
676
|
+
}
|
|
677
|
+
break;
|
|
678
|
+
}
|
|
679
|
+
case EventType.TIMER_CANCELED: {
|
|
680
|
+
if (event.attributes?.case !== 'timerCanceledEventAttributes') {
|
|
681
|
+
break;
|
|
682
|
+
}
|
|
683
|
+
const startedKey = normalizeEventId(event.attributes.value.startedEventId);
|
|
684
|
+
if (startedKey && timersByStartEventId.has(startedKey)) {
|
|
685
|
+
kinds.push('cancel-timer');
|
|
686
|
+
}
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
case EventType.SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: {
|
|
690
|
+
if (event.attributes?.case !== 'signalExternalWorkflowExecutionInitiatedEventAttributes') {
|
|
691
|
+
break;
|
|
692
|
+
}
|
|
693
|
+
if (event.attributes.value.signalName) {
|
|
694
|
+
kinds.push('signal-external-workflow');
|
|
695
|
+
}
|
|
696
|
+
break;
|
|
697
|
+
}
|
|
698
|
+
case EventType.REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: {
|
|
699
|
+
if (event.attributes?.case !== 'requestCancelExternalWorkflowExecutionInitiatedEventAttributes') {
|
|
700
|
+
break;
|
|
701
|
+
}
|
|
702
|
+
if (event.attributes.value.workflowExecution?.workflowId) {
|
|
703
|
+
kinds.push('request-cancel-external-workflow');
|
|
704
|
+
}
|
|
705
|
+
break;
|
|
706
|
+
}
|
|
707
|
+
case EventType.WORKFLOW_EXECUTION_CONTINUED_AS_NEW: {
|
|
708
|
+
if (event.attributes?.case !== 'workflowExecutionContinuedAsNewEventAttributes') {
|
|
709
|
+
break;
|
|
710
|
+
}
|
|
711
|
+
kinds.push('continue-as-new');
|
|
712
|
+
break;
|
|
713
|
+
}
|
|
714
|
+
case EventType.MARKER_RECORDED: {
|
|
715
|
+
if (event.attributes?.case !== 'markerRecordedEventAttributes') {
|
|
716
|
+
break;
|
|
717
|
+
}
|
|
718
|
+
if (event.attributes.value.markerName !== DETERMINISM_MARKER_NAME) {
|
|
719
|
+
kinds.push('record-marker');
|
|
720
|
+
}
|
|
721
|
+
break;
|
|
722
|
+
}
|
|
723
|
+
case EventType.UPSERT_WORKFLOW_SEARCH_ATTRIBUTES: {
|
|
724
|
+
if (event.attributes?.case !== 'upsertWorkflowSearchAttributesEventAttributes') {
|
|
725
|
+
break;
|
|
726
|
+
}
|
|
727
|
+
kinds.push('upsert-search-attributes');
|
|
728
|
+
break;
|
|
729
|
+
}
|
|
730
|
+
case EventType.WORKFLOW_PROPERTIES_MODIFIED: {
|
|
731
|
+
if (event.attributes?.case !== 'workflowPropertiesModifiedEventAttributes') {
|
|
732
|
+
break;
|
|
733
|
+
}
|
|
734
|
+
kinds.push('modify-workflow-properties');
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
737
|
+
default:
|
|
738
|
+
break;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
return kinds;
|
|
742
|
+
};
|
|
743
|
+
const reconstructDeterminismState = (events, intake, failureMetadata, precomputedUpdates, replayUpdateInvocations = [], options) => Effect.gen(function* () {
|
|
744
|
+
const seededState = options?.seedState ? cloneDeterminismState(options.seedState) : undefined;
|
|
745
|
+
let sequence = seededState?.commandHistory.length ?? 0;
|
|
746
|
+
const commandHistory = seededState ? [...seededState.commandHistory] : [];
|
|
747
|
+
const signalRecords = seededState ? [...seededState.signals] : [];
|
|
748
|
+
const queryRecords = seededState ? [...seededState.queries] : [];
|
|
749
|
+
const updateEntries = precomputedUpdates ?? collectWorkflowUpdateEntries(events);
|
|
750
|
+
const updates = seededState?.updates && seededState.updates.length > 0
|
|
751
|
+
? [...seededState.updates, ...updateEntries]
|
|
752
|
+
: updateEntries;
|
|
753
|
+
const { scheduledActivities, timersByStartEventId } = seedDeterminismEventMaps(seededState, options?.seedHistoryEvents);
|
|
393
754
|
for (const event of events) {
|
|
394
755
|
switch (event.eventType) {
|
|
395
756
|
case EventType.ACTIVITY_TASK_SCHEDULED: {
|
|
@@ -402,6 +763,10 @@ const reconstructDeterminismState = (events, intake, failureMetadata, precompute
|
|
|
402
763
|
intent,
|
|
403
764
|
metadata: buildCommandMetadata(event, event.attributes.value),
|
|
404
765
|
});
|
|
766
|
+
const scheduledKey = normalizeEventId(event.eventId);
|
|
767
|
+
if (scheduledKey) {
|
|
768
|
+
scheduledActivities.set(scheduledKey, intent.activityId);
|
|
769
|
+
}
|
|
405
770
|
sequence += 1;
|
|
406
771
|
}
|
|
407
772
|
break;
|
|
@@ -416,6 +781,10 @@ const reconstructDeterminismState = (events, intake, failureMetadata, precompute
|
|
|
416
781
|
intent,
|
|
417
782
|
metadata: buildCommandMetadata(event, event.attributes.value),
|
|
418
783
|
});
|
|
784
|
+
const startKey = normalizeEventId(event.eventId);
|
|
785
|
+
if (startKey) {
|
|
786
|
+
timersByStartEventId.set(startKey, intent.timerId);
|
|
787
|
+
}
|
|
419
788
|
sequence += 1;
|
|
420
789
|
}
|
|
421
790
|
break;
|
|
@@ -434,6 +803,34 @@ const reconstructDeterminismState = (events, intake, failureMetadata, precompute
|
|
|
434
803
|
}
|
|
435
804
|
break;
|
|
436
805
|
}
|
|
806
|
+
case EventType.ACTIVITY_TASK_CANCEL_REQUESTED: {
|
|
807
|
+
if (event.attributes?.case !== 'activityTaskCancelRequestedEventAttributes') {
|
|
808
|
+
break;
|
|
809
|
+
}
|
|
810
|
+
const intent = fromActivityTaskCancelRequested(event.attributes.value, sequence, scheduledActivities);
|
|
811
|
+
if (intent) {
|
|
812
|
+
commandHistory.push({
|
|
813
|
+
intent,
|
|
814
|
+
metadata: buildCommandMetadata(event, event.attributes.value),
|
|
815
|
+
});
|
|
816
|
+
sequence += 1;
|
|
817
|
+
}
|
|
818
|
+
break;
|
|
819
|
+
}
|
|
820
|
+
case EventType.TIMER_CANCELED: {
|
|
821
|
+
if (event.attributes?.case !== 'timerCanceledEventAttributes') {
|
|
822
|
+
break;
|
|
823
|
+
}
|
|
824
|
+
const intent = fromTimerCanceled(event.attributes.value, sequence, timersByStartEventId);
|
|
825
|
+
if (intent) {
|
|
826
|
+
commandHistory.push({
|
|
827
|
+
intent,
|
|
828
|
+
metadata: buildCommandMetadata(event, event.attributes.value),
|
|
829
|
+
});
|
|
830
|
+
sequence += 1;
|
|
831
|
+
}
|
|
832
|
+
break;
|
|
833
|
+
}
|
|
437
834
|
case EventType.SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: {
|
|
438
835
|
if (event.attributes?.case !== 'signalExternalWorkflowExecutionInitiatedEventAttributes') {
|
|
439
836
|
break;
|
|
@@ -448,6 +845,20 @@ const reconstructDeterminismState = (events, intake, failureMetadata, precompute
|
|
|
448
845
|
}
|
|
449
846
|
break;
|
|
450
847
|
}
|
|
848
|
+
case EventType.REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: {
|
|
849
|
+
if (event.attributes?.case !== 'requestCancelExternalWorkflowExecutionInitiatedEventAttributes') {
|
|
850
|
+
break;
|
|
851
|
+
}
|
|
852
|
+
const intent = fromRequestCancelExternalWorkflow(event.attributes.value, sequence, intake);
|
|
853
|
+
if (intent) {
|
|
854
|
+
commandHistory.push({
|
|
855
|
+
intent,
|
|
856
|
+
metadata: buildCommandMetadata(event, event.attributes.value),
|
|
857
|
+
});
|
|
858
|
+
sequence += 1;
|
|
859
|
+
}
|
|
860
|
+
break;
|
|
861
|
+
}
|
|
451
862
|
case EventType.WORKFLOW_EXECUTION_SIGNALED: {
|
|
452
863
|
if (event.attributes?.case !== 'workflowExecutionSignaledEventAttributes') {
|
|
453
864
|
break;
|
|
@@ -481,15 +892,61 @@ const reconstructDeterminismState = (events, intake, failureMetadata, precompute
|
|
|
481
892
|
}
|
|
482
893
|
break;
|
|
483
894
|
}
|
|
895
|
+
case EventType.MARKER_RECORDED: {
|
|
896
|
+
if (event.attributes?.case !== 'markerRecordedEventAttributes') {
|
|
897
|
+
break;
|
|
898
|
+
}
|
|
899
|
+
if (event.attributes.value.markerName === DETERMINISM_MARKER_NAME) {
|
|
900
|
+
break;
|
|
901
|
+
}
|
|
902
|
+
const intent = yield* fromMarkerRecorded(event.attributes.value, sequence, intake);
|
|
903
|
+
if (intent) {
|
|
904
|
+
commandHistory.push({
|
|
905
|
+
intent,
|
|
906
|
+
metadata: buildCommandMetadata(event, event.attributes.value),
|
|
907
|
+
});
|
|
908
|
+
sequence += 1;
|
|
909
|
+
}
|
|
910
|
+
break;
|
|
911
|
+
}
|
|
912
|
+
case EventType.UPSERT_WORKFLOW_SEARCH_ATTRIBUTES: {
|
|
913
|
+
if (event.attributes?.case !== 'upsertWorkflowSearchAttributesEventAttributes') {
|
|
914
|
+
break;
|
|
915
|
+
}
|
|
916
|
+
const intent = yield* fromUpsertSearchAttributes(event.attributes.value, sequence, intake);
|
|
917
|
+
if (intent) {
|
|
918
|
+
commandHistory.push({
|
|
919
|
+
intent,
|
|
920
|
+
metadata: buildCommandMetadata(event, event.attributes.value),
|
|
921
|
+
});
|
|
922
|
+
sequence += 1;
|
|
923
|
+
}
|
|
924
|
+
break;
|
|
925
|
+
}
|
|
926
|
+
case EventType.WORKFLOW_PROPERTIES_MODIFIED: {
|
|
927
|
+
if (event.attributes?.case !== 'workflowPropertiesModifiedEventAttributes') {
|
|
928
|
+
break;
|
|
929
|
+
}
|
|
930
|
+
const intent = yield* fromWorkflowPropertiesModified(event.attributes.value, sequence, intake);
|
|
931
|
+
if (intent) {
|
|
932
|
+
commandHistory.push({
|
|
933
|
+
intent,
|
|
934
|
+
metadata: buildCommandMetadata(event, event.attributes.value),
|
|
935
|
+
});
|
|
936
|
+
sequence += 1;
|
|
937
|
+
}
|
|
938
|
+
break;
|
|
939
|
+
}
|
|
484
940
|
default:
|
|
485
941
|
break;
|
|
486
942
|
}
|
|
487
943
|
}
|
|
944
|
+
const resolvedFailureMetadata = failureMetadata ?? seededState?.failureMetadata;
|
|
488
945
|
let determinismState = {
|
|
489
946
|
commandHistory,
|
|
490
|
-
randomValues: [],
|
|
491
|
-
timeValues: [],
|
|
492
|
-
...(
|
|
947
|
+
randomValues: seededState?.randomValues ?? [],
|
|
948
|
+
timeValues: seededState?.timeValues ?? [],
|
|
949
|
+
...(resolvedFailureMetadata ? { failureMetadata: resolvedFailureMetadata } : {}),
|
|
493
950
|
signals: signalRecords,
|
|
494
951
|
queries: queryRecords,
|
|
495
952
|
...(updates.length > 0 ? { updates } : {}),
|
|
@@ -497,7 +954,7 @@ const reconstructDeterminismState = (events, intake, failureMetadata, precompute
|
|
|
497
954
|
determinismState = mergePendingQueryRequests(determinismState, intake.queries);
|
|
498
955
|
return {
|
|
499
956
|
determinismState,
|
|
500
|
-
lastEventId: resolveHistoryLastEventId(events),
|
|
957
|
+
lastEventId: options?.historyLastEventId ?? resolveHistoryLastEventId(events),
|
|
501
958
|
hasDeterminismMarker: false,
|
|
502
959
|
...(replayUpdateInvocations.length > 0 ? { updates: replayUpdateInvocations } : {}),
|
|
503
960
|
};
|
|
@@ -622,6 +1079,85 @@ const fromContinueAsNew = (attributes, sequence, intake) => Effect.gen(function*
|
|
|
622
1079
|
};
|
|
623
1080
|
return intent;
|
|
624
1081
|
});
|
|
1082
|
+
const fromActivityTaskCancelRequested = (attributes, sequence, scheduledActivities) => {
|
|
1083
|
+
const scheduledEventId = normalizeBigintIdentifier(attributes.scheduledEventId);
|
|
1084
|
+
if (!scheduledEventId) {
|
|
1085
|
+
return undefined;
|
|
1086
|
+
}
|
|
1087
|
+
const activityId = scheduledActivities.get(scheduledEventId);
|
|
1088
|
+
if (!activityId) {
|
|
1089
|
+
return undefined;
|
|
1090
|
+
}
|
|
1091
|
+
return {
|
|
1092
|
+
id: `cancel-activity-${sequence}`,
|
|
1093
|
+
kind: 'request-cancel-activity',
|
|
1094
|
+
sequence,
|
|
1095
|
+
activityId,
|
|
1096
|
+
scheduledEventId,
|
|
1097
|
+
};
|
|
1098
|
+
};
|
|
1099
|
+
const fromTimerCanceled = (attributes, sequence, timersByStartEventId) => {
|
|
1100
|
+
const startedEventId = normalizeBigintIdentifier(attributes.startedEventId);
|
|
1101
|
+
const timerId = attributes.timerId || (startedEventId ? timersByStartEventId.get(startedEventId) : undefined);
|
|
1102
|
+
if (!timerId) {
|
|
1103
|
+
return undefined;
|
|
1104
|
+
}
|
|
1105
|
+
return {
|
|
1106
|
+
id: `cancel-timer-${sequence}`,
|
|
1107
|
+
kind: 'cancel-timer',
|
|
1108
|
+
sequence,
|
|
1109
|
+
timerId,
|
|
1110
|
+
startedEventId,
|
|
1111
|
+
};
|
|
1112
|
+
};
|
|
1113
|
+
const fromRequestCancelExternalWorkflow = (attributes, sequence, intake) => {
|
|
1114
|
+
const workflowId = attributes.workflowExecution?.workflowId ?? intake.info.workflowId;
|
|
1115
|
+
if (!workflowId) {
|
|
1116
|
+
return undefined;
|
|
1117
|
+
}
|
|
1118
|
+
return {
|
|
1119
|
+
id: `cancel-external-${sequence}`,
|
|
1120
|
+
kind: 'request-cancel-external-workflow',
|
|
1121
|
+
sequence,
|
|
1122
|
+
namespace: attributes.namespace || intake.info.namespace,
|
|
1123
|
+
workflowId,
|
|
1124
|
+
runId: attributes.workflowExecution?.runId || undefined,
|
|
1125
|
+
childWorkflowOnly: attributes.childWorkflowOnly ?? false,
|
|
1126
|
+
reason: attributes.reason || undefined,
|
|
1127
|
+
};
|
|
1128
|
+
};
|
|
1129
|
+
const fromMarkerRecorded = (attributes, sequence, intake) => Effect.gen(function* () {
|
|
1130
|
+
const markerName = attributes.markerName || 'marker';
|
|
1131
|
+
const details = yield* decodeMarkerDetails(intake.dataConverter, attributes.details);
|
|
1132
|
+
return {
|
|
1133
|
+
id: `record-marker-${sequence}`,
|
|
1134
|
+
kind: 'record-marker',
|
|
1135
|
+
sequence,
|
|
1136
|
+
markerName,
|
|
1137
|
+
details,
|
|
1138
|
+
};
|
|
1139
|
+
});
|
|
1140
|
+
const fromUpsertSearchAttributes = (attributes, sequence, intake) => Effect.gen(function* () {
|
|
1141
|
+
const searchAttributes = yield* decodeSearchAttributes(intake.dataConverter, attributes.searchAttributes);
|
|
1142
|
+
if (!searchAttributes) {
|
|
1143
|
+
return undefined;
|
|
1144
|
+
}
|
|
1145
|
+
return {
|
|
1146
|
+
id: `upsert-search-attributes-${sequence}`,
|
|
1147
|
+
kind: 'upsert-search-attributes',
|
|
1148
|
+
sequence,
|
|
1149
|
+
searchAttributes,
|
|
1150
|
+
};
|
|
1151
|
+
});
|
|
1152
|
+
const fromWorkflowPropertiesModified = (attributes, sequence, intake) => Effect.gen(function* () {
|
|
1153
|
+
const memo = yield* decodeMemo(intake.dataConverter, attributes.upsertedMemo);
|
|
1154
|
+
return {
|
|
1155
|
+
id: `modify-workflow-properties-${sequence}`,
|
|
1156
|
+
kind: 'modify-workflow-properties',
|
|
1157
|
+
sequence,
|
|
1158
|
+
memo,
|
|
1159
|
+
};
|
|
1160
|
+
});
|
|
625
1161
|
const collectWorkflowUpdateEntries = (events) => {
|
|
626
1162
|
const updates = [];
|
|
627
1163
|
for (const event of events) {
|
|
@@ -715,7 +1251,19 @@ const collectWorkflowUpdateEntries = (events) => {
|
|
|
715
1251
|
return updates;
|
|
716
1252
|
};
|
|
717
1253
|
const collectWorkflowUpdateInvocations = async (events, dataConverter) => {
|
|
718
|
-
const
|
|
1254
|
+
const candidates = [];
|
|
1255
|
+
let order = 0;
|
|
1256
|
+
const parseSequencing = (value) => {
|
|
1257
|
+
if (!value) {
|
|
1258
|
+
return undefined;
|
|
1259
|
+
}
|
|
1260
|
+
try {
|
|
1261
|
+
return BigInt(value);
|
|
1262
|
+
}
|
|
1263
|
+
catch {
|
|
1264
|
+
return undefined;
|
|
1265
|
+
}
|
|
1266
|
+
};
|
|
719
1267
|
for (const event of events) {
|
|
720
1268
|
if (event.eventType === EventType.WORKFLOW_EXECUTION_UPDATE_ACCEPTED) {
|
|
721
1269
|
if (event.attributes.case !== 'workflowExecutionUpdateAcceptedEventAttributes') {
|
|
@@ -731,25 +1279,8 @@ const collectWorkflowUpdateInvocations = async (events, dataConverter) => {
|
|
|
731
1279
|
dataConverter,
|
|
732
1280
|
});
|
|
733
1281
|
if (invocation) {
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
continue;
|
|
737
|
-
}
|
|
738
|
-
if (event.eventType === EventType.WORKFLOW_EXECUTION_UPDATE_REJECTED) {
|
|
739
|
-
if (event.attributes.case !== 'workflowExecutionUpdateRejectedEventAttributes') {
|
|
740
|
-
continue;
|
|
741
|
-
}
|
|
742
|
-
const attrs = event.attributes.value;
|
|
743
|
-
const invocation = await buildUpdateInvocationFromRequest({
|
|
744
|
-
request: attrs.rejectedRequest,
|
|
745
|
-
protocolInstanceId: attrs.protocolInstanceId,
|
|
746
|
-
requestMessageId: attrs.rejectedRequestMessageId,
|
|
747
|
-
sequencingEventId: attrs.rejectedRequestSequencingEventId,
|
|
748
|
-
fallbackEventId: event.eventId,
|
|
749
|
-
dataConverter,
|
|
750
|
-
});
|
|
751
|
-
if (invocation) {
|
|
752
|
-
invocations.push(invocation);
|
|
1282
|
+
candidates.push({ invocation, sequencing: parseSequencing(invocation.sequencingEventId), order });
|
|
1283
|
+
order += 1;
|
|
753
1284
|
}
|
|
754
1285
|
continue;
|
|
755
1286
|
}
|
|
@@ -767,11 +1298,59 @@ const collectWorkflowUpdateInvocations = async (events, dataConverter) => {
|
|
|
767
1298
|
dataConverter,
|
|
768
1299
|
});
|
|
769
1300
|
if (invocation) {
|
|
770
|
-
|
|
1301
|
+
candidates.push({ invocation, sequencing: parseSequencing(invocation.sequencingEventId), order });
|
|
1302
|
+
order += 1;
|
|
771
1303
|
}
|
|
772
1304
|
}
|
|
773
1305
|
}
|
|
774
|
-
|
|
1306
|
+
const byUpdateId = new Map();
|
|
1307
|
+
for (const candidate of candidates) {
|
|
1308
|
+
const updateId = candidate.invocation.updateId;
|
|
1309
|
+
const existing = byUpdateId.get(updateId);
|
|
1310
|
+
if (!existing) {
|
|
1311
|
+
byUpdateId.set(updateId, candidate);
|
|
1312
|
+
continue;
|
|
1313
|
+
}
|
|
1314
|
+
const existingSeq = existing.sequencing;
|
|
1315
|
+
const candidateSeq = candidate.sequencing;
|
|
1316
|
+
if (candidateSeq !== undefined && existingSeq !== undefined) {
|
|
1317
|
+
if (candidateSeq < existingSeq) {
|
|
1318
|
+
byUpdateId.set(updateId, candidate);
|
|
1319
|
+
}
|
|
1320
|
+
continue;
|
|
1321
|
+
}
|
|
1322
|
+
if (candidateSeq !== undefined && existingSeq === undefined) {
|
|
1323
|
+
byUpdateId.set(updateId, candidate);
|
|
1324
|
+
continue;
|
|
1325
|
+
}
|
|
1326
|
+
if (candidateSeq === undefined && existingSeq !== undefined) {
|
|
1327
|
+
continue;
|
|
1328
|
+
}
|
|
1329
|
+
if (candidate.order < existing.order) {
|
|
1330
|
+
byUpdateId.set(updateId, candidate);
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
return [...byUpdateId.values()]
|
|
1334
|
+
.sort((left, right) => {
|
|
1335
|
+
const leftSeq = left.sequencing;
|
|
1336
|
+
const rightSeq = right.sequencing;
|
|
1337
|
+
if (leftSeq !== undefined && rightSeq !== undefined) {
|
|
1338
|
+
if (leftSeq < rightSeq) {
|
|
1339
|
+
return -1;
|
|
1340
|
+
}
|
|
1341
|
+
if (leftSeq > rightSeq) {
|
|
1342
|
+
return 1;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
else if (leftSeq !== undefined) {
|
|
1346
|
+
return -1;
|
|
1347
|
+
}
|
|
1348
|
+
else if (rightSeq !== undefined) {
|
|
1349
|
+
return 1;
|
|
1350
|
+
}
|
|
1351
|
+
return left.order - right.order;
|
|
1352
|
+
})
|
|
1353
|
+
.map((entry) => entry.invocation);
|
|
775
1354
|
};
|
|
776
1355
|
const buildUpdateInvocationFromRequest = async (input) => {
|
|
777
1356
|
const req = input.request;
|
|
@@ -807,6 +1386,49 @@ const normalizeUpdateInvocationPayload = (values) => {
|
|
|
807
1386
|
return values;
|
|
808
1387
|
};
|
|
809
1388
|
const decodePayloadArray = (converter, payloads) => Effect.tryPromise(async () => await decodePayloadsToValues(converter, payloads?.payloads ?? []));
|
|
1389
|
+
const decodeMarkerDetails = (converter, details) => Effect.tryPromise(async () => {
|
|
1390
|
+
if (!details || Object.keys(details).length === 0) {
|
|
1391
|
+
return undefined;
|
|
1392
|
+
}
|
|
1393
|
+
const decoded = {};
|
|
1394
|
+
for (const [key, payloads] of Object.entries(details)) {
|
|
1395
|
+
const values = await decodePayloadsToValues(converter, payloads?.payloads ?? []);
|
|
1396
|
+
if (values.length === 0) {
|
|
1397
|
+
continue;
|
|
1398
|
+
}
|
|
1399
|
+
decoded[key] = values.length === 1 ? values[0] : values;
|
|
1400
|
+
}
|
|
1401
|
+
return Object.keys(decoded).length > 0 ? decoded : undefined;
|
|
1402
|
+
});
|
|
1403
|
+
const decodeSearchAttributes = (converter, input) => Effect.tryPromise(async () => {
|
|
1404
|
+
const fields = input?.indexedFields;
|
|
1405
|
+
if (!fields || Object.keys(fields).length === 0) {
|
|
1406
|
+
return undefined;
|
|
1407
|
+
}
|
|
1408
|
+
const decoded = {};
|
|
1409
|
+
for (const [key, payload] of Object.entries(fields)) {
|
|
1410
|
+
const values = await decodePayloadsToValues(converter, payload ? [payload] : []);
|
|
1411
|
+
if (values.length === 0) {
|
|
1412
|
+
continue;
|
|
1413
|
+
}
|
|
1414
|
+
decoded[key] = values.length === 1 ? values[0] : values;
|
|
1415
|
+
}
|
|
1416
|
+
return decoded;
|
|
1417
|
+
});
|
|
1418
|
+
const decodeMemo = (converter, memo) => Effect.tryPromise(async () => {
|
|
1419
|
+
const decoded = {};
|
|
1420
|
+
const fields = memo?.fields ?? {};
|
|
1421
|
+
for (const [key, payload] of Object.entries(fields)) {
|
|
1422
|
+
const values = await decodePayloadsToValues(converter, payload ? [payload] : []);
|
|
1423
|
+
if (values.length === 0) {
|
|
1424
|
+
decoded[key] = undefined;
|
|
1425
|
+
}
|
|
1426
|
+
else {
|
|
1427
|
+
decoded[key] = values.length === 1 ? values[0] : values;
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
return decoded;
|
|
1431
|
+
});
|
|
810
1432
|
const convertRetryPolicy = (policy) => {
|
|
811
1433
|
if (!policy) {
|
|
812
1434
|
return undefined;
|
|
@@ -870,19 +1492,63 @@ const resolveOutcomeStatus = (outcome) => {
|
|
|
870
1492
|
}
|
|
871
1493
|
return undefined;
|
|
872
1494
|
};
|
|
1495
|
+
const applyDeterminismDelta = (state, delta) => ({
|
|
1496
|
+
...state,
|
|
1497
|
+
commandHistory: [...state.commandHistory, ...(delta.commandHistory ?? [])],
|
|
1498
|
+
randomValues: [...state.randomValues, ...(delta.randomValues ?? [])],
|
|
1499
|
+
timeValues: [...state.timeValues, ...(delta.timeValues ?? [])],
|
|
1500
|
+
signals: [...state.signals, ...(delta.signals ?? [])],
|
|
1501
|
+
queries: [...state.queries, ...(delta.queries ?? [])],
|
|
1502
|
+
...(delta.updates
|
|
1503
|
+
? { updates: [...(state.updates ?? []), ...(delta.updates ?? [])] }
|
|
1504
|
+
: state.updates
|
|
1505
|
+
? { updates: [...state.updates] }
|
|
1506
|
+
: {}),
|
|
1507
|
+
...(delta.logCount !== undefined ? { logCount: delta.logCount } : {}),
|
|
1508
|
+
...(delta.failureMetadata ? { failureMetadata: delta.failureMetadata } : {}),
|
|
1509
|
+
});
|
|
1510
|
+
const applyDeterminismMarker = (state, marker) => {
|
|
1511
|
+
if (marker.schemaVersion === 1) {
|
|
1512
|
+
return marker.determinismState;
|
|
1513
|
+
}
|
|
1514
|
+
const base = marker.determinismState ?? state;
|
|
1515
|
+
if (!base) {
|
|
1516
|
+
return undefined;
|
|
1517
|
+
}
|
|
1518
|
+
if (!marker.determinismDelta) {
|
|
1519
|
+
return base;
|
|
1520
|
+
}
|
|
1521
|
+
return applyDeterminismDelta(base, marker.determinismDelta);
|
|
1522
|
+
};
|
|
873
1523
|
const sanitizeDeterminismMarkerEnvelope = (input) => {
|
|
874
1524
|
const schemaVersion = input.schemaVersion;
|
|
875
|
-
if (schemaVersion !==
|
|
1525
|
+
if (schemaVersion !== DETERMINISM_MARKER_SCHEMA_VERSION_V1 &&
|
|
1526
|
+
schemaVersion !== DETERMINISM_MARKER_SCHEMA_VERSION_V2) {
|
|
876
1527
|
throw new Error(`Unsupported determinism marker schema version: ${String(schemaVersion)}`);
|
|
877
1528
|
}
|
|
878
1529
|
const workflow = sanitizeWorkflowInfo(input.workflow);
|
|
879
|
-
const determinismState = sanitizeDeterminismState(input.determinismState);
|
|
880
1530
|
const lastEventId = normalizeEventId(input.lastEventId);
|
|
881
1531
|
const recordedAt = sanitizeRecordedAt(input.recordedAtIso);
|
|
1532
|
+
if (schemaVersion === DETERMINISM_MARKER_SCHEMA_VERSION_V1) {
|
|
1533
|
+
const determinismState = sanitizeDeterminismState(input.determinismState);
|
|
1534
|
+
return {
|
|
1535
|
+
schemaVersion: DETERMINISM_MARKER_SCHEMA_VERSION_V1,
|
|
1536
|
+
workflow,
|
|
1537
|
+
determinismState,
|
|
1538
|
+
lastEventId,
|
|
1539
|
+
recordedAtIso: recordedAt,
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1542
|
+
const determinismState = input.determinismState !== undefined ? sanitizeDeterminismState(input.determinismState) : undefined;
|
|
1543
|
+
const determinismDelta = input.determinismDelta !== undefined ? sanitizeDeterminismDelta(input.determinismDelta) : undefined;
|
|
1544
|
+
if (!determinismState && !determinismDelta) {
|
|
1545
|
+
throw new Error('Determinism marker missing determinism state or delta');
|
|
1546
|
+
}
|
|
882
1547
|
return {
|
|
883
|
-
schemaVersion:
|
|
1548
|
+
schemaVersion: DETERMINISM_MARKER_SCHEMA_VERSION_V2,
|
|
884
1549
|
workflow,
|
|
885
|
-
determinismState,
|
|
1550
|
+
...(determinismState ? { determinismState } : {}),
|
|
1551
|
+
...(determinismDelta ? { determinismDelta } : {}),
|
|
886
1552
|
lastEventId,
|
|
887
1553
|
recordedAtIso: recordedAt,
|
|
888
1554
|
};
|
|
@@ -906,6 +1572,7 @@ const sanitizeDeterminismState = (value) => {
|
|
|
906
1572
|
const commandHistoryRaw = value.commandHistory;
|
|
907
1573
|
const randomValuesRaw = value.randomValues;
|
|
908
1574
|
const timeValuesRaw = value.timeValues;
|
|
1575
|
+
const logCountRaw = value.logCount;
|
|
909
1576
|
const signalsRaw = Array.isArray(value.signals) ? value.signals : [];
|
|
910
1577
|
const queriesRaw = Array.isArray(value.queries) ? value.queries : [];
|
|
911
1578
|
const updatesRaw = value.updates;
|
|
@@ -924,6 +1591,7 @@ const sanitizeDeterminismState = (value) => {
|
|
|
924
1591
|
});
|
|
925
1592
|
const randomValues = randomValuesRaw.map((val, index) => coerceNumber(val, `determinism.randomValues[${index}]`));
|
|
926
1593
|
const timeValues = timeValuesRaw.map((val, index) => coerceNumber(val, `determinism.timeValues[${index}]`));
|
|
1594
|
+
const logCount = coerceOptionalNumber(logCountRaw, 'determinism.logCount');
|
|
927
1595
|
const failureMetadata = sanitizeFailureMetadata(value.failureMetadata);
|
|
928
1596
|
const signals = signalsRaw.map((record, index) => sanitizeSignalRecord(record, index));
|
|
929
1597
|
const queries = queriesRaw.map((record, index) => sanitizeQueryRecord(record, index));
|
|
@@ -932,12 +1600,40 @@ const sanitizeDeterminismState = (value) => {
|
|
|
932
1600
|
commandHistory,
|
|
933
1601
|
randomValues,
|
|
934
1602
|
timeValues,
|
|
1603
|
+
...(logCount !== undefined ? { logCount } : {}),
|
|
935
1604
|
...(failureMetadata ? { failureMetadata } : {}),
|
|
936
1605
|
signals,
|
|
937
1606
|
queries,
|
|
938
1607
|
...(updates ? { updates } : {}),
|
|
939
1608
|
};
|
|
940
1609
|
};
|
|
1610
|
+
const sanitizeDeterminismDelta = (value) => {
|
|
1611
|
+
if (!isRecord(value)) {
|
|
1612
|
+
throw new Error('Determinism marker contained invalid determinism delta');
|
|
1613
|
+
}
|
|
1614
|
+
const commandHistoryRaw = value.commandHistory ?? [];
|
|
1615
|
+
const randomValuesRaw = value.randomValues ?? [];
|
|
1616
|
+
const timeValuesRaw = value.timeValues ?? [];
|
|
1617
|
+
const signalsRaw = value.signals ?? [];
|
|
1618
|
+
const queriesRaw = value.queries ?? [];
|
|
1619
|
+
if (!Array.isArray(commandHistoryRaw) ||
|
|
1620
|
+
!Array.isArray(randomValuesRaw) ||
|
|
1621
|
+
!Array.isArray(timeValuesRaw) ||
|
|
1622
|
+
!Array.isArray(signalsRaw) ||
|
|
1623
|
+
!Array.isArray(queriesRaw)) {
|
|
1624
|
+
throw new Error('Determinism marker contained invalid determinism delta shape');
|
|
1625
|
+
}
|
|
1626
|
+
return sanitizeDeterminismState({
|
|
1627
|
+
commandHistory: commandHistoryRaw,
|
|
1628
|
+
randomValues: randomValuesRaw,
|
|
1629
|
+
timeValues: timeValuesRaw,
|
|
1630
|
+
signals: signalsRaw,
|
|
1631
|
+
queries: queriesRaw,
|
|
1632
|
+
...(value.updates !== undefined ? { updates: value.updates } : {}),
|
|
1633
|
+
...(value.logCount !== undefined ? { logCount: value.logCount } : {}),
|
|
1634
|
+
...(value.failureMetadata !== undefined ? { failureMetadata: value.failureMetadata } : {}),
|
|
1635
|
+
});
|
|
1636
|
+
};
|
|
941
1637
|
const sanitizeCommandMetadata = (value, index) => {
|
|
942
1638
|
if (value === undefined || value === null) {
|
|
943
1639
|
return undefined;
|