@hatchet-dev/typescript-sdk 1.30.0 → 1.30.1
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/clients/dispatcher/action-listener.d.ts +1 -1
- package/clients/dispatcher/action-listener.js +6 -0
- package/clients/listeners/durable-listener/durable-listener-client.d.ts +6 -1
- package/clients/listeners/durable-listener/durable-listener-client.js +124 -37
- package/clients/rest/generated/Api.d.ts +6 -1
- package/clients/rest/generated/Api.js +1 -1
- package/clients/rest/generated/data-contracts.d.ts +9 -1
- package/clients/rest/generated/data-contracts.js +1 -0
- package/package.json +1 -1
- package/protoc/v1/workflows.d.ts +15 -0
- package/protoc/v1/workflows.js +62 -2
- package/v1/client/worker/context.d.ts +2 -0
- package/v1/client/worker/context.js +26 -8
- package/v1/client/worker/eviction/eviction-manager.d.ts +1 -1
- package/v1/client/worker/eviction/eviction-manager.js +12 -5
- package/v1/client/worker/worker-internal.js +27 -15
- package/v1/embedded.d.ts +36 -2
- package/v1/embedded.js +35 -8
- package/v1/examples/durable_callback_ordering/workflow.d.ts +42 -0
- package/v1/examples/durable_callback_ordering/workflow.js +95 -0
- package/v1/examples/durable_eviction/workflow.d.ts +5 -0
- package/v1/examples/durable_eviction/workflow.js +15 -1
- package/v1/examples/e2e-worker.js +60 -55
- package/version.d.ts +1 -1
- package/version.js +1 -1
|
@@ -53,8 +53,15 @@ class DurableEvictionManager {
|
|
|
53
53
|
markActive(key) {
|
|
54
54
|
this._cache.markActive(key);
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
// The invocation count pins the teardown to the invocation that was
|
|
57
|
+
// actually evicted: between requesting eviction and the server's ack, the
|
|
58
|
+
// server may restore and re-dispatch the next invocation, which registers
|
|
59
|
+
// under the same action key and must not be torn down.
|
|
60
|
+
_evictRun(key, invocationCount) {
|
|
61
|
+
const current = this._cache.get(key);
|
|
62
|
+
if (current && current.invocationCount !== invocationCount)
|
|
63
|
+
return;
|
|
64
|
+
this._cancelLocal(key, invocationCount);
|
|
58
65
|
this.unregisterRun(key);
|
|
59
66
|
}
|
|
60
67
|
_tickSafe() {
|
|
@@ -89,7 +96,7 @@ class DurableEvictionManager {
|
|
|
89
96
|
this._logger.debug(`DurableEvictionManager: evicting task_run_external_id=${rec.taskRunExternalId} ` +
|
|
90
97
|
`wait_kind=${rec.waitKind} resource_id=${rec.waitResourceId}`);
|
|
91
98
|
yield this._requestEvictionWithAck(key, rec);
|
|
92
|
-
this._evictRun(key);
|
|
99
|
+
this._evictRun(key, rec.invocationCount);
|
|
93
100
|
}
|
|
94
101
|
});
|
|
95
102
|
}
|
|
@@ -101,7 +108,7 @@ class DurableEvictionManager {
|
|
|
101
108
|
if (rec && rec.invocationCount !== invocationCount)
|
|
102
109
|
return;
|
|
103
110
|
this._logger.info(`DurableEvictionManager: server-initiated eviction for task_run_external_id=${taskRunExternalId} invocation_count=${invocationCount}`);
|
|
104
|
-
this._evictRun(key);
|
|
111
|
+
this._evictRun(key, invocationCount);
|
|
105
112
|
}
|
|
106
113
|
evictAllWaiting() {
|
|
107
114
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -122,7 +129,7 @@ class DurableEvictionManager {
|
|
|
122
129
|
// Always cancel locally even if the server ACK failed, so the
|
|
123
130
|
// future settles and exitGracefully doesn't hang.
|
|
124
131
|
// This will get resolved by the reassignment of the task.
|
|
125
|
-
this._evictRun(rec.key);
|
|
132
|
+
this._evictRun(rec.key, rec.invocationCount);
|
|
126
133
|
evicted++;
|
|
127
134
|
}
|
|
128
135
|
return evicted;
|
|
@@ -383,28 +383,31 @@ class InternalWorker {
|
|
|
383
383
|
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;
|
|
384
384
|
this.evictionManager = new eviction_manager_1.DurableEvictionManager({
|
|
385
385
|
durableSlots: totalDurableSlots,
|
|
386
|
-
cancelLocal: (key) => {
|
|
386
|
+
cancelLocal: (key, invocationCount) => {
|
|
387
387
|
var _a;
|
|
388
388
|
const err = new task_run_terminated_error_1.TaskRunTerminatedError('evicted');
|
|
389
389
|
const ctx = this.contexts[key];
|
|
390
|
+
// A newer invocation may already be registered under this key (the
|
|
391
|
+
// server restored the run before the eviction ack arrived); it must
|
|
392
|
+
// not be aborted for the old invocation's eviction.
|
|
393
|
+
const ctxMatchesEvictedInvocation = ctx && ((_a = ctx.invocationCount) !== null && _a !== void 0 ? _a : 1) === invocationCount;
|
|
390
394
|
if (ctx) {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
395
|
+
// Abort before cleanup: waiters must settle as aborted (eviction),
|
|
396
|
+
// not with cleanup's generic rejection, which would surface as a
|
|
397
|
+
// task failure.
|
|
398
|
+
if (ctxMatchesEvictedInvocation && ctx.abortController) {
|
|
394
399
|
ctx.abortController.abort(err);
|
|
395
400
|
}
|
|
401
|
+
this.client.durableListener.cleanupTaskState(ctx.action.taskRunExternalId, invocationCount);
|
|
396
402
|
}
|
|
397
403
|
const future = this.futures[key];
|
|
398
|
-
if (future) {
|
|
404
|
+
if (future && ctxMatchesEvictedInvocation) {
|
|
399
405
|
future.promise.catch(() => undefined);
|
|
400
406
|
future.cancel(hatchet_promise_1.CancellationReason.EVICTED_BY_WORKER);
|
|
401
407
|
}
|
|
402
408
|
},
|
|
403
409
|
requestEvictionWithAck: (key, rec) => __awaiter(this, void 0, void 0, function* () {
|
|
404
|
-
|
|
405
|
-
const ctx = this.contexts[key];
|
|
406
|
-
const invocationCount = (_a = ctx === null || ctx === void 0 ? void 0 : ctx.invocationCount) !== null && _a !== void 0 ? _a : 1;
|
|
407
|
-
yield this.client.durableListener.sendEvictInvocation(rec.taskRunExternalId, invocationCount, rec.evictionReason);
|
|
410
|
+
yield this.client.durableListener.sendEvictInvocation(rec.taskRunExternalId, rec.invocationCount, rec.evictionReason);
|
|
408
411
|
}),
|
|
409
412
|
logger: this.logger,
|
|
410
413
|
});
|
|
@@ -415,12 +418,16 @@ class InternalWorker {
|
|
|
415
418
|
this.evictionManager.start();
|
|
416
419
|
return this.evictionManager;
|
|
417
420
|
}
|
|
418
|
-
cleanupRun(key) {
|
|
421
|
+
cleanupRun(key, attemptContext) {
|
|
419
422
|
var _a;
|
|
420
|
-
const ctx = this.contexts[key];
|
|
423
|
+
const ctx = attemptContext !== null && attemptContext !== void 0 ? attemptContext : this.contexts[key];
|
|
421
424
|
if (ctx instanceof context_1.DurableContext) {
|
|
422
425
|
this.client.durableListener.cleanupTaskState(ctx.action.taskRunExternalId, ctx.invocationCount);
|
|
423
426
|
}
|
|
427
|
+
// A restored invocation may have re-registered under this key while this
|
|
428
|
+
// attempt was shutting down; the key-owned state now belongs to it.
|
|
429
|
+
if (attemptContext && this.contexts[key] !== attemptContext)
|
|
430
|
+
return;
|
|
424
431
|
(_a = this.evictionManager) === null || _a === void 0 ? void 0 : _a.unregisterRun(key);
|
|
425
432
|
delete this.futures[key];
|
|
426
433
|
delete this.contexts[key];
|
|
@@ -456,7 +463,7 @@ class InternalWorker {
|
|
|
456
463
|
if (!step) {
|
|
457
464
|
this.logger.error(`Registered actions: '${Object.keys(this.action_registry).join(', ')}'`);
|
|
458
465
|
this.logger.error(`Could not find step '${actionId}'`);
|
|
459
|
-
this.cleanupRun(actionKey);
|
|
466
|
+
this.cleanupRun(actionKey, context);
|
|
460
467
|
return;
|
|
461
468
|
}
|
|
462
469
|
const run = () => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -517,7 +524,7 @@ class InternalWorker {
|
|
|
517
524
|
this.logger.error(`Could not send action event: ${actionEventError.message || actionEventError}`);
|
|
518
525
|
}
|
|
519
526
|
finally {
|
|
520
|
-
this.cleanupRun(actionKey);
|
|
527
|
+
this.cleanupRun(actionKey, context);
|
|
521
528
|
}
|
|
522
529
|
});
|
|
523
530
|
const failure = (error) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -526,6 +533,11 @@ class InternalWorker {
|
|
|
526
533
|
if (context.cancelled) {
|
|
527
534
|
return;
|
|
528
535
|
}
|
|
536
|
+
// The run was evicted or cancelled by the worker, not failed by
|
|
537
|
+
// user code; the server already accounts for it.
|
|
538
|
+
if ((0, task_run_terminated_error_1.isTaskRunTerminatedError)(error)) {
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
529
541
|
this.logger.error((0, logger_1.taskRunLog)(taskName, taskRunExternalId, `failed: ${error.message}`));
|
|
530
542
|
if (error.stack) {
|
|
531
543
|
this.logger.error(error.stack);
|
|
@@ -540,7 +552,7 @@ class InternalWorker {
|
|
|
540
552
|
this.logger.error(`Could not send action event: ${e.message}`);
|
|
541
553
|
}
|
|
542
554
|
finally {
|
|
543
|
-
this.cleanupRun(actionKey);
|
|
555
|
+
this.cleanupRun(actionKey, context);
|
|
544
556
|
}
|
|
545
557
|
});
|
|
546
558
|
const future = new hatchet_promise_1.default((() => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -581,7 +593,7 @@ class InternalWorker {
|
|
|
581
593
|
}
|
|
582
594
|
}
|
|
583
595
|
finally {
|
|
584
|
-
this.cleanupRun(actionKey);
|
|
596
|
+
this.cleanupRun(actionKey, context);
|
|
585
597
|
}
|
|
586
598
|
}
|
|
587
599
|
catch (e) {
|
package/v1/embedded.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { AxiosRequestConfig } from 'axios';
|
|
2
2
|
import type { ClientConfig, HatchetClientOptions } from '../clients/hatchet-client';
|
|
3
3
|
import { HatchetClient } from './client/client';
|
|
4
|
+
/**
|
|
5
|
+
* Options for the embedded engine sidecar. All fields are optional; by default the
|
|
6
|
+
* latest hatchet-embedded release is downloaded and started with a bundled Postgres.
|
|
7
|
+
*/
|
|
4
8
|
export interface EmbeddedOptions {
|
|
5
9
|
/**
|
|
6
10
|
* hatchet-embedded release tag to download (defaults to HATCHET_CLIENT_EMBEDDED_VERSION or
|
|
@@ -20,7 +24,9 @@ export interface EmbeddedOptions {
|
|
|
20
24
|
databaseUrl?: string;
|
|
21
25
|
/** store the bundled Postgres runtime and data under this directory */
|
|
22
26
|
postgresDataDir?: string;
|
|
27
|
+
/** bind the engine's gRPC server to this port */
|
|
23
28
|
grpcPort?: number;
|
|
29
|
+
/** bind the REST API server to this port */
|
|
24
30
|
apiPort?: number;
|
|
25
31
|
/** set to false to start only the engine + gRPC, no REST API */
|
|
26
32
|
startApi?: boolean;
|
|
@@ -28,14 +34,25 @@ export interface EmbeddedOptions {
|
|
|
28
34
|
runMigrations?: boolean;
|
|
29
35
|
/** use RabbitMQ instead of the Postgres message queue */
|
|
30
36
|
rabbitmqUrl?: string;
|
|
37
|
+
/** log level for the engine's output */
|
|
31
38
|
logLevel?: string;
|
|
39
|
+
/** how long to wait for the engine to become ready, in milliseconds (default 300000) */
|
|
32
40
|
readyTimeoutMs?: number;
|
|
33
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* A running embedded engine sidecar and its connection details, as returned by
|
|
44
|
+
* `startEmbeddedSidecar`.
|
|
45
|
+
*/
|
|
34
46
|
export interface EmbeddedSidecar {
|
|
47
|
+
/** API token for the sidecar's default tenant */
|
|
35
48
|
token: string;
|
|
49
|
+
/** ID of the sidecar's default tenant */
|
|
36
50
|
tenantId: string;
|
|
51
|
+
/** host:port of the engine's gRPC server */
|
|
37
52
|
grpcAddress: string;
|
|
53
|
+
/** base URL of the REST API (empty when `startApi` is false) */
|
|
38
54
|
apiUrl: string;
|
|
55
|
+
/** gracefully stops the sidecar and resolves once it has fully exited */
|
|
39
56
|
stop: () => Promise<void>;
|
|
40
57
|
}
|
|
41
58
|
/**
|
|
@@ -46,12 +63,25 @@ export interface EmbeddedSidecar {
|
|
|
46
63
|
* your program has returned.
|
|
47
64
|
*/
|
|
48
65
|
export declare function stopEmbeddedSidecar(): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Downloads (and caches) the hatchet-embedded sidecar binary, spawns it, and waits
|
|
68
|
+
* until the embedded engine is ready. The sidecar shuts down when this process exits.
|
|
69
|
+
* Use {@link HatchetEmbeddedClient.init} unless you need the raw connection details.
|
|
70
|
+
*/
|
|
49
71
|
export declare function startEmbeddedSidecar(opts?: EmbeddedOptions): Promise<EmbeddedSidecar>;
|
|
72
|
+
/**
|
|
73
|
+
* Entry point for embedded mode. `init()` starts a full local Hatchet engine and
|
|
74
|
+
* returns a regular Hatchet client connected to it. No API token or Docker is needed.
|
|
75
|
+
* See the [embedded mode guide](https://docs.hatchet.run/v1/embedded).
|
|
76
|
+
*/
|
|
50
77
|
export declare class HatchetEmbeddedClient {
|
|
51
78
|
/**
|
|
52
79
|
* Runs a full Hatchet engine locally via the hatchet-embedded sidecar (downloaded
|
|
53
|
-
* on first use) and returns a client wired to it.
|
|
54
|
-
*
|
|
80
|
+
* on first use) and returns a client wired to it. No API token or Docker is needed,
|
|
81
|
+
* which makes this a good fit for local development and CI. By default the sidecar
|
|
82
|
+
* starts a bundled Postgres; pass `databaseUrl` to point it at your own instead.
|
|
83
|
+
*
|
|
84
|
+
* See the [embedded mode guide](https://docs.hatchet.run/v1/embedded).
|
|
55
85
|
* @param embeddedOpts - Options for the embedded engine (version, ports, database, ...).
|
|
56
86
|
* @param config - Optional configuration overrides for the client.
|
|
57
87
|
* @param options - Optional client options.
|
|
@@ -60,6 +90,10 @@ export declare class HatchetEmbeddedClient {
|
|
|
60
90
|
*/
|
|
61
91
|
static init<T extends Record<string, any> = {}, U extends Record<string, any> = {}>(embeddedOpts?: EmbeddedOptions, config?: Omit<Partial<ClientConfig>, 'middleware'>, options?: HatchetClientOptions, axiosConfig?: AxiosRequestConfig): Promise<EmbeddedClient<T, U>>;
|
|
62
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* A `HatchetClient` connected to an embedded engine, extended with `stopEmbedded`.
|
|
95
|
+
* Returned by {@link HatchetEmbeddedClient.init}.
|
|
96
|
+
*/
|
|
63
97
|
export type EmbeddedClient<T extends Record<string, any> = {}, U extends Record<string, any> = {}> = HatchetClient<T, U> & {
|
|
64
98
|
/**
|
|
65
99
|
* Gracefully stops the embedded engine sidecar and resolves once it has
|
package/v1/embedded.js
CHANGED
|
@@ -52,6 +52,25 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
52
52
|
exports.HatchetEmbeddedClient = void 0;
|
|
53
53
|
exports.stopEmbeddedSidecar = stopEmbeddedSidecar;
|
|
54
54
|
exports.startEmbeddedSidecar = startEmbeddedSidecar;
|
|
55
|
+
/**
|
|
56
|
+
* Embedded mode runs a full local Hatchet engine as a sidecar process managed by your
|
|
57
|
+
* application. It needs no API token, no Docker, and no external services (by default
|
|
58
|
+
* the sidecar starts a bundled Postgres), which makes it a good fit for local
|
|
59
|
+
* development and CI.
|
|
60
|
+
*
|
|
61
|
+
* ```typescript
|
|
62
|
+
* import { HatchetEmbeddedClient } from './embedded.js';
|
|
63
|
+
*
|
|
64
|
+
* const hatchet = await HatchetEmbeddedClient.init();
|
|
65
|
+
* // ... register workers and run tasks as usual ...
|
|
66
|
+
* await hatchet.stopEmbedded();
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* See the [embedded mode guide](https://docs.hatchet.run/v1/embedded) for setup and
|
|
70
|
+
* configuration details.
|
|
71
|
+
*
|
|
72
|
+
* @module Embedded
|
|
73
|
+
*/
|
|
55
74
|
const child_process_1 = require("child_process");
|
|
56
75
|
const client_1 = require("./client/client");
|
|
57
76
|
const crypto_1 = require("crypto");
|
|
@@ -211,12 +230,7 @@ function waitForHandshake(child, handshakePath, timeoutMs) {
|
|
|
211
230
|
throw new Error(`hatchet embedded sidecar did not become ready within ${timeoutMs}ms`);
|
|
212
231
|
});
|
|
213
232
|
}
|
|
214
|
-
|
|
215
|
-
* Downloads (and caches) the hatchet-embedded sidecar binary, spawns it, and
|
|
216
|
-
* waits until the embedded engine is ready. The sidecar shuts down when this
|
|
217
|
-
* process exits. Use `HatchetEmbedded()` unless you need the raw
|
|
218
|
-
* connection details.
|
|
219
|
-
*/
|
|
233
|
+
// sidecars started in this process that have not been stopped yet
|
|
220
234
|
const activeSidecars = new Set();
|
|
221
235
|
/**
|
|
222
236
|
* Gracefully stops every sidecar started in this process by
|
|
@@ -232,6 +246,11 @@ function stopEmbeddedSidecar() {
|
|
|
232
246
|
}
|
|
233
247
|
});
|
|
234
248
|
}
|
|
249
|
+
/**
|
|
250
|
+
* Downloads (and caches) the hatchet-embedded sidecar binary, spawns it, and waits
|
|
251
|
+
* until the embedded engine is ready. The sidecar shuts down when this process exits.
|
|
252
|
+
* Use {@link HatchetEmbeddedClient.init} unless you need the raw connection details.
|
|
253
|
+
*/
|
|
235
254
|
function startEmbeddedSidecar() {
|
|
236
255
|
return __awaiter(this, arguments, void 0, function* (opts = {}) {
|
|
237
256
|
var _a, _b;
|
|
@@ -321,11 +340,19 @@ function startEmbeddedSidecar() {
|
|
|
321
340
|
return sidecar;
|
|
322
341
|
});
|
|
323
342
|
}
|
|
343
|
+
/**
|
|
344
|
+
* Entry point for embedded mode. `init()` starts a full local Hatchet engine and
|
|
345
|
+
* returns a regular Hatchet client connected to it. No API token or Docker is needed.
|
|
346
|
+
* See the [embedded mode guide](https://docs.hatchet.run/v1/embedded).
|
|
347
|
+
*/
|
|
324
348
|
class HatchetEmbeddedClient {
|
|
325
349
|
/**
|
|
326
350
|
* Runs a full Hatchet engine locally via the hatchet-embedded sidecar (downloaded
|
|
327
|
-
* on first use) and returns a client wired to it.
|
|
328
|
-
*
|
|
351
|
+
* on first use) and returns a client wired to it. No API token or Docker is needed,
|
|
352
|
+
* which makes this a good fit for local development and CI. By default the sidecar
|
|
353
|
+
* starts a bundled Postgres; pass `databaseUrl` to point it at your own instead.
|
|
354
|
+
*
|
|
355
|
+
* See the [embedded mode guide](https://docs.hatchet.run/v1/embedded).
|
|
329
356
|
* @param embeddedOpts - Options for the embedded engine (version, ports, database, ...).
|
|
330
357
|
* @param config - Optional configuration overrides for the client.
|
|
331
358
|
* @param options - Optional client options.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type LeafInput = {
|
|
2
|
+
mid: number;
|
|
3
|
+
branch: number;
|
|
4
|
+
delayMs: number;
|
|
5
|
+
generation: number;
|
|
6
|
+
};
|
|
7
|
+
export type LeafOutput = {
|
|
8
|
+
mid: number;
|
|
9
|
+
branch: number;
|
|
10
|
+
generation: number;
|
|
11
|
+
};
|
|
12
|
+
export type MidInput = {
|
|
13
|
+
mid: number;
|
|
14
|
+
branches: number;
|
|
15
|
+
childDelayMs: number;
|
|
16
|
+
delayStepMs: number;
|
|
17
|
+
};
|
|
18
|
+
export type MidOutput = {
|
|
19
|
+
mid: number;
|
|
20
|
+
completedBranches: number[];
|
|
21
|
+
invocationCount: number;
|
|
22
|
+
};
|
|
23
|
+
export type RootInput = {
|
|
24
|
+
durables?: number;
|
|
25
|
+
branches?: number;
|
|
26
|
+
childDelayMs?: number;
|
|
27
|
+
delayStepMs?: number;
|
|
28
|
+
};
|
|
29
|
+
export type RootOutput = {
|
|
30
|
+
rootInvocationCount: number;
|
|
31
|
+
midInvocationCounts: number[];
|
|
32
|
+
completedMids: number[];
|
|
33
|
+
};
|
|
34
|
+
export declare const ROOT_DEFAULTS: {
|
|
35
|
+
durables: number;
|
|
36
|
+
branches: number;
|
|
37
|
+
childDelayMs: number;
|
|
38
|
+
delayStepMs: number;
|
|
39
|
+
};
|
|
40
|
+
export declare const callbackOrderingLeaf: import("../..").TaskWorkflowDeclaration<LeafInput, LeafOutput, {}, {}, {}, {}>;
|
|
41
|
+
export declare const callbackOrderingMid: import("../..").TaskWorkflowDeclaration<MidInput, MidOutput, {}, {}, {}, {}>;
|
|
42
|
+
export declare const callbackOrderingRoot: import("../..").TaskWorkflowDeclaration<RootInput, RootOutput, {}, {}, {}, {}>;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.callbackOrderingRoot = exports.callbackOrderingMid = exports.callbackOrderingLeaf = exports.ROOT_DEFAULTS = void 0;
|
|
16
|
+
const sleep_1 = __importDefault(require("../../../util/sleep"));
|
|
17
|
+
const hatchet_client_1 = require("../hatchet-client");
|
|
18
|
+
const WORKFLOW_PREFIX = 'durable-callback-ordering';
|
|
19
|
+
const EVICTION_POLICY = {
|
|
20
|
+
ttl: 250,
|
|
21
|
+
allowCapacityEviction: true,
|
|
22
|
+
priority: 0,
|
|
23
|
+
};
|
|
24
|
+
exports.ROOT_DEFAULTS = {
|
|
25
|
+
durables: 4,
|
|
26
|
+
branches: 8,
|
|
27
|
+
childDelayMs: 1500,
|
|
28
|
+
delayStepMs: 3,
|
|
29
|
+
};
|
|
30
|
+
exports.callbackOrderingLeaf = hatchet_client_1.hatchet.task({
|
|
31
|
+
name: `${WORKFLOW_PREFIX}-leaf`,
|
|
32
|
+
executionTimeout: '1m',
|
|
33
|
+
fn: (input) => __awaiter(void 0, void 0, void 0, function* () {
|
|
34
|
+
yield (0, sleep_1.default)(input.delayMs);
|
|
35
|
+
return { mid: input.mid, branch: input.branch, generation: input.generation };
|
|
36
|
+
}),
|
|
37
|
+
});
|
|
38
|
+
exports.callbackOrderingMid = hatchet_client_1.hatchet.durableTask({
|
|
39
|
+
name: `${WORKFLOW_PREFIX}-mid`,
|
|
40
|
+
executionTimeout: '5m',
|
|
41
|
+
retries: 0,
|
|
42
|
+
evictionPolicy: EVICTION_POLICY,
|
|
43
|
+
fn: (input, ctx) => __awaiter(void 0, void 0, void 0, function* () {
|
|
44
|
+
// Staggered first-generation children complete out of spawn order, so each
|
|
45
|
+
// branch's second-generation spawn is emitted in completion order. Replays
|
|
46
|
+
// after eviction must re-deliver those completions in the recorded order or
|
|
47
|
+
// the re-emitted spawn sequence diverges from the event log.
|
|
48
|
+
const branch = (branchIndex) => __awaiter(void 0, void 0, void 0, function* () {
|
|
49
|
+
const firstDelayMs = input.childDelayMs + (input.branches - branchIndex - 1) * input.delayStepMs;
|
|
50
|
+
const first = yield exports.callbackOrderingLeaf.run({
|
|
51
|
+
mid: input.mid,
|
|
52
|
+
branch: branchIndex,
|
|
53
|
+
delayMs: firstDelayMs,
|
|
54
|
+
generation: 1,
|
|
55
|
+
});
|
|
56
|
+
const second = yield exports.callbackOrderingLeaf.run({
|
|
57
|
+
mid: input.mid,
|
|
58
|
+
branch: first.branch,
|
|
59
|
+
delayMs: input.childDelayMs,
|
|
60
|
+
generation: 2,
|
|
61
|
+
});
|
|
62
|
+
return second.branch;
|
|
63
|
+
});
|
|
64
|
+
const completed = yield Promise.all(Array.from({ length: input.branches }, (_, branchIndex) => branch(branchIndex)));
|
|
65
|
+
return {
|
|
66
|
+
mid: input.mid,
|
|
67
|
+
completedBranches: completed,
|
|
68
|
+
invocationCount: ctx.invocationCount,
|
|
69
|
+
};
|
|
70
|
+
}),
|
|
71
|
+
});
|
|
72
|
+
exports.callbackOrderingRoot = hatchet_client_1.hatchet.durableTask({
|
|
73
|
+
name: `${WORKFLOW_PREFIX}-root`,
|
|
74
|
+
executionTimeout: '10m',
|
|
75
|
+
retries: 0,
|
|
76
|
+
evictionPolicy: EVICTION_POLICY,
|
|
77
|
+
fn: (input, ctx) => __awaiter(void 0, void 0, void 0, function* () {
|
|
78
|
+
var _a;
|
|
79
|
+
const durables = (_a = input.durables) !== null && _a !== void 0 ? _a : exports.ROOT_DEFAULTS.durables;
|
|
80
|
+
const results = yield Promise.all(Array.from({ length: durables }, (_, midIndex) => {
|
|
81
|
+
var _a, _b, _c;
|
|
82
|
+
return exports.callbackOrderingMid.run({
|
|
83
|
+
mid: midIndex,
|
|
84
|
+
branches: (_a = input.branches) !== null && _a !== void 0 ? _a : exports.ROOT_DEFAULTS.branches,
|
|
85
|
+
childDelayMs: (_b = input.childDelayMs) !== null && _b !== void 0 ? _b : exports.ROOT_DEFAULTS.childDelayMs,
|
|
86
|
+
delayStepMs: (_c = input.delayStepMs) !== null && _c !== void 0 ? _c : exports.ROOT_DEFAULTS.delayStepMs,
|
|
87
|
+
});
|
|
88
|
+
}));
|
|
89
|
+
return {
|
|
90
|
+
rootInvocationCount: ctx.invocationCount,
|
|
91
|
+
midInvocationCounts: results.map((item) => item.invocationCount),
|
|
92
|
+
completedMids: results.map((item) => item.mid),
|
|
93
|
+
};
|
|
94
|
+
}),
|
|
95
|
+
});
|
|
@@ -13,6 +13,11 @@ export declare const evictableSleepForGracefulTermination: import("../..").TaskW
|
|
|
13
13
|
export declare const evictableWaitForEvent: import("../..").TaskWorkflowDeclaration<import("../..").JsonObject, {
|
|
14
14
|
status: string;
|
|
15
15
|
}, {}, {}, {}, {}>;
|
|
16
|
+
export declare const MEMO_EVENT_KEY = "durable-eviction:memo-event";
|
|
17
|
+
export declare const evictableMemoThenWaitForEvent: import("../..").TaskWorkflowDeclaration<import("../..").JsonObject, {
|
|
18
|
+
status: string;
|
|
19
|
+
memoizedNow: string;
|
|
20
|
+
}, {}, {}, {}, {}>;
|
|
16
21
|
export declare const evictableChildSpawn: import("../..").TaskWorkflowDeclaration<import("../..").JsonObject, {
|
|
17
22
|
child: {
|
|
18
23
|
child_status: string;
|
|
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.nonEvictableSleep = exports.capacityEvictableSleep = exports.CAPACITY_SLEEP_SECONDS = exports.evictableChildBulkSpawn = exports.bulkChildTask = exports.multipleEviction = exports.evictableChildSpawn = exports.evictableWaitForEvent = exports.evictableSleepForGracefulTermination = exports.evictableSleep = exports.childTask = exports.EVENT_KEY = exports.LONG_SLEEP_SECONDS = exports.EVICTION_TTL_SECONDS = void 0;
|
|
15
|
+
exports.nonEvictableSleep = exports.capacityEvictableSleep = exports.CAPACITY_SLEEP_SECONDS = exports.evictableChildBulkSpawn = exports.bulkChildTask = exports.multipleEviction = exports.evictableChildSpawn = exports.evictableMemoThenWaitForEvent = exports.MEMO_EVENT_KEY = exports.evictableWaitForEvent = exports.evictableSleepForGracefulTermination = exports.evictableSleep = exports.childTask = exports.EVENT_KEY = exports.LONG_SLEEP_SECONDS = exports.EVICTION_TTL_SECONDS = void 0;
|
|
16
16
|
const sleep_1 = __importDefault(require("../../../util/sleep"));
|
|
17
17
|
const hatchet_client_1 = require("../hatchet-client");
|
|
18
18
|
exports.EVICTION_TTL_SECONDS = 5;
|
|
@@ -66,6 +66,20 @@ exports.evictableWaitForEvent = hatchet_client_1.hatchet.durableTask({
|
|
|
66
66
|
return { status: 'completed' };
|
|
67
67
|
}),
|
|
68
68
|
});
|
|
69
|
+
exports.MEMO_EVENT_KEY = 'durable-eviction:memo-event';
|
|
70
|
+
exports.evictableMemoThenWaitForEvent = hatchet_client_1.hatchet.durableTask({
|
|
71
|
+
name: 'evictable-memo-then-wait-for-event',
|
|
72
|
+
executionTimeout: '5m',
|
|
73
|
+
evictionPolicy: EVICTION_POLICY,
|
|
74
|
+
fn: (_input, ctx) => __awaiter(void 0, void 0, void 0, function* () {
|
|
75
|
+
// now() records a memo node. On replay after eviction, the server
|
|
76
|
+
// re-delivers that node's completion, which never has a callback waiter
|
|
77
|
+
// and must not block the event completion queued behind it.
|
|
78
|
+
const memoizedNow = yield ctx.now();
|
|
79
|
+
yield ctx.waitForEvent(exports.MEMO_EVENT_KEY, 'true');
|
|
80
|
+
return { status: 'completed', memoizedNow: memoizedNow.toISOString() };
|
|
81
|
+
}),
|
|
82
|
+
});
|
|
69
83
|
exports.evictableChildSpawn = hatchet_client_1.hatchet.durableTask({
|
|
70
84
|
name: 'evictable-child-spawn',
|
|
71
85
|
executionTimeout: '5m',
|
|
@@ -30,24 +30,25 @@ const workflow_9 = require("./dag/workflow");
|
|
|
30
30
|
const workflow_10 = require("./durable/workflow");
|
|
31
31
|
const workflow_11 = require("./durable_event/workflow");
|
|
32
32
|
const workflow_12 = require("./durable_eviction/workflow");
|
|
33
|
-
const workflow_13 = require("./
|
|
34
|
-
const workflow_14 = require("./
|
|
35
|
-
const workflow_15 = require("./
|
|
36
|
-
const workflow_16 = require("./
|
|
37
|
-
const workflow_17 = require("./
|
|
38
|
-
const workflow_18 = require("./
|
|
39
|
-
const workflow_19 = require("./
|
|
40
|
-
const workflow_20 = require("./
|
|
33
|
+
const workflow_13 = require("./durable_callback_ordering/workflow");
|
|
34
|
+
const workflow_14 = require("./durable_sleep/workflow");
|
|
35
|
+
const workflow_15 = require("./logger/workflow");
|
|
36
|
+
const workflow_16 = require("./non_retryable/workflow");
|
|
37
|
+
const workflow_17 = require("./on_failure/workflow");
|
|
38
|
+
const workflow_18 = require("./idempotency/workflow");
|
|
39
|
+
const workflow_19 = require("./on_event/workflow");
|
|
40
|
+
const workflow_20 = require("./return_exceptions/workflow");
|
|
41
|
+
const workflow_21 = require("./run_details/workflow");
|
|
41
42
|
const e2e_workflows_1 = require("./simple/e2e-workflows");
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
const
|
|
43
|
+
const workflow_22 = require("./batch_assign/workflow");
|
|
44
|
+
const workflow_23 = require("./streaming/workflow");
|
|
45
|
+
const workflow_24 = require("./subscribe_to_stream/workflow");
|
|
46
|
+
const workflow_25 = require("./timeout/workflow");
|
|
47
|
+
const workflow_26 = require("./webhooks/workflow");
|
|
48
|
+
const workflow_27 = require("./child_index/workflow");
|
|
49
|
+
const workflow_28 = require("./support_agent/workflow");
|
|
50
|
+
const workflow_29 = require("./welcome_email/workflow");
|
|
51
|
+
const workflow_30 = require("./pdf_pipeline/workflow");
|
|
51
52
|
const workflows = [
|
|
52
53
|
workflow_1.bulkChild,
|
|
53
54
|
workflow_1.bulkParentWorkflow,
|
|
@@ -81,54 +82,58 @@ const workflows = [
|
|
|
81
82
|
workflow_10.errorRaisingDurableParent,
|
|
82
83
|
workflow_11.durableEvent,
|
|
83
84
|
workflow_11.durableEventWithFilter,
|
|
84
|
-
workflow_13.
|
|
85
|
+
workflow_13.callbackOrderingLeaf,
|
|
86
|
+
workflow_13.callbackOrderingMid,
|
|
87
|
+
workflow_13.callbackOrderingRoot,
|
|
88
|
+
workflow_14.durableSleep,
|
|
85
89
|
workflow_12.evictableSleep,
|
|
86
90
|
workflow_12.evictableWaitForEvent,
|
|
91
|
+
workflow_12.evictableMemoThenWaitForEvent,
|
|
87
92
|
workflow_12.evictableChildSpawn,
|
|
88
93
|
workflow_12.multipleEviction,
|
|
89
94
|
workflow_12.nonEvictableSleep,
|
|
90
95
|
workflow_12.childTask,
|
|
91
96
|
workflow_12.bulkChildTask,
|
|
92
97
|
workflow_12.evictableChildBulkSpawn,
|
|
93
|
-
(0,
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
(0, workflow_15.createLoggingWorkflow)(hatchet_client_1.hatchet),
|
|
99
|
+
workflow_16.nonRetryableWorkflow,
|
|
100
|
+
workflow_17.failureWorkflow,
|
|
101
|
+
workflow_18.idempotentTask,
|
|
102
|
+
workflow_18.idempotentTaskShortWindow,
|
|
103
|
+
workflow_19.lower,
|
|
104
|
+
workflow_20.returnExceptionsTask,
|
|
105
|
+
workflow_21.runDetailTestWorkflow,
|
|
101
106
|
e2e_workflows_1.helloWorld,
|
|
102
107
|
e2e_workflows_1.helloWorldDurable,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
108
|
+
workflow_23.streamingTask,
|
|
109
|
+
workflow_24.dagStream,
|
|
110
|
+
workflow_24.longStream,
|
|
111
|
+
workflow_25.timeoutTask,
|
|
112
|
+
workflow_25.refreshTimeoutTask,
|
|
113
|
+
workflow_26.webhookWorkflow,
|
|
114
|
+
workflow_27.childIndexChild,
|
|
115
|
+
workflow_27.childIndexParent,
|
|
116
|
+
workflow_27.scenarioTask,
|
|
117
|
+
workflow_27.orchestratorTask,
|
|
118
|
+
workflow_28.supportAgent,
|
|
119
|
+
workflow_28.triageTicket,
|
|
120
|
+
workflow_28.generateReply,
|
|
121
|
+
workflow_28.escalateTicket,
|
|
122
|
+
workflow_29.welcomeEmail,
|
|
123
|
+
workflow_30.pdfPipeline,
|
|
124
|
+
workflow_22.batchSimple,
|
|
125
|
+
workflow_22.batchKeyed,
|
|
126
|
+
workflow_22.batchKeyedFailable,
|
|
127
|
+
workflow_22.batchKeyedInterval,
|
|
128
|
+
workflow_22.batchLarge,
|
|
129
|
+
workflow_22.batchSingle,
|
|
130
|
+
workflow_22.batchOrdered,
|
|
131
|
+
workflow_22.batchBroadcast,
|
|
132
|
+
workflow_22.batchCancel,
|
|
133
|
+
workflow_22.child,
|
|
134
|
+
workflow_22.childBatch,
|
|
135
|
+
workflow_22.batchChildSpawn,
|
|
136
|
+
workflow_22.batchChildBatchSpawn,
|
|
132
137
|
];
|
|
133
138
|
function main() {
|
|
134
139
|
return __awaiter(this, void 0, void 0, function* () {
|
package/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const HATCHET_VERSION = "1.30.
|
|
1
|
+
export declare const HATCHET_VERSION = "1.30.1";
|
package/version.js
CHANGED