@hotmeshio/hotmesh 0.12.1 → 0.14.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 +18 -22
- package/build/modules/enums.d.ts +60 -5
- package/build/modules/enums.js +62 -7
- package/build/modules/errors.d.ts +15 -2
- package/build/modules/errors.js +17 -1
- package/build/modules/storage.d.ts +1 -0
- package/build/modules/storage.js +2 -1
- package/build/package.json +8 -2
- package/build/services/activities/activity/context.d.ts +22 -0
- package/build/services/activities/activity/context.js +76 -0
- package/build/services/activities/activity/index.d.ts +116 -0
- package/build/services/activities/activity/index.js +299 -0
- package/build/services/activities/activity/mapping.d.ts +12 -0
- package/build/services/activities/activity/mapping.js +63 -0
- package/build/services/activities/activity/process.d.ts +28 -0
- package/build/services/activities/activity/process.js +100 -0
- package/build/services/activities/activity/protocol.d.ts +39 -0
- package/build/services/activities/activity/protocol.js +151 -0
- package/build/services/activities/activity/state.d.ts +40 -0
- package/build/services/activities/activity/state.js +143 -0
- package/build/services/activities/activity/transition.d.ts +23 -0
- package/build/services/activities/activity/transition.js +71 -0
- package/build/services/activities/activity/verify.d.ts +22 -0
- package/build/services/activities/activity/verify.js +85 -0
- package/build/services/activities/await.d.ts +1 -4
- package/build/services/activities/await.js +2 -36
- package/build/services/activities/cycle.d.ts +1 -11
- package/build/services/activities/cycle.js +3 -46
- package/build/services/activities/hook.d.ts +2 -11
- package/build/services/activities/hook.js +30 -50
- package/build/services/activities/interrupt.d.ts +2 -4
- package/build/services/activities/interrupt.js +4 -38
- package/build/services/activities/signal.d.ts +1 -11
- package/build/services/activities/signal.js +3 -48
- package/build/services/activities/trigger.d.ts +1 -3
- package/build/services/activities/trigger.js +0 -3
- package/build/services/activities/worker.d.ts +3 -6
- package/build/services/activities/worker.js +4 -40
- package/build/services/connector/factory.d.ts +6 -0
- package/build/services/connector/factory.js +24 -0
- package/build/services/dba/index.d.ts +14 -4
- package/build/services/dba/index.js +57 -18
- package/build/services/durable/activity.d.ts +30 -0
- package/build/services/durable/activity.js +46 -0
- package/build/services/durable/client.d.ts +26 -31
- package/build/services/durable/client.js +26 -31
- package/build/services/durable/connection.d.ts +13 -7
- package/build/services/durable/connection.js +13 -7
- package/build/services/durable/exporter.d.ts +2 -2
- package/build/services/durable/exporter.js +27 -12
- package/build/services/durable/handle.d.ts +59 -41
- package/build/services/durable/handle.js +61 -41
- package/build/services/durable/index.d.ts +152 -283
- package/build/services/durable/index.js +161 -289
- package/build/services/durable/interceptor.d.ts +43 -33
- package/build/services/durable/interceptor.js +59 -39
- package/build/services/durable/schemas/factory.d.ts +2 -3
- package/build/services/durable/schemas/factory.js +180 -30
- package/build/services/durable/telemetry.d.ts +80 -0
- package/build/services/durable/telemetry.js +137 -0
- package/build/services/durable/worker.d.ts +100 -21
- package/build/services/durable/worker.js +314 -60
- package/build/services/durable/workflow/all.d.ts +1 -1
- package/build/services/durable/workflow/all.js +1 -1
- package/build/services/durable/workflow/cancellationScope.d.ts +104 -0
- package/build/services/durable/workflow/cancellationScope.js +139 -0
- package/build/services/durable/workflow/common.d.ts +5 -4
- package/build/services/durable/workflow/common.js +6 -1
- package/build/services/durable/workflow/{waitFor.d.ts → condition.d.ts} +9 -8
- package/build/services/durable/workflow/{waitFor.js → condition.js} +44 -11
- package/build/services/durable/workflow/continueAsNew.d.ts +65 -0
- package/build/services/durable/workflow/continueAsNew.js +92 -0
- package/build/services/durable/workflow/didRun.d.ts +2 -2
- package/build/services/durable/workflow/didRun.js +4 -4
- package/build/services/durable/workflow/enrich.d.ts +5 -0
- package/build/services/durable/workflow/enrich.js +5 -0
- package/build/services/durable/workflow/entityMethods.d.ts +7 -0
- package/build/services/durable/workflow/entityMethods.js +7 -0
- package/build/services/durable/workflow/execHook.js +3 -3
- package/build/services/durable/workflow/execHookBatch.js +2 -2
- package/build/services/durable/workflow/{execChild.d.ts → executeChild.d.ts} +4 -40
- package/build/services/durable/workflow/{execChild.js → executeChild.js} +36 -45
- package/build/services/durable/workflow/hook.d.ts +1 -1
- package/build/services/durable/workflow/hook.js +4 -3
- package/build/services/durable/workflow/index.d.ts +45 -50
- package/build/services/durable/workflow/index.js +46 -51
- package/build/services/durable/workflow/interruption.d.ts +7 -6
- package/build/services/durable/workflow/interruption.js +11 -7
- package/build/services/durable/workflow/patched.d.ts +72 -0
- package/build/services/durable/workflow/patched.js +110 -0
- package/build/services/durable/workflow/proxyActivities.d.ts +7 -7
- package/build/services/durable/workflow/proxyActivities.js +51 -15
- package/build/services/durable/workflow/searchMethods.d.ts +7 -0
- package/build/services/durable/workflow/searchMethods.js +7 -0
- package/build/services/durable/workflow/signal.d.ts +4 -4
- package/build/services/durable/workflow/signal.js +4 -4
- package/build/services/durable/workflow/{sleepFor.d.ts → sleep.d.ts} +7 -7
- package/build/services/durable/workflow/{sleepFor.js → sleep.js} +39 -10
- package/build/services/durable/workflow/terminate.d.ts +55 -0
- package/build/services/durable/workflow/{interrupt.js → terminate.js} +21 -21
- package/build/services/durable/workflow/trace.js +2 -2
- package/build/services/durable/workflow/uuid4.d.ts +14 -0
- package/build/services/durable/workflow/uuid4.js +39 -0
- package/build/services/durable/workflow/{context.d.ts → workflowInfo.d.ts} +5 -5
- package/build/services/durable/workflow/{context.js → workflowInfo.js} +7 -7
- package/build/services/engine/compiler.d.ts +19 -0
- package/build/services/engine/compiler.js +20 -0
- package/build/services/engine/completion.d.ts +46 -0
- package/build/services/engine/completion.js +145 -0
- package/build/services/engine/dispatch.d.ts +24 -0
- package/build/services/engine/dispatch.js +98 -0
- package/build/services/engine/index.d.ts +49 -81
- package/build/services/engine/index.js +175 -573
- package/build/services/engine/init.d.ts +42 -0
- package/build/services/engine/init.js +74 -0
- package/build/services/engine/pubsub.d.ts +50 -0
- package/build/services/engine/pubsub.js +118 -0
- package/build/services/engine/reporting.d.ts +20 -0
- package/build/services/engine/reporting.js +38 -0
- package/build/services/engine/schema.d.ts +23 -0
- package/build/services/engine/schema.js +62 -0
- package/build/services/engine/signal.d.ts +57 -0
- package/build/services/engine/signal.js +117 -0
- package/build/services/engine/state.d.ts +35 -0
- package/build/services/engine/state.js +61 -0
- package/build/services/engine/version.d.ts +31 -0
- package/build/services/engine/version.js +73 -0
- package/build/services/hotmesh/deployment.d.ts +21 -0
- package/build/services/hotmesh/deployment.js +25 -0
- package/build/services/hotmesh/index.d.ts +142 -533
- package/build/services/hotmesh/index.js +223 -674
- package/build/services/hotmesh/init.d.ts +42 -0
- package/build/services/hotmesh/init.js +93 -0
- package/build/services/hotmesh/jobs.d.ts +67 -0
- package/build/services/hotmesh/jobs.js +99 -0
- package/build/services/hotmesh/pubsub.d.ts +38 -0
- package/build/services/hotmesh/pubsub.js +54 -0
- package/build/services/hotmesh/quorum.d.ts +30 -0
- package/build/services/hotmesh/quorum.js +62 -0
- package/build/services/hotmesh/validation.d.ts +6 -0
- package/build/services/hotmesh/validation.js +28 -0
- package/build/services/quorum/index.js +1 -0
- package/build/services/router/consumption/index.d.ts +11 -5
- package/build/services/router/consumption/index.js +24 -17
- package/build/services/router/error-handling/index.d.ts +2 -2
- package/build/services/router/error-handling/index.js +14 -14
- package/build/services/router/index.d.ts +1 -1
- package/build/services/router/index.js +2 -2
- package/build/services/serializer/index.d.ts +22 -0
- package/build/services/serializer/index.js +39 -1
- package/build/services/store/index.d.ts +1 -0
- package/build/services/store/providers/postgres/exporter-sql.d.ts +2 -2
- package/build/services/store/providers/postgres/exporter-sql.js +4 -4
- package/build/services/store/providers/postgres/kvtables.js +7 -6
- package/build/services/store/providers/postgres/kvtypes/hash/basic.js +67 -52
- package/build/services/store/providers/postgres/kvtypes/hash/jsonb.js +87 -72
- package/build/services/store/providers/postgres/kvtypes/hash/udata.js +106 -79
- package/build/services/store/providers/postgres/kvtypes/hash/utils.d.ts +16 -0
- package/build/services/store/providers/postgres/kvtypes/hash/utils.js +29 -16
- package/build/services/store/providers/postgres/postgres.d.ts +1 -0
- package/build/services/store/providers/postgres/postgres.js +14 -4
- package/build/services/stream/factory.d.ts +3 -1
- package/build/services/stream/factory.js +2 -2
- package/build/services/stream/index.d.ts +1 -0
- package/build/services/stream/providers/nats/nats.d.ts +1 -0
- package/build/services/stream/providers/nats/nats.js +1 -0
- package/build/services/stream/providers/postgres/credentials.d.ts +56 -0
- package/build/services/stream/providers/postgres/credentials.js +129 -0
- package/build/services/stream/providers/postgres/kvtables.js +18 -0
- package/build/services/stream/providers/postgres/messages.js +7 -7
- package/build/services/stream/providers/postgres/notifications.js +16 -2
- package/build/services/stream/providers/postgres/postgres.d.ts +7 -0
- package/build/services/stream/providers/postgres/postgres.js +35 -4
- package/build/services/stream/providers/postgres/procedures.d.ts +21 -0
- package/build/services/stream/providers/postgres/procedures.js +213 -0
- package/build/services/stream/providers/postgres/secured.d.ts +34 -0
- package/build/services/stream/providers/postgres/secured.js +146 -0
- package/build/services/stream/providers/postgres/stats.d.ts +1 -0
- package/build/services/stream/providers/postgres/stats.js +1 -0
- package/build/services/stream/registry.d.ts +1 -1
- package/build/services/stream/registry.js +5 -2
- package/build/services/telemetry/index.d.ts +10 -1
- package/build/services/telemetry/index.js +40 -7
- package/build/services/worker/credentials.d.ts +51 -0
- package/build/services/worker/credentials.js +87 -0
- package/build/services/worker/index.d.ts +2 -2
- package/build/services/worker/index.js +7 -6
- package/build/types/codec.d.ts +84 -0
- package/build/types/codec.js +2 -0
- package/build/types/dba.d.ts +39 -3
- package/build/types/durable.d.ts +123 -25
- package/build/types/error.d.ts +10 -0
- package/build/types/exporter.d.ts +1 -1
- package/build/types/hotmesh.d.ts +67 -4
- package/build/types/index.d.ts +2 -1
- package/build/types/provider.d.ts +2 -2
- package/build/types/quorum.d.ts +35 -1
- package/build/types/stream.d.ts +12 -6
- package/package.json +8 -2
- package/build/services/activities/activity.d.ts +0 -192
- package/build/services/activities/activity.js +0 -786
- package/build/services/durable/workflow/interrupt.d.ts +0 -55
|
@@ -3,6 +3,7 @@ var _a;
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.WorkerService = void 0;
|
|
5
5
|
const enums_1 = require("../../modules/enums");
|
|
6
|
+
const cancellationScope_1 = require("./workflow/cancellationScope");
|
|
6
7
|
const errors_1 = require("../../modules/errors");
|
|
7
8
|
const storage_1 = require("../../modules/storage");
|
|
8
9
|
const utils_1 = require("../../modules/utils");
|
|
@@ -10,30 +11,95 @@ const hotmesh_1 = require("../hotmesh");
|
|
|
10
11
|
const stream_1 = require("../../types/stream");
|
|
11
12
|
const search_1 = require("./search");
|
|
12
13
|
const factory_1 = require("./schemas/factory");
|
|
14
|
+
const telemetry_1 = require("./telemetry");
|
|
15
|
+
const telemetry_2 = require("../telemetry");
|
|
16
|
+
const telemetry_3 = require("../../types/telemetry");
|
|
13
17
|
const index_1 = require("./index");
|
|
14
18
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
19
|
+
* Hosts workflow and activity functions, connecting them to Postgres
|
|
20
|
+
* for durable execution, replay, and automatic retry.
|
|
17
21
|
*
|
|
18
|
-
*
|
|
22
|
+
* ## Connection Modes
|
|
23
|
+
*
|
|
24
|
+
* ### Standard (legacy) — full admin access
|
|
25
|
+
*
|
|
26
|
+
* The worker connects with the same Postgres credentials as the engine.
|
|
27
|
+
* Simple to set up; all workers share the same connection pool.
|
|
28
|
+
*
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const worker = await Durable.Worker.create({
|
|
31
|
+
* connection: {
|
|
32
|
+
* class: Postgres,
|
|
33
|
+
* options: { connectionString: 'postgres://user:pass@host:5432/hotmesh' },
|
|
34
|
+
* },
|
|
35
|
+
* taskQueue: 'orders',
|
|
36
|
+
* workflow: orderWorkflow,
|
|
37
|
+
* });
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* ### Secured — scoped Postgres role (recommended for production)
|
|
41
|
+
*
|
|
42
|
+
* The worker connects as a restricted Postgres role that can only
|
|
43
|
+
* dequeue/ack/respond on its assigned stream names. All data access
|
|
44
|
+
* goes through SECURITY DEFINER stored procedures that validate the
|
|
45
|
+
* role's `app.allowed_streams` session variable before executing.
|
|
46
|
+
*
|
|
47
|
+
* **Step 1**: Provision a scoped credential (run once, from the engine/admin):
|
|
48
|
+
* ```typescript
|
|
49
|
+
* const cred = await Durable.provisionWorkerRole({
|
|
50
|
+
* connection: { class: Postgres, options: adminPgOptions },
|
|
51
|
+
* namespace: 'durable',
|
|
52
|
+
* streamNames: ['orders-activity'],
|
|
53
|
+
* });
|
|
54
|
+
* // cred = { roleName: 'hmsh_wrk_durable_orders_activity', password: '...' }
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* **Step 2**: Pass the credential when creating the worker:
|
|
19
58
|
* ```typescript
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
59
|
+
* const worker = await Durable.Worker.create({
|
|
60
|
+
* connection: {
|
|
61
|
+
* class: Postgres,
|
|
62
|
+
* options: { host: 'pg.prod', port: 5432, database: 'hotmesh' },
|
|
63
|
+
* },
|
|
64
|
+
* taskQueue: 'orders',
|
|
65
|
+
* workflow: orderWorkflow,
|
|
66
|
+
* workerCredentials: { user: cred.roleName, password: cred.password },
|
|
67
|
+
* });
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* The worker role **cannot**:
|
|
71
|
+
* - SELECT/INSERT/UPDATE/DELETE any table directly
|
|
72
|
+
* - Access `jobs`, `jobs_attributes`, or any engine tables
|
|
73
|
+
* - Dequeue messages from other workers' streams
|
|
74
|
+
* - LISTEN on other workers' notification channels
|
|
75
|
+
*
|
|
76
|
+
* See {@link Durable.provisionWorkerRole} for credential lifecycle management.
|
|
23
77
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
78
|
+
* ## Telemetry
|
|
79
|
+
*
|
|
80
|
+
* Workers automatically emit OpenTelemetry spans when an OTel SDK is
|
|
81
|
+
* registered. Initialize the SDK **before** calling `create()`:
|
|
82
|
+
*
|
|
83
|
+
* ```typescript
|
|
84
|
+
* import { NodeSDK } from '@opentelemetry/sdk-node';
|
|
85
|
+
* import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
|
|
86
|
+
* import { resourceFromAttributes } from '@opentelemetry/resources';
|
|
87
|
+
* import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
|
|
33
88
|
*
|
|
34
|
-
*
|
|
35
|
-
* }
|
|
89
|
+
* const sdk = new NodeSDK({
|
|
90
|
+
* resource: resourceFromAttributes({ [ATTR_SERVICE_NAME]: 'my-service' }),
|
|
91
|
+
* traceExporter: new OTLPTraceExporter({
|
|
92
|
+
* url: 'https://api.honeycomb.io/v1/traces',
|
|
93
|
+
* headers: { 'x-honeycomb-team': process.env.HONEYCOMB_API_KEY },
|
|
94
|
+
* }),
|
|
95
|
+
* });
|
|
96
|
+
* sdk.start();
|
|
36
97
|
* ```
|
|
98
|
+
*
|
|
99
|
+
* | `HMSH_TELEMETRY` | Spans emitted |
|
|
100
|
+
* |-------|---------------|
|
|
101
|
+
* | `'info'` (default) | `WORKFLOW/START`, `WORKFLOW/COMPLETE`, `WORKFLOW/ERROR`, `ACTIVITY/{name}` |
|
|
102
|
+
* | `'debug'` | All `info` spans + `DISPATCH/RETURN` per operation + engine internals |
|
|
37
103
|
*/
|
|
38
104
|
class WorkerService {
|
|
39
105
|
static hashOptions(connection) {
|
|
@@ -151,7 +217,7 @@ class WorkerService {
|
|
|
151
217
|
* sendEmail: (to: string, subject: string) => Promise<void>;
|
|
152
218
|
* }>({
|
|
153
219
|
* taskQueue: 'payment',
|
|
154
|
-
*
|
|
220
|
+
* retry: { maximumAttempts: 3 }
|
|
155
221
|
* });
|
|
156
222
|
*
|
|
157
223
|
* const result = await processPayment(amount);
|
|
@@ -180,28 +246,47 @@ class WorkerService {
|
|
|
180
246
|
* taskQueue: 'shared'
|
|
181
247
|
* }, { auditLog, collectMetrics }, 'shared');
|
|
182
248
|
*
|
|
183
|
-
* const interceptor:
|
|
249
|
+
* const interceptor: WorkflowInboundCallsInterceptor = {
|
|
184
250
|
* async execute(ctx, next) {
|
|
185
251
|
* const { auditLog } = Durable.workflow.proxyActivities<{
|
|
186
252
|
* auditLog: (id: string, action: string) => Promise<void>;
|
|
187
253
|
* }>({
|
|
188
254
|
* taskQueue: 'shared',
|
|
189
|
-
*
|
|
255
|
+
* retry: { maximumAttempts: 3 }
|
|
190
256
|
* });
|
|
191
257
|
* await auditLog(ctx.get('workflowId'), 'started');
|
|
192
258
|
* return next();
|
|
193
259
|
* }
|
|
194
260
|
* };
|
|
195
261
|
* ```
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* ```typescript
|
|
265
|
+
* // Secured worker with scoped Postgres credentials (VNF-style isolation)
|
|
266
|
+
* // Step 1: Admin provisions a credential (one-time)
|
|
267
|
+
* const cred = await Durable.provisionWorkerRole({
|
|
268
|
+
* connection: { class: Postgres, options: adminOptions },
|
|
269
|
+
* streamNames: ['payment-activity'],
|
|
270
|
+
* });
|
|
271
|
+
*
|
|
272
|
+
* // Step 2: Worker connects with scoped role — can only access payment-activity
|
|
273
|
+
* await Durable.registerActivityWorker({
|
|
274
|
+
* connection: { class: Postgres, options: { host: 'pg.prod', database: 'hotmesh' } },
|
|
275
|
+
* taskQueue: 'payment',
|
|
276
|
+
* workerCredentials: { user: cred.roleName, password: cred.password },
|
|
277
|
+
* }, { processPayment, refundPayment });
|
|
278
|
+
* ```
|
|
196
279
|
*/
|
|
197
280
|
static async registerActivityWorker(config, activities, activityTaskQueue) {
|
|
281
|
+
// Register as durable namespace so engine-layer spans are suppressed in 'info' mode
|
|
282
|
+
const targetNamespace = config?.namespace ?? factory_1.APP_ID;
|
|
283
|
+
telemetry_2.TelemetryService.durableNamespaces.add(targetNamespace);
|
|
198
284
|
// Register activities globally in the registry
|
|
199
285
|
WorkerService.registerActivities(activities);
|
|
200
286
|
// Use provided activityTaskQueue or fall back to config.taskQueue
|
|
201
287
|
const taskQueue = activityTaskQueue || config.taskQueue || 'durable-activities';
|
|
202
288
|
// Append '-activity' suffix for the worker topic
|
|
203
289
|
const activityTopic = `${taskQueue}-activity`;
|
|
204
|
-
const targetNamespace = config?.namespace ?? factory_1.APP_ID;
|
|
205
290
|
const optionsHash = WorkerService.hashOptions(config?.connection);
|
|
206
291
|
const targetTopic = `${optionsHash}.${targetNamespace}.${activityTopic}`;
|
|
207
292
|
// Return existing worker if already initialized (idempotent)
|
|
@@ -209,19 +294,21 @@ class WorkerService {
|
|
|
209
294
|
return await WorkerService.instances.get(targetTopic);
|
|
210
295
|
}
|
|
211
296
|
// Create activity worker that listens on '{taskQueue}-activity' topic
|
|
297
|
+
const workerEntry = {
|
|
298
|
+
topic: activityTopic,
|
|
299
|
+
connection: config.connection,
|
|
300
|
+
callback: WorkerService.createActivityCallback(),
|
|
301
|
+
};
|
|
302
|
+
if (config.workerCredentials) {
|
|
303
|
+
workerEntry.workerCredentials = config.workerCredentials;
|
|
304
|
+
}
|
|
212
305
|
const hotMeshWorker = await hotmesh_1.HotMesh.init({
|
|
213
306
|
guid: config.guid ? `${config.guid}XA` : undefined,
|
|
214
307
|
taskQueue,
|
|
215
308
|
logLevel: config.options?.logLevel ?? enums_1.HMSH_LOGLEVEL,
|
|
216
309
|
appId: targetNamespace,
|
|
217
310
|
engine: { connection: config.connection },
|
|
218
|
-
workers: [
|
|
219
|
-
{
|
|
220
|
-
topic: activityTopic,
|
|
221
|
-
connection: config.connection,
|
|
222
|
-
callback: WorkerService.createActivityCallback(),
|
|
223
|
-
},
|
|
224
|
-
],
|
|
311
|
+
workers: [workerEntry],
|
|
225
312
|
});
|
|
226
313
|
WorkerService.instances.set(targetTopic, hotMeshWorker);
|
|
227
314
|
return hotMeshWorker;
|
|
@@ -232,6 +319,7 @@ class WorkerService {
|
|
|
232
319
|
*/
|
|
233
320
|
static createActivityCallback() {
|
|
234
321
|
return async (data) => {
|
|
322
|
+
let activitySpan = null;
|
|
235
323
|
try {
|
|
236
324
|
//always run the activity function when instructed; return the response
|
|
237
325
|
const activityInput = data.data;
|
|
@@ -240,7 +328,31 @@ class WorkerService {
|
|
|
240
328
|
if (!activityFunction) {
|
|
241
329
|
throw new Error(`Activity '${activityName}' not found in registry`);
|
|
242
330
|
}
|
|
243
|
-
const
|
|
331
|
+
const activityContext = new Map();
|
|
332
|
+
activityContext.set('activityName', activityName);
|
|
333
|
+
activityContext.set('arguments', activityInput.arguments);
|
|
334
|
+
activityContext.set('headers', activityInput.headers ?? {});
|
|
335
|
+
activityContext.set('workflowId', activityInput.workflowId);
|
|
336
|
+
activityContext.set('workflowTopic', activityInput.workflowTopic);
|
|
337
|
+
// Start an ACTIVITY span if telemetry is enabled
|
|
338
|
+
const trc = data.metadata.trc;
|
|
339
|
+
const spn = data.metadata.spn;
|
|
340
|
+
activitySpan =
|
|
341
|
+
telemetry_1.DurableTelemetryService.isEnabled() && trc && spn
|
|
342
|
+
? telemetry_1.DurableTelemetryService.startSpan(trc, spn, `ACTIVITY/${activityName}`, {
|
|
343
|
+
'durable.activity.name': activityName,
|
|
344
|
+
'durable.workflow.id': activityInput.workflowId,
|
|
345
|
+
})
|
|
346
|
+
: null;
|
|
347
|
+
const interceptorService = index_1.Durable.getInterceptorService();
|
|
348
|
+
const pojoResponse = await storage_1.activityAsyncLocalStorage.run(activityContext, () => {
|
|
349
|
+
const executeFn = () => activityFunction.apply(null, activityInput.arguments);
|
|
350
|
+
if (interceptorService?.activityInbound?.length > 0) {
|
|
351
|
+
return interceptorService.executeActivityInboundChain(activityName, activityInput.arguments, executeFn);
|
|
352
|
+
}
|
|
353
|
+
return executeFn();
|
|
354
|
+
});
|
|
355
|
+
activitySpan?.end();
|
|
244
356
|
return {
|
|
245
357
|
status: stream_1.StreamStatus.SUCCESS,
|
|
246
358
|
metadata: { ...data.metadata },
|
|
@@ -248,6 +360,11 @@ class WorkerService {
|
|
|
248
360
|
};
|
|
249
361
|
}
|
|
250
362
|
catch (err) {
|
|
363
|
+
activitySpan?.setStatus({
|
|
364
|
+
code: telemetry_3.SpanStatusCode.ERROR,
|
|
365
|
+
message: err.message,
|
|
366
|
+
});
|
|
367
|
+
activitySpan?.end();
|
|
251
368
|
// Log error (note: we don't have access to this.activityRunner here)
|
|
252
369
|
console.error('durable-worker-activity-err', {
|
|
253
370
|
name: err.name,
|
|
@@ -318,7 +435,7 @@ class WorkerService {
|
|
|
318
435
|
};
|
|
319
436
|
}
|
|
320
437
|
/**
|
|
321
|
-
*
|
|
438
|
+
* Creates and starts a workflow worker.
|
|
322
439
|
*
|
|
323
440
|
* @example
|
|
324
441
|
* ```typescript
|
|
@@ -343,6 +460,9 @@ class WorkerService {
|
|
|
343
460
|
* ```
|
|
344
461
|
*/
|
|
345
462
|
static async create(config) {
|
|
463
|
+
// Register as durable namespace so engine-layer spans are suppressed in 'info' mode
|
|
464
|
+
const targetNamespace = config?.namespace ?? factory_1.APP_ID;
|
|
465
|
+
telemetry_2.TelemetryService.durableNamespaces.add(targetNamespace);
|
|
346
466
|
const workflow = config.workflow;
|
|
347
467
|
const [workflowFunctionName, workflowFunction] = WorkerService.resolveWorkflowTarget(workflow);
|
|
348
468
|
// Separate taskQueue from workflowName - no concatenation for stream_name
|
|
@@ -350,6 +470,10 @@ class WorkerService {
|
|
|
350
470
|
const activityTopic = `${taskQueue}-activity`;
|
|
351
471
|
// workflowTopic remains concatenated for engine-internal routing (graph.subscribes)
|
|
352
472
|
const workflowTopic = `${taskQueue}-${workflowFunctionName}`;
|
|
473
|
+
// Register activities passed via config
|
|
474
|
+
if (config.activities) {
|
|
475
|
+
WorkerService.registerActivities(config.activities);
|
|
476
|
+
}
|
|
353
477
|
//initialize supporting workflows
|
|
354
478
|
const worker = new WorkerService();
|
|
355
479
|
worker.activityRunner = await worker.initActivityWorker(config, activityTopic);
|
|
@@ -392,19 +516,21 @@ class WorkerService {
|
|
|
392
516
|
if (WorkerService.instances.has(targetTopic)) {
|
|
393
517
|
return await WorkerService.instances.get(targetTopic);
|
|
394
518
|
}
|
|
519
|
+
const workerEntry = {
|
|
520
|
+
topic: activityTopic,
|
|
521
|
+
connection: providerConfig,
|
|
522
|
+
callback: this.wrapActivityFunctions().bind(this),
|
|
523
|
+
};
|
|
524
|
+
if (config.workerCredentials) {
|
|
525
|
+
workerEntry.workerCredentials = config.workerCredentials;
|
|
526
|
+
}
|
|
395
527
|
const hotMeshWorker = await hotmesh_1.HotMesh.init({
|
|
396
528
|
guid: config.guid ? `${config.guid}XA` : undefined,
|
|
397
529
|
taskQueue: config.taskQueue,
|
|
398
530
|
logLevel: config.options?.logLevel ?? enums_1.HMSH_LOGLEVEL,
|
|
399
531
|
appId: targetNamespace,
|
|
400
532
|
engine: { connection: providerConfig },
|
|
401
|
-
workers: [
|
|
402
|
-
{
|
|
403
|
-
topic: activityTopic,
|
|
404
|
-
connection: providerConfig,
|
|
405
|
-
callback: this.wrapActivityFunctions().bind(this),
|
|
406
|
-
},
|
|
407
|
-
],
|
|
533
|
+
workers: [workerEntry],
|
|
408
534
|
});
|
|
409
535
|
WorkerService.instances.set(targetTopic, hotMeshWorker);
|
|
410
536
|
return hotMeshWorker;
|
|
@@ -414,12 +540,48 @@ class WorkerService {
|
|
|
414
540
|
*/
|
|
415
541
|
wrapActivityFunctions() {
|
|
416
542
|
return async (data) => {
|
|
543
|
+
let activitySpan = null;
|
|
417
544
|
try {
|
|
418
545
|
//always run the activity function when instructed; return the response
|
|
419
546
|
const activityInput = data.data;
|
|
420
547
|
const activityName = activityInput.activityName;
|
|
421
548
|
const activityFunction = WorkerService.activityRegistry[activityName];
|
|
422
|
-
const
|
|
549
|
+
const activityContext = new Map();
|
|
550
|
+
activityContext.set('activityName', activityName);
|
|
551
|
+
activityContext.set('arguments', activityInput.arguments);
|
|
552
|
+
activityContext.set('headers', activityInput.headers ?? {});
|
|
553
|
+
activityContext.set('workflowId', activityInput.workflowId);
|
|
554
|
+
activityContext.set('workflowTopic', activityInput.workflowTopic);
|
|
555
|
+
const interceptorService = index_1.Durable.getInterceptorService();
|
|
556
|
+
// Start an ACTIVITY span if telemetry is enabled
|
|
557
|
+
const trc = data.metadata.trc;
|
|
558
|
+
const spn = data.metadata.spn;
|
|
559
|
+
activitySpan =
|
|
560
|
+
telemetry_1.DurableTelemetryService.isEnabled() && trc && spn
|
|
561
|
+
? telemetry_1.DurableTelemetryService.startSpan(trc, spn, `ACTIVITY/${activityName}`, {
|
|
562
|
+
'durable.activity.name': activityName,
|
|
563
|
+
'durable.workflow.id': activityInput.workflowId,
|
|
564
|
+
})
|
|
565
|
+
: null;
|
|
566
|
+
const activityPromise = storage_1.activityAsyncLocalStorage.run(activityContext, () => {
|
|
567
|
+
const executeFn = () => activityFunction.apply(this, activityInput.arguments);
|
|
568
|
+
if (interceptorService?.activityInbound?.length > 0) {
|
|
569
|
+
return interceptorService.executeActivityInboundChain(activityName, activityInput.arguments, executeFn);
|
|
570
|
+
}
|
|
571
|
+
return executeFn();
|
|
572
|
+
});
|
|
573
|
+
let pojoResponse;
|
|
574
|
+
if (activityInput.startToCloseTimeout && activityInput.startToCloseTimeout > 0) {
|
|
575
|
+
const timeoutMs = activityInput.startToCloseTimeout * 1000;
|
|
576
|
+
pojoResponse = await Promise.race([
|
|
577
|
+
activityPromise,
|
|
578
|
+
new Promise((_, reject) => setTimeout(() => reject(new errors_1.DurableTimeoutError(`Activity '${activityName}' exceeded startToCloseTimeout of ${activityInput.startToCloseTimeout}s`)), timeoutMs)),
|
|
579
|
+
]);
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
pojoResponse = await activityPromise;
|
|
583
|
+
}
|
|
584
|
+
activitySpan?.end();
|
|
423
585
|
return {
|
|
424
586
|
status: stream_1.StreamStatus.SUCCESS,
|
|
425
587
|
metadata: { ...data.metadata },
|
|
@@ -427,6 +589,11 @@ class WorkerService {
|
|
|
427
589
|
};
|
|
428
590
|
}
|
|
429
591
|
catch (err) {
|
|
592
|
+
activitySpan?.setStatus({
|
|
593
|
+
code: telemetry_3.SpanStatusCode.ERROR,
|
|
594
|
+
message: err.message,
|
|
595
|
+
});
|
|
596
|
+
activitySpan?.end();
|
|
430
597
|
this.activityRunner.engine.logger.error('durable-worker-activity-err', {
|
|
431
598
|
name: err.name,
|
|
432
599
|
message: err.message,
|
|
@@ -477,20 +644,22 @@ class WorkerService {
|
|
|
477
644
|
const targetNamespace = config?.namespace ?? factory_1.APP_ID;
|
|
478
645
|
const optionsHash = WorkerService.hashOptions(config?.connection);
|
|
479
646
|
const targetTopic = `${optionsHash}.${targetNamespace}.${workflowTopic}`;
|
|
647
|
+
const workerEntry = {
|
|
648
|
+
topic: taskQueue,
|
|
649
|
+
workflowName: workflowFunctionName,
|
|
650
|
+
connection: providerConfig,
|
|
651
|
+
callback: this.wrapWorkflowFunction(workflowFunction, workflowTopic, workflowFunctionName, config).bind(this),
|
|
652
|
+
};
|
|
653
|
+
if (config.workerCredentials) {
|
|
654
|
+
workerEntry.workerCredentials = config.workerCredentials;
|
|
655
|
+
}
|
|
480
656
|
const hotMeshWorker = await hotmesh_1.HotMesh.init({
|
|
481
657
|
guid: config.guid,
|
|
482
658
|
taskQueue: config.taskQueue,
|
|
483
659
|
logLevel: config.options?.logLevel ?? enums_1.HMSH_LOGLEVEL,
|
|
484
660
|
appId: config.namespace ?? factory_1.APP_ID,
|
|
485
661
|
engine: { connection: providerConfig },
|
|
486
|
-
workers: [
|
|
487
|
-
{
|
|
488
|
-
topic: taskQueue,
|
|
489
|
-
workflowName: workflowFunctionName,
|
|
490
|
-
connection: providerConfig,
|
|
491
|
-
callback: this.wrapWorkflowFunction(workflowFunction, workflowTopic, workflowFunctionName, config).bind(this),
|
|
492
|
-
},
|
|
493
|
-
],
|
|
662
|
+
workers: [workerEntry],
|
|
494
663
|
});
|
|
495
664
|
WorkerService.instances.set(targetTopic, hotMeshWorker);
|
|
496
665
|
return hotMeshWorker;
|
|
@@ -502,7 +671,19 @@ class WorkerService {
|
|
|
502
671
|
return async (data) => {
|
|
503
672
|
const counter = { counter: 0 };
|
|
504
673
|
const interruptionRegistry = [];
|
|
674
|
+
const patchMarkers = {};
|
|
505
675
|
let isProcessing = false;
|
|
676
|
+
// Injects accumulated patch markers into any worker response so the
|
|
677
|
+
// engine can persist them via the YAML schema's job.maps mechanism.
|
|
678
|
+
// Include patchMarkers only when non-empty. The schema's
|
|
679
|
+
// job.maps patch-marker[-] is a no-op when the reference is absent.
|
|
680
|
+
const withPatchMarkers = (response) => {
|
|
681
|
+
if (Object.keys(patchMarkers).length > 0) {
|
|
682
|
+
response.data = response.data || {};
|
|
683
|
+
response.data.patchMarkers = patchMarkers;
|
|
684
|
+
}
|
|
685
|
+
return response;
|
|
686
|
+
};
|
|
506
687
|
try {
|
|
507
688
|
//incoming data payload has arguments and workflowId
|
|
508
689
|
const workflowInput = data.data;
|
|
@@ -511,6 +692,7 @@ class WorkerService {
|
|
|
511
692
|
context.set('expire', workflowInput.expire);
|
|
512
693
|
context.set('counter', counter);
|
|
513
694
|
context.set('interruptionRegistry', interruptionRegistry);
|
|
695
|
+
context.set('patchMarkers', patchMarkers);
|
|
514
696
|
context.set('connection', config.connection);
|
|
515
697
|
context.set('namespace', config.namespace ?? factory_1.APP_ID);
|
|
516
698
|
context.set('raw', data);
|
|
@@ -530,6 +712,13 @@ class WorkerService {
|
|
|
530
712
|
context.set('workflowDimension', workflowInput.workflowDimension);
|
|
531
713
|
replayQuery = `-*${workflowInput.workflowDimension}-*`;
|
|
532
714
|
}
|
|
715
|
+
else if (workflowInput.continueGeneration) {
|
|
716
|
+
//continueAsNew: use generation prefix for replay isolation;
|
|
717
|
+
//old generation keys remain but are invisible to the new execution
|
|
718
|
+
const genPrefix = `$${workflowInput.continueGeneration}`;
|
|
719
|
+
context.set('workflowDimension', genPrefix);
|
|
720
|
+
replayQuery = `-*${genPrefix}-*`;
|
|
721
|
+
}
|
|
533
722
|
else {
|
|
534
723
|
//last letter of words like 'hook', 'sleep', 'wait', 'signal', 'search', 'start', 'proxy', 'child', 'collator', 'trace', 'enrich', 'publish'
|
|
535
724
|
replayQuery = '-*[ehklptydr]-*';
|
|
@@ -546,6 +735,15 @@ class WorkerService {
|
|
|
546
735
|
context.set('activityInterceptorService', index_1.Durable.getInterceptorService());
|
|
547
736
|
// Execute workflow with interceptors
|
|
548
737
|
const workflowResponse = await storage_1.asyncLocalStorage.run(context, async () => {
|
|
738
|
+
// Emit WORKFLOW/START on first execution (not replay)
|
|
739
|
+
if (telemetry_1.DurableTelemetryService.isEnabled() &&
|
|
740
|
+
Object.keys(replay).length === 0) {
|
|
741
|
+
telemetry_1.DurableTelemetryService.emitPointSpan(data.metadata.trc, data.metadata.spn, `WORKFLOW/START/${workflowFunctionName}`, {
|
|
742
|
+
'durable.workflow.id': workflowInput.workflowId,
|
|
743
|
+
'durable.workflow.name': workflowFunctionName,
|
|
744
|
+
'durable.workflow.topic': workflowTopic,
|
|
745
|
+
});
|
|
746
|
+
}
|
|
549
747
|
// Get the interceptor service
|
|
550
748
|
const interceptorService = index_1.Durable.getInterceptorService();
|
|
551
749
|
// Create the workflow execution function
|
|
@@ -553,8 +751,16 @@ class WorkerService {
|
|
|
553
751
|
return await workflowFunction.apply(this, workflowInput.arguments);
|
|
554
752
|
};
|
|
555
753
|
// Execute the workflow through the interceptor chain
|
|
556
|
-
return await interceptorService.
|
|
754
|
+
return await interceptorService.executeInboundChain(context, execWorkflow);
|
|
557
755
|
});
|
|
756
|
+
// Emit WORKFLOW/COMPLETE when the workflow finishes successfully
|
|
757
|
+
if (telemetry_1.DurableTelemetryService.isEnabled()) {
|
|
758
|
+
telemetry_1.DurableTelemetryService.emitPointSpan(data.metadata.trc, data.metadata.spn, `WORKFLOW/COMPLETE/${workflowFunctionName}`, {
|
|
759
|
+
'durable.workflow.id': workflowInput.workflowId,
|
|
760
|
+
'durable.workflow.name': workflowFunctionName,
|
|
761
|
+
'durable.workflow.topic': workflowTopic,
|
|
762
|
+
});
|
|
763
|
+
}
|
|
558
764
|
//if the embedded function has a try/catch, it can interrup the throw
|
|
559
765
|
// throw here to interrupt the workflow if the embedded function caught and suppressed
|
|
560
766
|
if (interruptionRegistry.length > 0) {
|
|
@@ -568,6 +774,8 @@ class WorkerService {
|
|
|
568
774
|
throw new errors_1.DurableChildError(payload);
|
|
569
775
|
case 'DurableSleepError':
|
|
570
776
|
throw new errors_1.DurableSleepError(payload);
|
|
777
|
+
case 'DurableContinueAsNewError':
|
|
778
|
+
throw new errors_1.DurableContinueAsNewError(payload);
|
|
571
779
|
case 'DurableTimeoutError':
|
|
572
780
|
throw new errors_1.DurableTimeoutError(payload.message, payload.stack);
|
|
573
781
|
case 'DurableMaxedError':
|
|
@@ -580,12 +788,12 @@ class WorkerService {
|
|
|
580
788
|
throw new errors_1.DurableRetryError(`Unknown interruption type: ${payload.type}`);
|
|
581
789
|
}
|
|
582
790
|
}
|
|
583
|
-
return {
|
|
791
|
+
return withPatchMarkers({
|
|
584
792
|
code: 200,
|
|
585
793
|
status: stream_1.StreamStatus.SUCCESS,
|
|
586
794
|
metadata: { ...data.metadata },
|
|
587
795
|
data: { response: workflowResponse, done: true },
|
|
588
|
-
};
|
|
796
|
+
});
|
|
589
797
|
}
|
|
590
798
|
catch (err) {
|
|
591
799
|
if (isProcessing) {
|
|
@@ -594,12 +802,12 @@ class WorkerService {
|
|
|
594
802
|
if (err instanceof errors_1.DurableWaitForError ||
|
|
595
803
|
interruptionRegistry.length > 1) {
|
|
596
804
|
isProcessing = true;
|
|
597
|
-
//NOTE: this type is spawned when `Promise.all` is used OR if the interruption is a `
|
|
805
|
+
//NOTE: this type is spawned when `Promise.all` is used OR if the interruption is a `condition`
|
|
598
806
|
const workflowInput = data.data;
|
|
599
807
|
const execIndex = counter.counter - interruptionRegistry.length + 1;
|
|
600
808
|
const { workflowId, workflowTopic, workflowDimension, originJobId, expire, } = workflowInput;
|
|
601
809
|
const collatorFlowId = `${(0, utils_1.guid)()}$C`;
|
|
602
|
-
return {
|
|
810
|
+
return withPatchMarkers({
|
|
603
811
|
status: stream_1.StreamStatus.SUCCESS,
|
|
604
812
|
code: enums_1.HMSH_CODE_DURABLE_ALL,
|
|
605
813
|
metadata: { ...data.metadata },
|
|
@@ -615,12 +823,12 @@ class WorkerService {
|
|
|
615
823
|
workflowTopic: workflowTopic,
|
|
616
824
|
expire,
|
|
617
825
|
},
|
|
618
|
-
};
|
|
826
|
+
});
|
|
619
827
|
}
|
|
620
828
|
else if (err instanceof errors_1.DurableSleepError) {
|
|
621
829
|
//return the sleep interruption
|
|
622
830
|
isProcessing = true;
|
|
623
|
-
return {
|
|
831
|
+
return withPatchMarkers({
|
|
624
832
|
status: stream_1.StreamStatus.SUCCESS,
|
|
625
833
|
code: err.code,
|
|
626
834
|
metadata: { ...data.metadata },
|
|
@@ -635,12 +843,12 @@ class WorkerService {
|
|
|
635
843
|
index: err.index,
|
|
636
844
|
workflowDimension: err.workflowDimension,
|
|
637
845
|
},
|
|
638
|
-
};
|
|
846
|
+
});
|
|
639
847
|
}
|
|
640
848
|
else if (err instanceof errors_1.DurableProxyError) {
|
|
641
849
|
//return the proxyActivity interruption
|
|
642
850
|
isProcessing = true;
|
|
643
|
-
return {
|
|
851
|
+
return withPatchMarkers({
|
|
644
852
|
status: stream_1.StreamStatus.SUCCESS,
|
|
645
853
|
code: err.code,
|
|
646
854
|
metadata: { ...data.metadata },
|
|
@@ -653,6 +861,7 @@ class WorkerService {
|
|
|
653
861
|
dimension: err.workflowDimension,
|
|
654
862
|
}),
|
|
655
863
|
arguments: err.arguments,
|
|
864
|
+
headers: err.headers,
|
|
656
865
|
workflowDimension: err.workflowDimension,
|
|
657
866
|
index: err.index,
|
|
658
867
|
originJobId: err.originJobId,
|
|
@@ -662,10 +871,12 @@ class WorkerService {
|
|
|
662
871
|
workflowTopic: err.workflowTopic,
|
|
663
872
|
activityName: err.activityName,
|
|
664
873
|
backoffCoefficient: err.backoffCoefficient,
|
|
874
|
+
initialInterval: err.initialInterval,
|
|
665
875
|
maximumAttempts: err.maximumAttempts,
|
|
666
876
|
maximumInterval: err.maximumInterval,
|
|
877
|
+
startToCloseTimeout: err.startToCloseTimeout,
|
|
667
878
|
},
|
|
668
|
-
};
|
|
879
|
+
});
|
|
669
880
|
}
|
|
670
881
|
else if (err instanceof errors_1.DurableChildError) {
|
|
671
882
|
//return the child interruption
|
|
@@ -675,7 +886,7 @@ class WorkerService {
|
|
|
675
886
|
workflowId: err.workflowId,
|
|
676
887
|
dimension: err.workflowDimension,
|
|
677
888
|
};
|
|
678
|
-
return {
|
|
889
|
+
return withPatchMarkers({
|
|
679
890
|
status: stream_1.StreamStatus.SUCCESS,
|
|
680
891
|
code: err.code,
|
|
681
892
|
metadata: { ...data.metadata },
|
|
@@ -685,6 +896,7 @@ class WorkerService {
|
|
|
685
896
|
backoffCoefficient: err.backoffCoefficient || enums_1.HMSH_DURABLE_EXP_BACKOFF,
|
|
686
897
|
code: err.code,
|
|
687
898
|
index: err.index,
|
|
899
|
+
initialInterval: err.initialInterval,
|
|
688
900
|
message: JSON.stringify(msg),
|
|
689
901
|
maximumAttempts: err.maximumAttempts || enums_1.HMSH_DURABLE_MAX_ATTEMPTS,
|
|
690
902
|
maximumInterval: err.maximumInterval || (0, utils_1.s)(enums_1.HMSH_DURABLE_MAX_INTERVAL),
|
|
@@ -700,12 +912,54 @@ class WorkerService {
|
|
|
700
912
|
taskQueue: err.taskQueue,
|
|
701
913
|
workflowName: err.workflowName,
|
|
702
914
|
},
|
|
703
|
-
};
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
else if (err instanceof errors_1.DurableContinueAsNewError) {
|
|
918
|
+
//return the continueAsNew interruption
|
|
919
|
+
isProcessing = true;
|
|
920
|
+
return withPatchMarkers({
|
|
921
|
+
status: stream_1.StreamStatus.SUCCESS,
|
|
922
|
+
code: err.code,
|
|
923
|
+
metadata: { ...data.metadata },
|
|
924
|
+
data: {
|
|
925
|
+
code: err.code,
|
|
926
|
+
arguments: err.arguments,
|
|
927
|
+
index: err.index,
|
|
928
|
+
workflowDimension: err.workflowDimension,
|
|
929
|
+
},
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
if (err instanceof cancellationScope_1.CancelledFailure) {
|
|
933
|
+
// CancelledFailure that wasn't caught by the workflow:
|
|
934
|
+
// treat as fatal (no retry) so the workflow terminates.
|
|
935
|
+
isProcessing = true;
|
|
936
|
+
return withPatchMarkers({
|
|
937
|
+
status: stream_1.StreamStatus.SUCCESS,
|
|
938
|
+
code: enums_1.HMSH_CODE_DURABLE_FATAL,
|
|
939
|
+
metadata: { ...data.metadata },
|
|
940
|
+
data: {
|
|
941
|
+
$error: {
|
|
942
|
+
message: err.message,
|
|
943
|
+
type: 'CancelledFailure',
|
|
944
|
+
name: 'CancelledFailure',
|
|
945
|
+
stack: err.stack,
|
|
946
|
+
code: enums_1.HMSH_CODE_DURABLE_FATAL,
|
|
947
|
+
},
|
|
948
|
+
},
|
|
949
|
+
});
|
|
704
950
|
}
|
|
705
951
|
// ALL other errors are actual fatal errors (598, 597, 596)
|
|
706
952
|
// OR will be retried (599)
|
|
953
|
+
if (telemetry_1.DurableTelemetryService.isEnabled()) {
|
|
954
|
+
telemetry_1.DurableTelemetryService.emitPointSpan(data.metadata.trc, data.metadata.spn, `WORKFLOW/ERROR/${workflowFunctionName}`, {
|
|
955
|
+
'durable.workflow.id': data.data.workflowId,
|
|
956
|
+
'durable.workflow.name': workflowFunctionName,
|
|
957
|
+
'error.message': err.message,
|
|
958
|
+
'error.type': err.name || 'Error',
|
|
959
|
+
}, telemetry_3.SpanStatusCode.ERROR, err.message);
|
|
960
|
+
}
|
|
707
961
|
isProcessing = true;
|
|
708
|
-
return {
|
|
962
|
+
return withPatchMarkers({
|
|
709
963
|
status: stream_1.StreamStatus.SUCCESS,
|
|
710
964
|
code: err.code || new errors_1.DurableRetryError(err.message).code,
|
|
711
965
|
metadata: { ...data.metadata },
|
|
@@ -718,7 +972,7 @@ class WorkerService {
|
|
|
718
972
|
code: err.code || new errors_1.DurableRetryError(err.message).code,
|
|
719
973
|
},
|
|
720
974
|
},
|
|
721
|
-
};
|
|
975
|
+
});
|
|
722
976
|
}
|
|
723
977
|
};
|
|
724
978
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* multiple durable operations concurrently within a workflow function.
|
|
6
6
|
*
|
|
7
7
|
* In most cases, standard `Promise.all` works correctly for Durable
|
|
8
|
-
* operations (e.g., parallel `
|
|
8
|
+
* operations (e.g., parallel `condition` calls). Use `Durable.workflow.all`
|
|
9
9
|
* when you observe counter-sequencing issues with complex parallel
|
|
10
10
|
* patterns.
|
|
11
11
|
*
|
|
@@ -8,7 +8,7 @@ exports.all = void 0;
|
|
|
8
8
|
* multiple durable operations concurrently within a workflow function.
|
|
9
9
|
*
|
|
10
10
|
* In most cases, standard `Promise.all` works correctly for Durable
|
|
11
|
-
* operations (e.g., parallel `
|
|
11
|
+
* operations (e.g., parallel `condition` calls). Use `Durable.workflow.all`
|
|
12
12
|
* when you observe counter-sequencing issues with complex parallel
|
|
13
13
|
* patterns.
|
|
14
14
|
*
|