@smartmemory/stratum 0.4.6 → 0.5.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/dist/cli/flow.js +61 -0
- package/dist/cli/flow.js.map +1 -0
- package/dist/cli/learn.js +9 -16
- package/dist/cli/learn.js.map +1 -1
- package/dist/cli/query_gate.js +7 -2
- package/dist/cli/query_gate.js.map +1 -1
- package/dist/cli/stratum.js +3 -1
- package/dist/cli/stratum.js.map +1 -1
- package/dist/connectors/background.js +2 -2
- package/dist/connectors/background.js.map +1 -1
- package/dist/connectors/claude.js +2 -0
- package/dist/connectors/claude.js.map +1 -1
- package/dist/connectors/codex.js +4 -0
- package/dist/connectors/codex.js.map +1 -1
- package/dist/connectors/foreground_registry.js +589 -0
- package/dist/connectors/foreground_registry.js.map +1 -0
- package/dist/connectors/index.js +1 -0
- package/dist/connectors/index.js.map +1 -1
- package/dist/connectors/proc_identity.js +28 -0
- package/dist/connectors/proc_identity.js.map +1 -1
- package/dist/connectors/runner.js +2 -0
- package/dist/connectors/runner.js.map +1 -1
- package/dist/contracts/events.json +27 -1
- package/dist/contracts/mcp-surface.json +176 -6
- package/dist/engine/checkpoint.js +1 -1
- package/dist/engine/checkpoint.js.map +1 -1
- package/dist/engine/engine.js +860 -195
- package/dist/engine/engine.js.map +1 -1
- package/dist/engine/flow_cancel.js +158 -0
- package/dist/engine/flow_cancel.js.map +1 -0
- package/dist/engine/run_lock.js +628 -0
- package/dist/engine/run_lock.js.map +1 -0
- package/dist/engine/state.js +43 -2
- package/dist/engine/state.js.map +1 -1
- package/dist/ir/refs.js +12 -0
- package/dist/ir/refs.js.map +1 -1
- package/dist/ir/schema.js +9 -0
- package/dist/ir/schema.js.map +1 -1
- package/dist/ir/validate.js +243 -29
- package/dist/ir/validate.js.map +1 -1
- package/dist/learn/smartmemory_egress.js +3 -1
- package/dist/learn/smartmemory_egress.js.map +1 -1
- package/dist/mcp/server.js +193 -4
- package/dist/mcp/server.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { closeSync, fstatSync, linkSync, openSync, readSync, statSync, unlinkSync, writeFileSync, readFileSync } from "node:fs";
|
|
3
|
+
import { link, mkdir, open, readdir, stat, unlink } from "node:fs/promises";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { processIdentity } from "../connectors/proc_identity.js";
|
|
6
|
+
import { procStartTime } from "../connectors/proc_identity.js";
|
|
7
|
+
import { assertRunId } from "./state.js";
|
|
8
|
+
/** The general waiter's budget. 300s, not 30s: two locked sections await LLM-scale work —
|
|
9
|
+
* a judged ensure and the `evaluate:` runner — so a shorter budget would throw
|
|
10
|
+
* RUN_LOCK_TIMEOUT against a perfectly healthy holder. Staleness is decided by process
|
|
11
|
+
* identity, never by age, so a long hold is never mistaken for a dead one and this is a
|
|
12
|
+
* backstop against a wedge rather than a liveness check. */
|
|
13
|
+
export const DEFAULT_RUN_LOCK_TIMEOUT_MS = 300_000;
|
|
14
|
+
/** How long `flowCancel` waits to ACQUIRE the run lock, before anything is settled. */
|
|
15
|
+
export const DEFAULT_CANCEL_LOCK_WAIT_MS = 120_000;
|
|
16
|
+
/** The 0.4.0 acknowledgement budget for agent teardown, whose clock starts only AFTER
|
|
17
|
+
* settlement. A different measurement from the two above; collapsing any two of the three
|
|
18
|
+
* reintroduces either a false timeout or a cancel that appears to hang. */
|
|
19
|
+
export const DEFAULT_CANCEL_TIMEOUT_MS = 15_000;
|
|
20
|
+
/** A `<runId>.lock.<token>` (or `<runId>.lock-break.<token>`) tmp older than this belongs to a
|
|
21
|
+
* process that crashed between writing it and linking it, and is swept opportunistically. */
|
|
22
|
+
const RUN_LOCK_TMP_TTL_MS = 60_000;
|
|
23
|
+
/** The ONE age rule in this file, and it applies to exactly one thing: a `.lock-break` that
|
|
24
|
+
* carries no readable identity at all.
|
|
25
|
+
*
|
|
26
|
+
* Every other reclaim here is identity-only (R4-2) because there is an owner to judge. An
|
|
27
|
+
* opaque break-lock has none — no pid, no start time, nothing a probe can answer about — so
|
|
28
|
+
* identity can never clear it and the run would be wedged forever by a zero-byte file. Age is
|
|
29
|
+
* the only remaining discriminator, and it is safe here precisely because it is conditioned on
|
|
30
|
+
* the absence of an identity: a PARSEABLE break-lock is never aged out, however old. */
|
|
31
|
+
const BREAK_LOCK_OPAQUE_TTL_MS = 60_000;
|
|
32
|
+
/** A timeout that is NaN or Infinity is not a long budget, it is a missing one: `Date.now() +
|
|
33
|
+
* NaN` is NaN, every `Date.now() >= deadline` comparison against it is false, and the loop it
|
|
34
|
+
* bounds never ends. Reject the value where it is read, so a mistyped env var fails loudly at
|
|
35
|
+
* the first call instead of hanging a teardown (F6). */
|
|
36
|
+
export function requireDurationMs(value, name) {
|
|
37
|
+
if (!Number.isFinite(value) || value < 0)
|
|
38
|
+
throw new Error(`${name} must be a nonnegative finite number`);
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
export function runLockTimeoutMs() {
|
|
42
|
+
return requireDurationMs(Number(process.env.STRATUM_RUN_LOCK_TIMEOUT_MS ?? DEFAULT_RUN_LOCK_TIMEOUT_MS), "STRATUM_RUN_LOCK_TIMEOUT_MS");
|
|
43
|
+
}
|
|
44
|
+
export function cancelLockWaitMs() {
|
|
45
|
+
return requireDurationMs(Number(process.env.STRATUM_CANCEL_LOCK_WAIT_MS ?? DEFAULT_CANCEL_LOCK_WAIT_MS), "STRATUM_CANCEL_LOCK_WAIT_MS");
|
|
46
|
+
}
|
|
47
|
+
export function cancelTimeoutMs() {
|
|
48
|
+
return requireDurationMs(Number(process.env.STRATUM_CANCEL_TIMEOUT_MS ?? DEFAULT_CANCEL_TIMEOUT_MS), "STRATUM_CANCEL_TIMEOUT_MS");
|
|
49
|
+
}
|
|
50
|
+
function lockPath(root, runId) {
|
|
51
|
+
assertRunId(runId);
|
|
52
|
+
return join(root, `${runId}.lock`);
|
|
53
|
+
}
|
|
54
|
+
function breakLockPath(root, runId) {
|
|
55
|
+
assertRunId(runId);
|
|
56
|
+
return join(root, `${runId}.lock-break`);
|
|
57
|
+
}
|
|
58
|
+
function driverLeasePath(root, runId) {
|
|
59
|
+
assertRunId(runId);
|
|
60
|
+
return join(root, `${runId}.driver`);
|
|
61
|
+
}
|
|
62
|
+
function errorCode(error) {
|
|
63
|
+
return error?.code;
|
|
64
|
+
}
|
|
65
|
+
function isNotFound(error) {
|
|
66
|
+
return errorCode(error) === "ENOENT";
|
|
67
|
+
}
|
|
68
|
+
function isExists(error) {
|
|
69
|
+
return errorCode(error) === "EEXIST";
|
|
70
|
+
}
|
|
71
|
+
function delay(ms) {
|
|
72
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
73
|
+
}
|
|
74
|
+
function parseRecord(text) {
|
|
75
|
+
let value;
|
|
76
|
+
try {
|
|
77
|
+
value = JSON.parse(text);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
if (typeof value !== "object" || value === null)
|
|
83
|
+
return undefined;
|
|
84
|
+
const record = value;
|
|
85
|
+
if (!Number.isSafeInteger(record.pid) || typeof record.startTime !== "string" || !record.startTime)
|
|
86
|
+
return undefined;
|
|
87
|
+
if (typeof record.token !== "string")
|
|
88
|
+
return undefined;
|
|
89
|
+
return record;
|
|
90
|
+
}
|
|
91
|
+
/** Positional read, so the same descriptor can be read more than once. `handle.readFile`
|
|
92
|
+
* advances the file position and a second call returns "", which would silently turn the CAS
|
|
93
|
+
* re-read below into "the record vanished". */
|
|
94
|
+
async function readHandleRecord(handle) {
|
|
95
|
+
const info = await handle.stat();
|
|
96
|
+
if (info.size === 0 || info.size > 64 * 1024)
|
|
97
|
+
return undefined;
|
|
98
|
+
const buffer = Buffer.allocUnsafe(info.size);
|
|
99
|
+
await handle.read(buffer, 0, info.size, 0);
|
|
100
|
+
return parseRecord(buffer.toString("utf8"));
|
|
101
|
+
}
|
|
102
|
+
/** ABSENCE and FAILURE are different answers (F4). A blanket catch here reports an EACCES or an
|
|
103
|
+
* EIO as "there is no lock", and every caller reads that as a licence to proceed — which is how
|
|
104
|
+
* an unreadable-but-live lock file turns into two writers. Only ENOENT is absence. */
|
|
105
|
+
async function readRecord(path) {
|
|
106
|
+
let handle;
|
|
107
|
+
try {
|
|
108
|
+
handle = await open(path, "r");
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
if (isNotFound(error))
|
|
112
|
+
return undefined;
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
return await readHandleRecord(handle);
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
await handle.close();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Publish a COMPLETE record at `path` in one atomic step (R3-2): write the whole thing to a
|
|
124
|
+
* private tmp name, then `link()` it into place. The published file therefore never exists
|
|
125
|
+
* half-written, and a crash between the two leaves an orphan tmp and NO published record.
|
|
126
|
+
*
|
|
127
|
+
* Used for the run lock AND the break-lock. The break-lock used to be `open(…, "wx")` followed
|
|
128
|
+
* by a separate write, so a crash in between left a zero-byte `.lock-break` that parses to
|
|
129
|
+
* nothing, blocks every future breaker, and — having no identity — could never be reclaimed.
|
|
130
|
+
*
|
|
131
|
+
* @returns false when the path already exists (EEXIST): someone else published first.
|
|
132
|
+
*/
|
|
133
|
+
async function publishRecord(path, record) {
|
|
134
|
+
const temporary = `${path}.${record.token}`;
|
|
135
|
+
writeFileSync(temporary, JSON.stringify(record), { encoding: "utf8", mode: 0o600 });
|
|
136
|
+
let published = false;
|
|
137
|
+
try {
|
|
138
|
+
await link(temporary, path);
|
|
139
|
+
published = true;
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
if (!isExists(error)) {
|
|
143
|
+
await unlink(temporary).catch(() => undefined);
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
await unlink(temporary).catch(() => undefined);
|
|
148
|
+
return published;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* CAS-style removal: unlink `path` only while it still names the INODE we inspected AND that
|
|
152
|
+
* inode's record still carries `token`.
|
|
153
|
+
*
|
|
154
|
+
* The inspected descriptor is held OPEN across the whole comparison, and that is the load-
|
|
155
|
+
* bearing part. A closed descriptor's inode can be recycled between the read and the unlink,
|
|
156
|
+
* so a check that reads, closes, and later unlinks can pass its own comparison against a
|
|
157
|
+
* different file — which is exactly how two reclaimers both judge a dead owner, one removes it,
|
|
158
|
+
* a live successor publishes, and the delayed one deletes the successor.
|
|
159
|
+
*
|
|
160
|
+
* @returns whether this call is the one that removed the record.
|
|
161
|
+
*/
|
|
162
|
+
async function unlinkOwn(path, token, options = {}) {
|
|
163
|
+
let handle;
|
|
164
|
+
// `false` is reserved for the four benign verdicts: the record is gone, unparseable, carries
|
|
165
|
+
// another token, or names another inode. An EACCES/EIO is none of those — it is a lock we
|
|
166
|
+
// could not remove, and swallowing it reports a release that never happened and leaves a
|
|
167
|
+
// live-owned lock nobody can reclaim (F4).
|
|
168
|
+
try {
|
|
169
|
+
handle = await open(path, "r");
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
if (isNotFound(error))
|
|
173
|
+
return false;
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
const record = await readHandleRecord(handle);
|
|
178
|
+
if (record === undefined || record.token !== token)
|
|
179
|
+
return false;
|
|
180
|
+
const inode = (await handle.stat()).ino;
|
|
181
|
+
if (options.beforeOwnUnlink)
|
|
182
|
+
await options.beforeOwnUnlink();
|
|
183
|
+
let current;
|
|
184
|
+
try {
|
|
185
|
+
current = await stat(path);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
if (isNotFound(error))
|
|
189
|
+
return false;
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
if (current.ino !== inode)
|
|
193
|
+
return false; // the path names a successor now
|
|
194
|
+
const still = await readHandleRecord(handle); // same fd, so the same inode
|
|
195
|
+
if (still === undefined || still.token !== token)
|
|
196
|
+
return false;
|
|
197
|
+
try {
|
|
198
|
+
await unlink(path);
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
if (!isNotFound(error))
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
finally {
|
|
207
|
+
await handle.close();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/** The opaque-break-lock reclaim (see BREAK_LOCK_OPAQUE_TTL_MS). Conditional on the inode that
|
|
211
|
+
* was actually judged, exactly as `unlinkOwn` is, and refuses the moment the file turns out to
|
|
212
|
+
* be parseable after all — an identity that appeared between the two reads is an owner, and
|
|
213
|
+
* owners are judged by liveness, never by age. */
|
|
214
|
+
async function reclaimOpaqueBreakLock(path, options) {
|
|
215
|
+
const ttl = options.tmpTtlMs ?? BREAK_LOCK_OPAQUE_TTL_MS;
|
|
216
|
+
const nowMs = (options.now ?? Date.now)();
|
|
217
|
+
let handle;
|
|
218
|
+
try {
|
|
219
|
+
handle = await open(path, "r");
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
try {
|
|
225
|
+
const info = await handle.stat();
|
|
226
|
+
if (await readHandleRecord(handle) !== undefined)
|
|
227
|
+
return false; // it has an identity: not ours to age out
|
|
228
|
+
if (nowMs - info.mtimeMs < ttl)
|
|
229
|
+
return false; // a breaker may still be mid-write
|
|
230
|
+
let current;
|
|
231
|
+
try {
|
|
232
|
+
current = await stat(path);
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
if (isNotFound(error))
|
|
236
|
+
return false;
|
|
237
|
+
throw error;
|
|
238
|
+
}
|
|
239
|
+
if (current.ino !== info.ino)
|
|
240
|
+
return false;
|
|
241
|
+
try {
|
|
242
|
+
await unlink(path);
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
if (!isNotFound(error))
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
finally {
|
|
254
|
+
await handle.close();
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/** A `<runId>.lock.<token>` that was written but never linked is a crash between publication
|
|
258
|
+
* steps: there is no lock, only litter. Sweep the aged ones; a FRESH tmp belongs to a
|
|
259
|
+
* process that is one `link()` away from owning the lock and must be left alone (R4-8). */
|
|
260
|
+
async function sweepOrphanTemporaries(root, runId, options) {
|
|
261
|
+
const ttl = options.tmpTtlMs ?? RUN_LOCK_TMP_TTL_MS;
|
|
262
|
+
const nowMs = (options.now ?? Date.now)();
|
|
263
|
+
let names;
|
|
264
|
+
try {
|
|
265
|
+
names = await readdir(root);
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
// BOTH publication tmps: the run lock's `<runId>.lock.<token>` and the break-lock's
|
|
271
|
+
// `<runId>.lock-break.<token>`. The break-lock is published by the same protocol, so it
|
|
272
|
+
// leaves the same litter and needs the same sweep.
|
|
273
|
+
const prefixes = [`${runId}.lock.`, `${runId}.lock-break.`];
|
|
274
|
+
for (const name of names) {
|
|
275
|
+
if (!prefixes.some((prefix) => name.startsWith(prefix)))
|
|
276
|
+
continue;
|
|
277
|
+
const path = join(root, name);
|
|
278
|
+
try {
|
|
279
|
+
const info = await stat(path);
|
|
280
|
+
if (nowMs - info.mtimeMs < ttl)
|
|
281
|
+
continue;
|
|
282
|
+
await unlink(path);
|
|
283
|
+
}
|
|
284
|
+
catch { /* best-effort: never block an acquisition on the sweep */ }
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Break a lock whose owner is provably dead.
|
|
289
|
+
*
|
|
290
|
+
* Runs under its own tiny lock so two processes cannot race the sequence, and the removal is
|
|
291
|
+
* conditional on the INODE that was actually judged (R3-2): without that re-check, breaker A
|
|
292
|
+
* can read a dead owner's record, be descheduled while B breaks the lock and C acquires it,
|
|
293
|
+
* and then unlink C's live lock.
|
|
294
|
+
*
|
|
295
|
+
* There is no age-based reclaim anywhere here (R4-2). A breaker legitimately waiting out a
|
|
296
|
+
* slow identity probe would be evicted by one, and that eviction then races the breaker it
|
|
297
|
+
* evicted through the very sequence the break-lock exists to serialise.
|
|
298
|
+
*
|
|
299
|
+
* @returns true when the caller should retry the acquire immediately.
|
|
300
|
+
*/
|
|
301
|
+
async function breakStaleLock(root, runId, selfStartTime, identity, options) {
|
|
302
|
+
const path = lockPath(root, runId);
|
|
303
|
+
const breakPath = breakLockPath(root, runId);
|
|
304
|
+
const breakToken = randomUUID();
|
|
305
|
+
const breakRecord = { pid: process.pid, startTime: selfStartTime, token: breakToken, at: new Date().toISOString() };
|
|
306
|
+
if (!await publishRecord(breakPath, breakRecord)) {
|
|
307
|
+
// Someone else is mid-break. Reclaim only a provably dead breaker — or, if the record
|
|
308
|
+
// carries no identity at all, only an aged one (BREAK_LOCK_OPAQUE_TTL_MS).
|
|
309
|
+
const owner = await readRecord(breakPath);
|
|
310
|
+
if (owner === undefined)
|
|
311
|
+
await reclaimOpaqueBreakLock(breakPath, options);
|
|
312
|
+
else if (await identity(owner.pid, owner.startTime) === "dead")
|
|
313
|
+
await unlinkOwn(breakPath, owner.token, options);
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
try {
|
|
317
|
+
// The descriptor stays OPEN from the read through the comparison. Closing it first lets the
|
|
318
|
+
// inode be recycled underneath the check, which makes the inode comparison meaningless.
|
|
319
|
+
let handle;
|
|
320
|
+
try {
|
|
321
|
+
handle = await open(path, "r");
|
|
322
|
+
}
|
|
323
|
+
catch (error) {
|
|
324
|
+
if (isNotFound(error))
|
|
325
|
+
return true; // released already
|
|
326
|
+
throw error;
|
|
327
|
+
}
|
|
328
|
+
try {
|
|
329
|
+
const inode = (await handle.stat()).ino;
|
|
330
|
+
const record = await readHandleRecord(handle);
|
|
331
|
+
// Unparseable, or missing an identity: treat as LIVE. Never guess — an unreadable
|
|
332
|
+
// identity is "unknown", and nothing reclaims on "unknown".
|
|
333
|
+
if (record === undefined)
|
|
334
|
+
return false;
|
|
335
|
+
if (await identity(record.pid, record.startTime) !== "dead")
|
|
336
|
+
return false;
|
|
337
|
+
if (options.beforeBreakUnlink)
|
|
338
|
+
await options.beforeBreakUnlink();
|
|
339
|
+
let current;
|
|
340
|
+
try {
|
|
341
|
+
current = await stat(path);
|
|
342
|
+
}
|
|
343
|
+
catch (error) {
|
|
344
|
+
if (isNotFound(error))
|
|
345
|
+
return true;
|
|
346
|
+
throw error;
|
|
347
|
+
}
|
|
348
|
+
// Someone else already broke it and a NEW lock is published. Do NOT unlink.
|
|
349
|
+
if (current.ino !== inode)
|
|
350
|
+
return true;
|
|
351
|
+
// Inode AND token, immediately before the removal, off the descriptor we judged.
|
|
352
|
+
const still = await readHandleRecord(handle);
|
|
353
|
+
if (still === undefined || still.token !== record.token)
|
|
354
|
+
return false;
|
|
355
|
+
try {
|
|
356
|
+
await unlink(path);
|
|
357
|
+
}
|
|
358
|
+
catch (error) {
|
|
359
|
+
if (!isNotFound(error))
|
|
360
|
+
throw error;
|
|
361
|
+
}
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
finally {
|
|
365
|
+
await handle.close();
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
finally {
|
|
369
|
+
await unlinkOwn(breakPath, breakToken, options);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Acquire the cross-process lock for one run record.
|
|
374
|
+
*
|
|
375
|
+
* Publication is a single atomic step: the complete owner record is written to a private tmp
|
|
376
|
+
* name and then `link()`ed into place, so the lock file never exists half-written and a crash
|
|
377
|
+
* between the two leaves an orphan tmp and NO lock (R3-2).
|
|
378
|
+
*/
|
|
379
|
+
export async function acquireRunLock(root, runId, options = {}) {
|
|
380
|
+
assertRunId(runId);
|
|
381
|
+
const identity = options.identity ?? processIdentity;
|
|
382
|
+
const startTime = await (options.selfStartTime ?? (() => procStartTime(process.pid)))();
|
|
383
|
+
if (startTime === undefined) {
|
|
384
|
+
throw Object.assign(new Error("cannot establish process identity; run locking is unavailable"), { code: "RUN_LOCK_IDENTITY_UNAVAILABLE" });
|
|
385
|
+
}
|
|
386
|
+
const path = lockPath(root, runId);
|
|
387
|
+
const nowMs = options.now ?? Date.now;
|
|
388
|
+
// A CALLER-supplied budget gets the same validation as the env one (F4). `nowMs() >= NaN` is
|
|
389
|
+
// false forever, so a mistyped timeout did not shorten the wait, it removed the deadline: the
|
|
390
|
+
// acquire loop retried against a lock somebody else holds and never raised RUN_LOCK_TIMEOUT.
|
|
391
|
+
const deadline = nowMs() + requireDurationMs(options.timeoutMs ?? runLockTimeoutMs(), "timeoutMs");
|
|
392
|
+
await mkdir(root, { recursive: true });
|
|
393
|
+
for (;;) {
|
|
394
|
+
await sweepOrphanTemporaries(root, runId, options);
|
|
395
|
+
const token = randomUUID();
|
|
396
|
+
const record = { pid: process.pid, startTime, token, at: new Date().toISOString() };
|
|
397
|
+
if (await publishRecord(path, record))
|
|
398
|
+
return () => releaseRunLock(path, token, options);
|
|
399
|
+
if (!await breakStaleLock(root, runId, startTime, identity, options)) {
|
|
400
|
+
if (nowMs() >= deadline) {
|
|
401
|
+
const held = await readRecord(path);
|
|
402
|
+
throw Object.assign(new Error(`run ${runId} lock is held by pid ${held?.pid ?? "?"}`), { code: "RUN_LOCK_TIMEOUT", ...(held?.pid !== undefined ? { holderPid: held.pid } : {}) });
|
|
403
|
+
}
|
|
404
|
+
await delay(10 + Math.random() * 40);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
/** Release unlinks only its OWN lock. A mismatched token means our lock was broken and the
|
|
409
|
+
* run may already have been re-acquired: unlinking there would delete a live holder's lock,
|
|
410
|
+
* which is the one way this scheme produces two concurrent writers. */
|
|
411
|
+
async function releaseRunLock(path, token, options = {}) {
|
|
412
|
+
const record = await readRecord(path);
|
|
413
|
+
if (record === undefined)
|
|
414
|
+
return;
|
|
415
|
+
if (record.token !== token) {
|
|
416
|
+
process.stderr.write(`stratum: run lock ${path} is no longer ours (held by pid ${record.pid}); release is a no-op\n`);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
// Not a bare unlink: the same token+inode CAS the break-lock reclaim uses. The token read
|
|
420
|
+
// above is already stale by the time we act on it.
|
|
421
|
+
await unlinkOwn(path, token, options);
|
|
422
|
+
}
|
|
423
|
+
export async function readRunLock(root, runId) {
|
|
424
|
+
return readRecord(lockPath(root, runId));
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* The driver lease: a durable declaration that ONE live process holds this run's in-memory
|
|
428
|
+
* object (R4-4). Written on the 0 -> 1 pin and unlinked on the return to 0.
|
|
429
|
+
*
|
|
430
|
+
* Written SYNCHRONOUSLY because `retainRun` is synchronous by design — `scheduleFanout` pins
|
|
431
|
+
* with the scheduler's own run object and must not yield between the pin and the launch.
|
|
432
|
+
*
|
|
433
|
+
* The claim is EXCLUSIVE. It used to be a `rename()`, which silently overwrote whatever was
|
|
434
|
+
* there — so a second driver could publish its lease over a live one and the run would have two
|
|
435
|
+
* processes each believing they owned the in-memory copy, which is the single condition the
|
|
436
|
+
* lease exists to make impossible. `link()` fails with EEXIST instead, and the caller must
|
|
437
|
+
* resolve the incumbent by IDENTITY (dead -> reclaim, alive or unknown -> refuse) before
|
|
438
|
+
* retrying. The one case resolved here is an incumbent that is literally us: the same pid AND
|
|
439
|
+
* the same start time is our own leftover lease, and reclaiming it needs no probe.
|
|
440
|
+
*
|
|
441
|
+
* IDENTITY IS NOT OWNERSHIP (F1). The reclaim used to fire on "same pid AND same start time",
|
|
442
|
+
* which is true of every engine instance inside ONE process — so a second engine sharing a state
|
|
443
|
+
* root read a live sibling's lease as its own leftover, deleted it, and claimed the run the
|
|
444
|
+
* sibling was actively driving. The only lease this call may reclaim is one carrying a token
|
|
445
|
+
* THIS engine instance recorded for THIS run (`ownToken`); a same-identity lease with any other
|
|
446
|
+
* token is HELD, and the caller resolves it by liveness through `prepareLease`.
|
|
447
|
+
*
|
|
448
|
+
* @param ownToken the token this caller last received for `runId`, if it still holds one.
|
|
449
|
+
* @throws DRIVER_LEASE_HELD when a lease that is not ours already exists.
|
|
450
|
+
*/
|
|
451
|
+
export function writeDriverLeaseSync(root, runId, startTime, ownToken) {
|
|
452
|
+
const path = driverLeasePath(root, runId);
|
|
453
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
454
|
+
const token = randomUUID();
|
|
455
|
+
const lease = { pid: process.pid, startTime, token, at: new Date().toISOString() };
|
|
456
|
+
const temporary = `${path}.${process.pid}.${token}.tmp`;
|
|
457
|
+
writeFileSync(temporary, JSON.stringify(lease), { encoding: "utf8", mode: 0o600 });
|
|
458
|
+
let claimed = false;
|
|
459
|
+
try {
|
|
460
|
+
linkSync(temporary, path);
|
|
461
|
+
claimed = true;
|
|
462
|
+
}
|
|
463
|
+
catch (error) {
|
|
464
|
+
if (!isExists(error)) {
|
|
465
|
+
try {
|
|
466
|
+
unlinkSync(temporary);
|
|
467
|
+
}
|
|
468
|
+
catch { /* best effort */ }
|
|
469
|
+
throw error;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
try {
|
|
473
|
+
unlinkSync(temporary);
|
|
474
|
+
}
|
|
475
|
+
catch { /* best effort */ }
|
|
476
|
+
if (claimed)
|
|
477
|
+
return token;
|
|
478
|
+
const held = readDriverLeaseSync(root, runId);
|
|
479
|
+
// OUR OWN acquisition, by token — never merely our own pid.
|
|
480
|
+
if (held !== undefined && ownToken !== undefined && held.token === ownToken) {
|
|
481
|
+
releaseDriverLeaseSync(root, runId, held.token);
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
throw Object.assign(new Error(`run ${runId} driver lease is held${held !== undefined ? ` by pid ${held.pid}` : ""}`), { code: "DRIVER_LEASE_HELD", ...(held?.pid !== undefined ? { holderPid: held.pid } : {}) });
|
|
485
|
+
}
|
|
486
|
+
throw Object.assign(new Error(`run ${runId} driver lease could not be claimed`), { code: "DRIVER_LEASE_HELD" });
|
|
487
|
+
}
|
|
488
|
+
export function readDriverLeaseSync(root, runId) {
|
|
489
|
+
const path = driverLeasePath(root, runId);
|
|
490
|
+
let text;
|
|
491
|
+
// ENOENT is "no lease"; every other failure is "we could not tell", and reporting that as
|
|
492
|
+
// "no lease" is a licence to publish over a live one (F4).
|
|
493
|
+
try {
|
|
494
|
+
text = readFileSync(path, "utf8");
|
|
495
|
+
}
|
|
496
|
+
catch (error) {
|
|
497
|
+
if (isNotFound(error))
|
|
498
|
+
return undefined;
|
|
499
|
+
throw error;
|
|
500
|
+
}
|
|
501
|
+
const record = parseRecord(text);
|
|
502
|
+
return record;
|
|
503
|
+
}
|
|
504
|
+
/** Token-checked exactly as the run lock's release is: never unlink a successor's lease.
|
|
505
|
+
*
|
|
506
|
+
* ENOENT is the only swallowed failure. A blanket catch here turns EACCES or EIO into a lease
|
|
507
|
+
* that is never removed, which wedges the run for every other process — and reports success
|
|
508
|
+
* while doing it. */
|
|
509
|
+
export function releaseDriverLeaseSync(root, runId, token) {
|
|
510
|
+
unlinkOwnSync(driverLeasePath(root, runId), token);
|
|
511
|
+
}
|
|
512
|
+
/** The synchronous twin of `unlinkOwn`: token AND inode, both re-checked off the descriptor that
|
|
513
|
+
* was judged, immediately before the removal (F1). The read-then-unlink it replaces could delete
|
|
514
|
+
* a SUCCESSOR's lease — our lease is reclaimed by a breaker, a new driver publishes, and our
|
|
515
|
+
* late release unlinks theirs. `openSync` keeps the judged inode pinned across the comparison.
|
|
516
|
+
* ENOENT is the only swallowed failure; anything else propagates (F4). */
|
|
517
|
+
function unlinkOwnSync(path, token) {
|
|
518
|
+
let fd;
|
|
519
|
+
try {
|
|
520
|
+
fd = openSync(path, "r");
|
|
521
|
+
}
|
|
522
|
+
catch (error) {
|
|
523
|
+
if (isNotFound(error))
|
|
524
|
+
return;
|
|
525
|
+
throw error;
|
|
526
|
+
}
|
|
527
|
+
try {
|
|
528
|
+
const before = fstatSync(fd);
|
|
529
|
+
if (before.size === 0 || before.size > 64 * 1024)
|
|
530
|
+
return;
|
|
531
|
+
const buffer = Buffer.allocUnsafe(before.size);
|
|
532
|
+
readSync(fd, buffer, 0, before.size, 0);
|
|
533
|
+
if (parseRecord(buffer.toString("utf8"))?.token !== token)
|
|
534
|
+
return;
|
|
535
|
+
let current;
|
|
536
|
+
try {
|
|
537
|
+
current = statSync(path);
|
|
538
|
+
}
|
|
539
|
+
catch (error) {
|
|
540
|
+
if (isNotFound(error))
|
|
541
|
+
return;
|
|
542
|
+
throw error;
|
|
543
|
+
}
|
|
544
|
+
if (current.ino !== before.ino)
|
|
545
|
+
return; // the path names a successor now
|
|
546
|
+
const after = fstatSync(fd); // same fd, so the same inode
|
|
547
|
+
const recheck = Buffer.allocUnsafe(after.size);
|
|
548
|
+
readSync(fd, recheck, 0, after.size, 0);
|
|
549
|
+
if (parseRecord(recheck.toString("utf8"))?.token !== token)
|
|
550
|
+
return;
|
|
551
|
+
try {
|
|
552
|
+
unlinkSync(path);
|
|
553
|
+
}
|
|
554
|
+
catch (error) {
|
|
555
|
+
if (!isNotFound(error))
|
|
556
|
+
throw error;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
finally {
|
|
560
|
+
closeSync(fd);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
/* An UNCONDITIONAL lease removal used to live here. It is gone on purpose (F1): every reclaim
|
|
564
|
+
* now names the token it judged, so a slow reclaimer can no longer delete the lease of the
|
|
565
|
+
* successor that replaced the one it read. */
|
|
566
|
+
/**
|
|
567
|
+
* Load-modify-save one run record under the real cross-process lock.
|
|
568
|
+
*
|
|
569
|
+
* `stratum learn egress` used to build its own `Map`-of-promises lock, which is local to one
|
|
570
|
+
* CLI invocation and therefore excludes nothing — not another `stratum learn`, and certainly
|
|
571
|
+
* not a running engine (R3-3c).
|
|
572
|
+
*/
|
|
573
|
+
export async function lockedSave(store, runId, mutate, options = {}) {
|
|
574
|
+
const release = await acquireRunLock(store.root, runId, options);
|
|
575
|
+
try {
|
|
576
|
+
// The run lock alone is NOT enough to write a run record (F3). A pinned run's authority is
|
|
577
|
+
// the DRIVER'S in-memory copy, not the file: a CLI that takes the lock, loads from disk and
|
|
578
|
+
// saves is writing a snapshot the driver has already moved past — which is how an egress
|
|
579
|
+
// receipt marked `sent` comes back as `pending`. And a cancelled run is written exactly
|
|
580
|
+
// once (invariant 4b), by the settle and nothing else.
|
|
581
|
+
await assertNoForeignDriverLease(store.root, runId, options);
|
|
582
|
+
const run = await store.load(runId);
|
|
583
|
+
assertNotCancelled(run, runId);
|
|
584
|
+
const result = await mutate(run);
|
|
585
|
+
await store.save(run);
|
|
586
|
+
return result;
|
|
587
|
+
}
|
|
588
|
+
finally {
|
|
589
|
+
await release();
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* The READ half of `lockedSave`, and a separate function on purpose (F3): a snapshot taken
|
|
594
|
+
* through the mutating path re-saves the record it only meant to read, so a pure read
|
|
595
|
+
* rewrote the file, bumped its mtime, and could revert a concurrent driver's writes.
|
|
596
|
+
*
|
|
597
|
+
* A read is refused by neither the lease nor the cancelled guard — observing a run another
|
|
598
|
+
* process drives is always safe. It takes the lock so the snapshot is not torn.
|
|
599
|
+
*/
|
|
600
|
+
export async function lockedRead(store, runId, read, options = {}) {
|
|
601
|
+
const release = await acquireRunLock(store.root, runId, options);
|
|
602
|
+
try {
|
|
603
|
+
return await read(await store.load(runId));
|
|
604
|
+
}
|
|
605
|
+
finally {
|
|
606
|
+
await release();
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
function assertNotCancelled(run, runId) {
|
|
610
|
+
if (run.status !== "cancelled" && run.cancelRequested !== true)
|
|
611
|
+
return;
|
|
612
|
+
throw Object.assign(new Error(`run ${runId} is cancelled; no further record updates are accepted`), { code: "PERSIST_ON_CANCELLED_RUN" });
|
|
613
|
+
}
|
|
614
|
+
/** A lease naming a LIVE process means that process owns this run's in-memory copy: refuse.
|
|
615
|
+
* A provably dead owner's lease is reclaimed, because its memory is gone and disk is truth
|
|
616
|
+
* again. `unknown` is never reclaimed — the same rule the engine's own pin path follows. */
|
|
617
|
+
async function assertNoForeignDriverLease(root, runId, options) {
|
|
618
|
+
const lease = readDriverLeaseSync(root, runId);
|
|
619
|
+
if (lease === undefined)
|
|
620
|
+
return;
|
|
621
|
+
const identity = options.identity ?? processIdentity;
|
|
622
|
+
if (await identity(lease.pid, lease.startTime) === "dead") {
|
|
623
|
+
releaseDriverLeaseSync(root, runId, lease.token);
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
throw Object.assign(new Error(`run ${runId} is driven by pid ${lease.pid}; it cannot be written from this process`), { code: "DRIVER_LEASE_HELD", holderPid: lease.pid });
|
|
627
|
+
}
|
|
628
|
+
//# sourceMappingURL=run_lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run_lock.js","sourceRoot":"","sources":["../../src/engine/run_lock.ts"],"names":[],"mappings":"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"}
|