@namzu/sdk 20.3.0 → 20.4.0
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 +142 -0
- package/dist/public-runtime.d.ts +1 -0
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +5 -0
- package/dist/public-runtime.js.map +1 -1
- package/dist/runtime/query/checkpoint.d.ts +27 -1
- package/dist/runtime/query/checkpoint.d.ts.map +1 -1
- package/dist/runtime/query/checkpoint.js +34 -4
- package/dist/runtime/query/checkpoint.js.map +1 -1
- package/dist/runtime/query/index.d.ts +21 -1
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +4 -0
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/resume-run.d.ts +9 -1
- package/dist/runtime/query/resume-run.d.ts.map +1 -1
- package/dist/runtime/query/resume-run.js +2 -1
- package/dist/runtime/query/resume-run.js.map +1 -1
- package/dist/store/index.d.ts +1 -1
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/index.js +1 -1
- package/dist/store/index.js.map +1 -1
- package/dist/store/run/checkpoint-disk.d.ts +18 -2
- package/dist/store/run/checkpoint-disk.d.ts.map +1 -1
- package/dist/store/run/checkpoint-disk.js +50 -5
- package/dist/store/run/checkpoint-disk.js.map +1 -1
- package/dist/store/run/checkpoint-memory.d.ts +22 -2
- package/dist/store/run/checkpoint-memory.d.ts.map +1 -1
- package/dist/store/run/checkpoint-memory.js +99 -4
- package/dist/store/run/checkpoint-memory.js.map +1 -1
- package/dist/store/run/claim-disk.d.ts +130 -0
- package/dist/store/run/claim-disk.d.ts.map +1 -0
- package/dist/store/run/claim-disk.js +550 -0
- package/dist/store/run/claim-disk.js.map +1 -0
- package/dist/store/run/listing.d.ts +44 -1
- package/dist/store/run/listing.d.ts.map +1 -1
- package/dist/store/run/listing.js +92 -1
- package/dist/store/run/listing.js.map +1 -1
- package/dist/types/run/checkpoint-store.d.ts +178 -2
- package/dist/types/run/checkpoint-store.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/public-runtime.ts +5 -0
- package/src/runtime/query/checkpoint.ts +42 -5
- package/src/runtime/query/index.ts +26 -1
- package/src/runtime/query/resume-run.ts +12 -2
- package/src/store/index.ts +4 -0
- package/src/store/run/checkpoint-disk.ts +70 -5
- package/src/store/run/checkpoint-memory.ts +118 -3
- package/src/store/run/claim-disk.ts +593 -0
- package/src/store/run/listing.ts +116 -1
- package/src/types/run/checkpoint-store.ts +189 -2
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A run claim on a filesystem, correct across PROCESSES.
|
|
3
|
+
*
|
|
4
|
+
* ## The fence is the filename
|
|
5
|
+
*
|
|
6
|
+
* A run's claims live at `{runDir}/claims/{fence}.json`. Taking the run means
|
|
7
|
+
* exclusively creating the next number; the kernel makes exactly one of any
|
|
8
|
+
* number of simultaneous callers the creator, and every other gets `EEXIST`.
|
|
9
|
+
* The current holding is the highest-numbered file.
|
|
10
|
+
*
|
|
11
|
+
* That single decision is the whole mechanism, and it is what the first
|
|
12
|
+
* version of this file got wrong. That version kept ONE mutable `claim.json`
|
|
13
|
+
* and protected it with a lock, which needed a stale-lock breaker, which was
|
|
14
|
+
* `unlink` followed by an exclusive create — two operations. An adversarial
|
|
15
|
+
* pass reproduced the consequence from separate processes: two workers both
|
|
16
|
+
* judge a stale guard breakable, the second unlinks the FIRST one's fresh
|
|
17
|
+
* guard, both end up inside the section believing they hold it, neither has
|
|
18
|
+
* written yet so neither can be the loser of a re-read, and both write the
|
|
19
|
+
* same fence. Twenty-six of three hundred runs went to two workers at an
|
|
20
|
+
* identical fence — and an identical fence fences nobody out, because the
|
|
21
|
+
* comparison is `<`.
|
|
22
|
+
*
|
|
23
|
+
* Numbering the files instead removes every one of those steps. There is no
|
|
24
|
+
* lock to go stale, so no breaker, so no window. Concurrency is decided by
|
|
25
|
+
* one `O_CREAT | O_EXCL`.
|
|
26
|
+
*
|
|
27
|
+
* ## Four properties this layout gives for free
|
|
28
|
+
*
|
|
29
|
+
* **Monotonic across release and deletion.** Fences are file names that stay,
|
|
30
|
+
* so releasing cannot rewind the counter. Deleting `claim.json` used to send
|
|
31
|
+
* the next caller down a fresh-claim path that minted fence 1 again — so a
|
|
32
|
+
* worker stalled at fence 1 could write alongside the new holder, and the
|
|
33
|
+
* documented `finally { releaseRun() }` did it on every pass. Releasing here
|
|
34
|
+
* appends a tombstone rather than removing anything.
|
|
35
|
+
*
|
|
36
|
+
* **Unreadable content cannot wedge the run.** The fence is in the name, so a
|
|
37
|
+
* damaged or half-written body never hides the ordering. A caller reads the
|
|
38
|
+
* highest number and takes the next one; the previous holder, alive or not,
|
|
39
|
+
* is fenced out by arithmetic. Refusing to take an unparseable claim was safe
|
|
40
|
+
* against double-writing and left the run permanently unclaimable, which is
|
|
41
|
+
* the failure a lease exists to prevent.
|
|
42
|
+
*
|
|
43
|
+
* **The write-time fence check needs no parsing at all.** It compares a
|
|
44
|
+
* number against a directory listing, so a corrupt body cannot make the check
|
|
45
|
+
* skip itself.
|
|
46
|
+
*
|
|
47
|
+
* **No `rename` and no `unlink` on the contended path.** Renaming over a path
|
|
48
|
+
* another process merely holds open for reading fails on non-POSIX
|
|
49
|
+
* filesystems — measured at 84% under two concurrent readers — and a listing
|
|
50
|
+
* sweep reads exactly these files. Creating a new name never collides with a
|
|
51
|
+
* reader.
|
|
52
|
+
*
|
|
53
|
+
* ## The name appears already complete, because `link` publishes it
|
|
54
|
+
*
|
|
55
|
+
* An exclusive create decides the winner, but `wx` is open-THEN-write: for an
|
|
56
|
+
* instant the winning name exists and is empty. A reader landing in it parses
|
|
57
|
+
* nothing, reports the holding expired, and a second worker takes the next
|
|
58
|
+
* fence. Their fences differ so the loser's first checkpoint is refused — and
|
|
59
|
+
* both have already restored the run and executed its tools by then, and tool
|
|
60
|
+
* side effects are fenced by nothing.
|
|
61
|
+
*
|
|
62
|
+
* So the body is written to a temporary name first and the fence name is
|
|
63
|
+
* created by `link`ing to it. `link` fails `EEXIST` when the destination
|
|
64
|
+
* exists, so it arbitrates exactly as `wx` did, and the destination it creates
|
|
65
|
+
* is a second name for a file that was already whole. There is no instant at
|
|
66
|
+
* which the fence name exists and its body does not.
|
|
67
|
+
*
|
|
68
|
+
* Measured from separate OS processes on both platform families:
|
|
69
|
+
*
|
|
70
|
+
* - `link` refused an existing destination 20,000/20,000 times. Six writers
|
|
71
|
+
* over 6,000 fences produced no fence with two winners and none with none.
|
|
72
|
+
* - Paired identical fixtures: `wx` showed an empty destination in 15,985 of
|
|
73
|
+
* 16,000 first observations, `link` in 0 of 16,000. All 156,000 frontier
|
|
74
|
+
* observations were `ENOENT` or a complete parseable body — none empty, none
|
|
75
|
+
* torn.
|
|
76
|
+
* - `rename` is disqualified, and not for the reason first assumed. It never
|
|
77
|
+
* reports `EEXIST`; it silently REPLACES, 20,000/20,000. It cannot arbitrate
|
|
78
|
+
* a race at all — two workers publishing one fence would both succeed and
|
|
79
|
+
* the second would erase the first. (Its `EPERM`-under-readers failure is
|
|
80
|
+
* real too, at 93.7% on the non-POSIX family, but the exclusivity failure
|
|
81
|
+
* disqualifies it first.)
|
|
82
|
+
*
|
|
83
|
+
* Cost: three syscalls rather than one, +1.0 ms per acquisition on the
|
|
84
|
+
* non-POSIX family and +0.04 ms on POSIX, for an operation that runs once per
|
|
85
|
+
* run plus renewals.
|
|
86
|
+
*
|
|
87
|
+
* ## What it still does not do
|
|
88
|
+
*
|
|
89
|
+
* It does not detect liveness. Nothing can from here: a stalled holder, a
|
|
90
|
+
* suspended container and a partitioned network are indistinguishable, and
|
|
91
|
+
* they are indistinguishable from the holder's own side too, which is why it
|
|
92
|
+
* keeps writing. The fence is the answer — the write is checked, not the
|
|
93
|
+
* writer.
|
|
94
|
+
*/
|
|
95
|
+
import type { ClaimFence, ClaimRunOptions, RunClaim } from '../../types/run/checkpoint-store.js';
|
|
96
|
+
/**
|
|
97
|
+
* The highest fence ever issued for this run, or 0 when it has never been
|
|
98
|
+
* claimed.
|
|
99
|
+
*
|
|
100
|
+
* Reads names only. This is deliberately the one question the contended path
|
|
101
|
+
* asks, because a name cannot be half-written: a file either exists or does
|
|
102
|
+
* not, where a body can be observed mid-write.
|
|
103
|
+
*/
|
|
104
|
+
export declare function currentFence(runDir: string): Promise<ClaimFence>;
|
|
105
|
+
/**
|
|
106
|
+
* The run's current holding, or `null` when it has never been claimed.
|
|
107
|
+
*
|
|
108
|
+
* Returns `null` for an unreadable body too, and that is safe HERE in a way
|
|
109
|
+
* it was not in the previous design: the fence is known from the name
|
|
110
|
+
* regardless, so an unreadable body means "somebody took this number and its
|
|
111
|
+
* details are unavailable", and the caller's response is to take the NEXT
|
|
112
|
+
* number rather than to give up. Nothing is inferred from the absence.
|
|
113
|
+
*/
|
|
114
|
+
export declare function readClaim(runDir: string): Promise<RunClaim | null>;
|
|
115
|
+
/** Take or extend the run's claim. `null` when somebody else holds it. */
|
|
116
|
+
export declare function acquireClaim(runDir: string, options: ClaimRunOptions): Promise<RunClaim | null>;
|
|
117
|
+
/**
|
|
118
|
+
* Give up a holding early, so the run returns to the queue without waiting
|
|
119
|
+
* out its lease.
|
|
120
|
+
*
|
|
121
|
+
* Appends a tombstone at the next fence rather than deleting anything. The
|
|
122
|
+
* counter must never rewind: a worker stalled at an old fence has to stay
|
|
123
|
+
* fenced out forever, and removing the record would let a later claimer be
|
|
124
|
+
* issued a number that stalled worker already believes it holds.
|
|
125
|
+
*
|
|
126
|
+
* A stale fence releases nothing — a worker that stalled past its lease must
|
|
127
|
+
* not be able to hand away a run somebody else now holds.
|
|
128
|
+
*/
|
|
129
|
+
export declare function releaseClaim(runDir: string, fence: ClaimFence): Promise<void>;
|
|
130
|
+
//# sourceMappingURL=claim-disk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-disk.d.ts","sourceRoot":"","sources":["../../../src/store/run/claim-disk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6FG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAA;AAoRhG;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAGtE;AA8DD;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA6DxE;AAED,0EAA0E;AAC1E,wBAAsB,YAAY,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,eAAe,GACtB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAoC1B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBnF"}
|