@parcha/pi-durable-disk 0.1.0-beta.18 → 0.1.0-beta.19
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/CHANGELOG.md +26 -0
- package/README.md +176 -892
- package/dist/cli.d.ts +4 -2
- package/dist/cli.js +27 -1
- package/dist/run.d.ts +2 -0
- package/dist/run.js +3 -0
- package/package.json +1 -1
package/dist/cli.d.ts
CHANGED
|
@@ -56,12 +56,14 @@ export interface InstanceLife {
|
|
|
56
56
|
* A drain stops new submissions and waits up to `life.drainMs` for running work; with `resume` it then writes the run
|
|
57
57
|
* sleeping with its wake (the deadline its work waits for, null when idle and idle parking is on, otherwise now), so
|
|
58
58
|
* the supervisor starts it again and a deploy never strands it; a wake hook that refuses falls back to a due wake.
|
|
59
|
-
* `pause` seals it paused.
|
|
59
|
+
* `pause` seals it paused. An app that released the run itself (`run.release()`, in `onOpen` or after it) has nothing left to
|
|
60
|
+
* serve: the instance exits 0 when `onOpen` returns, and a stop after that exits 0 without a drain, since the Harness is
|
|
61
|
+
* closed and the run's status final. A rejection of `onOpen` is the app failing (exit 1), except once a drain or a park's release
|
|
60
62
|
* began: closing the Harness under the app makes its work reject ("Session is closed"), and ending the process there
|
|
61
63
|
* would cut the release before its unmount (a container's FUSE daemon dies with the process, leaving the delegation
|
|
62
64
|
* orphaned).
|
|
63
65
|
*/
|
|
64
|
-
export declare function serveUntilDone(run: Pick<DurableRun, "setStatus" | "release" | "record"
|
|
66
|
+
export declare function serveUntilDone(run: Pick<DurableRun, "setStatus" | "release" | "record"> & Partial<Pick<DurableRun, "releasing">>, onOpen: ((run: DurableRun) => void | Promise<void>) | undefined, onSigterm: string | undefined, log: (event: string, extra?: Record<string, unknown>) => void, signals?: Pick<NodeJS.EventEmitter, "once">, life?: InstanceLife): Promise<number>;
|
|
65
67
|
/**
|
|
66
68
|
* The flags every instance this supervisor starts gets after `run`: `--app MODULE` (made absolute, since the unit's working
|
|
67
69
|
* directory is not the supervisor's) and any `--run-arg`, in that order.
|
package/dist/cli.js
CHANGED
|
@@ -285,7 +285,9 @@ async function runInstance(values) {
|
|
|
285
285
|
* A drain stops new submissions and waits up to `life.drainMs` for running work; with `resume` it then writes the run
|
|
286
286
|
* sleeping with its wake (the deadline its work waits for, null when idle and idle parking is on, otherwise now), so
|
|
287
287
|
* the supervisor starts it again and a deploy never strands it; a wake hook that refuses falls back to a due wake.
|
|
288
|
-
* `pause` seals it paused.
|
|
288
|
+
* `pause` seals it paused. An app that released the run itself (`run.release()`, in `onOpen` or after it) has nothing left to
|
|
289
|
+
* serve: the instance exits 0 when `onOpen` returns, and a stop after that exits 0 without a drain, since the Harness is
|
|
290
|
+
* closed and the run's status final. A rejection of `onOpen` is the app failing (exit 1), except once a drain or a park's release
|
|
289
291
|
* began: closing the Harness under the app makes its work reject ("Session is closed"), and ending the process there
|
|
290
292
|
* would cut the release before its unmount (a container's FUSE daemon dies with the process, leaving the delegation
|
|
291
293
|
* orphaned).
|
|
@@ -305,11 +307,25 @@ export function serveUntilDone(run, onOpen, onSigterm, log, signals = process, l
|
|
|
305
307
|
let releasing = false;
|
|
306
308
|
let parking;
|
|
307
309
|
const sleeping = (at) => run.setStatus("sleeping", { reason: "drained" }, { wakeAt: at === null ? null : new Date(at).toISOString() });
|
|
310
|
+
// The app released the run: wait for its release to end (a second `release()` joins it) and leave.
|
|
311
|
+
const leaveReleased = async (event) => {
|
|
312
|
+
try {
|
|
313
|
+
await run.release();
|
|
314
|
+
log("released", { ...event, record: run.record });
|
|
315
|
+
done(0);
|
|
316
|
+
}
|
|
317
|
+
catch (err) {
|
|
318
|
+
log("release failed", { code: err.code, message: err.message });
|
|
319
|
+
done(exitCodeFor(err));
|
|
320
|
+
}
|
|
321
|
+
};
|
|
308
322
|
const shutdown = async (signal) => {
|
|
309
323
|
if (releasing)
|
|
310
324
|
return;
|
|
311
325
|
releasing = true;
|
|
312
326
|
log("draining", { signal, onSigterm, drainMs });
|
|
327
|
+
if (run.releasing === true)
|
|
328
|
+
return leaveReleased({ drained: "released" });
|
|
313
329
|
try {
|
|
314
330
|
if (await parking?.stop())
|
|
315
331
|
return done(0);
|
|
@@ -358,6 +374,12 @@ export function serveUntilDone(run, onOpen, onSigterm, log, signals = process, l
|
|
|
358
374
|
// onOpen may return at once (work submitted) or run for the instance's life.
|
|
359
375
|
Promise.resolve()
|
|
360
376
|
.then(() => onOpen?.(durable))
|
|
377
|
+
.then(() => {
|
|
378
|
+
if (releasing || run.releasing !== true)
|
|
379
|
+
return;
|
|
380
|
+
releasing = true;
|
|
381
|
+
void leaveReleased({ drained: "app" });
|
|
382
|
+
})
|
|
361
383
|
.catch((err) => {
|
|
362
384
|
if (releasing)
|
|
363
385
|
return;
|
|
@@ -642,6 +664,10 @@ async function forkRun(values) {
|
|
|
642
664
|
const COMMON = { disk: { type: "string" }, region: { type: "string" }, id: { type: "string" }, "mount-root": { type: "string" }, "api-key-env": { type: "string" }, "host-name": { type: "string" } };
|
|
643
665
|
export async function main(argv) {
|
|
644
666
|
const [command, ...rest] = argv;
|
|
667
|
+
if (command === "--help" || command === "-h" || command === "help" || (command !== undefined && rest[0] === "--help")) {
|
|
668
|
+
process.stdout.write(`${USAGE}\n`);
|
|
669
|
+
return 0;
|
|
670
|
+
}
|
|
645
671
|
try {
|
|
646
672
|
switch (command) {
|
|
647
673
|
case "run":
|
package/dist/run.d.ts
CHANGED
|
@@ -277,6 +277,8 @@ export interface DurableRun {
|
|
|
277
277
|
/** `run.json` as last written. */
|
|
278
278
|
readonly record: RunRecord;
|
|
279
279
|
readonly fenced: boolean;
|
|
280
|
+
/** True once `release()` began: the Harness is closed, the status is final, and the instance has nothing left to serve. */
|
|
281
|
+
readonly releasing: boolean;
|
|
280
282
|
/** Write a status transition. `done` and `failed` run the barrier first, so a terminal status never names workspace files that are not durable. */
|
|
281
283
|
setStatus(status: RunStatus, detail?: JsonValue | null, options?: {
|
|
282
284
|
readonly wakeAt?: string | null;
|
package/dist/run.js
CHANGED
|
@@ -754,6 +754,9 @@ class Run {
|
|
|
754
754
|
await this.#store.storage.close(this.#context);
|
|
755
755
|
});
|
|
756
756
|
}
|
|
757
|
+
get releasing() {
|
|
758
|
+
return this.#releasing !== undefined || this.#released;
|
|
759
|
+
}
|
|
757
760
|
async setStatus(status, detail = null, options = {}) {
|
|
758
761
|
if (!this.#lease.fenced && (this.#released || this.#releasing))
|
|
759
762
|
throw new RunError("RUN_RELEASED", `run ${this.ref.id} is released`);
|
package/package.json
CHANGED