@mark1russell7/service 0.5.4 → 0.5.6
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 +9 -0
- package/build/http.d.ts +6 -1
- package/build/http.js +102 -10
- package/build/http.js.map +1 -1
- package/build/index.js +35 -10
- package/build/index.js.map +1 -1
- package/build/{knobs-BxG-c7Zb.js → knobs-D2ClI68I.js} +35 -2
- package/build/knobs-D2ClI68I.js.map +1 -0
- package/package.json +2 -2
- package/build/knobs-BxG-c7Zb.js.map +0 -1
package/build/index.js
CHANGED
|
@@ -3,9 +3,9 @@ import { detectionTime, heartbeat } from "./heartbeat.js";
|
|
|
3
3
|
import { a as recoverSpan, i as heartbeatDetection, n as asWindow, r as failSpan, t as asDebounce } from "./timing-DGRi3XCI.js";
|
|
4
4
|
import { n as operationProblems, t as mayReissue } from "./operation-DL5RxFds.js";
|
|
5
5
|
import { a as limiterProblems, i as limitRule, n as freeAt, o as longestRetryAfter, r as idleSpan, s as retryAfterSeconds, t as decide } from "./policy-CgS4y94U.js";
|
|
6
|
+
import { a as lateHolds, l as takeOutside, n as countOf, r as millisecondsOf, s as ownStream, t as boundedMillisecondsOf } from "./knobs-D2ClI68I.js";
|
|
6
7
|
import { backoffBand, backoffDelay, backoffProblems } from "./backoff.js";
|
|
7
8
|
import { n as memoryStore, t as keyedLimiter } from "./limiter-wv0uXDk9.js";
|
|
8
|
-
import { n as countOf, r as millisecondsOf, t as boundedMillisecondsOf } from "./knobs-BxG-c7Zb.js";
|
|
9
9
|
import { convertExact, convertible, quantity, rationals, valueIn } from "@mark1russell7/quantity";
|
|
10
10
|
import { replay } from "@mark1russell7/algebra/replay";
|
|
11
11
|
//#region src/description.ts
|
|
@@ -1150,8 +1150,11 @@ const lifecycle = (description, runtime, components, options = {}, bind = () =>
|
|
|
1150
1150
|
if (found.as === void 0) triggered("ipc-message");
|
|
1151
1151
|
else triggered(found.as, `${found.as} (a message of the IPC channel)`);
|
|
1152
1152
|
};
|
|
1153
|
-
/**
|
|
1154
|
-
|
|
1153
|
+
/**
|
|
1154
|
+
* This function does the work of one phase, until its cap. It tells if the work ended in time. When
|
|
1155
|
+
* work is left at the cap, `excuse` gives the cause of a cap that is not a failure, or null.
|
|
1156
|
+
*/
|
|
1157
|
+
const runPhase = async (phase, hold, work, excuse = () => null) => {
|
|
1155
1158
|
const cap = valueIn(phase.cap, "ms");
|
|
1156
1159
|
const deadline = clock.now() + cap;
|
|
1157
1160
|
const capped = new AbortController();
|
|
@@ -1175,9 +1178,13 @@ const lifecycle = (description, runtime, components, options = {}, bind = () =>
|
|
|
1175
1178
|
cancelCap = clock.after(cap, () => resolve("cap"));
|
|
1176
1179
|
});
|
|
1177
1180
|
if (await Promise.race([all.then(() => "work"), end]) === "work" && hold) await end;
|
|
1181
|
+
const excused = pending > 0 ? excuse(pending) : null;
|
|
1178
1182
|
cancelCap();
|
|
1179
1183
|
capped.abort(/* @__PURE__ */ new Error(`the phase ${phase.name} reached its cap`));
|
|
1180
|
-
return
|
|
1184
|
+
return {
|
|
1185
|
+
inTime: pending === 0,
|
|
1186
|
+
excused
|
|
1187
|
+
};
|
|
1181
1188
|
};
|
|
1182
1189
|
async function shutdown(cause = "the service asked for it", request = {}) {
|
|
1183
1190
|
if (request.code !== void 0 && !isExitCode(request.code)) throw new Error(`${description.name}: the exit code ${request.code} is not a whole number from 0 to 255`);
|
|
@@ -1209,10 +1216,11 @@ const lifecycle = (description, runtime, components, options = {}, bind = () =>
|
|
|
1209
1216
|
};
|
|
1210
1217
|
const phases = description.shutdown.phases;
|
|
1211
1218
|
let on = "exit.request";
|
|
1219
|
+
let detail;
|
|
1212
1220
|
for (const phase of phases) {
|
|
1213
1221
|
if (exitState === "exited") break;
|
|
1214
1222
|
const rank = stageRank(phase.name);
|
|
1215
|
-
moveDrain(phase.name, on);
|
|
1223
|
+
moveDrain(phase.name, on, detail);
|
|
1216
1224
|
if (rank > ABORT_AFTER) abortService();
|
|
1217
1225
|
const work = [];
|
|
1218
1226
|
const owned = components.phaseWork.get(phase.name) ?? [];
|
|
@@ -1224,6 +1232,7 @@ const lifecycle = (description, runtime, components, options = {}, bind = () =>
|
|
|
1224
1232
|
for (const [, impl] of startedTransports) work.push(() => impl.stopIntake());
|
|
1225
1233
|
}
|
|
1226
1234
|
let cancelAbort = () => {};
|
|
1235
|
+
let excuse;
|
|
1227
1236
|
if (phase.name === "drain") {
|
|
1228
1237
|
const cap = valueIn(phase.cap, "ms");
|
|
1229
1238
|
const cooperative = new AbortController();
|
|
@@ -1234,7 +1243,20 @@ const lifecycle = (description, runtime, components, options = {}, bind = () =>
|
|
|
1234
1243
|
record("drain", drainState, drainState, "drain.cooperative-abort");
|
|
1235
1244
|
});
|
|
1236
1245
|
work.push(({ deadline }) => requests.drain(deadline));
|
|
1237
|
-
|
|
1246
|
+
let transportsLeft = 0;
|
|
1247
|
+
for (const [, impl] of startedTransports) work.push(async ({ deadline }) => {
|
|
1248
|
+
transportsLeft += 1;
|
|
1249
|
+
try {
|
|
1250
|
+
await impl.drain(deadline, cooperative.signal);
|
|
1251
|
+
} finally {
|
|
1252
|
+
transportsLeft -= 1;
|
|
1253
|
+
}
|
|
1254
|
+
});
|
|
1255
|
+
excuse = (left) => {
|
|
1256
|
+
const holds = lateHolds(life);
|
|
1257
|
+
if (left > transportsLeft || transportsLeft > holds.length || holds.some((each) => each.running > 0)) return null;
|
|
1258
|
+
return `answers not flushed: ${holds.reduce((sum, each) => sum + each.unflushed, 0)}`;
|
|
1259
|
+
};
|
|
1238
1260
|
}
|
|
1239
1261
|
if (phase.name === "close transports") for (const [, impl] of startedTransports) work.push(() => impl.close());
|
|
1240
1262
|
if (phase.name === "stop the poller") work.push(() => {
|
|
@@ -1259,12 +1281,15 @@ const lifecycle = (description, runtime, components, options = {}, bind = () =>
|
|
|
1259
1281
|
await port.flush?.();
|
|
1260
1282
|
});
|
|
1261
1283
|
else for (const each of owned) work.push(each);
|
|
1262
|
-
const inTime = await runPhase(phase, phase.name === "fail readiness", work);
|
|
1284
|
+
const { inTime, excused } = await runPhase(phase, phase.name === "fail readiness", work, excuse);
|
|
1263
1285
|
cancelAbort();
|
|
1264
|
-
if (!inTime) fail(`the phase ${phase.name} did not end in its cap of ${valueIn(phase.cap, "s")} s`);
|
|
1286
|
+
if (!inTime && excused === null) fail(`the phase ${phase.name} did not end in its cap of ${valueIn(phase.cap, "s")} s`);
|
|
1265
1287
|
on = inTime ? "phase.done" : "phase.cap";
|
|
1288
|
+
const outsideCount = phase.name === "drain" ? takeOutside(life) : 0;
|
|
1289
|
+
const parts = [inTime ? null : excused, outsideCount > 0 ? `streams outside the lifecycle: ${outsideCount}` : null].filter((part) => part !== null);
|
|
1290
|
+
detail = parts.length > 0 ? parts.join(", ") : void 0;
|
|
1266
1291
|
}
|
|
1267
|
-
if (exitState !== "exited") moveDrain("done", on);
|
|
1292
|
+
if (exitState !== "exited") moveDrain("done", on, detail);
|
|
1268
1293
|
abortService();
|
|
1269
1294
|
runtime.stop();
|
|
1270
1295
|
components.stopPollers();
|
|
@@ -1289,7 +1314,7 @@ const lifecycle = (description, runtime, components, options = {}, bind = () =>
|
|
|
1289
1314
|
done,
|
|
1290
1315
|
signal: service.signal,
|
|
1291
1316
|
shutdown,
|
|
1292
|
-
request: (controller, route, work, request) => requests.request(controller, route, work, request),
|
|
1317
|
+
request: (controller, route, work, request) => ownStream(requests.request(controller, route, work, request), life),
|
|
1293
1318
|
inFlight: (controller) => requests.inFlight(controller),
|
|
1294
1319
|
keepAlive: () => state === "running" && runtime.verdict().ready
|
|
1295
1320
|
};
|