@mark1russell7/service 0.5.9 → 0.5.11
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 +2 -1
- package/build/{builder-DyDB7zM9.d.ts → builder-Dn0cby6Q.d.ts} +3 -3
- package/build/{clock-D-38uH_z.d.ts → clock-mVqhXD0b.d.ts} +2 -2
- package/build/clock.d.ts +1 -1
- package/build/clock.js +11 -3
- package/build/clock.js.map +1 -1
- package/build/heartbeat.d.ts +1 -1
- package/build/http.d.ts +2 -2
- package/build/http.js +18 -3
- package/build/http.js.map +1 -1
- package/build/index.d.ts +9 -7
- package/build/index.js +262 -240
- package/build/index.js.map +1 -1
- package/build/{lifecycle-Chu9Pri2.d.ts → lifecycle-CGEegMI0.d.ts} +2 -2
- package/build/{limiter-DCDdzVwe.d.ts → limiter-B0849Vhp.d.ts} +2 -2
- package/build/limiter.d.ts +2 -2
- package/build/redis.d.ts +4 -4
- package/build/{store-DzTyosou.d.ts → store-D5HLgBnW.d.ts} +2 -2
- package/build/testing.d.ts +3 -3
- package/package.json +3 -3
package/build/index.js
CHANGED
|
@@ -431,7 +431,7 @@ const healthRuntime = (description, runs, options = {}) => {
|
|
|
431
431
|
if (kind === "liveness") return answer(kind, now.alive);
|
|
432
432
|
if (kind === "report") return answer(kind, now.ready && now.alive);
|
|
433
433
|
if (controller === void 0) return answer(kind, now.ready);
|
|
434
|
-
const known = now.controllers[controller];
|
|
434
|
+
const known = Object.hasOwn(now.controllers, controller) ? now.controllers[controller] : void 0;
|
|
435
435
|
if (known === void 0) throw new Error(`${description.name} has no controller ${controller}`);
|
|
436
436
|
return answer(kind, known, controller);
|
|
437
437
|
},
|
|
@@ -720,6 +720,248 @@ const compilePattern = (pattern) => {
|
|
|
720
720
|
};
|
|
721
721
|
};
|
|
722
722
|
//#endregion
|
|
723
|
+
//#region src/units.ts
|
|
724
|
+
/** This function gives the cause of an abort as text. */
|
|
725
|
+
const causeOf = (signal) => signal.reason instanceof Error ? signal.reason.message : String(signal.reason ?? "the unit got an abort");
|
|
726
|
+
/**
|
|
727
|
+
* This function does one unit of work, and gives its end. With a request, the unit goes through
|
|
728
|
+
* `life.request`, thus the service counts it, drains it and can cut it. A throw after the abort of
|
|
729
|
+
* the transport is a cut, not a failure (S6 A10). A throw after the bound is a failure.
|
|
730
|
+
*/
|
|
731
|
+
const runUnit = async (run) => {
|
|
732
|
+
const { clock, bound, abort, life, request, unit, work } = run;
|
|
733
|
+
const timeout = new AbortController();
|
|
734
|
+
const cancel = Number.isFinite(bound) ? clock.after(bound, () => timeout.abort(/* @__PURE__ */ new Error(`the unit did not end in its unit_processing_time of ${bound} ms`))) : () => {};
|
|
735
|
+
const call = async (tracker) => work(AbortSignal.any(tracker === void 0 ? [abort.signal, timeout.signal] : [
|
|
736
|
+
abort.signal,
|
|
737
|
+
timeout.signal,
|
|
738
|
+
tracker
|
|
739
|
+
]));
|
|
740
|
+
const thrown = (error) => {
|
|
741
|
+
if (abort.signal.aborted) return {
|
|
742
|
+
kind: "cut",
|
|
743
|
+
cause: causeOf(abort.signal),
|
|
744
|
+
retryable: false
|
|
745
|
+
};
|
|
746
|
+
if (timeout.signal.aborted) return {
|
|
747
|
+
kind: "failed",
|
|
748
|
+
error: timeout.signal.reason
|
|
749
|
+
};
|
|
750
|
+
return {
|
|
751
|
+
kind: "failed",
|
|
752
|
+
error
|
|
753
|
+
};
|
|
754
|
+
};
|
|
755
|
+
try {
|
|
756
|
+
if (request === void 0) try {
|
|
757
|
+
return {
|
|
758
|
+
kind: "done",
|
|
759
|
+
value: await call()
|
|
760
|
+
};
|
|
761
|
+
} catch (error) {
|
|
762
|
+
return thrown(error);
|
|
763
|
+
}
|
|
764
|
+
let collected;
|
|
765
|
+
try {
|
|
766
|
+
const route = typeof request.route === "string" ? request.route : request.route(unit);
|
|
767
|
+
const detail = request.detail?.(unit);
|
|
768
|
+
collected = life.request(request.controller, route, (signal) => call(signal).then((value) => ({ value })), detail === void 0 ? {} : { detail }).collect();
|
|
769
|
+
} catch (error) {
|
|
770
|
+
return {
|
|
771
|
+
kind: "failed",
|
|
772
|
+
error
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
const { items, end } = await collected;
|
|
776
|
+
if (end.kind === "done") return {
|
|
777
|
+
kind: "done",
|
|
778
|
+
value: items[0]?.value
|
|
779
|
+
};
|
|
780
|
+
if (end.kind === "failed") return thrown(end.error);
|
|
781
|
+
if (end.kind === "refused") return {
|
|
782
|
+
kind: "refused",
|
|
783
|
+
cause: end.cause
|
|
784
|
+
};
|
|
785
|
+
return {
|
|
786
|
+
kind: "cut",
|
|
787
|
+
cause: end.cause,
|
|
788
|
+
retryable: end.retryable
|
|
789
|
+
};
|
|
790
|
+
} finally {
|
|
791
|
+
cancel();
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
/** This function makes a flight set on a clock. */
|
|
795
|
+
const flights = (clock) => {
|
|
796
|
+
const units = /* @__PURE__ */ new Set();
|
|
797
|
+
const idle = /* @__PURE__ */ new Set();
|
|
798
|
+
const lower = /* @__PURE__ */ new Set();
|
|
799
|
+
let aborted = null;
|
|
800
|
+
const settle = () => {
|
|
801
|
+
for (const done of [...lower]) done();
|
|
802
|
+
lower.clear();
|
|
803
|
+
if (units.size === 0) for (const done of [...idle]) done();
|
|
804
|
+
};
|
|
805
|
+
const abort = (reason) => {
|
|
806
|
+
aborted ??= { reason };
|
|
807
|
+
for (const unit of [...units]) if (!unit.signal.aborted) unit.abort(aborted.reason);
|
|
808
|
+
};
|
|
809
|
+
return {
|
|
810
|
+
get size() {
|
|
811
|
+
return units.size;
|
|
812
|
+
},
|
|
813
|
+
add(unit) {
|
|
814
|
+
units.add(unit);
|
|
815
|
+
if (aborted !== null && !unit.signal.aborted) unit.abort(aborted.reason);
|
|
816
|
+
return () => {
|
|
817
|
+
if (units.delete(unit)) settle();
|
|
818
|
+
};
|
|
819
|
+
},
|
|
820
|
+
abort,
|
|
821
|
+
clear() {
|
|
822
|
+
units.clear();
|
|
823
|
+
settle();
|
|
824
|
+
},
|
|
825
|
+
drain(deadline, signal) {
|
|
826
|
+
const cooperative = () => abort(signal.reason);
|
|
827
|
+
if (signal.aborted) cooperative();
|
|
828
|
+
else signal.addEventListener("abort", cooperative, { once: true });
|
|
829
|
+
if (units.size === 0 || deadline <= clock.now()) {
|
|
830
|
+
signal.removeEventListener("abort", cooperative);
|
|
831
|
+
return Promise.resolve();
|
|
832
|
+
}
|
|
833
|
+
return new Promise((resolve) => {
|
|
834
|
+
let cancel = () => {};
|
|
835
|
+
const done = () => {
|
|
836
|
+
idle.delete(done);
|
|
837
|
+
cancel();
|
|
838
|
+
signal.removeEventListener("abort", cooperative);
|
|
839
|
+
resolve();
|
|
840
|
+
};
|
|
841
|
+
idle.add(done);
|
|
842
|
+
cancel = clock.after(Math.max(0, deadline - clock.now()), done);
|
|
843
|
+
});
|
|
844
|
+
},
|
|
845
|
+
less() {
|
|
846
|
+
return new Promise((resolve) => lower.add(resolve));
|
|
847
|
+
},
|
|
848
|
+
release() {
|
|
849
|
+
for (const done of [...lower]) done();
|
|
850
|
+
lower.clear();
|
|
851
|
+
}
|
|
852
|
+
};
|
|
853
|
+
};
|
|
854
|
+
/** This function makes a sleeper on a clock. */
|
|
855
|
+
const sleeper = (clock) => {
|
|
856
|
+
let wakes = 0;
|
|
857
|
+
let stopped = false;
|
|
858
|
+
const sleeps = /* @__PURE__ */ new Set();
|
|
859
|
+
const backoffs = /* @__PURE__ */ new Set();
|
|
860
|
+
const wait = (ms, into) => stopped ? Promise.resolve() : new Promise((resolve) => {
|
|
861
|
+
let cancel = () => {};
|
|
862
|
+
const done = () => {
|
|
863
|
+
if (!into.delete(done)) return;
|
|
864
|
+
cancel();
|
|
865
|
+
resolve();
|
|
866
|
+
};
|
|
867
|
+
into.add(done);
|
|
868
|
+
if (Number.isFinite(ms)) cancel = clock.after(ms, done);
|
|
869
|
+
});
|
|
870
|
+
return {
|
|
871
|
+
get wakes() {
|
|
872
|
+
return wakes;
|
|
873
|
+
},
|
|
874
|
+
sleep(ms, since) {
|
|
875
|
+
if (since !== wakes) return Promise.resolve();
|
|
876
|
+
return wait(ms, sleeps);
|
|
877
|
+
},
|
|
878
|
+
backoff(ms) {
|
|
879
|
+
return wait(ms, backoffs);
|
|
880
|
+
},
|
|
881
|
+
wake() {
|
|
882
|
+
wakes += 1;
|
|
883
|
+
const ended = sleeps.size;
|
|
884
|
+
for (const done of [...sleeps]) done();
|
|
885
|
+
return ended;
|
|
886
|
+
},
|
|
887
|
+
stop() {
|
|
888
|
+
stopped = true;
|
|
889
|
+
for (const done of [...sleeps, ...backoffs]) done();
|
|
890
|
+
}
|
|
891
|
+
};
|
|
892
|
+
};
|
|
893
|
+
/** This function waits for a work until a bound on the clock, in milliseconds. */
|
|
894
|
+
const settleWithin = (clock, ms, work) => new Promise((resolve) => {
|
|
895
|
+
const cancel = Number.isFinite(ms) ? clock.after(ms, () => resolve({ kind: "bound" })) : () => {};
|
|
896
|
+
work.then(() => {
|
|
897
|
+
cancel();
|
|
898
|
+
resolve({ kind: "done" });
|
|
899
|
+
}, (error) => {
|
|
900
|
+
cancel();
|
|
901
|
+
resolve({
|
|
902
|
+
kind: "failed",
|
|
903
|
+
error
|
|
904
|
+
});
|
|
905
|
+
});
|
|
906
|
+
});
|
|
907
|
+
/**
|
|
908
|
+
* This function gives the value of a promise, or `stop` when the signal aborts first. Each wait adds
|
|
909
|
+
* its own listener, and removes it when the promise settles. A race against one pending promise
|
|
910
|
+
* keeps each settled value until that promise ends.
|
|
911
|
+
*/
|
|
912
|
+
const untilAbort = (promise, signal, stop) => {
|
|
913
|
+
if (signal.aborted) return Promise.race([promise, Promise.resolve(stop)]);
|
|
914
|
+
return new Promise((resolve, reject) => {
|
|
915
|
+
const onAbort = () => resolve(stop);
|
|
916
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
917
|
+
promise.then((value) => {
|
|
918
|
+
signal.removeEventListener("abort", onAbort);
|
|
919
|
+
resolve(value);
|
|
920
|
+
}, (error) => {
|
|
921
|
+
signal.removeEventListener("abort", onAbort);
|
|
922
|
+
reject(error);
|
|
923
|
+
});
|
|
924
|
+
});
|
|
925
|
+
};
|
|
926
|
+
/**
|
|
927
|
+
* This function gives the backoff of the knobs of a loop. The first delay is `retry_base`, and each
|
|
928
|
+
* delay is two times longer, up to `retry_cap`. Each delay has a jitter up to the base, as in the Redis client.
|
|
929
|
+
*/
|
|
930
|
+
const retryOf = (knobs, owner) => {
|
|
931
|
+
const base = boundedMillisecondsOf(knobs, "retry_base", owner);
|
|
932
|
+
const backoff = {
|
|
933
|
+
base,
|
|
934
|
+
factor: 2,
|
|
935
|
+
cap: boundedMillisecondsOf(knobs, "retry_cap", owner),
|
|
936
|
+
jitter: {
|
|
937
|
+
kind: "add",
|
|
938
|
+
amount: base
|
|
939
|
+
}
|
|
940
|
+
};
|
|
941
|
+
const problems = backoffProblems(backoff);
|
|
942
|
+
if (problems.length > 0) throw new Error(`${owner}: the retry_base and retry_cap give no backoff: ${problems.join(", ")}`);
|
|
943
|
+
return backoff;
|
|
944
|
+
};
|
|
945
|
+
/**
|
|
946
|
+
* This function gives the problems of the bounds of a loop in a declaration. The close bound fits
|
|
947
|
+
* in the cap of `close transports`. The bound of a unit fits in the cap of `drain`, or the drain can cut a unit (S6 A10).
|
|
948
|
+
*/
|
|
949
|
+
const boundProblems = (owner, closeBound, unitBound, description) => {
|
|
950
|
+
const capOf = (name) => {
|
|
951
|
+
const phase = description.shutdown.phases.find((each) => each.name === name);
|
|
952
|
+
return phase === void 0 ? void 0 : valueIn(phase.cap, "ms");
|
|
953
|
+
};
|
|
954
|
+
const found = [];
|
|
955
|
+
const close = capOf("close transports");
|
|
956
|
+
if (close === void 0) found.push(`${owner} needs the phase "close transports" to close`);
|
|
957
|
+
else if (closeBound >= close) found.push(`the close_bound of ${owner}, ${closeBound} ms, does not fit in the cap of "close transports", ${close} ms`);
|
|
958
|
+
const drain = capOf("drain");
|
|
959
|
+
if (drain === void 0) found.push(`${owner} needs the phase "drain" to end its units`);
|
|
960
|
+
else if (!Number.isFinite(unitBound)) found.push(`the unit_processing_time of ${owner} has no bound, thus the drain can cut a unit at its cap of ${drain} ms`);
|
|
961
|
+
else if (unitBound >= drain) found.push(`the unit_processing_time of ${owner}, ${unitBound} ms, does not fit in the cap of "drain", ${drain} ms`);
|
|
962
|
+
return found;
|
|
963
|
+
};
|
|
964
|
+
//#endregion
|
|
723
965
|
//#region src/requests.ts
|
|
724
966
|
const isAsyncIterable$1 = (value) => value !== null && (typeof value === "object" || typeof value === "function") && typeof value[Symbol.asyncIterator] === "function";
|
|
725
967
|
const isPromiseLike = (value) => value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
|
|
@@ -815,10 +1057,8 @@ const tracker = (controllers, clock, refuses, choices = /* @__PURE__ */ new Map(
|
|
|
815
1057
|
let returned = false;
|
|
816
1058
|
let source = null;
|
|
817
1059
|
let unary = null;
|
|
818
|
-
|
|
819
|
-
const
|
|
820
|
-
interrupt = () => resolve("interrupted");
|
|
821
|
-
});
|
|
1060
|
+
const interruption = new AbortController();
|
|
1061
|
+
const interrupt = () => interruption.abort();
|
|
822
1062
|
const cutEnd = (cause) => {
|
|
823
1063
|
const since = quantity(Math.max(0, clock.now() - started) / 1e3, "s");
|
|
824
1064
|
const retryable = mayReissue(operation, since);
|
|
@@ -887,7 +1127,7 @@ const tracker = (controllers, clock, refuses, choices = /* @__PURE__ */ new Map(
|
|
|
887
1127
|
}
|
|
888
1128
|
const next = async () => {
|
|
889
1129
|
if (ended) return null;
|
|
890
|
-
if (end === null) await
|
|
1130
|
+
if (end === null) await untilAbort(ready, interruption.signal, "interrupted");
|
|
891
1131
|
if (end === null && unary !== null) {
|
|
892
1132
|
const { value } = unary;
|
|
893
1133
|
unary = null;
|
|
@@ -905,7 +1145,7 @@ const tracker = (controllers, clock, refuses, choices = /* @__PURE__ */ new Map(
|
|
|
905
1145
|
const pending = source.next();
|
|
906
1146
|
let step;
|
|
907
1147
|
try {
|
|
908
|
-
step = await Promise.
|
|
1148
|
+
step = await untilAbort(Promise.resolve(pending), interruption.signal, "interrupted");
|
|
909
1149
|
} catch (error) {
|
|
910
1150
|
failed(error);
|
|
911
1151
|
step = "interrupted";
|
|
@@ -1901,10 +2141,16 @@ const marginOf = (inequality, index, bind) => {
|
|
|
1901
2141
|
if (typeof bind.margin === "number") return bind.margin;
|
|
1902
2142
|
return typeof inequality.margin === "number" ? inequality.margin : 0;
|
|
1903
2143
|
};
|
|
2144
|
+
/** This function tells if an inequality reads a symbol with no limit: "inf", "off" or an infinite number. */
|
|
2145
|
+
const readsAbsent = (inequality, bind) => [...inequality.lhs.terms, ...inequality.rhs.terms].some((term) => {
|
|
2146
|
+
const bound = boundOf$1(bind, keyOf(term));
|
|
2147
|
+
return bound !== void 0 && value(bound) === Infinity;
|
|
2148
|
+
});
|
|
1904
2149
|
/**
|
|
1905
2150
|
* This function evaluates each inequality on one binding. "<" holds when lhs + margin < rhs, and
|
|
1906
2151
|
* ">" holds when lhs > rhs + margin. Only the inequalities with all their symbols bound apply.
|
|
1907
|
-
* The others are null.
|
|
2152
|
+
* The others are null. Absent timers can leave no order: the same infinity on each side, or both
|
|
2153
|
+
* signs of infinity on one side. Then the inequality holds only because a timer is absent.
|
|
1908
2154
|
*/
|
|
1909
2155
|
const evaluate = (rule, bind) => inequalities(rule).map((inequality, index) => {
|
|
1910
2156
|
const lhs = sideOf(inequality.lhs, bind);
|
|
@@ -1916,6 +2162,12 @@ const evaluate = (rule, bind) => inequalities(rule).map((inequality, index) => {
|
|
|
1916
2162
|
margin,
|
|
1917
2163
|
holds: null
|
|
1918
2164
|
};
|
|
2165
|
+
if ((Number.isNaN(lhs) || Number.isNaN(rhs) || lhs === rhs && !Number.isFinite(lhs)) && readsAbsent(inequality, bind)) return {
|
|
2166
|
+
lhs,
|
|
2167
|
+
rhs,
|
|
2168
|
+
margin,
|
|
2169
|
+
holds: true
|
|
2170
|
+
};
|
|
1919
2171
|
return {
|
|
1920
2172
|
lhs,
|
|
1921
2173
|
rhs,
|
|
@@ -1934,13 +2186,7 @@ const verdictOf = (rule, bind, evaluated) => {
|
|
|
1934
2186
|
if (applicable.length === 0) return "depends";
|
|
1935
2187
|
if (applicable.some((index) => evaluated[index]?.holds === false)) return "violated";
|
|
1936
2188
|
const all = inequalities(rule);
|
|
1937
|
-
return applicable.some((index) =>
|
|
1938
|
-
const inequality = all[index];
|
|
1939
|
-
return [...inequality.lhs.terms, ...inequality.rhs.terms].some((term) => {
|
|
1940
|
-
const bound = boundOf$1(bind, keyOf(term));
|
|
1941
|
-
return bound !== void 0 && value(bound) === Infinity;
|
|
1942
|
-
});
|
|
1943
|
-
}) ? "unbounded" : "holds";
|
|
2189
|
+
return applicable.some((index) => readsAbsent(all[index], bind)) ? "unbounded" : "holds";
|
|
1944
2190
|
};
|
|
1945
2191
|
//#endregion
|
|
1946
2192
|
//#region src/lint.ts
|
|
@@ -2296,229 +2542,6 @@ const kubernetes = (pod) => {
|
|
|
2296
2542
|
};
|
|
2297
2543
|
};
|
|
2298
2544
|
//#endregion
|
|
2299
|
-
//#region src/units.ts
|
|
2300
|
-
/** This function gives the cause of an abort as text. */
|
|
2301
|
-
const causeOf = (signal) => signal.reason instanceof Error ? signal.reason.message : String(signal.reason ?? "the unit got an abort");
|
|
2302
|
-
/**
|
|
2303
|
-
* This function does one unit of work, and gives its end. With a request, the unit goes through
|
|
2304
|
-
* `life.request`, thus the service counts it, drains it and can cut it. A throw after the abort of
|
|
2305
|
-
* the transport is a cut, not a failure (S6 A10). A throw after the bound is a failure.
|
|
2306
|
-
*/
|
|
2307
|
-
const runUnit = async (run) => {
|
|
2308
|
-
const { clock, bound, abort, life, request, unit, work } = run;
|
|
2309
|
-
const timeout = new AbortController();
|
|
2310
|
-
const cancel = Number.isFinite(bound) ? clock.after(bound, () => timeout.abort(/* @__PURE__ */ new Error(`the unit did not end in its unit_processing_time of ${bound} ms`))) : () => {};
|
|
2311
|
-
const call = async (tracker) => work(AbortSignal.any(tracker === void 0 ? [abort.signal, timeout.signal] : [
|
|
2312
|
-
abort.signal,
|
|
2313
|
-
timeout.signal,
|
|
2314
|
-
tracker
|
|
2315
|
-
]));
|
|
2316
|
-
const thrown = (error) => {
|
|
2317
|
-
if (abort.signal.aborted) return {
|
|
2318
|
-
kind: "cut",
|
|
2319
|
-
cause: causeOf(abort.signal),
|
|
2320
|
-
retryable: false
|
|
2321
|
-
};
|
|
2322
|
-
if (timeout.signal.aborted) return {
|
|
2323
|
-
kind: "failed",
|
|
2324
|
-
error: timeout.signal.reason
|
|
2325
|
-
};
|
|
2326
|
-
return {
|
|
2327
|
-
kind: "failed",
|
|
2328
|
-
error
|
|
2329
|
-
};
|
|
2330
|
-
};
|
|
2331
|
-
try {
|
|
2332
|
-
if (request === void 0) try {
|
|
2333
|
-
return {
|
|
2334
|
-
kind: "done",
|
|
2335
|
-
value: await call()
|
|
2336
|
-
};
|
|
2337
|
-
} catch (error) {
|
|
2338
|
-
return thrown(error);
|
|
2339
|
-
}
|
|
2340
|
-
let collected;
|
|
2341
|
-
try {
|
|
2342
|
-
const route = typeof request.route === "string" ? request.route : request.route(unit);
|
|
2343
|
-
const detail = request.detail?.(unit);
|
|
2344
|
-
collected = life.request(request.controller, route, (signal) => call(signal).then((value) => ({ value })), detail === void 0 ? {} : { detail }).collect();
|
|
2345
|
-
} catch (error) {
|
|
2346
|
-
return {
|
|
2347
|
-
kind: "failed",
|
|
2348
|
-
error
|
|
2349
|
-
};
|
|
2350
|
-
}
|
|
2351
|
-
const { items, end } = await collected;
|
|
2352
|
-
if (end.kind === "done") return {
|
|
2353
|
-
kind: "done",
|
|
2354
|
-
value: items[0]?.value
|
|
2355
|
-
};
|
|
2356
|
-
if (end.kind === "failed") return thrown(end.error);
|
|
2357
|
-
if (end.kind === "refused") return {
|
|
2358
|
-
kind: "refused",
|
|
2359
|
-
cause: end.cause
|
|
2360
|
-
};
|
|
2361
|
-
return {
|
|
2362
|
-
kind: "cut",
|
|
2363
|
-
cause: end.cause,
|
|
2364
|
-
retryable: end.retryable
|
|
2365
|
-
};
|
|
2366
|
-
} finally {
|
|
2367
|
-
cancel();
|
|
2368
|
-
}
|
|
2369
|
-
};
|
|
2370
|
-
/** This function makes a flight set on a clock. */
|
|
2371
|
-
const flights = (clock) => {
|
|
2372
|
-
const units = /* @__PURE__ */ new Set();
|
|
2373
|
-
const idle = /* @__PURE__ */ new Set();
|
|
2374
|
-
const lower = /* @__PURE__ */ new Set();
|
|
2375
|
-
let aborted = null;
|
|
2376
|
-
const settle = () => {
|
|
2377
|
-
for (const done of [...lower]) done();
|
|
2378
|
-
lower.clear();
|
|
2379
|
-
if (units.size === 0) for (const done of [...idle]) done();
|
|
2380
|
-
};
|
|
2381
|
-
const abort = (reason) => {
|
|
2382
|
-
aborted ??= { reason };
|
|
2383
|
-
for (const unit of [...units]) if (!unit.signal.aborted) unit.abort(aborted.reason);
|
|
2384
|
-
};
|
|
2385
|
-
return {
|
|
2386
|
-
get size() {
|
|
2387
|
-
return units.size;
|
|
2388
|
-
},
|
|
2389
|
-
add(unit) {
|
|
2390
|
-
units.add(unit);
|
|
2391
|
-
if (aborted !== null && !unit.signal.aborted) unit.abort(aborted.reason);
|
|
2392
|
-
return () => {
|
|
2393
|
-
if (units.delete(unit)) settle();
|
|
2394
|
-
};
|
|
2395
|
-
},
|
|
2396
|
-
abort,
|
|
2397
|
-
clear() {
|
|
2398
|
-
units.clear();
|
|
2399
|
-
settle();
|
|
2400
|
-
},
|
|
2401
|
-
drain(deadline, signal) {
|
|
2402
|
-
const cooperative = () => abort(signal.reason);
|
|
2403
|
-
if (signal.aborted) cooperative();
|
|
2404
|
-
else signal.addEventListener("abort", cooperative, { once: true });
|
|
2405
|
-
if (units.size === 0 || deadline <= clock.now()) {
|
|
2406
|
-
signal.removeEventListener("abort", cooperative);
|
|
2407
|
-
return Promise.resolve();
|
|
2408
|
-
}
|
|
2409
|
-
return new Promise((resolve) => {
|
|
2410
|
-
let cancel = () => {};
|
|
2411
|
-
const done = () => {
|
|
2412
|
-
idle.delete(done);
|
|
2413
|
-
cancel();
|
|
2414
|
-
signal.removeEventListener("abort", cooperative);
|
|
2415
|
-
resolve();
|
|
2416
|
-
};
|
|
2417
|
-
idle.add(done);
|
|
2418
|
-
cancel = clock.after(Math.max(0, deadline - clock.now()), done);
|
|
2419
|
-
});
|
|
2420
|
-
},
|
|
2421
|
-
less() {
|
|
2422
|
-
return new Promise((resolve) => lower.add(resolve));
|
|
2423
|
-
},
|
|
2424
|
-
release() {
|
|
2425
|
-
for (const done of [...lower]) done();
|
|
2426
|
-
lower.clear();
|
|
2427
|
-
}
|
|
2428
|
-
};
|
|
2429
|
-
};
|
|
2430
|
-
/** This function makes a sleeper on a clock. */
|
|
2431
|
-
const sleeper = (clock) => {
|
|
2432
|
-
let wakes = 0;
|
|
2433
|
-
let stopped = false;
|
|
2434
|
-
const sleeps = /* @__PURE__ */ new Set();
|
|
2435
|
-
const backoffs = /* @__PURE__ */ new Set();
|
|
2436
|
-
const wait = (ms, into) => stopped ? Promise.resolve() : new Promise((resolve) => {
|
|
2437
|
-
let cancel = () => {};
|
|
2438
|
-
const done = () => {
|
|
2439
|
-
if (!into.delete(done)) return;
|
|
2440
|
-
cancel();
|
|
2441
|
-
resolve();
|
|
2442
|
-
};
|
|
2443
|
-
into.add(done);
|
|
2444
|
-
if (Number.isFinite(ms)) cancel = clock.after(ms, done);
|
|
2445
|
-
});
|
|
2446
|
-
return {
|
|
2447
|
-
get wakes() {
|
|
2448
|
-
return wakes;
|
|
2449
|
-
},
|
|
2450
|
-
sleep(ms, since) {
|
|
2451
|
-
if (since !== wakes) return Promise.resolve();
|
|
2452
|
-
return wait(ms, sleeps);
|
|
2453
|
-
},
|
|
2454
|
-
backoff(ms) {
|
|
2455
|
-
return wait(ms, backoffs);
|
|
2456
|
-
},
|
|
2457
|
-
wake() {
|
|
2458
|
-
wakes += 1;
|
|
2459
|
-
const ended = sleeps.size;
|
|
2460
|
-
for (const done of [...sleeps]) done();
|
|
2461
|
-
return ended;
|
|
2462
|
-
},
|
|
2463
|
-
stop() {
|
|
2464
|
-
stopped = true;
|
|
2465
|
-
for (const done of [...sleeps, ...backoffs]) done();
|
|
2466
|
-
}
|
|
2467
|
-
};
|
|
2468
|
-
};
|
|
2469
|
-
/** This function waits for a work until a bound on the clock, in milliseconds. */
|
|
2470
|
-
const settleWithin = (clock, ms, work) => new Promise((resolve) => {
|
|
2471
|
-
const cancel = Number.isFinite(ms) ? clock.after(ms, () => resolve({ kind: "bound" })) : () => {};
|
|
2472
|
-
work.then(() => {
|
|
2473
|
-
cancel();
|
|
2474
|
-
resolve({ kind: "done" });
|
|
2475
|
-
}, (error) => {
|
|
2476
|
-
cancel();
|
|
2477
|
-
resolve({
|
|
2478
|
-
kind: "failed",
|
|
2479
|
-
error
|
|
2480
|
-
});
|
|
2481
|
-
});
|
|
2482
|
-
});
|
|
2483
|
-
/**
|
|
2484
|
-
* This function gives the backoff of the knobs of a loop. The first delay is `retry_base`, and each
|
|
2485
|
-
* delay is two times longer, up to `retry_cap`. Each delay has a jitter up to the base, as in the Redis client.
|
|
2486
|
-
*/
|
|
2487
|
-
const retryOf = (knobs, owner) => {
|
|
2488
|
-
const base = boundedMillisecondsOf(knobs, "retry_base", owner);
|
|
2489
|
-
const backoff = {
|
|
2490
|
-
base,
|
|
2491
|
-
factor: 2,
|
|
2492
|
-
cap: boundedMillisecondsOf(knobs, "retry_cap", owner),
|
|
2493
|
-
jitter: {
|
|
2494
|
-
kind: "add",
|
|
2495
|
-
amount: base
|
|
2496
|
-
}
|
|
2497
|
-
};
|
|
2498
|
-
const problems = backoffProblems(backoff);
|
|
2499
|
-
if (problems.length > 0) throw new Error(`${owner}: the retry_base and retry_cap give no backoff: ${problems.join(", ")}`);
|
|
2500
|
-
return backoff;
|
|
2501
|
-
};
|
|
2502
|
-
/**
|
|
2503
|
-
* This function gives the problems of the bounds of a loop in a declaration. The close bound fits
|
|
2504
|
-
* in the cap of `close transports`. The bound of a unit fits in the cap of `drain`, or the drain can cut a unit (S6 A10).
|
|
2505
|
-
*/
|
|
2506
|
-
const boundProblems = (owner, closeBound, unitBound, description) => {
|
|
2507
|
-
const capOf = (name) => {
|
|
2508
|
-
const phase = description.shutdown.phases.find((each) => each.name === name);
|
|
2509
|
-
return phase === void 0 ? void 0 : valueIn(phase.cap, "ms");
|
|
2510
|
-
};
|
|
2511
|
-
const found = [];
|
|
2512
|
-
const close = capOf("close transports");
|
|
2513
|
-
if (close === void 0) found.push(`${owner} needs the phase "close transports" to close`);
|
|
2514
|
-
else if (closeBound >= close) found.push(`the close_bound of ${owner}, ${closeBound} ms, does not fit in the cap of "close transports", ${close} ms`);
|
|
2515
|
-
const drain = capOf("drain");
|
|
2516
|
-
if (drain === void 0) found.push(`${owner} needs the phase "drain" to end its units`);
|
|
2517
|
-
else if (!Number.isFinite(unitBound)) found.push(`the unit_processing_time of ${owner} has no bound, thus the drain can cut a unit at its cap of ${drain} ms`);
|
|
2518
|
-
else if (unitBound >= drain) found.push(`the unit_processing_time of ${owner}, ${unitBound} ms, does not fit in the cap of "drain", ${drain} ms`);
|
|
2519
|
-
return found;
|
|
2520
|
-
};
|
|
2521
|
-
//#endregion
|
|
2522
2545
|
//#region src/worker.ts
|
|
2523
2546
|
const OWNER$1 = "workerLoop";
|
|
2524
2547
|
/**
|
|
@@ -2706,7 +2729,6 @@ const queueDrain = (knobs, options) => {
|
|
|
2706
2729
|
const running = flights(clock);
|
|
2707
2730
|
const rests = sleeper(clock);
|
|
2708
2731
|
const intake = new AbortController();
|
|
2709
|
-
const stopped = new Promise((resolve) => intake.signal.addEventListener("abort", () => resolve(STOPPED), { once: true }));
|
|
2710
2732
|
const pending = /* @__PURE__ */ new Set();
|
|
2711
2733
|
const active = /* @__PURE__ */ new Set();
|
|
2712
2734
|
let refusals = 0;
|
|
@@ -2861,7 +2883,7 @@ const queueDrain = (knobs, options) => {
|
|
|
2861
2883
|
kind: "error",
|
|
2862
2884
|
error
|
|
2863
2885
|
}));
|
|
2864
|
-
const first = await
|
|
2886
|
+
const first = await untilAbort(taking, intake.signal, STOPPED);
|
|
2865
2887
|
if (first === STOPPED || intake.signal.aborted) {
|
|
2866
2888
|
if (first === STOPPED) taking.then((late) => {
|
|
2867
2889
|
if (late.kind === "job") handBack(late.job);
|