@hatchet-dev/typescript-sdk 1.16.0 → 1.17.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/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 +116 -15
- package/clients/listeners/durable-listener/durable-listener-client.js +770 -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 +535 -100
- package/protoc/events/events.js +54 -14
- package/protoc/google/protobuf/timestamp.js +1 -1
- package/protoc/v1/dispatcher.d.ts +169 -0
- package/protoc/v1/dispatcher.js +2096 -8
- package/protoc/v1/shared/condition.js +37 -11
- package/protoc/v1/shared/trigger.d.ts +89 -0
- package/protoc/v1/shared/trigger.js +524 -0
- package/protoc/v1/workflows.d.ts +34 -34
- package/protoc/v1/workflows.js +452 -254
- package/protoc/workflows/workflows.d.ts +2 -75
- package/protoc/workflows/workflows.js +157 -529
- 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
|
@@ -30,6 +30,10 @@ const transformer_1 = require("../../conditions/transformer");
|
|
|
30
30
|
const condition_1 = require("../../../protoc/v1/shared/condition");
|
|
31
31
|
const apply_namespace_1 = require("../../../util/apply-namespace");
|
|
32
32
|
const abort_error_1 = require("../../../util/abort-error");
|
|
33
|
+
const crypto_1 = require("crypto");
|
|
34
|
+
const duration_1 = require("../duration");
|
|
35
|
+
const engine_version_1 = require("./engine-version");
|
|
36
|
+
const pre_eviction_1 = require("./deprecated/pre-eviction");
|
|
33
37
|
/**
|
|
34
38
|
* ContextWorker is a wrapper around the V1Worker class that provides a more user-friendly interface for the worker from the context of a run.
|
|
35
39
|
*/
|
|
@@ -328,7 +332,7 @@ class Context {
|
|
|
328
332
|
this._logger.warn('cannot refresh timeout from context without stepRunId');
|
|
329
333
|
return;
|
|
330
334
|
}
|
|
331
|
-
yield this.v1.dispatcher.refreshTimeout(incrementBy, taskRunExternalId);
|
|
335
|
+
yield this.v1.dispatcher.refreshTimeout((0, duration_1.durationToString)(incrementBy), taskRunExternalId);
|
|
332
336
|
});
|
|
333
337
|
}
|
|
334
338
|
/**
|
|
@@ -634,19 +638,68 @@ exports.Context = Context;
|
|
|
634
638
|
* It extends the Context class and includes additional methods for durable execution like sleepFor and waitFor.
|
|
635
639
|
*/
|
|
636
640
|
class DurableContext extends Context {
|
|
637
|
-
constructor() {
|
|
638
|
-
super(
|
|
639
|
-
this.
|
|
641
|
+
constructor(action, v1, worker, durableListener, evictionManager, engineVersion) {
|
|
642
|
+
super(action, v1, worker);
|
|
643
|
+
this._waitKey = 0;
|
|
644
|
+
this._durableListener = durableListener;
|
|
645
|
+
this._evictionManager = evictionManager;
|
|
646
|
+
this._engineVersion = engineVersion;
|
|
647
|
+
}
|
|
648
|
+
get supportsEviction() {
|
|
649
|
+
return (0, engine_version_1.supportsEviction)(this._engineVersion);
|
|
650
|
+
}
|
|
651
|
+
get durableListener() {
|
|
652
|
+
return this._durableListener;
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* The invocation count for the current durable task. Used for deduplication across replays.
|
|
656
|
+
*/
|
|
657
|
+
get invocationCount() {
|
|
658
|
+
var _a;
|
|
659
|
+
return (_a = this.action.durableTaskInvocationCount) !== null && _a !== void 0 ? _a : 1;
|
|
660
|
+
}
|
|
661
|
+
get _actionKey() {
|
|
662
|
+
return this.action.key;
|
|
663
|
+
}
|
|
664
|
+
withEvictionWait(waitKind, resourceId, fn) {
|
|
665
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
666
|
+
var _a, _b;
|
|
667
|
+
(_a = this._evictionManager) === null || _a === void 0 ? void 0 : _a.markWaiting(this._actionKey, waitKind, resourceId);
|
|
668
|
+
try {
|
|
669
|
+
return yield fn();
|
|
670
|
+
}
|
|
671
|
+
finally {
|
|
672
|
+
(_b = this._evictionManager) === null || _b === void 0 ? void 0 : _b.markActive(this._actionKey);
|
|
673
|
+
}
|
|
674
|
+
});
|
|
640
675
|
}
|
|
641
676
|
/**
|
|
642
677
|
* Pauses execution for the specified duration.
|
|
643
678
|
* Duration is "global" meaning it will wait in real time regardless of transient failures like worker restarts.
|
|
644
679
|
* @param duration - The duration to sleep for.
|
|
645
|
-
* @returns A promise that resolves when the sleep duration has elapsed.
|
|
680
|
+
* @returns A promise that resolves with a SleepResult when the sleep duration has elapsed.
|
|
646
681
|
*/
|
|
647
682
|
sleepFor(duration, readableDataKey) {
|
|
648
683
|
return __awaiter(this, void 0, void 0, function* () {
|
|
649
|
-
|
|
684
|
+
const res = yield this.waitFor({ sleepFor: duration, readableDataKey });
|
|
685
|
+
const matches = res['CREATE'] || {};
|
|
686
|
+
const [firstMatch] = Object.values(matches);
|
|
687
|
+
if (!firstMatch || firstMatch.length === 0) {
|
|
688
|
+
return { durationMs: (0, duration_1.durationToMs)(duration) };
|
|
689
|
+
}
|
|
690
|
+
const [sleep] = firstMatch;
|
|
691
|
+
const sleepDuration = sleep === null || sleep === void 0 ? void 0 : sleep.sleep_duration;
|
|
692
|
+
if (sleepDuration) {
|
|
693
|
+
const DURATION_RE = /^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/;
|
|
694
|
+
const match = sleepDuration.match(DURATION_RE);
|
|
695
|
+
if (match) {
|
|
696
|
+
const [, h, m, s] = match;
|
|
697
|
+
const ms = (parseInt(h !== null && h !== void 0 ? h : '0', 10) * 3600 + parseInt(m !== null && m !== void 0 ? m : '0', 10) * 60 + parseInt(s !== null && s !== void 0 ? s : '0', 10)) *
|
|
698
|
+
1000;
|
|
699
|
+
return { durationMs: ms };
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
return { durationMs: (0, duration_1.durationToMs)(duration) };
|
|
650
703
|
});
|
|
651
704
|
}
|
|
652
705
|
/**
|
|
@@ -658,23 +711,196 @@ class DurableContext extends Context {
|
|
|
658
711
|
waitFor(conditions) {
|
|
659
712
|
return __awaiter(this, void 0, void 0, function* () {
|
|
660
713
|
this.throwIfCancelled();
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
714
|
+
if (!this.supportsEviction) {
|
|
715
|
+
return this._waitForPreEviction(conditions);
|
|
716
|
+
}
|
|
717
|
+
const rendered = (0, conditions_1.Render)(condition_1.Action.CREATE, conditions);
|
|
718
|
+
const pbConditions = (0, transformer_1.conditionsToPb)(rendered, this.v1.config.namespace);
|
|
719
|
+
const ack = yield this._durableListener.sendEvent(this.action.taskRunExternalId, this.invocationCount, {
|
|
720
|
+
kind: 'waitFor',
|
|
721
|
+
waitForConditions: {
|
|
722
|
+
sleepConditions: pbConditions.sleepConditions,
|
|
723
|
+
userEventConditions: pbConditions.userEventConditions,
|
|
724
|
+
},
|
|
725
|
+
});
|
|
726
|
+
const resourceId = rendered
|
|
727
|
+
.map((c) => c.base.readableDataKey)
|
|
728
|
+
.filter(Boolean)
|
|
729
|
+
.join(',') || `node:${ack.nodeId}`;
|
|
730
|
+
return this.withEvictionWait('waitFor', resourceId, () => __awaiter(this, void 0, void 0, function* () {
|
|
731
|
+
const result = yield this._durableListener.waitForCallback(this.action.taskRunExternalId, this.invocationCount, ack.branchId, ack.nodeId, { signal: this.abortController.signal });
|
|
732
|
+
return result.payload || {};
|
|
733
|
+
}));
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
waitForEvent(key, expression, payloadSchema) {
|
|
737
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
738
|
+
const res = yield this.waitFor({ eventKey: key, expression });
|
|
739
|
+
// The engine returns an object like:
|
|
740
|
+
// {"CREATE": {"signal_key_1": [{"id": ..., "data": {...}}]}}
|
|
741
|
+
// Since we have a single match, the list will only have one item.
|
|
742
|
+
const matches = res['CREATE'] || {};
|
|
743
|
+
const [firstMatch] = Object.values(matches);
|
|
744
|
+
if (!firstMatch || firstMatch.length === 0) {
|
|
745
|
+
if (payloadSchema) {
|
|
746
|
+
return payloadSchema.parse({});
|
|
747
|
+
}
|
|
748
|
+
return {};
|
|
749
|
+
}
|
|
750
|
+
const [rawPayload] = firstMatch;
|
|
751
|
+
if (payloadSchema) {
|
|
752
|
+
return payloadSchema.parse(rawPayload);
|
|
753
|
+
}
|
|
754
|
+
return rawPayload;
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Durably sleep until a specific timestamp.
|
|
759
|
+
* Uses the memoized `now()` to compute the remaining duration, then delegates to `sleepFor`.
|
|
760
|
+
*
|
|
761
|
+
* @param wakeAt - The timestamp to sleep until.
|
|
762
|
+
* @returns A SleepResult containing the actual duration slept.
|
|
763
|
+
*/
|
|
764
|
+
sleepUntil(wakeAt) {
|
|
765
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
766
|
+
const now = yield this.now();
|
|
767
|
+
const remainingMs = wakeAt.getTime() - now.getTime();
|
|
768
|
+
return this.sleepFor(`${Math.max(0, Math.ceil(remainingMs / 1000))}s`);
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Get the current timestamp, memoized across replays. Returns the same Date on every replay of the same task run.
|
|
773
|
+
* @returns The memoized current timestamp.
|
|
774
|
+
*/
|
|
775
|
+
now() {
|
|
776
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
777
|
+
const result = yield this.memo(() => __awaiter(this, void 0, void 0, function* () {
|
|
778
|
+
return { ts: new Date().toISOString() };
|
|
779
|
+
}), ['now']);
|
|
780
|
+
return new Date(result.ts);
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
_waitForPreEviction(conditions) {
|
|
784
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
785
|
+
const { result, nextWaitKey } = yield (0, pre_eviction_1.waitForPreEviction)(this._durableListener, this.action.taskRunExternalId, this._waitKey, conditions, this.v1.config.namespace, this.abortController.signal);
|
|
786
|
+
this._waitKey = nextWaitKey;
|
|
787
|
+
return result;
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
_buildTriggerOpts(workflow, input, options) {
|
|
791
|
+
let workflowName;
|
|
792
|
+
if (typeof workflow === 'string') {
|
|
793
|
+
workflowName = workflow;
|
|
794
|
+
}
|
|
795
|
+
else {
|
|
796
|
+
workflowName = workflow.name;
|
|
797
|
+
}
|
|
798
|
+
workflowName = (0, apply_namespace_1.applyNamespace)(workflowName, this.v1.config.namespace).toLowerCase();
|
|
799
|
+
const triggerOpts = {
|
|
800
|
+
name: workflowName,
|
|
801
|
+
input: JSON.stringify(input || {}),
|
|
802
|
+
parentId: this.action.workflowRunId,
|
|
803
|
+
parentTaskRunExternalId: this.action.taskRunExternalId,
|
|
804
|
+
childIndex: this.spawnIndex,
|
|
805
|
+
childKey: options === null || options === void 0 ? void 0 : options.key,
|
|
806
|
+
additionalMetadata: (options === null || options === void 0 ? void 0 : options.additionalMetadata)
|
|
807
|
+
? JSON.stringify(options.additionalMetadata)
|
|
808
|
+
: undefined,
|
|
809
|
+
desiredWorkerId: (options === null || options === void 0 ? void 0 : options.sticky) ? this.worker.id() : undefined,
|
|
810
|
+
priority: options === null || options === void 0 ? void 0 : options.priority,
|
|
811
|
+
desiredWorkerLabels: {},
|
|
812
|
+
};
|
|
813
|
+
this.spawnIndex += 1;
|
|
814
|
+
return { workflowName, triggerOpts };
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Spawns a child workflow through the durable event log, waits for the child to complete.
|
|
818
|
+
* @param workflow - The workflow to spawn.
|
|
819
|
+
* @param input - The input data for the child workflow.
|
|
820
|
+
* @param options - Options for spawning the child workflow.
|
|
821
|
+
* @returns The result of the child workflow.
|
|
822
|
+
*/
|
|
823
|
+
spawnChild(workflow, input, options) {
|
|
824
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
825
|
+
if (!this.supportsEviction) {
|
|
826
|
+
const { workflowName, opts } = this.spawnOptions(workflow, options);
|
|
827
|
+
const ref = yield this.v1.admin.runWorkflow(workflowName, (input || {}), opts);
|
|
828
|
+
ref.defaultSignal = this.abortController.signal;
|
|
829
|
+
return ref.output;
|
|
830
|
+
}
|
|
831
|
+
const results = yield this.spawnChildren([
|
|
832
|
+
{ workflow, input: (input || {}), options },
|
|
833
|
+
]);
|
|
834
|
+
return results[0];
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Spawns multiple child workflows through the durable event log, waits for all to complete.
|
|
839
|
+
* @param children - An array of objects containing the workflow, input, and options for each child.
|
|
840
|
+
* @returns A list of results from the child workflows.
|
|
841
|
+
*/
|
|
842
|
+
spawnChildren(children) {
|
|
843
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
844
|
+
this.throwIfCancelled();
|
|
845
|
+
if (!this.supportsEviction) {
|
|
846
|
+
const workflows = children.map((c) => {
|
|
847
|
+
const { workflowName, opts } = this.spawnOptions(c.workflow, c.options);
|
|
848
|
+
return { workflowName, input: c.input, options: opts };
|
|
849
|
+
});
|
|
850
|
+
const refs = yield this.v1.admin.runWorkflows(workflows);
|
|
851
|
+
for (const r of refs) {
|
|
852
|
+
r.defaultSignal = this.abortController.signal;
|
|
853
|
+
}
|
|
854
|
+
return Promise.all(refs.map((r) => r.output));
|
|
855
|
+
}
|
|
856
|
+
const triggerOptsList = children.map((child) => {
|
|
857
|
+
const { triggerOpts } = this._buildTriggerOpts(child.workflow, child.input, child.options);
|
|
858
|
+
return triggerOpts;
|
|
859
|
+
});
|
|
860
|
+
const ack = yield this._durableListener.sendEvent(this.action.taskRunExternalId, this.invocationCount, {
|
|
861
|
+
kind: 'runChildren',
|
|
862
|
+
triggerOpts: triggerOptsList,
|
|
668
863
|
});
|
|
669
|
-
const
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
864
|
+
const results = yield Promise.all(ack.runEntries.map((entry) => this.withEvictionWait('runChild', `workflow:bulk-child`, () => __awaiter(this, void 0, void 0, function* () {
|
|
865
|
+
const result = yield this._durableListener.waitForCallback(this.action.taskRunExternalId, this.invocationCount, entry.branchId, entry.nodeId, { signal: this.abortController.signal });
|
|
866
|
+
return (result.payload || {});
|
|
867
|
+
}))));
|
|
868
|
+
return results;
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* Memoize a function by storing its result in durable storage. Avoids recomputation on replay.
|
|
873
|
+
*
|
|
874
|
+
* @param fn - The async function to compute the value.
|
|
875
|
+
* @param deps - Dependency values that form the memoization key.
|
|
876
|
+
* @returns The memoized value, either from durable storage or freshly computed.
|
|
877
|
+
*/
|
|
878
|
+
memo(fn, deps) {
|
|
879
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
880
|
+
this.throwIfCancelled();
|
|
881
|
+
if (!this.supportsEviction) {
|
|
882
|
+
return fn();
|
|
883
|
+
}
|
|
884
|
+
const memoKey = computeMemoKey(this.action.taskRunExternalId, deps);
|
|
885
|
+
const ack = yield this._durableListener.sendEvent(this.action.taskRunExternalId, this.invocationCount, {
|
|
886
|
+
kind: 'memo',
|
|
887
|
+
memoKey,
|
|
888
|
+
});
|
|
889
|
+
if (ack.memoAlreadyExisted && ack.memoResultPayload && ack.memoResultPayload.length > 0) {
|
|
890
|
+
const serialized = new TextDecoder().decode(ack.memoResultPayload);
|
|
891
|
+
return JSON.parse(serialized);
|
|
892
|
+
}
|
|
893
|
+
const result = yield fn();
|
|
894
|
+
const serializedResult = new TextEncoder().encode(JSON.stringify(result));
|
|
895
|
+
yield this._durableListener.sendMemoCompletedNotification(this.action.taskRunExternalId, ack.nodeId, ack.branchId, this.invocationCount, memoKey, serializedResult);
|
|
896
|
+
return result;
|
|
677
897
|
});
|
|
678
898
|
}
|
|
679
899
|
}
|
|
680
900
|
exports.DurableContext = DurableContext;
|
|
901
|
+
function computeMemoKey(taskRunExternalId, args) {
|
|
902
|
+
const h = (0, crypto_1.createHash)('sha256');
|
|
903
|
+
h.update(taskRunExternalId);
|
|
904
|
+
h.update(JSON.stringify(args));
|
|
905
|
+
return new Uint8Array(h.digest());
|
|
906
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { isLegacyEngine, LegacyDualWorker } from './legacy-worker';
|
|
1
|
+
export { isLegacyEngine, fetchEngineVersion, LegacyDualWorker } from './legacy-worker';
|
|
2
2
|
export { LegacyV1Worker } from './legacy-v1-worker';
|
|
3
3
|
export { legacyGetActionListener } from './legacy-registration';
|
|
4
4
|
export { emitDeprecationNotice, DeprecationError, parseSemver, semverLessThan, } from './deprecation';
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.semverLessThan = exports.parseSemver = exports.DeprecationError = exports.emitDeprecationNotice = exports.legacyGetActionListener = exports.LegacyV1Worker = exports.LegacyDualWorker = exports.isLegacyEngine = void 0;
|
|
3
|
+
exports.semverLessThan = exports.parseSemver = exports.DeprecationError = exports.emitDeprecationNotice = exports.legacyGetActionListener = exports.LegacyV1Worker = exports.LegacyDualWorker = exports.fetchEngineVersion = exports.isLegacyEngine = void 0;
|
|
4
4
|
var legacy_worker_1 = require("./legacy-worker");
|
|
5
5
|
Object.defineProperty(exports, "isLegacyEngine", { enumerable: true, get: function () { return legacy_worker_1.isLegacyEngine; } });
|
|
6
|
+
Object.defineProperty(exports, "fetchEngineVersion", { enumerable: true, get: function () { return legacy_worker_1.fetchEngineVersion; } });
|
|
6
7
|
Object.defineProperty(exports, "LegacyDualWorker", { enumerable: true, get: function () { return legacy_worker_1.LegacyDualWorker; } });
|
|
7
8
|
var legacy_v1_worker_1 = require("./legacy-v1-worker");
|
|
8
9
|
Object.defineProperty(exports, "LegacyV1Worker", { enumerable: true, get: function () { return legacy_v1_worker_1.LegacyV1Worker; } });
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
import { HatchetClient } from '../../..';
|
|
9
9
|
import { CreateWorkerOpts } from '../worker';
|
|
10
10
|
import { LegacyV1Worker } from './legacy-v1-worker';
|
|
11
|
+
/**
|
|
12
|
+
* Fetches the engine version from the dispatcher.
|
|
13
|
+
* Returns the semver string, or undefined if the engine is too old to support GetVersion.
|
|
14
|
+
*/
|
|
15
|
+
export declare function fetchEngineVersion(v1: HatchetClient): Promise<string | undefined>;
|
|
11
16
|
/**
|
|
12
17
|
* Checks if the connected engine is legacy by comparing its semantic version
|
|
13
18
|
* against the minimum required version for slot_config support.
|
|
@@ -17,22 +17,40 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
17
17
|
};
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
19
|
exports.LegacyDualWorker = void 0;
|
|
20
|
+
exports.fetchEngineVersion = fetchEngineVersion;
|
|
20
21
|
exports.isLegacyEngine = isLegacyEngine;
|
|
21
22
|
const nice_grpc_1 = require("nice-grpc");
|
|
22
|
-
const grpc_error_1 = require("../../../../util/grpc-error");
|
|
23
23
|
const declaration_1 = require("../../../declaration");
|
|
24
24
|
const legacy_v1_worker_1 = require("./legacy-v1-worker");
|
|
25
25
|
const deprecation_1 = require("./deprecation");
|
|
26
26
|
const legacy_transformer_1 = require("../../../../legacy/legacy-transformer");
|
|
27
|
+
const engine_version_1 = require("../engine-version");
|
|
28
|
+
const grpc_error_1 = require("../../../../util/grpc-error");
|
|
27
29
|
const DEFAULT_DEFAULT_SLOTS = 100;
|
|
28
30
|
const DEFAULT_DURABLE_SLOTS = 1000;
|
|
29
31
|
/** The date when slot_config support was released. */
|
|
30
32
|
const LEGACY_ENGINE_START = new Date('2026-02-12T00:00:00Z');
|
|
31
|
-
/** Minimum engine version that supports multiple slot types. */
|
|
32
|
-
const MIN_SLOT_CONFIG_VERSION = 'v0.78.23';
|
|
33
33
|
const LEGACY_ENGINE_MESSAGE = 'Connected to an older Hatchet engine that does not support multiple slot types. ' +
|
|
34
34
|
'Falling back to legacy worker registration. ' +
|
|
35
35
|
'Please upgrade your Hatchet engine to the latest version.';
|
|
36
|
+
/**
|
|
37
|
+
* Fetches the engine version from the dispatcher.
|
|
38
|
+
* Returns the semver string, or undefined if the engine is too old to support GetVersion.
|
|
39
|
+
*/
|
|
40
|
+
function fetchEngineVersion(v1) {
|
|
41
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
42
|
+
try {
|
|
43
|
+
const version = yield v1.dispatcher.getVersion();
|
|
44
|
+
return version || undefined;
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
if ((0, grpc_error_1.getGrpcErrorCode)(e) == nice_grpc_1.Status.UNIMPLEMENTED) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
throw e;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
36
54
|
/**
|
|
37
55
|
* Checks if the connected engine is legacy by comparing its semantic version
|
|
38
56
|
* against the minimum required version for slot_config support.
|
|
@@ -41,29 +59,20 @@ const LEGACY_ENGINE_MESSAGE = 'Connected to an older Hatchet engine that does no
|
|
|
41
59
|
*/
|
|
42
60
|
function isLegacyEngine(v1) {
|
|
43
61
|
return __awaiter(this, void 0, void 0, function* () {
|
|
44
|
-
|
|
45
|
-
const version = yield v1.dispatcher.getVersion();
|
|
46
|
-
// If the version is empty or older than the minimum, treat as legacy
|
|
47
|
-
if (!version || (0, deprecation_1.semverLessThan)(version, MIN_SLOT_CONFIG_VERSION)) {
|
|
48
|
-
const logger = v1.config.logger('Worker', v1.config.log_level);
|
|
49
|
-
(0, deprecation_1.emitDeprecationNotice)('legacy-engine', LEGACY_ENGINE_MESSAGE, LEGACY_ENGINE_START, logger, {
|
|
50
|
-
errorDays: 180,
|
|
51
|
-
});
|
|
52
|
-
return true;
|
|
53
|
-
}
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
catch (e) {
|
|
62
|
+
const version = yield fetchEngineVersion(v1).catch((e) => {
|
|
57
63
|
if ((0, grpc_error_1.getGrpcErrorCode)(e) === nice_grpc_1.Status.UNIMPLEMENTED) {
|
|
58
|
-
|
|
59
|
-
(0, deprecation_1.emitDeprecationNotice)('legacy-engine', LEGACY_ENGINE_MESSAGE, LEGACY_ENGINE_START, logger, {
|
|
60
|
-
errorDays: 180,
|
|
61
|
-
});
|
|
62
|
-
return true;
|
|
64
|
+
return undefined;
|
|
63
65
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
throw e;
|
|
67
|
+
});
|
|
68
|
+
if (!version || (0, deprecation_1.semverLessThan)(version, engine_version_1.MinEngineVersion.SLOT_CONFIG)) {
|
|
69
|
+
const logger = v1.config.logger('Worker', v1.config.log_level);
|
|
70
|
+
(0, deprecation_1.emitDeprecationNotice)('legacy-engine', LEGACY_ENGINE_MESSAGE, LEGACY_ENGINE_START, logger, {
|
|
71
|
+
errorDays: 180,
|
|
72
|
+
});
|
|
73
|
+
return true;
|
|
66
74
|
}
|
|
75
|
+
return false;
|
|
67
76
|
});
|
|
68
77
|
}
|
|
69
78
|
/**
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-eviction fallback for DurableContext.
|
|
3
|
+
*
|
|
4
|
+
* Supports engines older than MinEngineVersion.DURABLE_EVICTION.
|
|
5
|
+
* Remove this module when support for those engines is dropped.
|
|
6
|
+
*/
|
|
7
|
+
import { Conditions } from '../../../conditions';
|
|
8
|
+
import { DurableListenerClient } from '../../../../clients/listeners/durable-listener/durable-listener-client';
|
|
9
|
+
export declare function waitForPreEviction(durableListener: DurableListenerClient, taskRunExternalId: string, waitKey: number, conditions: Conditions | Conditions[], namespace?: string, signal?: AbortSignal): Promise<{
|
|
10
|
+
result: Record<string, unknown>;
|
|
11
|
+
nextWaitKey: number;
|
|
12
|
+
}>;
|
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.waitForPreEviction = waitForPreEviction;
|
|
13
|
+
/**
|
|
14
|
+
* Pre-eviction fallback for DurableContext.
|
|
15
|
+
*
|
|
16
|
+
* Supports engines older than MinEngineVersion.DURABLE_EVICTION.
|
|
17
|
+
* Remove this module when support for those engines is dropped.
|
|
18
|
+
*/
|
|
19
|
+
const conditions_1 = require("../../../conditions");
|
|
20
|
+
const transformer_1 = require("../../../conditions/transformer");
|
|
21
|
+
const condition_1 = require("../../../../protoc/v1/shared/condition");
|
|
22
|
+
function waitForPreEviction(durableListener, taskRunExternalId, waitKey, conditions, namespace, signal) {
|
|
23
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
+
const pbConditions = (0, transformer_1.conditionsToPb)((0, conditions_1.Render)(condition_1.Action.CREATE, conditions), namespace);
|
|
25
|
+
const key = `waitFor-${waitKey}`;
|
|
26
|
+
yield durableListener.registerDurableEvent({
|
|
27
|
+
taskId: taskRunExternalId,
|
|
28
|
+
signalKey: key,
|
|
29
|
+
sleepConditions: pbConditions.sleepConditions,
|
|
30
|
+
userEventConditions: pbConditions.userEventConditions,
|
|
31
|
+
});
|
|
32
|
+
const event = yield durableListener.result({ taskId: taskRunExternalId, signalKey: key }, { signal });
|
|
33
|
+
const eventData = event.data instanceof Uint8Array ? new TextDecoder().decode(event.data) : event.data;
|
|
34
|
+
const res = JSON.parse(eventData);
|
|
35
|
+
return { result: res.CREATE, nextWaitKey: waitKey + 1 };
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MinEngineVersion = void 0;
|
|
4
|
+
exports.supportsEviction = supportsEviction;
|
|
5
|
+
const deprecation_1 = require("./deprecated/deprecation");
|
|
6
|
+
exports.MinEngineVersion = {
|
|
7
|
+
SLOT_CONFIG: 'v0.78.23',
|
|
8
|
+
DURABLE_EVICTION: 'v0.80.0',
|
|
9
|
+
};
|
|
10
|
+
function supportsEviction(engineVersion) {
|
|
11
|
+
if (!engineVersion)
|
|
12
|
+
return false;
|
|
13
|
+
return !(0, deprecation_1.semverLessThan)(engineVersion, exports.MinEngineVersion.DURABLE_EVICTION);
|
|
14
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ActionKey } from '../../../../clients/dispatcher/action-listener';
|
|
2
|
+
import { EvictionPolicy } from './eviction-policy';
|
|
3
|
+
export type { ActionKey };
|
|
4
|
+
export declare enum EvictionCause {
|
|
5
|
+
TTL_EXCEEDED = "ttl_exceeded",
|
|
6
|
+
CAPACITY_PRESSURE = "capacity_pressure",
|
|
7
|
+
WORKER_SHUTDOWN = "worker_shutdown"
|
|
8
|
+
}
|
|
9
|
+
export interface DurableRunRecord {
|
|
10
|
+
key: ActionKey;
|
|
11
|
+
taskRunExternalId: string;
|
|
12
|
+
invocationCount: number;
|
|
13
|
+
evictionPolicy: EvictionPolicy | undefined;
|
|
14
|
+
registeredAt: number;
|
|
15
|
+
waitingSince: number | undefined;
|
|
16
|
+
waitKind: string | undefined;
|
|
17
|
+
waitResourceId: string | undefined;
|
|
18
|
+
_waitCount: number;
|
|
19
|
+
evictionReason: string | undefined;
|
|
20
|
+
}
|
|
21
|
+
export declare class DurableEvictionCache {
|
|
22
|
+
private _runs;
|
|
23
|
+
registerRun(key: ActionKey, taskRunExternalId: string, invocationCount: number, now: number, evictionPolicy: EvictionPolicy | undefined): void;
|
|
24
|
+
unregisterRun(key: ActionKey): void;
|
|
25
|
+
get(key: ActionKey): DurableRunRecord | undefined;
|
|
26
|
+
getAllWaiting(): DurableRunRecord[];
|
|
27
|
+
findKeyByTaskRunExternalId(taskRunExternalId: string): ActionKey | undefined;
|
|
28
|
+
markWaiting(key: ActionKey, now: number, waitKind: string, resourceId: string): void;
|
|
29
|
+
markActive(key: ActionKey): void;
|
|
30
|
+
selectEvictionCandidate(now: number, durableSlots: number, reserveSlots: number, minWaitForCapacityEvictionMs: number): ActionKey | undefined;
|
|
31
|
+
private _hasCapacityPressure;
|
|
32
|
+
}
|
|
33
|
+
export declare function buildEvictionReason(cause: EvictionCause, rec: DurableRunRecord): string;
|