@hatchet-dev/typescript-sdk 1.16.0 → 1.17.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 +14 -2
- package/clients/admin/admin-client.d.ts +2 -2
- package/clients/dispatcher/action-listener.d.ts +3 -6
- package/clients/dispatcher/action-listener.js +12 -17
- package/clients/listeners/durable-listener/durable-listener-client.d.ts +115 -15
- package/clients/listeners/durable-listener/durable-listener-client.js +769 -19
- package/clients/listeners/durable-listener/pooled-durable-listener-client.js +1 -14
- package/clients/listeners/run-listener/pooled-child-listener-client.js +1 -14
- package/clients/rest/generated/Api.d.ts +5 -1
- package/clients/rest/generated/data-contracts.d.ts +16 -2
- package/clients/rest/generated/data-contracts.js +7 -3
- package/legacy/examples/affinity-workers.js +2 -2
- package/legacy/legacy-client.js +1 -1
- package/legacy/step.d.ts +2 -2
- package/legacy/step.js +3 -2
- package/legacy/workflow.d.ts +25 -25
- package/package.json +1 -1
- package/protoc/dispatcher/dispatcher.d.ts +20 -0
- package/protoc/dispatcher/dispatcher.js +136 -2
- package/protoc/v1/dispatcher.d.ts +168 -0
- package/protoc/v1/dispatcher.js +1920 -1
- package/protoc/v1/shared/trigger.d.ts +89 -0
- package/protoc/v1/shared/trigger.js +493 -0
- package/protoc/v1/workflows.d.ts +34 -34
- package/protoc/v1/workflows.js +252 -200
- package/protoc/workflows/workflows.d.ts +2 -75
- package/protoc/workflows/workflows.js +16 -491
- package/util/abort-error.d.ts +10 -0
- package/util/abort-error.js +15 -0
- package/util/errors/eviction-not-supported-error.d.ts +5 -0
- package/util/errors/eviction-not-supported-error.js +18 -0
- package/util/errors/non-determinism-error.d.ts +7 -0
- package/util/errors/non-determinism-error.js +21 -0
- package/util/errors/task-run-terminated-error.d.ts +6 -0
- package/util/errors/task-run-terminated-error.js +15 -0
- package/util/hatchet-promise/hatchet-promise.d.ts +2 -1
- package/util/hatchet-promise/hatchet-promise.js +10 -1
- package/util/sleep.d.ts +3 -2
- package/util/sleep.js +6 -4
- package/v1/client/admin.d.ts +2 -2
- package/v1/client/client.js +1 -1
- package/v1/client/duration.d.ts +11 -1
- package/v1/client/duration.js +44 -0
- package/v1/client/features/runs.d.ts +16 -3
- package/v1/client/features/runs.js +38 -3
- package/v1/client/worker/context.d.ts +101 -6
- package/v1/client/worker/context.js +247 -21
- package/v1/client/worker/deprecated/index.d.ts +1 -1
- package/v1/client/worker/deprecated/index.js +2 -1
- package/v1/client/worker/deprecated/legacy-worker.d.ts +5 -0
- package/v1/client/worker/deprecated/legacy-worker.js +32 -23
- package/v1/client/worker/deprecated/pre-eviction.d.ts +12 -0
- package/v1/client/worker/deprecated/pre-eviction.js +37 -0
- package/v1/client/worker/engine-version.d.ts +5 -0
- package/v1/client/worker/engine-version.js +14 -0
- package/v1/client/worker/eviction/eviction-cache.d.ts +33 -0
- package/v1/client/worker/eviction/eviction-cache.js +139 -0
- package/v1/client/worker/eviction/eviction-manager.d.ts +42 -0
- package/v1/client/worker/eviction/eviction-manager.js +132 -0
- package/v1/client/worker/eviction/eviction-policy.d.ts +19 -0
- package/v1/client/worker/eviction/eviction-policy.js +8 -0
- package/v1/client/worker/eviction/index.d.ts +3 -0
- package/v1/client/worker/eviction/index.js +11 -0
- package/v1/client/worker/worker-internal.d.ts +23 -4
- package/v1/client/worker/worker-internal.js +177 -138
- package/v1/client/worker/worker.d.ts +1 -0
- package/v1/client/worker/worker.js +34 -1
- package/v1/conditions/sleep-condition.js +2 -1
- package/v1/conditions/transformer.js +2 -1
- package/v1/declaration.d.ts +5 -3
- package/v1/declaration.js +8 -0
- package/v1/examples/__e2e__/harness.d.ts +5 -0
- package/v1/examples/__e2e__/harness.js +13 -0
- package/v1/examples/concurrency_workflow_level/workflow.d.ts +1 -1
- package/v1/examples/concurrency_workflow_level/workflow.js +1 -1
- package/v1/examples/durable/workflow.d.ts +57 -0
- package/v1/examples/durable/workflow.js +162 -7
- package/v1/examples/durable-event/workflow.js +2 -7
- package/v1/examples/durable_event/workflow.d.ts +1 -0
- package/v1/examples/durable_event/workflow.js +4 -9
- package/v1/examples/durable_eviction/capacity-worker.d.ts +1 -0
- package/v1/examples/durable_eviction/capacity-worker.js +31 -0
- package/v1/examples/durable_eviction/worker.d.ts +1 -0
- package/v1/examples/durable_eviction/worker.js +34 -0
- package/v1/examples/durable_eviction/workflow.d.ts +44 -0
- package/v1/examples/durable_eviction/workflow.js +129 -0
- package/v1/examples/e2e-worker.js +42 -19
- package/v1/index.d.ts +5 -0
- package/v1/index.js +10 -0
- package/v1/parent-run-context-vars.d.ts +6 -0
- package/v1/task.d.ts +10 -2
- package/v1/task.js +2 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
|
@@ -54,7 +54,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
54
54
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
55
55
|
exports.InternalWorker = void 0;
|
|
56
56
|
exports.mapRateLimitPb = mapRateLimitPb;
|
|
57
|
+
exports.resolveExecutionTimeout = resolveExecutionTimeout;
|
|
58
|
+
exports.resolveScheduleTimeout = resolveScheduleTimeout;
|
|
57
59
|
const hatchet_error_1 = __importDefault(require("../../../util/errors/hatchet-error"));
|
|
60
|
+
const task_run_terminated_error_1 = require("../../../util/errors/task-run-terminated-error");
|
|
58
61
|
const dispatcher_1 = require("../../../protoc/dispatcher");
|
|
59
62
|
const hatchet_promise_1 = __importStar(require("../../../util/hatchet-promise/hatchet-promise"));
|
|
60
63
|
const workflows_1 = require("../../../protoc/workflows");
|
|
@@ -65,12 +68,18 @@ const zod_to_json_schema_1 = require("zod-to-json-schema");
|
|
|
65
68
|
const apply_namespace_1 = require("../../../util/apply-namespace");
|
|
66
69
|
const sleep_1 = __importDefault(require("../../../util/sleep"));
|
|
67
70
|
const abort_error_1 = require("../../../util/abort-error");
|
|
71
|
+
const duration_1 = require("../duration");
|
|
68
72
|
const context_1 = require("./context");
|
|
69
73
|
const parent_run_context_vars_1 = require("../../parent-run-context-vars");
|
|
70
74
|
const health_server_1 = require("./health-server");
|
|
75
|
+
const eviction_manager_1 = require("./eviction/eviction-manager");
|
|
76
|
+
const eviction_policy_1 = require("./eviction/eviction-policy");
|
|
77
|
+
const engine_version_1 = require("./engine-version");
|
|
71
78
|
class InternalWorker {
|
|
72
79
|
constructor(client, options) {
|
|
73
80
|
var _a, _b, _c, _d;
|
|
81
|
+
this.durable_action_set = new Set();
|
|
82
|
+
this.eviction_policies = new Map();
|
|
74
83
|
this.workflow_registry = [];
|
|
75
84
|
this.futures = {};
|
|
76
85
|
this.contexts = {};
|
|
@@ -128,7 +137,12 @@ class InternalWorker {
|
|
|
128
137
|
const newActions = workflow._durableTasks
|
|
129
138
|
.filter((task) => !!task.fn)
|
|
130
139
|
.reduce((acc, task) => {
|
|
131
|
-
|
|
140
|
+
const actionId = `${(0, apply_namespace_1.applyNamespace)(workflow.name, this.client.config.namespace).toLowerCase()}:${task.name.toLowerCase()}`;
|
|
141
|
+
acc[actionId] = (ctx) => task.fn(ctx.input, ctx);
|
|
142
|
+
this.durable_action_set.add(actionId);
|
|
143
|
+
this.eviction_policies.set(actionId, task.evictionPolicy !== undefined
|
|
144
|
+
? task.evictionPolicy
|
|
145
|
+
: eviction_policy_1.DEFAULT_DURABLE_TASK_EVICTION_POLICY);
|
|
132
146
|
return acc;
|
|
133
147
|
}, {});
|
|
134
148
|
this.action_registry = Object.assign(Object.assign({}, this.action_registry), newActions);
|
|
@@ -154,7 +168,7 @@ class InternalWorker {
|
|
|
154
168
|
}
|
|
155
169
|
registerWorkflow(initWorkflow_1) {
|
|
156
170
|
return __awaiter(this, arguments, void 0, function* (initWorkflow, durable = false) {
|
|
157
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
|
|
171
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
|
|
158
172
|
// patch the namespace
|
|
159
173
|
const workflow = Object.assign(Object.assign({}, initWorkflow.definition), { name: (0, apply_namespace_1.applyNamespace)(initWorkflow.definition.name, this.client.config.namespace).toLowerCase() });
|
|
160
174
|
try {
|
|
@@ -177,19 +191,20 @@ class InternalWorker {
|
|
|
177
191
|
}
|
|
178
192
|
if (workflow.onFailure && typeof workflow.onFailure === 'object') {
|
|
179
193
|
const onFailure = workflow.onFailure;
|
|
194
|
+
const scheduleTimeout = (_a = onFailure.scheduleTimeout) !== null && _a !== void 0 ? _a : (_b = workflow.taskDefaults) === null || _b === void 0 ? void 0 : _b.scheduleTimeout;
|
|
180
195
|
onFailureTask = {
|
|
181
196
|
readableId: 'on-failure-task',
|
|
182
197
|
action: onFailureTaskName(workflow),
|
|
183
|
-
timeout: onFailure.executionTimeout || ((
|
|
184
|
-
scheduleTimeout:
|
|
198
|
+
timeout: (0, duration_1.durationToString)(onFailure.executionTimeout || ((_c = workflow.taskDefaults) === null || _c === void 0 ? void 0 : _c.executionTimeout) || '60s'),
|
|
199
|
+
scheduleTimeout: scheduleTimeout ? (0, duration_1.durationToString)(scheduleTimeout) : undefined,
|
|
185
200
|
inputs: '{}',
|
|
186
201
|
parents: [],
|
|
187
|
-
retries: onFailure.retries || ((
|
|
188
|
-
rateLimits: mapRateLimitPb(onFailure.rateLimits || ((
|
|
189
|
-
workerLabels: mapWorkerLabelPb(onFailure.desiredWorkerLabels || ((
|
|
202
|
+
retries: onFailure.retries || ((_d = workflow.taskDefaults) === null || _d === void 0 ? void 0 : _d.retries) || 0,
|
|
203
|
+
rateLimits: mapRateLimitPb(onFailure.rateLimits || ((_e = workflow.taskDefaults) === null || _e === void 0 ? void 0 : _e.rateLimits)),
|
|
204
|
+
workerLabels: mapWorkerLabelPb(onFailure.desiredWorkerLabels || ((_f = workflow.taskDefaults) === null || _f === void 0 ? void 0 : _f.workerLabels)),
|
|
190
205
|
concurrency: [],
|
|
191
|
-
backoffFactor: ((
|
|
192
|
-
backoffMaxSeconds: ((
|
|
206
|
+
backoffFactor: ((_g = onFailure.backoff) === null || _g === void 0 ? void 0 : _g.factor) || ((_j = (_h = workflow.taskDefaults) === null || _h === void 0 ? void 0 : _h.backoff) === null || _j === void 0 ? void 0 : _j.factor),
|
|
207
|
+
backoffMaxSeconds: ((_k = onFailure.backoff) === null || _k === void 0 ? void 0 : _k.maxSeconds) || ((_m = (_l = workflow.taskDefaults) === null || _l === void 0 ? void 0 : _l.backoff) === null || _m === void 0 ? void 0 : _m.maxSeconds),
|
|
193
208
|
isDurable: false,
|
|
194
209
|
slotRequests: { default: 1 },
|
|
195
210
|
};
|
|
@@ -214,14 +229,14 @@ class InternalWorker {
|
|
|
214
229
|
onSuccessTask = {
|
|
215
230
|
name: 'on-success-task',
|
|
216
231
|
fn: onSuccess.fn,
|
|
217
|
-
executionTimeout: onSuccess.executionTimeout || ((
|
|
218
|
-
scheduleTimeout: onSuccess.scheduleTimeout || ((
|
|
232
|
+
executionTimeout: onSuccess.executionTimeout || ((_o = workflow.taskDefaults) === null || _o === void 0 ? void 0 : _o.executionTimeout) || '60s',
|
|
233
|
+
scheduleTimeout: onSuccess.scheduleTimeout || ((_p = workflow.taskDefaults) === null || _p === void 0 ? void 0 : _p.scheduleTimeout),
|
|
219
234
|
parents,
|
|
220
|
-
retries: onSuccess.retries || ((
|
|
221
|
-
rateLimits: onSuccess.rateLimits || ((
|
|
222
|
-
desiredWorkerLabels: onSuccess.desiredWorkerLabels || ((
|
|
223
|
-
concurrency: onSuccess.concurrency || ((
|
|
224
|
-
backoff: onSuccess.backoff || ((
|
|
235
|
+
retries: onSuccess.retries || ((_q = workflow.taskDefaults) === null || _q === void 0 ? void 0 : _q.retries) || 0,
|
|
236
|
+
rateLimits: onSuccess.rateLimits || ((_r = workflow.taskDefaults) === null || _r === void 0 ? void 0 : _r.rateLimits),
|
|
237
|
+
desiredWorkerLabels: onSuccess.desiredWorkerLabels || ((_s = workflow.taskDefaults) === null || _s === void 0 ? void 0 : _s.workerLabels),
|
|
238
|
+
concurrency: onSuccess.concurrency || ((_t = workflow.taskDefaults) === null || _t === void 0 ? void 0 : _t.concurrency),
|
|
239
|
+
backoff: onSuccess.backoff || ((_u = workflow.taskDefaults) === null || _u === void 0 ? void 0 : _u.backoff),
|
|
225
240
|
};
|
|
226
241
|
}
|
|
227
242
|
if (onSuccessTask) {
|
|
@@ -285,23 +300,20 @@ class InternalWorker {
|
|
|
285
300
|
defaultPriority: workflow.defaultPriority,
|
|
286
301
|
inputJsonSchema,
|
|
287
302
|
tasks: [...workflow._tasks, ...workflow._durableTasks].map((task) => {
|
|
288
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m
|
|
303
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
289
304
|
return ({
|
|
290
305
|
readableId: task.name,
|
|
291
306
|
action: `${workflow.name}:${task.name}`,
|
|
292
|
-
timeout: task.
|
|
293
|
-
|
|
294
|
-
((_a = workflow.taskDefaults) === null || _a === void 0 ? void 0 : _a.executionTimeout) ||
|
|
295
|
-
'60s',
|
|
296
|
-
scheduleTimeout: task.scheduleTimeout || ((_b = workflow.taskDefaults) === null || _b === void 0 ? void 0 : _b.scheduleTimeout),
|
|
307
|
+
timeout: resolveExecutionTimeout(task, workflow.taskDefaults),
|
|
308
|
+
scheduleTimeout: resolveScheduleTimeout(task, workflow.taskDefaults),
|
|
297
309
|
inputs: '{}',
|
|
298
|
-
parents: (
|
|
310
|
+
parents: (_b = (_a = task.parents) === null || _a === void 0 ? void 0 : _a.map((p) => p.name)) !== null && _b !== void 0 ? _b : [],
|
|
299
311
|
userData: '{}',
|
|
300
|
-
retries: task.retries || ((
|
|
301
|
-
rateLimits: mapRateLimitPb(task.rateLimits || ((
|
|
302
|
-
workerLabels: mapWorkerLabelPb(task.desiredWorkerLabels || ((
|
|
303
|
-
backoffFactor: ((
|
|
304
|
-
backoffMaxSeconds: ((
|
|
312
|
+
retries: task.retries || ((_c = workflow.taskDefaults) === null || _c === void 0 ? void 0 : _c.retries) || 0,
|
|
313
|
+
rateLimits: mapRateLimitPb(task.rateLimits || ((_d = workflow.taskDefaults) === null || _d === void 0 ? void 0 : _d.rateLimits)),
|
|
314
|
+
workerLabels: mapWorkerLabelPb(task.desiredWorkerLabels || ((_e = workflow.taskDefaults) === null || _e === void 0 ? void 0 : _e.workerLabels)),
|
|
315
|
+
backoffFactor: ((_f = task.backoff) === null || _f === void 0 ? void 0 : _f.factor) || ((_h = (_g = workflow.taskDefaults) === null || _g === void 0 ? void 0 : _g.backoff) === null || _h === void 0 ? void 0 : _h.factor),
|
|
316
|
+
backoffMaxSeconds: ((_j = task.backoff) === null || _j === void 0 ? void 0 : _j.maxSeconds) || ((_l = (_k = workflow.taskDefaults) === null || _k === void 0 ? void 0 : _k.backoff) === null || _l === void 0 ? void 0 : _l.maxSeconds),
|
|
305
317
|
conditions: (0, transformer_1.taskConditionsToPb)(task, this.client.config.namespace),
|
|
306
318
|
isDurable: durableTaskSet.has(task),
|
|
307
319
|
slotRequests: task.slotRequests || (durableTaskSet.has(task) ? { durable: 1 } : { default: 1 }),
|
|
@@ -309,7 +321,7 @@ class InternalWorker {
|
|
|
309
321
|
? Array.isArray(task.concurrency)
|
|
310
322
|
? task.concurrency
|
|
311
323
|
: [task.concurrency]
|
|
312
|
-
: ((
|
|
324
|
+
: ((_m = workflow.taskDefaults) === null || _m === void 0 ? void 0 : _m.concurrency)
|
|
313
325
|
? Array.isArray(workflow.taskDefaults.concurrency)
|
|
314
326
|
? workflow.taskDefaults.concurrency
|
|
315
327
|
: [workflow.taskDefaults.concurrency]
|
|
@@ -317,11 +329,11 @@ class InternalWorker {
|
|
|
317
329
|
});
|
|
318
330
|
}),
|
|
319
331
|
concurrency: concurrencySolo,
|
|
320
|
-
defaultFilters: (
|
|
332
|
+
defaultFilters: (_w = (_v = workflow.defaultFilters) === null || _v === void 0 ? void 0 : _v.map((f) => ({
|
|
321
333
|
scope: f.scope,
|
|
322
334
|
expression: f.expression,
|
|
323
335
|
payload: f.payload ? new TextEncoder().encode(JSON.stringify(f.payload)) : undefined,
|
|
324
|
-
}))) !== null &&
|
|
336
|
+
}))) !== null && _w !== void 0 ? _w : [],
|
|
325
337
|
});
|
|
326
338
|
this.registeredWorkflowPromises.push(registeredWorkflow);
|
|
327
339
|
yield registeredWorkflow;
|
|
@@ -333,17 +345,83 @@ class InternalWorker {
|
|
|
333
345
|
this.registerActions(workflow);
|
|
334
346
|
});
|
|
335
347
|
}
|
|
348
|
+
ensureEvictionManager() {
|
|
349
|
+
var _a, _b, _c;
|
|
350
|
+
if (this.evictionManager)
|
|
351
|
+
return this.evictionManager;
|
|
352
|
+
const totalDurableSlots = (_c = (_b = (_a = this.slotConfig) === null || _a === void 0 ? void 0 : _a.durable) !== null && _b !== void 0 ? _b : this.durableSlots) !== null && _c !== void 0 ? _c : 0;
|
|
353
|
+
this.evictionManager = new eviction_manager_1.DurableEvictionManager({
|
|
354
|
+
durableSlots: totalDurableSlots,
|
|
355
|
+
cancelLocal: (key) => {
|
|
356
|
+
var _a;
|
|
357
|
+
const err = new task_run_terminated_error_1.TaskRunTerminatedError('evicted');
|
|
358
|
+
const ctx = this.contexts[key];
|
|
359
|
+
if (ctx) {
|
|
360
|
+
const invocationCount = (_a = ctx.invocationCount) !== null && _a !== void 0 ? _a : 1;
|
|
361
|
+
this.client.durableListener.cleanupTaskState(ctx.action.taskRunExternalId, invocationCount);
|
|
362
|
+
if (ctx.abortController) {
|
|
363
|
+
ctx.abortController.abort(err);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const future = this.futures[key];
|
|
367
|
+
if (future) {
|
|
368
|
+
future.promise.catch(() => undefined);
|
|
369
|
+
future.cancel(hatchet_promise_1.CancellationReason.EVICTED_BY_WORKER);
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
requestEvictionWithAck: (key, rec) => __awaiter(this, void 0, void 0, function* () {
|
|
373
|
+
var _a;
|
|
374
|
+
const ctx = this.contexts[key];
|
|
375
|
+
const invocationCount = (_a = ctx === null || ctx === void 0 ? void 0 : ctx.invocationCount) !== null && _a !== void 0 ? _a : 1;
|
|
376
|
+
yield this.client.durableListener.sendEvictInvocation(rec.taskRunExternalId, invocationCount, rec.evictionReason);
|
|
377
|
+
}),
|
|
378
|
+
logger: this.logger,
|
|
379
|
+
});
|
|
380
|
+
this.client.durableListener.onServerEvict = (durableTaskExternalId, invocationCount) => {
|
|
381
|
+
var _a;
|
|
382
|
+
(_a = this.evictionManager) === null || _a === void 0 ? void 0 : _a.handleServerEviction(durableTaskExternalId, invocationCount);
|
|
383
|
+
};
|
|
384
|
+
this.evictionManager.start();
|
|
385
|
+
return this.evictionManager;
|
|
386
|
+
}
|
|
387
|
+
cleanupRun(key) {
|
|
388
|
+
var _a;
|
|
389
|
+
const ctx = this.contexts[key];
|
|
390
|
+
if (ctx instanceof context_1.DurableContext) {
|
|
391
|
+
this.client.durableListener.cleanupTaskState(ctx.action.taskRunExternalId, ctx.invocationCount);
|
|
392
|
+
}
|
|
393
|
+
(_a = this.evictionManager) === null || _a === void 0 ? void 0 : _a.unregisterRun(key);
|
|
394
|
+
delete this.futures[key];
|
|
395
|
+
delete this.contexts[key];
|
|
396
|
+
}
|
|
336
397
|
handleStartStepRun(action) {
|
|
337
398
|
return __awaiter(this, void 0, void 0, function* () {
|
|
399
|
+
var _a;
|
|
338
400
|
const { actionId, taskRunExternalId, taskName } = action;
|
|
401
|
+
const actionKey = action.key;
|
|
339
402
|
try {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
403
|
+
const isDurable = this.durable_action_set.has(actionId);
|
|
404
|
+
let context;
|
|
405
|
+
if (isDurable) {
|
|
406
|
+
const { durableListener } = this.client;
|
|
407
|
+
let mgr;
|
|
408
|
+
if ((0, engine_version_1.supportsEviction)(this.engineVersion)) {
|
|
409
|
+
yield durableListener.ensureStarted(this.workerId || '');
|
|
410
|
+
mgr = this.ensureEvictionManager();
|
|
411
|
+
const evictionPolicy = this.eviction_policies.get(actionId);
|
|
412
|
+
mgr.registerRun(actionKey, taskRunExternalId, (_a = action.durableTaskInvocationCount) !== null && _a !== void 0 ? _a : 1, evictionPolicy);
|
|
413
|
+
}
|
|
414
|
+
context = new context_1.DurableContext(action, this.client, this, durableListener, mgr, this.engineVersion);
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
context = new context_1.Context(action, this.client, this);
|
|
418
|
+
}
|
|
419
|
+
this.contexts[actionKey] = context;
|
|
343
420
|
const step = this.action_registry[actionId];
|
|
344
421
|
if (!step) {
|
|
345
422
|
this.logger.error(`Registered actions: '${Object.keys(this.action_registry).join(', ')}'`);
|
|
346
423
|
this.logger.error(`Could not find step '${actionId}'`);
|
|
424
|
+
this.cleanupRun(actionKey);
|
|
347
425
|
return;
|
|
348
426
|
}
|
|
349
427
|
const run = () => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -367,8 +445,8 @@ class InternalWorker {
|
|
|
367
445
|
childIndex: 0,
|
|
368
446
|
desiredWorkerId: this.workerId || '',
|
|
369
447
|
signal: context.abortController.signal,
|
|
448
|
+
durableContext: isDurable && context instanceof context_1.DurableContext ? context : undefined,
|
|
370
449
|
}, () => {
|
|
371
|
-
// Precheck: if cancellation already happened, don't execute user code.
|
|
372
450
|
(0, abort_error_1.throwIfAborted)(context.abortController.signal);
|
|
373
451
|
return step(context);
|
|
374
452
|
});
|
|
@@ -389,13 +467,11 @@ class InternalWorker {
|
|
|
389
467
|
return;
|
|
390
468
|
}
|
|
391
469
|
this.logger.info((0, logger_1.taskRunLog)(taskName, taskRunExternalId, 'completed'));
|
|
392
|
-
// Send the action event to the dispatcher
|
|
393
470
|
const event = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_COMPLETED, false, result || null, action.retryCount);
|
|
394
471
|
yield this.client.dispatcher.sendStepActionEvent(event);
|
|
395
472
|
}
|
|
396
473
|
catch (actionEventError) {
|
|
397
474
|
this.logger.error(`Could not send completed action event: ${actionEventError.message || actionEventError}`);
|
|
398
|
-
// send a failure event
|
|
399
475
|
const failureEvent = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_FAILED, false, actionEventError.message, action.retryCount);
|
|
400
476
|
try {
|
|
401
477
|
yield this.client.dispatcher.sendStepActionEvent(failureEvent);
|
|
@@ -406,9 +482,7 @@ class InternalWorker {
|
|
|
406
482
|
this.logger.error(`Could not send action event: ${actionEventError.message || actionEventError}`);
|
|
407
483
|
}
|
|
408
484
|
finally {
|
|
409
|
-
|
|
410
|
-
delete this.futures[taskRunExternalId];
|
|
411
|
-
delete this.contexts[taskRunExternalId];
|
|
485
|
+
this.cleanupRun(actionKey);
|
|
412
486
|
}
|
|
413
487
|
});
|
|
414
488
|
const failure = (error) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -421,7 +495,6 @@ class InternalWorker {
|
|
|
421
495
|
if (error.stack) {
|
|
422
496
|
this.logger.error(error.stack);
|
|
423
497
|
}
|
|
424
|
-
// Send the action event to the dispatcher
|
|
425
498
|
const event = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_FAILED, shouldNotRetry, {
|
|
426
499
|
message: error === null || error === void 0 ? void 0 : error.message,
|
|
427
500
|
stack: error === null || error === void 0 ? void 0 : error.stack,
|
|
@@ -432,9 +505,7 @@ class InternalWorker {
|
|
|
432
505
|
this.logger.error(`Could not send action event: ${e.message}`);
|
|
433
506
|
}
|
|
434
507
|
finally {
|
|
435
|
-
|
|
436
|
-
delete this.futures[taskRunExternalId];
|
|
437
|
-
delete this.contexts[taskRunExternalId];
|
|
508
|
+
this.cleanupRun(actionKey);
|
|
438
509
|
}
|
|
439
510
|
});
|
|
440
511
|
const future = new hatchet_promise_1.default((() => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -457,7 +528,7 @@ class InternalWorker {
|
|
|
457
528
|
(0, abort_error_1.throwIfAborted)(context.abortController.signal);
|
|
458
529
|
yield success(result);
|
|
459
530
|
}))());
|
|
460
|
-
this.futures[
|
|
531
|
+
this.futures[actionKey] = future;
|
|
461
532
|
// Send the action event to the dispatcher
|
|
462
533
|
const event = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_STARTED, false, undefined, action.retryCount);
|
|
463
534
|
this.client.dispatcher.sendStepActionEvent(event).catch((e) => {
|
|
@@ -467,87 +538,18 @@ class InternalWorker {
|
|
|
467
538
|
yield future.promise;
|
|
468
539
|
}
|
|
469
540
|
catch (e) {
|
|
470
|
-
|
|
471
|
-
// TODO is this cased correctly...
|
|
472
|
-
if (!message.includes('Cancelled')) {
|
|
541
|
+
if (!(0, task_run_terminated_error_1.isTaskRunTerminatedError)(e)) {
|
|
473
542
|
this.logger.error(`Could not wait for task run ${taskRunExternalId} to finish. ` +
|
|
474
543
|
`See https://docs.hatchet.run/home/cancellation for best practices on handling cancellation: `, e);
|
|
475
544
|
}
|
|
476
545
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
this.logger.error('Could not send action event (outer): ', e);
|
|
480
|
-
}
|
|
481
|
-
});
|
|
482
|
-
}
|
|
483
|
-
handleStartGroupKeyRun(action) {
|
|
484
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
485
|
-
const { actionId, getGroupKeyRunId, taskRunExternalId, taskName } = action;
|
|
486
|
-
this.logger.error('Concurrency Key Functions have been deprecated and will be removed in a future release. Use Concurrency Expressions instead.');
|
|
487
|
-
try {
|
|
488
|
-
const context = new context_1.Context(action, this.client, this);
|
|
489
|
-
const key = getGroupKeyRunId;
|
|
490
|
-
if (!key) {
|
|
491
|
-
this.logger.error(`No group key run id provided for action ${actionId}`);
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
this.contexts[key] = context;
|
|
495
|
-
this.logger.debug(`Starting group key run ${key}`);
|
|
496
|
-
const step = this.action_registry[actionId];
|
|
497
|
-
if (!step) {
|
|
498
|
-
this.logger.error(`Could not find step '${actionId}'`);
|
|
499
|
-
return;
|
|
546
|
+
finally {
|
|
547
|
+
this.cleanupRun(actionKey);
|
|
500
548
|
}
|
|
501
|
-
const run = () => __awaiter(this, void 0, void 0, function* () {
|
|
502
|
-
return step(context);
|
|
503
|
-
});
|
|
504
|
-
const success = (result) => {
|
|
505
|
-
this.logger.info((0, logger_1.taskRunLog)(taskName, taskRunExternalId, 'completed'));
|
|
506
|
-
try {
|
|
507
|
-
// Send the action event to the dispatcher
|
|
508
|
-
const event = this.getGroupKeyActionEvent(action, dispatcher_1.GroupKeyActionEventType.GROUP_KEY_EVENT_TYPE_COMPLETED, result);
|
|
509
|
-
this.client.dispatcher.sendGroupKeyActionEvent(event).catch((e) => {
|
|
510
|
-
this.logger.error(`Could not send action event: ${e.message}`);
|
|
511
|
-
});
|
|
512
|
-
}
|
|
513
|
-
catch (e) {
|
|
514
|
-
this.logger.error(`Could not send action event: ${e.message}`);
|
|
515
|
-
}
|
|
516
|
-
finally {
|
|
517
|
-
// delete the run from the futures
|
|
518
|
-
delete this.futures[key];
|
|
519
|
-
delete this.contexts[key];
|
|
520
|
-
}
|
|
521
|
-
};
|
|
522
|
-
const failure = (error) => {
|
|
523
|
-
this.logger.error((0, logger_1.taskRunLog)(taskName, taskRunExternalId, `failed: ${error.message}`));
|
|
524
|
-
try {
|
|
525
|
-
// Send the action event to the dispatcher
|
|
526
|
-
const event = this.getGroupKeyActionEvent(action, dispatcher_1.GroupKeyActionEventType.GROUP_KEY_EVENT_TYPE_FAILED, error);
|
|
527
|
-
this.client.dispatcher.sendGroupKeyActionEvent(event).catch((e) => {
|
|
528
|
-
this.logger.error(`Could not send action event: ${e.message}`);
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
catch (e) {
|
|
532
|
-
this.logger.error(`Could not send action event: ${e.message}`);
|
|
533
|
-
}
|
|
534
|
-
finally {
|
|
535
|
-
// delete the run from the futures
|
|
536
|
-
delete this.futures[key];
|
|
537
|
-
delete this.contexts[key];
|
|
538
|
-
}
|
|
539
|
-
};
|
|
540
|
-
const future = new hatchet_promise_1.default(run().then(success).catch(failure));
|
|
541
|
-
this.futures[key] = future;
|
|
542
|
-
// Send the action event to the dispatcher
|
|
543
|
-
const event = this.getGroupKeyActionEvent(action, dispatcher_1.GroupKeyActionEventType.GROUP_KEY_EVENT_TYPE_STARTED);
|
|
544
|
-
this.client.dispatcher.sendGroupKeyActionEvent(event).catch((e) => {
|
|
545
|
-
this.logger.error(`Could not send action event: ${e.message}`);
|
|
546
|
-
});
|
|
547
|
-
yield future.promise;
|
|
548
549
|
}
|
|
549
550
|
catch (e) {
|
|
550
|
-
this.
|
|
551
|
+
this.cleanupRun(actionKey);
|
|
552
|
+
this.logger.error('Could not send action event (outer): ', e);
|
|
551
553
|
}
|
|
552
554
|
});
|
|
553
555
|
}
|
|
@@ -584,11 +586,13 @@ class InternalWorker {
|
|
|
584
586
|
return __awaiter(this, void 0, void 0, function* () {
|
|
585
587
|
var _a, _b, _c;
|
|
586
588
|
const { taskRunExternalId, taskName } = action;
|
|
589
|
+
const actionKey = action.key;
|
|
587
590
|
try {
|
|
588
|
-
const future = this.futures[
|
|
589
|
-
const context = this.contexts[
|
|
591
|
+
const future = this.futures[actionKey];
|
|
592
|
+
const context = this.contexts[actionKey];
|
|
593
|
+
const cancelErr = new task_run_terminated_error_1.TaskRunTerminatedError('cancelled', 'Cancelled by worker');
|
|
590
594
|
if (context && context.abortController) {
|
|
591
|
-
context.abortController.abort(
|
|
595
|
+
context.abortController.abort(cancelErr);
|
|
592
596
|
}
|
|
593
597
|
if (future) {
|
|
594
598
|
const start = Date.now();
|
|
@@ -638,8 +642,7 @@ class InternalWorker {
|
|
|
638
642
|
this.logger.error(`Cancellation: error while supervising cancellation for task run ${taskRunExternalId}: ${(e === null || e === void 0 ? void 0 : e.message) || e}`);
|
|
639
643
|
}
|
|
640
644
|
finally {
|
|
641
|
-
|
|
642
|
-
delete this.contexts[taskRunExternalId];
|
|
645
|
+
this.cleanupRun(actionKey);
|
|
643
646
|
}
|
|
644
647
|
});
|
|
645
648
|
}
|
|
@@ -654,6 +657,33 @@ class InternalWorker {
|
|
|
654
657
|
this.killing = true;
|
|
655
658
|
this.setStatus(health_server_1.workerStatus.UNHEALTHY);
|
|
656
659
|
this.logger.info('Starting to exit...');
|
|
660
|
+
// Pause the worker on the server so it stops receiving new task assignments
|
|
661
|
+
// before we evict waiting durable runs, mirroring Python's pause_task_assignment().
|
|
662
|
+
if (this.workerId) {
|
|
663
|
+
try {
|
|
664
|
+
yield this.client.workers.pause(this.workerId);
|
|
665
|
+
}
|
|
666
|
+
catch (e) {
|
|
667
|
+
this.logger.error(`Could not pause worker: ${e.message}`);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
if (this.evictionManager) {
|
|
671
|
+
try {
|
|
672
|
+
const evicted = yield this.evictionManager.evictAllWaiting();
|
|
673
|
+
if (evicted > 0) {
|
|
674
|
+
this.logger.info(`Evicted ${evicted} waiting durable run(s) during shutdown`);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
catch (e) {
|
|
678
|
+
this.logger.error(`Could not evict waiting runs: ${e.message}`);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
try {
|
|
682
|
+
yield this.client.durableListener.stop();
|
|
683
|
+
}
|
|
684
|
+
catch (e) {
|
|
685
|
+
this.logger.error(`Could not stop durable listener: ${e.message}`);
|
|
686
|
+
}
|
|
657
687
|
try {
|
|
658
688
|
yield ((_a = this.listener) === null || _a === void 0 ? void 0 : _a.unregister());
|
|
659
689
|
}
|
|
@@ -749,20 +779,22 @@ class InternalWorker {
|
|
|
749
779
|
}
|
|
750
780
|
handleAction(action) {
|
|
751
781
|
return __awaiter(this, void 0, void 0, function* () {
|
|
752
|
-
const type = action.actionType
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
782
|
+
const type = (0, dispatcher_1.actionTypeFromJSON)(action.actionType) || dispatcher_1.ActionType.START_STEP_RUN;
|
|
783
|
+
switch (type) {
|
|
784
|
+
case dispatcher_1.ActionType.START_STEP_RUN:
|
|
785
|
+
return this.handleStartStepRun(action);
|
|
786
|
+
case dispatcher_1.ActionType.CANCEL_STEP_RUN:
|
|
787
|
+
return this.handleCancelStepRun(action);
|
|
788
|
+
case dispatcher_1.ActionType.START_GET_GROUP_KEY:
|
|
789
|
+
this.logger.error(`Worker ${this.name} received unsupported action type START_GET_GROUP_KEY, please upgrade to V1...`);
|
|
790
|
+
return Promise.resolve();
|
|
791
|
+
case dispatcher_1.ActionType.UNRECOGNIZED:
|
|
792
|
+
this.logger.error(`Worker ${this.name} received unrecognized action type ${action.actionType}`);
|
|
793
|
+
return Promise.resolve();
|
|
794
|
+
default: {
|
|
795
|
+
const _ = type;
|
|
796
|
+
throw new Error(`Unhandled action type: ${_}`);
|
|
797
|
+
}
|
|
766
798
|
}
|
|
767
799
|
});
|
|
768
800
|
}
|
|
@@ -888,3 +920,10 @@ function validateCelExpression(_expr) {
|
|
|
888
920
|
// For now, we'll just return true to mimic the behavior.
|
|
889
921
|
return true;
|
|
890
922
|
}
|
|
923
|
+
function resolveExecutionTimeout(task, workflowDefaults) {
|
|
924
|
+
return (0, duration_1.durationToString)(task.executionTimeout || task.timeout || (workflowDefaults === null || workflowDefaults === void 0 ? void 0 : workflowDefaults.executionTimeout) || '60s');
|
|
925
|
+
}
|
|
926
|
+
function resolveScheduleTimeout(task, workflowDefaults) {
|
|
927
|
+
const value = task.scheduleTimeout || (workflowDefaults === null || workflowDefaults === void 0 ? void 0 : workflowDefaults.scheduleTimeout);
|
|
928
|
+
return value ? (0, duration_1.durationToString)(value) : undefined;
|
|
929
|
+
}
|
|
@@ -14,10 +14,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.__testing = exports.Worker = void 0;
|
|
16
16
|
const sleep_1 = __importDefault(require("../../../util/sleep"));
|
|
17
|
+
const declaration_1 = require("../../declaration");
|
|
17
18
|
const legacy_transformer_1 = require("../../../legacy/legacy-transformer");
|
|
18
19
|
const worker_internal_1 = require("./worker-internal");
|
|
19
20
|
const slot_utils_1 = require("./slot-utils");
|
|
20
21
|
const deprecated_1 = require("./deprecated");
|
|
22
|
+
const engine_version_1 = require("./engine-version");
|
|
21
23
|
/**
|
|
22
24
|
* HatchetWorker class for workflow execution runtime
|
|
23
25
|
*/
|
|
@@ -97,9 +99,35 @@ class Worker {
|
|
|
97
99
|
this._legacyWorker = yield deprecated_1.LegacyDualWorker.create(this._v1, this.name, legacyConfig);
|
|
98
100
|
return this._legacyWorker.start();
|
|
99
101
|
}
|
|
102
|
+
const engineVersion = yield (0, deprecated_1.fetchEngineVersion)(this._v1).catch(() => undefined);
|
|
103
|
+
this._checkEvictionSupport(engineVersion);
|
|
104
|
+
this._internal.engineVersion = engineVersion;
|
|
100
105
|
return this._internal.start();
|
|
101
106
|
});
|
|
102
107
|
}
|
|
108
|
+
_checkEvictionSupport(engineVersion) {
|
|
109
|
+
if ((0, engine_version_1.supportsEviction)(engineVersion))
|
|
110
|
+
return;
|
|
111
|
+
const workflows = (this.config.workflows || []);
|
|
112
|
+
const tasksWithEviction = [];
|
|
113
|
+
for (const wf of workflows) {
|
|
114
|
+
if (!(wf instanceof declaration_1.BaseWorkflowDeclaration))
|
|
115
|
+
continue;
|
|
116
|
+
for (const task of wf.definition._durableTasks) {
|
|
117
|
+
if (task.evictionPolicy) {
|
|
118
|
+
tasksWithEviction.push(`${wf.definition.name}:${task.name}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (tasksWithEviction.length === 0)
|
|
123
|
+
return;
|
|
124
|
+
const names = tasksWithEviction.join(', ');
|
|
125
|
+
const logger = this._v1.config.logger('Worker', this._v1.config.log_level);
|
|
126
|
+
(0, deprecated_1.emitDeprecationNotice)('pre-eviction-engine', `Engine ${engineVersion || 'unknown'} does not support durable eviction ` +
|
|
127
|
+
`(requires >= ${engine_version_1.MinEngineVersion.DURABLE_EVICTION}). ` +
|
|
128
|
+
`Eviction policies will be ignored for tasks: ${names}. ` +
|
|
129
|
+
`Please upgrade your Hatchet engine.`, new Date('2026-03-01T00:00:00Z'), logger);
|
|
130
|
+
}
|
|
103
131
|
/**
|
|
104
132
|
* Stops the worker
|
|
105
133
|
* @returns Promise that resolves when the worker stops
|
|
@@ -163,9 +191,14 @@ class Worker {
|
|
|
163
191
|
const pollInterval = 200;
|
|
164
192
|
const start = Date.now();
|
|
165
193
|
while (Date.now() - start < timeoutMs) {
|
|
166
|
-
|
|
194
|
+
// start() may asynchronously detect a legacy engine and set _legacyWorker
|
|
195
|
+
// after waitUntilReady has already entered this loop
|
|
196
|
+
if (this._legacyWorker) {
|
|
197
|
+
yield (0, sleep_1.default)(2000);
|
|
167
198
|
return;
|
|
168
199
|
}
|
|
200
|
+
if ((_a = this._internal) === null || _a === void 0 ? void 0 : _a.workerId)
|
|
201
|
+
return;
|
|
169
202
|
yield (0, sleep_1.default)(pollInterval);
|
|
170
203
|
}
|
|
171
204
|
throw new Error(`Worker ${this.name} did not become ready within ${timeoutMs}ms`);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SleepCondition = void 0;
|
|
4
|
+
const duration_1 = require("../client/duration");
|
|
4
5
|
const base_1 = require("./base");
|
|
5
6
|
/**
|
|
6
7
|
* Represents a condition that waits for a specified duration before proceeding.
|
|
@@ -27,7 +28,7 @@ class SleepCondition extends base_1.Condition {
|
|
|
27
28
|
*/
|
|
28
29
|
constructor(sleepFor, readableDataKey, action) {
|
|
29
30
|
super({
|
|
30
|
-
readableDataKey: readableDataKey || `sleep-${sleepFor}`,
|
|
31
|
+
readableDataKey: readableDataKey || `sleep-${(0, duration_1.durationToString)(sleepFor)}`,
|
|
31
32
|
action,
|
|
32
33
|
orGroupId: '',
|
|
33
34
|
expression: '',
|
|
@@ -6,6 +6,7 @@ const apply_namespace_1 = require("../../util/apply-namespace");
|
|
|
6
6
|
const _1 = require(".");
|
|
7
7
|
const base_1 = require("./base");
|
|
8
8
|
const parent_condition_1 = require("./parent-condition");
|
|
9
|
+
const duration_1 = require("../client/duration");
|
|
9
10
|
function taskConditionsToPb(task, namespace) {
|
|
10
11
|
const waitForConditions = (0, _1.Render)(base_1.Action.QUEUE, task.waitFor);
|
|
11
12
|
const cancelIfConditions = (0, _1.Render)(base_1.Action.CANCEL, task.cancelIf);
|
|
@@ -21,7 +22,7 @@ function conditionsToPb(conditions, namespace) {
|
|
|
21
22
|
if (condition instanceof _1.SleepCondition) {
|
|
22
23
|
sleepConditions.push({
|
|
23
24
|
base: baseToPb(condition.base),
|
|
24
|
-
sleepFor: condition.sleepFor,
|
|
25
|
+
sleepFor: (0, duration_1.durationToString)(condition.sleepFor),
|
|
25
26
|
});
|
|
26
27
|
}
|
|
27
28
|
else if (condition instanceof _1.UserEventCondition) {
|