@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,14 +2,15 @@ import { pathToFileURL } from 'node:url';
|
|
|
2
2
|
import { create } from '@bufbuild/protobuf';
|
|
3
3
|
import { Code, ConnectError, createClient } from '@connectrpc/connect';
|
|
4
4
|
import { createGrpcTransport } from '@connectrpc/connect-node';
|
|
5
|
-
import { Cause, Effect, Exit, Fiber } from 'effect';
|
|
5
|
+
import { Cause, Duration, Effect, Exit, Fiber, Schedule } 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';
|
|
@@ -23,10 +24,12 @@ import { HistoryEventFilterType, TimeoutType, VersioningBehavior } from '../prot
|
|
|
23
24
|
import { StickyExecutionAttributesSchema, TaskQueueSchema, } from '../proto/temporal/api/taskqueue/v1/message_pb';
|
|
24
25
|
import { GetWorkflowExecutionHistoryRequestSchema, PollActivityTaskQueueRequestSchema, PollWorkflowTaskQueueRequestSchema, RespondActivityTaskCanceledRequestSchema, RespondActivityTaskCompletedRequestSchema, RespondActivityTaskFailedRequestSchema, RespondQueryTaskCompletedRequestSchema, RespondWorkflowTaskCompletedRequestSchema, RespondWorkflowTaskFailedRequestSchema, } from '../proto/temporal/api/workflowservice/v1/request_response_pb';
|
|
25
26
|
import { WorkflowService } from '../proto/temporal/api/workflowservice/v1/service_pb';
|
|
27
|
+
import { intentsEqual, stableStringify } from '../workflow/determinism';
|
|
26
28
|
import { WorkflowNondeterminismError } from '../workflow/errors';
|
|
27
29
|
import { WorkflowExecutor } from '../workflow/executor';
|
|
30
|
+
import { CHILD_WORKFLOW_COMPLETED_SIGNAL, } from '../workflow/inbound';
|
|
28
31
|
import { WorkflowRegistry } from '../workflow/registry';
|
|
29
|
-
import { DETERMINISM_MARKER_NAME, diffDeterminismState,
|
|
32
|
+
import { DETERMINISM_MARKER_NAME, diffDeterminismState, encodeDeterminismMarkerDetailsWithSize, ingestWorkflowHistory, resolveHistoryLastEventId, } from '../workflow/replay';
|
|
30
33
|
import { runWithActivityContext } from './activity-context';
|
|
31
34
|
import { checkWorkerVersioningCapability, registerWorkerBuildIdCompatibility } from './build-id';
|
|
32
35
|
import { makeWorkerScheduler, } from './concurrency';
|
|
@@ -52,11 +55,19 @@ const mergeUpdateInvocations = (historyInvocations, messageInvocations) => {
|
|
|
52
55
|
const POLL_TIMEOUT_MS = 60_000;
|
|
53
56
|
const RESPOND_TIMEOUT_MS = 15_000;
|
|
54
57
|
const HISTORY_FETCH_TIMEOUT_MS = 60_000;
|
|
58
|
+
const DEFAULT_METRICS_FLUSH_INTERVAL_MS = 10_000;
|
|
55
59
|
const HEARTBEAT_RETRY_INITIAL_DELAY_MS = 250;
|
|
56
60
|
const HEARTBEAT_RETRY_MAX_DELAY_MS = 5_000;
|
|
57
61
|
const HEARTBEAT_RETRY_MAX_ATTEMPTS = 5;
|
|
58
62
|
const HEARTBEAT_RETRY_BACKOFF = 2;
|
|
59
63
|
const HEARTBEAT_RETRY_JITTER = 0.2;
|
|
64
|
+
const parseMetricsFlushInterval = (value) => {
|
|
65
|
+
if (!value) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
const parsed = Number.parseInt(value, 10);
|
|
69
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
70
|
+
};
|
|
60
71
|
const STICKY_QUEUE_PREFIX = 'sticky';
|
|
61
72
|
const COMPLETION_COMMAND_TYPES = new Set([
|
|
62
73
|
CommandType.COMPLETE_WORKFLOW_EXECUTION,
|
|
@@ -66,7 +77,6 @@ const COMPLETION_COMMAND_TYPES = new Set([
|
|
|
66
77
|
export class WorkerRuntime {
|
|
67
78
|
static async create(options = {}) {
|
|
68
79
|
const config = options.config ?? (await loadTemporalConfig());
|
|
69
|
-
const dataConverter = options.dataConverter ?? createDefaultDataConverter();
|
|
70
80
|
const namespace = options.namespace ?? config.namespace;
|
|
71
81
|
if (!namespace) {
|
|
72
82
|
throw new Error('Temporal namespace must be provided');
|
|
@@ -80,12 +90,6 @@ export class WorkerRuntime {
|
|
|
80
90
|
if (workflows.length === 0) {
|
|
81
91
|
throw new Error('No workflow definitions were registered; provide workflows or workflowsPath');
|
|
82
92
|
}
|
|
83
|
-
const registry = new WorkflowRegistry();
|
|
84
|
-
registry.registerMany(workflows);
|
|
85
|
-
const executor = new WorkflowExecutor({
|
|
86
|
-
registry,
|
|
87
|
-
dataConverter,
|
|
88
|
-
});
|
|
89
93
|
const activities = options.activities ?? {};
|
|
90
94
|
const observability = await Effect.runPromise(createObservabilityServices({
|
|
91
95
|
logLevel: config.logLevel,
|
|
@@ -96,7 +100,23 @@ export class WorkerRuntime {
|
|
|
96
100
|
metricsRegistry: options.metrics,
|
|
97
101
|
metricsExporter: options.metricsExporter,
|
|
98
102
|
}));
|
|
99
|
-
const { logger, metricsRegistry, metricsExporter } = observability;
|
|
103
|
+
const { logger, metricsRegistry, metricsExporter, openTelemetry } = observability;
|
|
104
|
+
const metricsFlushIntervalMs = options.metricsFlushIntervalMs ??
|
|
105
|
+
parseMetricsFlushInterval(process.env.TEMPORAL_METRICS_FLUSH_INTERVAL_MS) ??
|
|
106
|
+
DEFAULT_METRICS_FLUSH_INTERVAL_MS;
|
|
107
|
+
const dataConverter = options.dataConverter ??
|
|
108
|
+
createDefaultDataConverter({
|
|
109
|
+
payloadCodecs: buildCodecsFromConfig(config.payloadCodecs),
|
|
110
|
+
logger,
|
|
111
|
+
metricsRegistry,
|
|
112
|
+
});
|
|
113
|
+
const registry = new WorkflowRegistry();
|
|
114
|
+
registry.registerMany(workflows);
|
|
115
|
+
const executor = new WorkflowExecutor({
|
|
116
|
+
registry,
|
|
117
|
+
dataConverter,
|
|
118
|
+
logger,
|
|
119
|
+
});
|
|
100
120
|
const runtimeMetrics = await WorkerRuntime.#initMetrics(metricsRegistry);
|
|
101
121
|
let workflowService;
|
|
102
122
|
if (options.workflowService) {
|
|
@@ -156,6 +176,11 @@ export class WorkerRuntime {
|
|
|
156
176
|
const stickySchedulingEnabled = options.stickyScheduling ??
|
|
157
177
|
(config.stickySchedulingEnabled &&
|
|
158
178
|
(hasStickyCacheInstance ? config.workerStickyCacheSize > 0 : stickyCacheSize > 0));
|
|
179
|
+
const determinismMarkerMode = config.determinismMarkerMode;
|
|
180
|
+
const determinismMarkerIntervalTasks = config.determinismMarkerIntervalTasks;
|
|
181
|
+
const determinismMarkerFullSnapshotIntervalTasks = config.determinismMarkerFullSnapshotIntervalTasks;
|
|
182
|
+
const determinismMarkerSkipUnchanged = config.determinismMarkerSkipUnchanged;
|
|
183
|
+
const determinismMarkerMaxDetailBytes = config.determinismMarkerMaxDetailBytes;
|
|
159
184
|
const deploymentName = options.deployment?.name ?? config.workerDeploymentName ?? WorkerRuntime.#defaultDeploymentName(taskQueue);
|
|
160
185
|
const buildId = options.deployment?.buildId ?? config.workerBuildId ?? identity;
|
|
161
186
|
const workerVersioningMode = options.deployment?.versioningMode ?? WorkerVersioningMode.UNVERSIONED;
|
|
@@ -167,6 +192,7 @@ export class WorkerRuntime {
|
|
|
167
192
|
buildId,
|
|
168
193
|
workerVersioningMode,
|
|
169
194
|
});
|
|
195
|
+
const rpcDeploymentOptions = workerVersioningMode === WorkerVersioningMode.VERSIONED ? deploymentOptions : undefined;
|
|
170
196
|
if (workerVersioningMode === WorkerVersioningMode.VERSIONED) {
|
|
171
197
|
const capability = await checkWorkerVersioningCapability(workflowService, namespace, taskQueue);
|
|
172
198
|
if (capability.supported) {
|
|
@@ -181,6 +207,22 @@ export class WorkerRuntime {
|
|
|
181
207
|
}));
|
|
182
208
|
}
|
|
183
209
|
}
|
|
210
|
+
const tracingEnabled = options.tracingEnabled ?? config.tracingInterceptorsEnabled ?? false;
|
|
211
|
+
const workerInterceptorBuilder = options.interceptorBuilder ?? {
|
|
212
|
+
build: (input) => makeDefaultWorkerInterceptors(input),
|
|
213
|
+
};
|
|
214
|
+
const defaultWorkerInterceptors = await Effect.runPromise(workerInterceptorBuilder.build({
|
|
215
|
+
namespace,
|
|
216
|
+
taskQueue,
|
|
217
|
+
identity,
|
|
218
|
+
buildId,
|
|
219
|
+
logger,
|
|
220
|
+
metricsRegistry,
|
|
221
|
+
metricsExporter,
|
|
222
|
+
dataConverter,
|
|
223
|
+
tracingEnabled,
|
|
224
|
+
}));
|
|
225
|
+
const workerInterceptors = [...defaultWorkerInterceptors, ...(options.interceptors ?? [])];
|
|
184
226
|
const activityLifecycle = await Effect.runPromise(makeActivityLifecycle({
|
|
185
227
|
heartbeatIntervalMs: config.activityHeartbeatIntervalMs,
|
|
186
228
|
heartbeatRpcTimeoutMs: config.activityHeartbeatRpcTimeoutMs,
|
|
@@ -204,11 +246,17 @@ export class WorkerRuntime {
|
|
|
204
246
|
workflowConcurrency,
|
|
205
247
|
activityConcurrency,
|
|
206
248
|
stickySchedulingEnabled,
|
|
249
|
+
determinismMarkerMode,
|
|
250
|
+
determinismMarkerIntervalTasks,
|
|
251
|
+
determinismMarkerFullSnapshotIntervalTasks,
|
|
252
|
+
determinismMarkerSkipUnchanged,
|
|
253
|
+
determinismMarkerMaxDetailBytes,
|
|
207
254
|
deploymentName,
|
|
208
255
|
buildId,
|
|
209
256
|
logLevel: config.logLevel,
|
|
210
257
|
logFormat: config.logFormat,
|
|
211
258
|
metricsExporter: config.metricsExporter.type,
|
|
259
|
+
metricsFlushIntervalMs,
|
|
212
260
|
}));
|
|
213
261
|
return new WorkerRuntime({
|
|
214
262
|
config,
|
|
@@ -220,7 +268,9 @@ export class WorkerRuntime {
|
|
|
220
268
|
logger,
|
|
221
269
|
metricsRegistry,
|
|
222
270
|
metricsExporter,
|
|
271
|
+
metricsFlushIntervalMs,
|
|
223
272
|
metrics: runtimeMetrics,
|
|
273
|
+
openTelemetry,
|
|
224
274
|
namespace,
|
|
225
275
|
taskQueue,
|
|
226
276
|
identity,
|
|
@@ -230,9 +280,16 @@ export class WorkerRuntime {
|
|
|
230
280
|
stickyQueue,
|
|
231
281
|
stickyScheduleToStartTimeoutMs,
|
|
232
282
|
deploymentOptions,
|
|
283
|
+
rpcDeploymentOptions,
|
|
233
284
|
versioningBehavior,
|
|
234
285
|
stickySchedulingEnabled,
|
|
286
|
+
determinismMarkerMode,
|
|
287
|
+
determinismMarkerIntervalTasks,
|
|
288
|
+
determinismMarkerFullSnapshotIntervalTasks,
|
|
289
|
+
determinismMarkerSkipUnchanged,
|
|
290
|
+
determinismMarkerMaxDetailBytes,
|
|
235
291
|
workflowPollerCount,
|
|
292
|
+
interceptors: workerInterceptors,
|
|
236
293
|
});
|
|
237
294
|
}
|
|
238
295
|
#config;
|
|
@@ -245,16 +302,26 @@ export class WorkerRuntime {
|
|
|
245
302
|
#metricsRegistry;
|
|
246
303
|
#metrics;
|
|
247
304
|
#metricsExporter;
|
|
305
|
+
#metricsFlushIntervalMs;
|
|
306
|
+
#metricsFlushInFlight = false;
|
|
307
|
+
#openTelemetry;
|
|
248
308
|
#namespace;
|
|
249
309
|
#taskQueue;
|
|
250
310
|
#identity;
|
|
311
|
+
#interceptors;
|
|
251
312
|
#activityLifecycle;
|
|
252
313
|
#scheduler;
|
|
253
314
|
#stickyCache;
|
|
254
315
|
#stickyQueue;
|
|
255
316
|
#stickySchedulingEnabled;
|
|
317
|
+
#determinismMarkerMode;
|
|
318
|
+
#determinismMarkerIntervalTasks;
|
|
319
|
+
#determinismMarkerFullSnapshotIntervalTasks;
|
|
320
|
+
#determinismMarkerSkipUnchanged;
|
|
321
|
+
#determinismMarkerMaxDetailBytes;
|
|
256
322
|
#stickyAttributes;
|
|
257
323
|
#deploymentOptions;
|
|
324
|
+
#rpcDeploymentOptions;
|
|
258
325
|
#versioningBehavior;
|
|
259
326
|
#workflowPollerCount;
|
|
260
327
|
#running = false;
|
|
@@ -272,15 +339,24 @@ export class WorkerRuntime {
|
|
|
272
339
|
this.#metricsRegistry = params.metricsRegistry;
|
|
273
340
|
this.#metrics = params.metrics;
|
|
274
341
|
this.#metricsExporter = params.metricsExporter;
|
|
342
|
+
this.#metricsFlushIntervalMs = Math.max(0, params.metricsFlushIntervalMs);
|
|
343
|
+
this.#openTelemetry = params.openTelemetry;
|
|
275
344
|
this.#namespace = params.namespace;
|
|
276
345
|
this.#taskQueue = params.taskQueue;
|
|
277
346
|
this.#identity = params.identity;
|
|
347
|
+
this.#interceptors = params.interceptors;
|
|
278
348
|
this.#activityLifecycle = params.activityLifecycle;
|
|
279
349
|
this.#scheduler = params.scheduler;
|
|
280
350
|
this.#stickyCache = params.stickyCache;
|
|
281
351
|
this.#stickyQueue = params.stickyQueue;
|
|
282
352
|
this.#stickySchedulingEnabled = params.stickySchedulingEnabled;
|
|
353
|
+
this.#determinismMarkerMode = params.determinismMarkerMode;
|
|
354
|
+
this.#determinismMarkerIntervalTasks = Math.max(1, params.determinismMarkerIntervalTasks);
|
|
355
|
+
this.#determinismMarkerFullSnapshotIntervalTasks = Math.max(1, params.determinismMarkerFullSnapshotIntervalTasks);
|
|
356
|
+
this.#determinismMarkerSkipUnchanged = params.determinismMarkerSkipUnchanged;
|
|
357
|
+
this.#determinismMarkerMaxDetailBytes = Math.max(0, params.determinismMarkerMaxDetailBytes);
|
|
283
358
|
this.#deploymentOptions = params.deploymentOptions;
|
|
359
|
+
this.#rpcDeploymentOptions = params.rpcDeploymentOptions;
|
|
284
360
|
this.#versioningBehavior = params.versioningBehavior;
|
|
285
361
|
this.#workflowPollerCount = params.workflowPollerCount;
|
|
286
362
|
this.#stickyAttributes = create(StickyExecutionAttributesSchema, {
|
|
@@ -353,6 +429,10 @@ export class WorkerRuntime {
|
|
|
353
429
|
workflowTaskCompleted: await makeCounter('temporal_worker_workflow_tasks_completed_total', 'Workflow tasks completed by the scheduler'),
|
|
354
430
|
activityTaskStarted: await makeCounter('temporal_worker_activity_tasks_started_total', 'Activity tasks dispatched to the scheduler'),
|
|
355
431
|
activityTaskCompleted: await makeCounter('temporal_worker_activity_tasks_completed_total', 'Activity tasks completed by the scheduler'),
|
|
432
|
+
queryTaskStarted: await makeCounter('temporal_worker_query_started_total', 'Query-only workflow tasks dispatched to the scheduler'),
|
|
433
|
+
queryTaskCompleted: await makeCounter('temporal_worker_query_completed_total', 'Query-only workflow tasks completed successfully'),
|
|
434
|
+
queryTaskFailed: await makeCounter('temporal_worker_query_failed_total', 'Query-only workflow tasks responded with failure'),
|
|
435
|
+
queryTaskLatency: await makeHistogram('temporal_worker_query_latency_ms', 'End-to-end workflow query latency (ms)'),
|
|
356
436
|
};
|
|
357
437
|
}
|
|
358
438
|
async run() {
|
|
@@ -377,6 +457,7 @@ export class WorkerRuntime {
|
|
|
377
457
|
if (!runtimeFiber) {
|
|
378
458
|
await this.#stopScheduler();
|
|
379
459
|
await this.#flushMetrics();
|
|
460
|
+
await this.#shutdownOpenTelemetry();
|
|
380
461
|
this.#running = false;
|
|
381
462
|
this.#log('info', 'temporal worker shutdown complete', this.#runtimeLogFields({ drained: false }));
|
|
382
463
|
return;
|
|
@@ -392,6 +473,7 @@ export class WorkerRuntime {
|
|
|
392
473
|
this.#runFiber = null;
|
|
393
474
|
this.#running = false;
|
|
394
475
|
}
|
|
476
|
+
await this.#shutdownOpenTelemetry();
|
|
395
477
|
this.#log('info', 'temporal worker shutdown complete', this.#runtimeLogFields({ drained: true }));
|
|
396
478
|
}
|
|
397
479
|
#buildRuntimeEffect() {
|
|
@@ -408,6 +490,10 @@ export class WorkerRuntime {
|
|
|
408
490
|
workflowPollers: runtime.#workflowPollerCount,
|
|
409
491
|
stickySchedulingEnabled: runtime.#stickySchedulingEnabled,
|
|
410
492
|
}));
|
|
493
|
+
if (runtime.#metricsFlushIntervalMs > 0) {
|
|
494
|
+
const flushLoop = Effect.repeat(Effect.promise(() => runtime.#flushMetrics()), Schedule.spaced(Duration.millis(runtime.#metricsFlushIntervalMs)));
|
|
495
|
+
yield* Effect.forkScoped(flushLoop);
|
|
496
|
+
}
|
|
411
497
|
if (pollerLoops.length > 0) {
|
|
412
498
|
yield* Effect.forEach(pollerLoops, (loop) => Effect.forkScoped(loop), { concurrency: 'unbounded' });
|
|
413
499
|
}
|
|
@@ -415,9 +501,16 @@ export class WorkerRuntime {
|
|
|
415
501
|
})).pipe(Effect.ensuring(Effect.promise(async () => {
|
|
416
502
|
await runtime.#stopScheduler();
|
|
417
503
|
await runtime.#flushMetrics();
|
|
504
|
+
await runtime.#shutdownOpenTelemetry();
|
|
418
505
|
runtime.#log('info', 'temporal worker runtime stopped', runtime.#runtimeLogFields());
|
|
419
506
|
})));
|
|
420
507
|
}
|
|
508
|
+
async #shutdownOpenTelemetry() {
|
|
509
|
+
if (!this.#openTelemetry) {
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
await this.#openTelemetry.shutdown();
|
|
513
|
+
}
|
|
421
514
|
async #awaitRuntimeFiber(fiber) {
|
|
422
515
|
const exit = await Effect.runPromiseExit(Fiber.join(fiber));
|
|
423
516
|
if (Exit.isFailure(exit)) {
|
|
@@ -453,7 +546,7 @@ export class WorkerRuntime {
|
|
|
453
546
|
namespace: this.#namespace,
|
|
454
547
|
taskQueue: create(TaskQueueSchema, { name: queueName }),
|
|
455
548
|
identity: this.#identity,
|
|
456
|
-
deploymentOptions: this.#
|
|
549
|
+
deploymentOptions: this.#rpcDeploymentOptions,
|
|
457
550
|
});
|
|
458
551
|
const pollOnce = this.#withRpcAbort(async (signal) => {
|
|
459
552
|
const start = Date.now();
|
|
@@ -487,7 +580,7 @@ export class WorkerRuntime {
|
|
|
487
580
|
namespace: this.#namespace,
|
|
488
581
|
taskQueue: create(TaskQueueSchema, { name: this.#taskQueue }),
|
|
489
582
|
identity: this.#identity,
|
|
490
|
-
deploymentOptions: this.#
|
|
583
|
+
deploymentOptions: this.#rpcDeploymentOptions,
|
|
491
584
|
});
|
|
492
585
|
const pollOnce = this.#withRpcAbort(async (signal) => {
|
|
493
586
|
const start = Date.now();
|
|
@@ -514,17 +607,38 @@ export class WorkerRuntime {
|
|
|
514
607
|
#isRpcAbortError(error) {
|
|
515
608
|
return isAbortError(error) || (error instanceof ConnectError && error.code === Code.Canceled);
|
|
516
609
|
}
|
|
610
|
+
#isBenignPollTimeout(error) {
|
|
611
|
+
if (error instanceof ConnectError) {
|
|
612
|
+
return error.code === Code.DeadlineExceeded || error.code === Code.Canceled;
|
|
613
|
+
}
|
|
614
|
+
if (error instanceof Error) {
|
|
615
|
+
const msg = error.message.toLowerCase();
|
|
616
|
+
return msg.includes('deadline') && msg.includes('exceeded');
|
|
617
|
+
}
|
|
618
|
+
if (typeof error === 'string') {
|
|
619
|
+
const msg = error.toLowerCase();
|
|
620
|
+
return msg.includes('deadline') && msg.includes('exceeded');
|
|
621
|
+
}
|
|
622
|
+
return false;
|
|
623
|
+
}
|
|
517
624
|
async #enqueueActivityTask(response) {
|
|
518
625
|
const taskToken = response.taskToken ?? new Uint8Array();
|
|
519
626
|
const envelope = {
|
|
520
627
|
taskToken,
|
|
521
|
-
handler: () => this.#
|
|
628
|
+
handler: () => this.#runActivityTask(response),
|
|
522
629
|
args: [],
|
|
523
630
|
};
|
|
524
631
|
await Effect.runPromise(this.#scheduler.enqueueActivity(envelope));
|
|
525
632
|
}
|
|
526
633
|
#handleWorkflowPollerError(queueName, error) {
|
|
527
634
|
return Effect.promise(async () => {
|
|
635
|
+
if (this.#isBenignPollTimeout(error)) {
|
|
636
|
+
this.#log('debug', 'workflow poll timeout (no tasks)', {
|
|
637
|
+
queueName,
|
|
638
|
+
namespace: this.#namespace,
|
|
639
|
+
});
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
528
642
|
this.#incrementCounter(this.#metrics.workflowPollErrors);
|
|
529
643
|
this.#log('warn', 'workflow polling failed', {
|
|
530
644
|
queueName,
|
|
@@ -536,6 +650,13 @@ export class WorkerRuntime {
|
|
|
536
650
|
}
|
|
537
651
|
#handleActivityPollerError(error) {
|
|
538
652
|
return Effect.promise(async () => {
|
|
653
|
+
if (this.#isBenignPollTimeout(error)) {
|
|
654
|
+
this.#log('debug', 'activity poll timeout (no tasks)', {
|
|
655
|
+
namespace: this.#namespace,
|
|
656
|
+
taskQueue: this.#taskQueue,
|
|
657
|
+
});
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
539
660
|
this.#incrementCounter(this.#metrics.activityPollErrors);
|
|
540
661
|
this.#log('warn', 'activity polling failed', {
|
|
541
662
|
namespace: this.#namespace,
|
|
@@ -547,10 +668,54 @@ export class WorkerRuntime {
|
|
|
547
668
|
}
|
|
548
669
|
async #handleWorkflowTask(response, nondeterminismRetry = 0) {
|
|
549
670
|
const execution = this.#resolveWorkflowExecution(response);
|
|
671
|
+
const queryCount = response.queries && typeof response.queries === 'object' && !Array.isArray(response.queries)
|
|
672
|
+
? Object.keys(response.queries).length
|
|
673
|
+
: Array.isArray(response.queries)
|
|
674
|
+
? response.queries.length
|
|
675
|
+
: 0;
|
|
676
|
+
const hasQueryRequests = Boolean(response.query) || queryCount > 0;
|
|
677
|
+
const hasUpdateMessages = (response.messages?.length ?? 0) > 0;
|
|
678
|
+
const kind = hasUpdateMessages
|
|
679
|
+
? 'worker.updateTask'
|
|
680
|
+
: hasQueryRequests
|
|
681
|
+
? 'worker.queryTask'
|
|
682
|
+
: 'worker.workflowTask';
|
|
683
|
+
const context = {
|
|
684
|
+
kind,
|
|
685
|
+
namespace: this.#namespace,
|
|
686
|
+
taskQueue: this.#taskQueue,
|
|
687
|
+
identity: this.#identity,
|
|
688
|
+
buildId: this.#deploymentOptions.buildId,
|
|
689
|
+
workflowId: execution.workflowId,
|
|
690
|
+
runId: execution.runId,
|
|
691
|
+
attempt: Number(response.attempt ?? 1),
|
|
692
|
+
metadata: { nondeterminismRetry },
|
|
693
|
+
};
|
|
694
|
+
const effect = runWorkerInterceptors(this.#interceptors, context, () => Effect.tryPromise(() => this.#processWorkflowTask(response, nondeterminismRetry, execution)));
|
|
695
|
+
await Effect.runPromise(effect);
|
|
696
|
+
}
|
|
697
|
+
async #processWorkflowTask(response, nondeterminismRetry = 0, executionOverride) {
|
|
698
|
+
const execution = executionOverride ?? this.#resolveWorkflowExecution(response);
|
|
550
699
|
const workflowTaskAttempt = Number(response.attempt ?? 1);
|
|
551
|
-
const
|
|
700
|
+
const isLegacyQueryTask = Boolean(response.query);
|
|
701
|
+
const queryStartTime = isLegacyQueryTask ? Date.now() : null;
|
|
702
|
+
if (isLegacyQueryTask) {
|
|
703
|
+
this.#incrementCounter(this.#metrics.queryTaskStarted);
|
|
704
|
+
}
|
|
705
|
+
const queryCount = response.queries && typeof response.queries === 'object' && !Array.isArray(response.queries)
|
|
706
|
+
? Object.keys(response.queries).length
|
|
707
|
+
: Array.isArray(response.queries)
|
|
708
|
+
? response.queries.length
|
|
709
|
+
: 0;
|
|
710
|
+
const hasHistoryEvents = (response.history?.events?.length ?? 0) > 0;
|
|
711
|
+
const hasMoreHistory = (response.nextPageToken?.length ?? 0) > 0;
|
|
712
|
+
const isLegacyQueryOnly = isLegacyQueryTask && !hasHistoryEvents && !hasMoreHistory;
|
|
713
|
+
const hasQueryPayloads = Boolean(response.query) || queryCount > 0;
|
|
714
|
+
const historyEvents = await this.#collectWorkflowHistory(execution, response, {
|
|
715
|
+
forceFullHistory: (hasQueryPayloads && !isLegacyQueryOnly) || nondeterminismRetry > 0,
|
|
716
|
+
skipFetchOnMissingStart: isLegacyQueryOnly,
|
|
717
|
+
});
|
|
552
718
|
const workflowType = this.#resolveWorkflowType(response, historyEvents);
|
|
553
|
-
const args = await this.#decodeWorkflowArgs(historyEvents);
|
|
554
719
|
const workflowInfo = this.#buildWorkflowInfo(workflowType, execution);
|
|
555
720
|
const collectedUpdates = await collectWorkflowUpdates({
|
|
556
721
|
messages: response.messages ?? [],
|
|
@@ -587,6 +752,26 @@ export class WorkerRuntime {
|
|
|
587
752
|
});
|
|
588
753
|
const stickyKey = this.#buildStickyKey(execution.workflowId, execution.runId);
|
|
589
754
|
const stickyEntry = stickyKey ? await this.#getStickyEntry(stickyKey) : undefined;
|
|
755
|
+
const decodedArgs = await this.#decodeWorkflowArgs(historyEvents);
|
|
756
|
+
let args = decodedArgs.args;
|
|
757
|
+
let argsSource = decodedArgs.hasStartEvent ? 'history' : 'unknown';
|
|
758
|
+
if (argsSource === 'unknown' && stickyEntry?.workflowArguments !== undefined) {
|
|
759
|
+
args = stickyEntry.workflowArguments;
|
|
760
|
+
argsSource = 'sticky';
|
|
761
|
+
}
|
|
762
|
+
if (argsSource === 'unknown' && !isLegacyQueryOnly) {
|
|
763
|
+
const fetchedArgs = await this.#fetchWorkflowStartArguments(execution);
|
|
764
|
+
if (fetchedArgs !== undefined) {
|
|
765
|
+
args = fetchedArgs;
|
|
766
|
+
argsSource = 'fetch';
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
if (argsSource !== 'history') {
|
|
770
|
+
this.#log('debug', 'workflow arguments resolved from fallback source', {
|
|
771
|
+
...baseLogFields,
|
|
772
|
+
argsSource,
|
|
773
|
+
});
|
|
774
|
+
}
|
|
590
775
|
const historyReplay = await this.#ingestDeterminismState(workflowInfo, historyEvents, {
|
|
591
776
|
queryRequests,
|
|
592
777
|
});
|
|
@@ -637,7 +822,21 @@ export class WorkerRuntime {
|
|
|
637
822
|
}
|
|
638
823
|
const expectedDeterminismState = previousState;
|
|
639
824
|
try {
|
|
640
|
-
const activityResults = await this.#extractActivityResolutions(historyEvents);
|
|
825
|
+
const { results: activityResults, scheduledEventIds: activityScheduleEventIds } = await this.#extractActivityResolutions(historyEvents);
|
|
826
|
+
const timerResults = await this.#extractTimerResolutions(historyEvents);
|
|
827
|
+
const mergedActivityResults = new Map(stickyEntry?.activityResults ?? []);
|
|
828
|
+
for (const [activityId, resolution] of activityResults.entries()) {
|
|
829
|
+
mergedActivityResults.set(activityId, resolution);
|
|
830
|
+
}
|
|
831
|
+
const mergedScheduleEventIds = new Map(stickyEntry?.activityScheduleEventIds ?? []);
|
|
832
|
+
for (const [activityId, scheduleId] of activityScheduleEventIds.entries()) {
|
|
833
|
+
mergedScheduleEventIds.set(activityId, scheduleId);
|
|
834
|
+
}
|
|
835
|
+
const mergedTimerResults = new Set(stickyEntry?.timerResults ?? []);
|
|
836
|
+
for (const timerId of timerResults) {
|
|
837
|
+
mergedTimerResults.add(timerId);
|
|
838
|
+
}
|
|
839
|
+
const pendingChildWorkflows = await this.#extractPendingChildWorkflows(historyEvents);
|
|
641
840
|
const replayUpdates = historyReplay?.updates ?? [];
|
|
642
841
|
const mergedUpdates = mergeUpdateInvocations(replayUpdates, collectedUpdates.invocations);
|
|
643
842
|
const output = await this.#executor.execute({
|
|
@@ -648,10 +847,14 @@ export class WorkerRuntime {
|
|
|
648
847
|
taskQueue: this.#taskQueue,
|
|
649
848
|
arguments: args,
|
|
650
849
|
determinismState: previousState,
|
|
651
|
-
activityResults,
|
|
850
|
+
activityResults: mergedActivityResults,
|
|
851
|
+
activityScheduleEventIds: mergedScheduleEventIds,
|
|
852
|
+
pendingChildWorkflows,
|
|
652
853
|
signalDeliveries,
|
|
854
|
+
timerResults: mergedTimerResults,
|
|
653
855
|
queryRequests,
|
|
654
856
|
updates: mergedUpdates,
|
|
857
|
+
mode: isLegacyQueryTask ? 'query' : 'workflow',
|
|
655
858
|
});
|
|
656
859
|
this.#log('debug', 'workflow query evaluation summary', {
|
|
657
860
|
...baseLogFields,
|
|
@@ -681,10 +884,38 @@ export class WorkerRuntime {
|
|
|
681
884
|
legacyQueryResult = entry;
|
|
682
885
|
}
|
|
683
886
|
}
|
|
887
|
+
if (isLegacyQueryTask) {
|
|
888
|
+
const target = legacyQueryResult ?? output.queryResults.find((entry) => entry.request.source === 'legacy');
|
|
889
|
+
if (!target) {
|
|
890
|
+
throw new Error('Legacy query result missing from workflow execution');
|
|
891
|
+
}
|
|
892
|
+
await this.#respondLegacyQueryTask(response, target);
|
|
893
|
+
if (queryStartTime !== null) {
|
|
894
|
+
this.#observeHistogram(this.#metrics.queryTaskLatency, Date.now() - queryStartTime);
|
|
895
|
+
}
|
|
896
|
+
this.#incrementCounter(this.#metrics.queryTaskCompleted);
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
684
899
|
const cacheBaselineEventId = this.#resolveCurrentStartedEventId(response) ?? historyReplay?.lastEventId ?? null;
|
|
685
|
-
const shouldRecordMarker = output.completion === 'pending';
|
|
686
900
|
let commandsForResponse = output.commands;
|
|
687
|
-
const
|
|
901
|
+
const workflowTaskCount = output.completion === 'pending'
|
|
902
|
+
? (stickyEntry?.workflowTaskCount ?? 0) + 1
|
|
903
|
+
: (stickyEntry?.workflowTaskCount ?? 0);
|
|
904
|
+
let markerHash = stickyEntry?.lastDeterminismMarkerHash;
|
|
905
|
+
let lastMarkerTask = stickyEntry?.lastDeterminismMarkerTask;
|
|
906
|
+
let lastFullSnapshotTask = stickyEntry?.lastDeterminismFullSnapshotTask;
|
|
907
|
+
let lastMarkerState = stickyEntry?.lastDeterminismMarkerState;
|
|
908
|
+
let markerType = output.completion === 'pending' ? this.#resolveDeterminismMarkerType(workflowTaskCount, stickyEntry) : null;
|
|
909
|
+
let determinismDelta;
|
|
910
|
+
let markerLastEventId = null;
|
|
911
|
+
const markerBaseState = historyReplay?.markerState ?? lastMarkerState;
|
|
912
|
+
const dispatchesForNewMessages = (output.updateDispatches ?? []).filter((dispatch) => {
|
|
913
|
+
if (dispatch.type === 'acceptance' || dispatch.type === 'rejection') {
|
|
914
|
+
return collectedUpdates.requestsByUpdateId.has(dispatch.updateId);
|
|
915
|
+
}
|
|
916
|
+
// Allow completion messages to be emitted even if the request metadata was seen on a prior task.
|
|
917
|
+
return true;
|
|
918
|
+
});
|
|
688
919
|
const updateProtocolMessages = await buildUpdateProtocolMessages({
|
|
689
920
|
dispatches: dispatchesForNewMessages,
|
|
690
921
|
collected: collectedUpdates,
|
|
@@ -692,40 +923,153 @@ export class WorkerRuntime {
|
|
|
692
923
|
defaultIdentity: this.#identity,
|
|
693
924
|
log: (level, message, fields) => this.#log(level, message, fields),
|
|
694
925
|
});
|
|
695
|
-
if (
|
|
696
|
-
if (
|
|
697
|
-
|
|
698
|
-
this.#log('debug', 'sticky cache snapshot persisted', {
|
|
926
|
+
if (markerType === 'delta') {
|
|
927
|
+
if (!markerBaseState) {
|
|
928
|
+
this.#log('warn', 'determinism delta base unavailable; falling back to full snapshot', {
|
|
699
929
|
...baseLogFields,
|
|
700
|
-
cacheBaselineEventId,
|
|
701
930
|
});
|
|
931
|
+
markerType = 'full';
|
|
702
932
|
}
|
|
703
933
|
else {
|
|
704
|
-
|
|
705
|
-
|
|
934
|
+
determinismDelta = this.#buildDeterminismDelta(markerBaseState, output.determinismState);
|
|
935
|
+
}
|
|
936
|
+
if (!determinismDelta) {
|
|
937
|
+
markerType = 'full';
|
|
938
|
+
}
|
|
939
|
+
else if (this.#determinismMarkerSkipUnchanged && this.#isDeterminismDeltaEmpty(determinismDelta)) {
|
|
940
|
+
markerType = null;
|
|
706
941
|
}
|
|
707
942
|
}
|
|
708
|
-
if (
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
const
|
|
719
|
-
|
|
943
|
+
if (markerType) {
|
|
944
|
+
markerLastEventId =
|
|
945
|
+
historyReplay?.lastEventId ??
|
|
946
|
+
this.#resolveWorkflowHistoryLastEventId(response) ??
|
|
947
|
+
stickyEntry?.lastEventId ??
|
|
948
|
+
null;
|
|
949
|
+
let resolvedMarkerType = markerType;
|
|
950
|
+
let markerDetails = null;
|
|
951
|
+
let markerSizeBytes = 0;
|
|
952
|
+
const limitBytes = this.#determinismMarkerMaxDetailBytes;
|
|
953
|
+
const markerHashCandidate = this.#hashDeterminismMarker(this.#buildDeterminismMarkerSignature(output.determinismState));
|
|
954
|
+
if (this.#determinismMarkerSkipUnchanged && markerHashCandidate === stickyEntry?.lastDeterminismMarkerHash) {
|
|
955
|
+
resolvedMarkerType = null;
|
|
956
|
+
}
|
|
957
|
+
if (resolvedMarkerType) {
|
|
958
|
+
const buildMarkerDetails = async (targetType) => Effect.runPromise(encodeDeterminismMarkerDetailsWithSize(this.#dataConverter, {
|
|
959
|
+
info: workflowInfo,
|
|
960
|
+
determinismState: output.determinismState,
|
|
961
|
+
determinismDelta: targetType === 'delta' ? determinismDelta : undefined,
|
|
962
|
+
markerType: targetType,
|
|
963
|
+
lastEventId: markerLastEventId,
|
|
964
|
+
}));
|
|
965
|
+
if (resolvedMarkerType === 'full') {
|
|
966
|
+
const fullResult = await buildMarkerDetails('full');
|
|
967
|
+
if (fullResult.sizeBytes > limitBytes && determinismDelta) {
|
|
968
|
+
this.#log('debug', 'determinism full marker payload exceeds size limit; falling back to delta', {
|
|
969
|
+
...baseLogFields,
|
|
970
|
+
sizeBytes: fullResult.sizeBytes,
|
|
971
|
+
limitBytes,
|
|
972
|
+
});
|
|
973
|
+
const deltaResult = await buildMarkerDetails('delta');
|
|
974
|
+
if (deltaResult.sizeBytes <= limitBytes) {
|
|
975
|
+
resolvedMarkerType = 'delta';
|
|
976
|
+
markerDetails = deltaResult.details;
|
|
977
|
+
markerSizeBytes = deltaResult.sizeBytes;
|
|
978
|
+
}
|
|
979
|
+
else {
|
|
980
|
+
this.#log('warn', 'determinism marker payload exceeds size limit', {
|
|
981
|
+
...baseLogFields,
|
|
982
|
+
markerType: 'delta',
|
|
983
|
+
sizeBytes: deltaResult.sizeBytes,
|
|
984
|
+
limitBytes,
|
|
985
|
+
});
|
|
986
|
+
resolvedMarkerType = null;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
else if (fullResult.sizeBytes <= limitBytes) {
|
|
990
|
+
markerDetails = fullResult.details;
|
|
991
|
+
markerSizeBytes = fullResult.sizeBytes;
|
|
992
|
+
}
|
|
993
|
+
else {
|
|
994
|
+
this.#log('warn', 'determinism marker payload exceeds size limit', {
|
|
995
|
+
...baseLogFields,
|
|
996
|
+
markerType: 'full',
|
|
997
|
+
sizeBytes: fullResult.sizeBytes,
|
|
998
|
+
limitBytes,
|
|
999
|
+
});
|
|
1000
|
+
resolvedMarkerType = null;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
else if (resolvedMarkerType === 'delta') {
|
|
1004
|
+
const deltaResult = await buildMarkerDetails('delta');
|
|
1005
|
+
if (deltaResult.sizeBytes <= limitBytes) {
|
|
1006
|
+
markerDetails = deltaResult.details;
|
|
1007
|
+
markerSizeBytes = deltaResult.sizeBytes;
|
|
1008
|
+
}
|
|
1009
|
+
else {
|
|
1010
|
+
this.#log('warn', 'determinism marker payload exceeds size limit', {
|
|
1011
|
+
...baseLogFields,
|
|
1012
|
+
markerType: 'delta',
|
|
1013
|
+
sizeBytes: deltaResult.sizeBytes,
|
|
1014
|
+
limitBytes,
|
|
1015
|
+
});
|
|
1016
|
+
resolvedMarkerType = null;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
if (resolvedMarkerType) {
|
|
1021
|
+
markerHash = markerHashCandidate;
|
|
1022
|
+
lastMarkerTask = workflowTaskCount;
|
|
1023
|
+
if (resolvedMarkerType === 'full') {
|
|
1024
|
+
lastFullSnapshotTask = workflowTaskCount;
|
|
1025
|
+
}
|
|
1026
|
+
lastMarkerState = output.determinismState;
|
|
1027
|
+
if (markerDetails) {
|
|
1028
|
+
this.#log('debug', 'determinism marker recorded', {
|
|
1029
|
+
...baseLogFields,
|
|
1030
|
+
markerType: resolvedMarkerType,
|
|
1031
|
+
sizeBytes: markerSizeBytes,
|
|
1032
|
+
});
|
|
1033
|
+
const markerCommand = this.#buildDeterminismMarkerCommand(markerDetails);
|
|
1034
|
+
commandsForResponse = this.#injectDeterminismMarker(commandsForResponse, markerCommand);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
let pendingStickyUpdate = null;
|
|
1039
|
+
let shouldClearSticky = false;
|
|
1040
|
+
if (stickyKey) {
|
|
1041
|
+
if (output.completion === 'pending') {
|
|
1042
|
+
const resolvedWorkflowArgs = argsSource === 'unknown' ? stickyEntry?.workflowArguments : args;
|
|
1043
|
+
pendingStickyUpdate = {
|
|
1044
|
+
determinismState: output.determinismState,
|
|
1045
|
+
cacheBaselineEventId,
|
|
1046
|
+
workflowType,
|
|
1047
|
+
metadata: {
|
|
1048
|
+
workflowTaskCount,
|
|
1049
|
+
lastDeterminismMarkerHash: markerHash,
|
|
1050
|
+
lastDeterminismMarkerTask: lastMarkerTask,
|
|
1051
|
+
lastDeterminismFullSnapshotTask: lastFullSnapshotTask,
|
|
1052
|
+
...(lastMarkerState ? { lastDeterminismMarkerState: lastMarkerState } : {}),
|
|
1053
|
+
...(resolvedWorkflowArgs !== undefined ? { workflowArguments: resolvedWorkflowArgs } : {}),
|
|
1054
|
+
activityResults: mergedActivityResults,
|
|
1055
|
+
activityScheduleEventIds: mergedScheduleEventIds,
|
|
1056
|
+
timerResults: mergedTimerResults,
|
|
1057
|
+
},
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
1060
|
+
else {
|
|
1061
|
+
shouldClearSticky = true;
|
|
1062
|
+
}
|
|
720
1063
|
}
|
|
721
1064
|
const shouldRespondWorkflowTask = hasMultiQueries || !hasLegacyQueries;
|
|
1065
|
+
let workflowTaskCommitted = false;
|
|
722
1066
|
if (shouldRespondWorkflowTask) {
|
|
723
1067
|
const completion = create(RespondWorkflowTaskCompletedRequestSchema, {
|
|
724
1068
|
taskToken: response.taskToken,
|
|
725
1069
|
commands: commandsForResponse,
|
|
726
1070
|
identity: this.#identity,
|
|
727
1071
|
namespace: this.#namespace,
|
|
728
|
-
deploymentOptions: this.#
|
|
1072
|
+
deploymentOptions: this.#rpcDeploymentOptions,
|
|
729
1073
|
queryResults: multiQueryResults,
|
|
730
1074
|
...(this.#stickySchedulingEnabled && !hasLegacyQueries ? { stickyAttributes: this.#stickyAttributes } : {}),
|
|
731
1075
|
...(this.#versioningBehavior !== null ? { versioningBehavior: this.#versioningBehavior } : {}),
|
|
@@ -733,6 +1077,7 @@ export class WorkerRuntime {
|
|
|
733
1077
|
});
|
|
734
1078
|
try {
|
|
735
1079
|
await this.#workflowService.respondWorkflowTaskCompleted(completion, { timeoutMs: RESPOND_TIMEOUT_MS });
|
|
1080
|
+
workflowTaskCommitted = true;
|
|
736
1081
|
}
|
|
737
1082
|
catch (rpcError) {
|
|
738
1083
|
this.#log('error', 'debug: respondWorkflowTaskCompleted failed', {
|
|
@@ -741,6 +1086,10 @@ export class WorkerRuntime {
|
|
|
741
1086
|
});
|
|
742
1087
|
if (this.#isTaskNotFoundError(rpcError)) {
|
|
743
1088
|
this.#logWorkflowTaskNotFound('respondWorkflowTaskCompleted', execution);
|
|
1089
|
+
if (stickyKey) {
|
|
1090
|
+
await this.#removeStickyEntry(stickyKey);
|
|
1091
|
+
await this.#removeStickyEntriesForWorkflow(stickyKey.workflowId);
|
|
1092
|
+
}
|
|
744
1093
|
return;
|
|
745
1094
|
}
|
|
746
1095
|
throw rpcError;
|
|
@@ -749,17 +1098,48 @@ export class WorkerRuntime {
|
|
|
749
1098
|
if (legacyQueryResult) {
|
|
750
1099
|
await this.#respondLegacyQueryTask(response, legacyQueryResult);
|
|
751
1100
|
}
|
|
1101
|
+
if (stickyKey && workflowTaskCommitted) {
|
|
1102
|
+
if (pendingStickyUpdate) {
|
|
1103
|
+
await this.#upsertStickyEntry(stickyKey, pendingStickyUpdate.determinismState, pendingStickyUpdate.cacheBaselineEventId, pendingStickyUpdate.workflowType, pendingStickyUpdate.metadata);
|
|
1104
|
+
this.#log('debug', 'sticky cache snapshot persisted', {
|
|
1105
|
+
...baseLogFields,
|
|
1106
|
+
cacheBaselineEventId: pendingStickyUpdate.cacheBaselineEventId,
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
else if (shouldClearSticky) {
|
|
1110
|
+
await this.#removeStickyEntry(stickyKey);
|
|
1111
|
+
await this.#removeStickyEntriesForWorkflow(stickyKey.workflowId);
|
|
1112
|
+
this.#log('debug', 'sticky cache entry cleared (workflow completed)', baseLogFields);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
752
1115
|
}
|
|
753
1116
|
catch (error) {
|
|
754
1117
|
let stickyEntryCleared = false;
|
|
755
1118
|
if (stickyKey) {
|
|
756
1119
|
await this.#removeStickyEntry(stickyKey);
|
|
1120
|
+
await this.#removeStickyEntriesForWorkflow(stickyKey.workflowId);
|
|
757
1121
|
stickyEntryCleared = true;
|
|
758
1122
|
}
|
|
759
1123
|
if (this.#isTaskNotFoundError(error)) {
|
|
760
1124
|
this.#logWorkflowTaskNotFound('respondWorkflowTaskCompleted', execution);
|
|
761
1125
|
return;
|
|
762
1126
|
}
|
|
1127
|
+
if (isLegacyQueryTask) {
|
|
1128
|
+
this.#incrementCounter(this.#metrics.queryTaskFailed);
|
|
1129
|
+
if (queryStartTime !== null) {
|
|
1130
|
+
this.#observeHistogram(this.#metrics.queryTaskLatency, Date.now() - queryStartTime);
|
|
1131
|
+
}
|
|
1132
|
+
if (error instanceof WorkflowNondeterminismError) {
|
|
1133
|
+
const mismatches = await this.#computeNondeterminismMismatches(error, expectedDeterminismState);
|
|
1134
|
+
this.#incrementCounter(this.#metrics.nondeterminism);
|
|
1135
|
+
this.#log('error', 'workflow query nondeterminism detected', {
|
|
1136
|
+
...baseLogFields,
|
|
1137
|
+
mismatches,
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
await this.#respondLegacyQueryFailure(response, error);
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
763
1143
|
if (error instanceof WorkflowNondeterminismError) {
|
|
764
1144
|
const mismatches = await this.#computeNondeterminismMismatches(error, expectedDeterminismState);
|
|
765
1145
|
if (stickyKey && stickyEntryCleared) {
|
|
@@ -820,24 +1200,98 @@ export class WorkerRuntime {
|
|
|
820
1200
|
queries: options?.queryRequests,
|
|
821
1201
|
}));
|
|
822
1202
|
}
|
|
823
|
-
async #collectWorkflowHistory(execution,
|
|
1203
|
+
async #collectWorkflowHistory(execution, response, options) {
|
|
824
1204
|
const events = [];
|
|
825
|
-
|
|
826
|
-
|
|
1205
|
+
const initialEvents = response.history?.events ?? [];
|
|
1206
|
+
if (initialEvents.length > 0) {
|
|
1207
|
+
events.push(...initialEvents);
|
|
1208
|
+
}
|
|
1209
|
+
let token = response.nextPageToken && response.nextPageToken.length > 0 ? response.nextPageToken : undefined;
|
|
1210
|
+
while (token && token.length > 0) {
|
|
827
1211
|
const page = await this.#fetchWorkflowHistoryPage(execution, token);
|
|
828
1212
|
if (page.events.length > 0) {
|
|
829
1213
|
events.push(...page.events);
|
|
830
1214
|
}
|
|
831
|
-
|
|
1215
|
+
token = page.nextPageToken && page.nextPageToken.length > 0 ? page.nextPageToken : undefined;
|
|
1216
|
+
}
|
|
1217
|
+
let sorted = this.#sortHistoryEvents(events);
|
|
1218
|
+
const shouldFetchFullHistory = options?.forceFullHistory || (!options?.skipFetchOnMissingStart && !this.#findWorkflowStartedEvent(sorted));
|
|
1219
|
+
if (shouldFetchFullHistory) {
|
|
1220
|
+
const fullHistory = await this.#fetchWorkflowHistoryFromStart(execution);
|
|
1221
|
+
if (fullHistory.length > 0) {
|
|
1222
|
+
sorted = this.#sortHistoryEvents([...fullHistory, ...sorted]);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
const bounded = this.#limitHistoryToStartedEvent(sorted, response.startedEventId);
|
|
1226
|
+
return this.#dedupeHistoryEvents(bounded);
|
|
1227
|
+
}
|
|
1228
|
+
#sortHistoryEvents(events) {
|
|
1229
|
+
return events.slice().sort((left, right) => {
|
|
1230
|
+
const leftId = this.#resolveEventIdForSort(left.eventId);
|
|
1231
|
+
const rightId = this.#resolveEventIdForSort(right.eventId);
|
|
1232
|
+
if (leftId === rightId) {
|
|
1233
|
+
return 0;
|
|
1234
|
+
}
|
|
1235
|
+
return leftId < rightId ? -1 : 1;
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
#dedupeHistoryEvents(events) {
|
|
1239
|
+
if (events.length <= 1) {
|
|
1240
|
+
return events;
|
|
1241
|
+
}
|
|
1242
|
+
const deduped = [];
|
|
1243
|
+
let lastId = null;
|
|
1244
|
+
for (const event of events) {
|
|
1245
|
+
const currentId = this.#resolveEventIdForSort(event.eventId);
|
|
1246
|
+
if (currentId !== 0n) {
|
|
1247
|
+
if (lastId !== null && currentId === lastId) {
|
|
1248
|
+
continue;
|
|
1249
|
+
}
|
|
1250
|
+
lastId = currentId;
|
|
1251
|
+
}
|
|
1252
|
+
deduped.push(event);
|
|
1253
|
+
}
|
|
1254
|
+
return deduped;
|
|
1255
|
+
}
|
|
1256
|
+
#limitHistoryToStartedEvent(events, startedEventId) {
|
|
1257
|
+
const limit = this.#resolveEventIdForSort(startedEventId);
|
|
1258
|
+
if (limit <= 0n) {
|
|
1259
|
+
return events;
|
|
1260
|
+
}
|
|
1261
|
+
const bounded = [];
|
|
1262
|
+
for (const event of events) {
|
|
1263
|
+
const currentId = this.#resolveEventIdForSort(event.eventId);
|
|
1264
|
+
if (currentId !== 0n && currentId > limit) {
|
|
832
1265
|
break;
|
|
833
1266
|
}
|
|
834
|
-
|
|
1267
|
+
bounded.push(event);
|
|
835
1268
|
}
|
|
836
|
-
return
|
|
1269
|
+
return bounded;
|
|
1270
|
+
}
|
|
1271
|
+
#resolveEventIdForSort(value) {
|
|
1272
|
+
if (value === undefined || value === null) {
|
|
1273
|
+
return 0n;
|
|
1274
|
+
}
|
|
1275
|
+
if (typeof value === 'bigint') {
|
|
1276
|
+
return value;
|
|
1277
|
+
}
|
|
1278
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
1279
|
+
return BigInt(value);
|
|
1280
|
+
}
|
|
1281
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
1282
|
+
try {
|
|
1283
|
+
return BigInt(value);
|
|
1284
|
+
}
|
|
1285
|
+
catch {
|
|
1286
|
+
return 0n;
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
return 0n;
|
|
837
1290
|
}
|
|
838
1291
|
async #extractActivityResolutions(events) {
|
|
839
1292
|
const resolutions = new Map();
|
|
840
1293
|
const scheduledActivityIds = new Map();
|
|
1294
|
+
const activityScheduleById = new Map();
|
|
841
1295
|
const normalizeEventId = (value) => {
|
|
842
1296
|
if (value === undefined || value === null) {
|
|
843
1297
|
return undefined;
|
|
@@ -868,6 +1322,7 @@ export class WorkerRuntime {
|
|
|
868
1322
|
const scheduledKey = normalizeEventId(event.eventId);
|
|
869
1323
|
if (activityId && scheduledKey) {
|
|
870
1324
|
scheduledActivityIds.set(scheduledKey, activityId);
|
|
1325
|
+
activityScheduleById.set(activityId, scheduledKey);
|
|
871
1326
|
}
|
|
872
1327
|
break;
|
|
873
1328
|
}
|
|
@@ -932,7 +1387,7 @@ export class WorkerRuntime {
|
|
|
932
1387
|
break;
|
|
933
1388
|
}
|
|
934
1389
|
}
|
|
935
|
-
return resolutions;
|
|
1390
|
+
return { results: resolutions, scheduledEventIds: activityScheduleById };
|
|
936
1391
|
}
|
|
937
1392
|
async #extractSignalDeliveries(events) {
|
|
938
1393
|
const deliveries = [];
|
|
@@ -945,31 +1400,155 @@ export class WorkerRuntime {
|
|
|
945
1400
|
}
|
|
946
1401
|
return value.toString();
|
|
947
1402
|
};
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
if (event.attributes?.case !== 'workflowExecutionSignaledEventAttributes') {
|
|
953
|
-
continue;
|
|
1403
|
+
const recordChildCompletion = (event, status, attrs) => {
|
|
1404
|
+
const workflowId = attrs.workflowExecution?.workflowId;
|
|
1405
|
+
if (!workflowId) {
|
|
1406
|
+
return;
|
|
954
1407
|
}
|
|
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
1408
|
deliveries.push({
|
|
962
|
-
name:
|
|
963
|
-
args
|
|
1409
|
+
name: CHILD_WORKFLOW_COMPLETED_SIGNAL,
|
|
1410
|
+
args: [
|
|
1411
|
+
{
|
|
1412
|
+
workflowId,
|
|
1413
|
+
...(attrs.workflowExecution?.runId ? { runId: attrs.workflowExecution.runId } : {}),
|
|
1414
|
+
status,
|
|
1415
|
+
},
|
|
1416
|
+
],
|
|
964
1417
|
metadata: {
|
|
965
1418
|
eventId: normalizeEventId(event.eventId),
|
|
966
|
-
workflowTaskCompletedEventId,
|
|
967
|
-
identity: attrs.identity ?? null,
|
|
968
1419
|
},
|
|
969
1420
|
});
|
|
1421
|
+
};
|
|
1422
|
+
for (const event of events) {
|
|
1423
|
+
switch (event.eventType) {
|
|
1424
|
+
case EventType.WORKFLOW_EXECUTION_SIGNALED: {
|
|
1425
|
+
if (event.attributes?.case !== 'workflowExecutionSignaledEventAttributes') {
|
|
1426
|
+
break;
|
|
1427
|
+
}
|
|
1428
|
+
const attrs = event.attributes.value;
|
|
1429
|
+
const args = await decodePayloadsToValues(this.#dataConverter, attrs.input?.payloads ?? []);
|
|
1430
|
+
const workflowTaskCompletedEventId = 'workflowTaskCompletedEventId' in attrs
|
|
1431
|
+
? normalizeEventId(attrs
|
|
1432
|
+
.workflowTaskCompletedEventId)
|
|
1433
|
+
: null;
|
|
1434
|
+
deliveries.push({
|
|
1435
|
+
name: attrs.signalName ?? 'unknown',
|
|
1436
|
+
args,
|
|
1437
|
+
metadata: {
|
|
1438
|
+
eventId: normalizeEventId(event.eventId),
|
|
1439
|
+
workflowTaskCompletedEventId,
|
|
1440
|
+
identity: attrs.identity ?? null,
|
|
1441
|
+
},
|
|
1442
|
+
});
|
|
1443
|
+
break;
|
|
1444
|
+
}
|
|
1445
|
+
case EventType.CHILD_WORKFLOW_EXECUTION_COMPLETED: {
|
|
1446
|
+
if (event.attributes?.case !== 'childWorkflowExecutionCompletedEventAttributes') {
|
|
1447
|
+
break;
|
|
1448
|
+
}
|
|
1449
|
+
recordChildCompletion(event, 'completed', event.attributes.value);
|
|
1450
|
+
break;
|
|
1451
|
+
}
|
|
1452
|
+
case EventType.CHILD_WORKFLOW_EXECUTION_FAILED: {
|
|
1453
|
+
if (event.attributes?.case !== 'childWorkflowExecutionFailedEventAttributes') {
|
|
1454
|
+
break;
|
|
1455
|
+
}
|
|
1456
|
+
recordChildCompletion(event, 'failed', event.attributes.value);
|
|
1457
|
+
break;
|
|
1458
|
+
}
|
|
1459
|
+
case EventType.CHILD_WORKFLOW_EXECUTION_CANCELED: {
|
|
1460
|
+
if (event.attributes?.case !== 'childWorkflowExecutionCanceledEventAttributes') {
|
|
1461
|
+
break;
|
|
1462
|
+
}
|
|
1463
|
+
recordChildCompletion(event, 'canceled', event.attributes.value);
|
|
1464
|
+
break;
|
|
1465
|
+
}
|
|
1466
|
+
case EventType.CHILD_WORKFLOW_EXECUTION_TERMINATED: {
|
|
1467
|
+
if (event.attributes?.case !== 'childWorkflowExecutionTerminatedEventAttributes') {
|
|
1468
|
+
break;
|
|
1469
|
+
}
|
|
1470
|
+
recordChildCompletion(event, 'terminated', event.attributes.value);
|
|
1471
|
+
break;
|
|
1472
|
+
}
|
|
1473
|
+
case EventType.CHILD_WORKFLOW_EXECUTION_TIMED_OUT: {
|
|
1474
|
+
if (event.attributes?.case !== 'childWorkflowExecutionTimedOutEventAttributes') {
|
|
1475
|
+
break;
|
|
1476
|
+
}
|
|
1477
|
+
recordChildCompletion(event, 'timed_out', event.attributes.value);
|
|
1478
|
+
break;
|
|
1479
|
+
}
|
|
1480
|
+
default:
|
|
1481
|
+
break;
|
|
1482
|
+
}
|
|
970
1483
|
}
|
|
971
1484
|
return deliveries;
|
|
972
1485
|
}
|
|
1486
|
+
async #extractTimerResolutions(events) {
|
|
1487
|
+
const fired = new Set();
|
|
1488
|
+
for (const event of events) {
|
|
1489
|
+
if (event.eventType !== EventType.TIMER_FIRED) {
|
|
1490
|
+
continue;
|
|
1491
|
+
}
|
|
1492
|
+
if (event.attributes?.case !== 'timerFiredEventAttributes') {
|
|
1493
|
+
continue;
|
|
1494
|
+
}
|
|
1495
|
+
const attrs = event.attributes.value;
|
|
1496
|
+
if (attrs.timerId) {
|
|
1497
|
+
fired.add(attrs.timerId);
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
return fired;
|
|
1501
|
+
}
|
|
1502
|
+
async #extractPendingChildWorkflows(events) {
|
|
1503
|
+
const pending = new Map();
|
|
1504
|
+
const normalizeEventId = (value) => {
|
|
1505
|
+
if (value === undefined || value === null) {
|
|
1506
|
+
return null;
|
|
1507
|
+
}
|
|
1508
|
+
if (typeof value === 'string') {
|
|
1509
|
+
return value;
|
|
1510
|
+
}
|
|
1511
|
+
return value.toString();
|
|
1512
|
+
};
|
|
1513
|
+
for (const event of events) {
|
|
1514
|
+
switch (event.eventType) {
|
|
1515
|
+
case EventType.START_CHILD_WORKFLOW_EXECUTION_INITIATED: {
|
|
1516
|
+
if (event.attributes?.case !== 'startChildWorkflowExecutionInitiatedEventAttributes') {
|
|
1517
|
+
break;
|
|
1518
|
+
}
|
|
1519
|
+
const attrs = event.attributes.value;
|
|
1520
|
+
const initiatedId = normalizeEventId(event.eventId);
|
|
1521
|
+
if (initiatedId && attrs.workflowId) {
|
|
1522
|
+
pending.set(initiatedId, attrs.workflowId);
|
|
1523
|
+
}
|
|
1524
|
+
break;
|
|
1525
|
+
}
|
|
1526
|
+
case EventType.CHILD_WORKFLOW_EXECUTION_STARTED: {
|
|
1527
|
+
if (event.attributes?.case !== 'childWorkflowExecutionStartedEventAttributes') {
|
|
1528
|
+
break;
|
|
1529
|
+
}
|
|
1530
|
+
const initiatedId = normalizeEventId(event.attributes.value.initiatedEventId);
|
|
1531
|
+
if (initiatedId) {
|
|
1532
|
+
pending.delete(initiatedId);
|
|
1533
|
+
}
|
|
1534
|
+
break;
|
|
1535
|
+
}
|
|
1536
|
+
case EventType.START_CHILD_WORKFLOW_EXECUTION_FAILED: {
|
|
1537
|
+
if (event.attributes?.case !== 'startChildWorkflowExecutionFailedEventAttributes') {
|
|
1538
|
+
break;
|
|
1539
|
+
}
|
|
1540
|
+
const initiatedId = normalizeEventId(event.attributes.value.initiatedEventId);
|
|
1541
|
+
if (initiatedId) {
|
|
1542
|
+
pending.delete(initiatedId);
|
|
1543
|
+
}
|
|
1544
|
+
break;
|
|
1545
|
+
}
|
|
1546
|
+
default:
|
|
1547
|
+
break;
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
return new Set(pending.values());
|
|
1551
|
+
}
|
|
973
1552
|
async #extractWorkflowQueryRequests(response) {
|
|
974
1553
|
const requests = [];
|
|
975
1554
|
const map = response.queries ?? {};
|
|
@@ -1035,6 +1614,34 @@ export class WorkerRuntime {
|
|
|
1035
1614
|
});
|
|
1036
1615
|
await this.#workflowService.respondQueryTaskCompleted(request, { timeoutMs: RESPOND_TIMEOUT_MS });
|
|
1037
1616
|
}
|
|
1617
|
+
async #respondLegacyQueryFailure(response, cause) {
|
|
1618
|
+
const failure = await encodeErrorToFailure(this.#dataConverter, cause);
|
|
1619
|
+
const message = cause instanceof Error ? cause.message : 'Workflow query failed';
|
|
1620
|
+
const request = create(RespondQueryTaskCompletedRequestSchema, {
|
|
1621
|
+
taskToken: response.taskToken ?? new Uint8Array(),
|
|
1622
|
+
completedType: QueryResultType.FAILED,
|
|
1623
|
+
errorMessage: message,
|
|
1624
|
+
namespace: this.#namespace,
|
|
1625
|
+
failure,
|
|
1626
|
+
cause: WorkflowTaskFailedCause.UNSPECIFIED,
|
|
1627
|
+
});
|
|
1628
|
+
try {
|
|
1629
|
+
await this.#workflowService.respondQueryTaskCompleted(request, { timeoutMs: RESPOND_TIMEOUT_MS });
|
|
1630
|
+
}
|
|
1631
|
+
catch (rpcError) {
|
|
1632
|
+
this.#log('error', 'respondQueryTaskCompleted failed for legacy query', {
|
|
1633
|
+
namespace: this.#namespace,
|
|
1634
|
+
workflowId: response.workflowExecution?.workflowId,
|
|
1635
|
+
runId: response.workflowExecution?.runId,
|
|
1636
|
+
error: rpcError instanceof Error ? rpcError.message : String(rpcError),
|
|
1637
|
+
});
|
|
1638
|
+
if (this.#isTaskNotFoundError(rpcError)) {
|
|
1639
|
+
this.#logWorkflowTaskNotFound('respondQueryTaskCompleted', this.#resolveWorkflowExecution(response));
|
|
1640
|
+
return;
|
|
1641
|
+
}
|
|
1642
|
+
throw rpcError;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1038
1645
|
async #fetchWorkflowHistoryPage(execution, nextPageToken) {
|
|
1039
1646
|
if (!execution.workflowId || !execution.runId) {
|
|
1040
1647
|
return { events: [], nextPageToken: new Uint8Array() };
|
|
@@ -1059,6 +1666,18 @@ export class WorkerRuntime {
|
|
|
1059
1666
|
nextPageToken: historyResponse.nextPageToken ?? new Uint8Array(),
|
|
1060
1667
|
};
|
|
1061
1668
|
}
|
|
1669
|
+
async #fetchWorkflowHistoryFromStart(execution) {
|
|
1670
|
+
const events = [];
|
|
1671
|
+
let token;
|
|
1672
|
+
do {
|
|
1673
|
+
const page = await this.#fetchWorkflowHistoryPage(execution, token);
|
|
1674
|
+
if (page.events.length > 0) {
|
|
1675
|
+
events.push(...page.events);
|
|
1676
|
+
}
|
|
1677
|
+
token = page.nextPageToken && page.nextPageToken.length > 0 ? page.nextPageToken : undefined;
|
|
1678
|
+
} while (token && token.length > 0);
|
|
1679
|
+
return events;
|
|
1680
|
+
}
|
|
1062
1681
|
#buildWorkflowInfo(workflowType, execution) {
|
|
1063
1682
|
return {
|
|
1064
1683
|
namespace: this.#namespace,
|
|
@@ -1071,19 +1690,145 @@ export class WorkerRuntime {
|
|
|
1071
1690
|
async #getStickyEntry(key) {
|
|
1072
1691
|
return await Effect.runPromise(this.#stickyCache.get(key));
|
|
1073
1692
|
}
|
|
1074
|
-
async #upsertStickyEntry(key, state, lastEventId, workflowType) {
|
|
1693
|
+
async #upsertStickyEntry(key, state, lastEventId, workflowType, metadata) {
|
|
1075
1694
|
const entry = {
|
|
1076
1695
|
key,
|
|
1077
1696
|
determinismState: state,
|
|
1078
1697
|
lastEventId,
|
|
1079
1698
|
lastAccessed: Date.now(),
|
|
1080
1699
|
workflowType,
|
|
1700
|
+
...metadata,
|
|
1081
1701
|
};
|
|
1082
1702
|
await Effect.runPromise(this.#stickyCache.upsert(entry));
|
|
1083
1703
|
}
|
|
1084
1704
|
#resolveWorkflowHistoryLastEventId(response) {
|
|
1085
1705
|
return resolveHistoryLastEventId(response.history?.events ?? []);
|
|
1086
1706
|
}
|
|
1707
|
+
#resolveDeterminismMarkerType(taskIndex, stickyEntry) {
|
|
1708
|
+
if (this.#determinismMarkerMode === 'never') {
|
|
1709
|
+
return null;
|
|
1710
|
+
}
|
|
1711
|
+
if (this.#determinismMarkerMode === 'always') {
|
|
1712
|
+
return 'full';
|
|
1713
|
+
}
|
|
1714
|
+
const interval = Math.max(1, this.#determinismMarkerIntervalTasks);
|
|
1715
|
+
const shouldRecord = taskIndex === 1 || interval === 1 || taskIndex % interval === 0;
|
|
1716
|
+
if (!shouldRecord) {
|
|
1717
|
+
return null;
|
|
1718
|
+
}
|
|
1719
|
+
if (this.#determinismMarkerMode === 'delta') {
|
|
1720
|
+
const lastFullSnapshot = stickyEntry?.lastDeterminismFullSnapshotTask ?? 0;
|
|
1721
|
+
const fullInterval = Math.max(1, this.#determinismMarkerFullSnapshotIntervalTasks);
|
|
1722
|
+
const fullDue = taskIndex === 1 || fullInterval === 1 || taskIndex - lastFullSnapshot >= fullInterval;
|
|
1723
|
+
return fullDue ? 'full' : 'delta';
|
|
1724
|
+
}
|
|
1725
|
+
return 'full';
|
|
1726
|
+
}
|
|
1727
|
+
#buildDeterminismDelta(previous, current) {
|
|
1728
|
+
if (!previous) {
|
|
1729
|
+
return undefined;
|
|
1730
|
+
}
|
|
1731
|
+
const normalizeOptional = (value) => (value === undefined ? null : value);
|
|
1732
|
+
const optionalEquals = (left, right) => normalizeOptional(left) === normalizeOptional(right);
|
|
1733
|
+
const signalsEqual = (left, right) => left.signalName === right.signalName &&
|
|
1734
|
+
left.handlerName === right.handlerName &&
|
|
1735
|
+
left.payloadHash === right.payloadHash &&
|
|
1736
|
+
optionalEquals(left.eventId, right.eventId) &&
|
|
1737
|
+
optionalEquals(left.workflowTaskCompletedEventId, right.workflowTaskCompletedEventId) &&
|
|
1738
|
+
optionalEquals(left.identity, right.identity);
|
|
1739
|
+
const queriesEqual = (left, right) => left.queryName === right.queryName &&
|
|
1740
|
+
left.handlerName === right.handlerName &&
|
|
1741
|
+
left.requestHash === right.requestHash &&
|
|
1742
|
+
optionalEquals(left.identity, right.identity) &&
|
|
1743
|
+
optionalEquals(left.queryId, right.queryId) &&
|
|
1744
|
+
left.resultHash === right.resultHash &&
|
|
1745
|
+
left.failureHash === right.failureHash;
|
|
1746
|
+
const updatesEqual = (left, right) => left.updateId === right.updateId &&
|
|
1747
|
+
left.stage === right.stage &&
|
|
1748
|
+
left.handlerName === right.handlerName &&
|
|
1749
|
+
left.identity === right.identity &&
|
|
1750
|
+
left.sequencingEventId === right.sequencingEventId &&
|
|
1751
|
+
left.messageId === right.messageId &&
|
|
1752
|
+
left.acceptedEventId === right.acceptedEventId &&
|
|
1753
|
+
left.outcome === right.outcome &&
|
|
1754
|
+
left.failureMessage === right.failureMessage &&
|
|
1755
|
+
left.historyEventId === right.historyEventId;
|
|
1756
|
+
const sliceAppend = (currentList, previousList, equals = Object.is) => {
|
|
1757
|
+
if (currentList.length < previousList.length) {
|
|
1758
|
+
return undefined;
|
|
1759
|
+
}
|
|
1760
|
+
for (let index = 0; index < previousList.length; index += 1) {
|
|
1761
|
+
if (!equals(previousList[index], currentList[index])) {
|
|
1762
|
+
return undefined;
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
return currentList.slice(previousList.length);
|
|
1766
|
+
};
|
|
1767
|
+
const commandHistory = sliceAppend(current.commandHistory, previous.commandHistory, (left, right) => intentsEqual(left?.intent, right?.intent));
|
|
1768
|
+
const randomValues = sliceAppend(current.randomValues, previous.randomValues);
|
|
1769
|
+
const timeValues = sliceAppend(current.timeValues, previous.timeValues);
|
|
1770
|
+
const signals = sliceAppend(current.signals, previous.signals, signalsEqual);
|
|
1771
|
+
const queries = sliceAppend(current.queries, previous.queries, queriesEqual);
|
|
1772
|
+
if (!commandHistory || !randomValues || !timeValues || !signals || !queries) {
|
|
1773
|
+
return undefined;
|
|
1774
|
+
}
|
|
1775
|
+
const updatesPrev = previous.updates ?? [];
|
|
1776
|
+
const updatesNext = current.updates ?? [];
|
|
1777
|
+
if (updatesNext.length < updatesPrev.length) {
|
|
1778
|
+
return undefined;
|
|
1779
|
+
}
|
|
1780
|
+
const updates = sliceAppend(updatesNext, updatesPrev, updatesEqual);
|
|
1781
|
+
if (!updates) {
|
|
1782
|
+
return undefined;
|
|
1783
|
+
}
|
|
1784
|
+
const logCount = current.logCount !== undefined && current.logCount !== previous.logCount ? current.logCount : undefined;
|
|
1785
|
+
const failureMetadata = current.failureMetadata && previous.failureMetadata
|
|
1786
|
+
? current.failureMetadata.eventId !== previous.failureMetadata.eventId ||
|
|
1787
|
+
current.failureMetadata.eventType !== previous.failureMetadata.eventType ||
|
|
1788
|
+
current.failureMetadata.failureType !== previous.failureMetadata.failureType ||
|
|
1789
|
+
current.failureMetadata.failureMessage !== previous.failureMetadata.failureMessage ||
|
|
1790
|
+
current.failureMetadata.retryState !== previous.failureMetadata.retryState
|
|
1791
|
+
? current.failureMetadata
|
|
1792
|
+
: undefined
|
|
1793
|
+
: (current.failureMetadata ?? undefined);
|
|
1794
|
+
return {
|
|
1795
|
+
commandHistory,
|
|
1796
|
+
randomValues,
|
|
1797
|
+
timeValues,
|
|
1798
|
+
signals,
|
|
1799
|
+
queries,
|
|
1800
|
+
...(updates.length > 0 ? { updates } : {}),
|
|
1801
|
+
...(logCount !== undefined ? { logCount } : {}),
|
|
1802
|
+
...(failureMetadata ? { failureMetadata } : {}),
|
|
1803
|
+
};
|
|
1804
|
+
}
|
|
1805
|
+
#isDeterminismDeltaEmpty(delta) {
|
|
1806
|
+
const updatesCount = delta.updates ? delta.updates.length : 0;
|
|
1807
|
+
return ((delta.commandHistory?.length ?? 0) === 0 &&
|
|
1808
|
+
(delta.randomValues?.length ?? 0) === 0 &&
|
|
1809
|
+
(delta.timeValues?.length ?? 0) === 0 &&
|
|
1810
|
+
(delta.signals?.length ?? 0) === 0 &&
|
|
1811
|
+
(delta.queries?.length ?? 0) === 0 &&
|
|
1812
|
+
updatesCount === 0 &&
|
|
1813
|
+
delta.logCount === undefined &&
|
|
1814
|
+
delta.failureMetadata === undefined);
|
|
1815
|
+
}
|
|
1816
|
+
#buildDeterminismMarkerSignature(state) {
|
|
1817
|
+
const failureMetadataHash = state.failureMetadata ? stableStringify(state.failureMetadata) : undefined;
|
|
1818
|
+
return {
|
|
1819
|
+
commandHistoryLength: state.commandHistory.length,
|
|
1820
|
+
randomValuesLength: state.randomValues.length,
|
|
1821
|
+
timeValuesLength: state.timeValues.length,
|
|
1822
|
+
signalsLength: state.signals.length,
|
|
1823
|
+
queriesLength: state.queries.length,
|
|
1824
|
+
updatesLength: state.updates?.length ?? 0,
|
|
1825
|
+
logCount: state.logCount ?? 0,
|
|
1826
|
+
...(failureMetadataHash ? { failureMetadataHash } : {}),
|
|
1827
|
+
};
|
|
1828
|
+
}
|
|
1829
|
+
#hashDeterminismMarker(signature) {
|
|
1830
|
+
return stableStringify(signature);
|
|
1831
|
+
}
|
|
1087
1832
|
#isTaskNotFoundError(error) {
|
|
1088
1833
|
return error instanceof ConnectError && error.code === Code.NotFound;
|
|
1089
1834
|
}
|
|
@@ -1316,6 +2061,11 @@ export class WorkerRuntime {
|
|
|
1316
2061
|
async #removeStickyEntry(key) {
|
|
1317
2062
|
await Effect.runPromise(this.#stickyCache.remove(key));
|
|
1318
2063
|
}
|
|
2064
|
+
async #removeStickyEntriesForWorkflow(workflowId) {
|
|
2065
|
+
if (!workflowId)
|
|
2066
|
+
return;
|
|
2067
|
+
await Effect.runPromise(this.#stickyCache.removeByWorkflow({ namespace: this.#namespace, workflowId }));
|
|
2068
|
+
}
|
|
1319
2069
|
async #failWorkflowTask(response, execution, error, cause = WorkflowTaskFailedCause.UNSPECIFIED) {
|
|
1320
2070
|
const failure = await encodeErrorToFailure(this.#dataConverter, error);
|
|
1321
2071
|
const encoded = await encodeFailurePayloads(this.#dataConverter, failure);
|
|
@@ -1325,7 +2075,7 @@ export class WorkerRuntime {
|
|
|
1325
2075
|
failure: encoded,
|
|
1326
2076
|
identity: this.#identity,
|
|
1327
2077
|
namespace: this.#namespace,
|
|
1328
|
-
deploymentOptions: this.#
|
|
2078
|
+
deploymentOptions: this.#rpcDeploymentOptions,
|
|
1329
2079
|
});
|
|
1330
2080
|
try {
|
|
1331
2081
|
await this.#workflowService.respondWorkflowTaskFailed(failed, { timeoutMs: RESPOND_TIMEOUT_MS });
|
|
@@ -1339,6 +2089,20 @@ export class WorkerRuntime {
|
|
|
1339
2089
|
throw rpcError;
|
|
1340
2090
|
}
|
|
1341
2091
|
}
|
|
2092
|
+
async #runActivityTask(response) {
|
|
2093
|
+
const context = {
|
|
2094
|
+
kind: 'worker.activityTask',
|
|
2095
|
+
namespace: this.#namespace,
|
|
2096
|
+
taskQueue: this.#taskQueue,
|
|
2097
|
+
identity: this.#identity,
|
|
2098
|
+
buildId: this.#deploymentOptions.buildId,
|
|
2099
|
+
workflowId: response.workflowExecution?.workflowId ?? undefined,
|
|
2100
|
+
runId: response.workflowExecution?.runId ?? undefined,
|
|
2101
|
+
attempt: Number(response.attempt ?? 1),
|
|
2102
|
+
};
|
|
2103
|
+
const effect = runWorkerInterceptors(this.#interceptors, context, () => Effect.tryPromise(() => this.#processActivityTask(response)));
|
|
2104
|
+
await Effect.runPromise(effect);
|
|
2105
|
+
}
|
|
1342
2106
|
async #processActivityTask(response) {
|
|
1343
2107
|
const cancelRequested = isActivityCancelRequested(response);
|
|
1344
2108
|
if (cancelRequested) {
|
|
@@ -1411,7 +2175,7 @@ export class WorkerRuntime {
|
|
|
1411
2175
|
identity: this.#identity,
|
|
1412
2176
|
namespace: this.#namespace,
|
|
1413
2177
|
result: payloads && payloads.length > 0 ? create(PayloadsSchema, { payloads }) : undefined,
|
|
1414
|
-
deploymentOptions: this.#
|
|
2178
|
+
deploymentOptions: this.#rpcDeploymentOptions,
|
|
1415
2179
|
});
|
|
1416
2180
|
await this.#workflowService.respondActivityTaskCompleted(completion, { timeoutMs: RESPOND_TIMEOUT_MS });
|
|
1417
2181
|
break;
|
|
@@ -1467,7 +2231,7 @@ export class WorkerRuntime {
|
|
|
1467
2231
|
namespace: this.#namespace,
|
|
1468
2232
|
failure: encoded,
|
|
1469
2233
|
lastHeartbeatDetails,
|
|
1470
|
-
deploymentOptions: this.#
|
|
2234
|
+
deploymentOptions: this.#rpcDeploymentOptions,
|
|
1471
2235
|
});
|
|
1472
2236
|
await this.#workflowService.respondActivityTaskFailed(request, { timeoutMs: RESPOND_TIMEOUT_MS });
|
|
1473
2237
|
this.#incrementCounter(this.#metrics.activityFailures);
|
|
@@ -1479,7 +2243,7 @@ export class WorkerRuntime {
|
|
|
1479
2243
|
identity: this.#identity,
|
|
1480
2244
|
namespace: this.#namespace,
|
|
1481
2245
|
details,
|
|
1482
|
-
deploymentOptions: this.#
|
|
2246
|
+
deploymentOptions: this.#rpcDeploymentOptions,
|
|
1483
2247
|
});
|
|
1484
2248
|
await this.#workflowService.respondActivityTaskCanceled(request, { timeoutMs: RESPOND_TIMEOUT_MS });
|
|
1485
2249
|
}
|
|
@@ -1594,6 +2358,10 @@ export class WorkerRuntime {
|
|
|
1594
2358
|
return scheduleDeadline ?? startDeadline ?? undefined;
|
|
1595
2359
|
}
|
|
1596
2360
|
async #flushMetrics() {
|
|
2361
|
+
if (this.#metricsFlushInFlight) {
|
|
2362
|
+
return;
|
|
2363
|
+
}
|
|
2364
|
+
this.#metricsFlushInFlight = true;
|
|
1597
2365
|
try {
|
|
1598
2366
|
await Effect.runPromise(this.#metricsExporter.flush());
|
|
1599
2367
|
}
|
|
@@ -1602,21 +2370,36 @@ export class WorkerRuntime {
|
|
|
1602
2370
|
error: error instanceof Error ? error.message : String(error),
|
|
1603
2371
|
});
|
|
1604
2372
|
}
|
|
2373
|
+
finally {
|
|
2374
|
+
this.#metricsFlushInFlight = false;
|
|
2375
|
+
}
|
|
1605
2376
|
}
|
|
1606
2377
|
async #decodeWorkflowArgs(events) {
|
|
1607
2378
|
const startEvent = this.#findWorkflowStartedEvent(events);
|
|
1608
2379
|
if (!startEvent) {
|
|
1609
|
-
return [];
|
|
2380
|
+
return { args: [], hasStartEvent: false };
|
|
1610
2381
|
}
|
|
2382
|
+
const args = (await this.#decodeWorkflowArgsFromStartEvent(startEvent)) ?? [];
|
|
2383
|
+
return { args, hasStartEvent: true };
|
|
2384
|
+
}
|
|
2385
|
+
async #decodeWorkflowArgsFromStartEvent(startEvent) {
|
|
1611
2386
|
const attributes = startEvent.attributes?.case === 'workflowExecutionStartedEventAttributes'
|
|
1612
2387
|
? startEvent.attributes.value
|
|
1613
2388
|
: undefined;
|
|
1614
2389
|
if (!attributes) {
|
|
1615
|
-
return
|
|
2390
|
+
return undefined;
|
|
1616
2391
|
}
|
|
1617
2392
|
const inputPayloads = attributes.input?.payloads ?? [];
|
|
1618
2393
|
return await decodePayloadsToValues(this.#dataConverter, inputPayloads);
|
|
1619
2394
|
}
|
|
2395
|
+
async #fetchWorkflowStartArguments(execution) {
|
|
2396
|
+
const page = await this.#fetchWorkflowHistoryPage(execution);
|
|
2397
|
+
const startEvent = this.#findWorkflowStartedEvent(page.events);
|
|
2398
|
+
if (!startEvent) {
|
|
2399
|
+
return undefined;
|
|
2400
|
+
}
|
|
2401
|
+
return await this.#decodeWorkflowArgsFromStartEvent(startEvent);
|
|
2402
|
+
}
|
|
1620
2403
|
#resolveWorkflowType(response, events) {
|
|
1621
2404
|
if (response.workflowType?.name) {
|
|
1622
2405
|
return response.workflowType.name;
|