@namzu/sdk 20.2.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 +185 -0
- package/dist/manager/run/persistence.d.ts +2 -2
- package/dist/manager/run/persistence.d.ts.map +1 -1
- package/dist/manager/run/persistence.js +14 -5
- package/dist/manager/run/persistence.js.map +1 -1
- package/dist/public-runtime.d.ts +2 -1
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +6 -1
- 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/context.d.ts +2 -0
- package/dist/runtime/query/context.d.ts.map +1 -1
- package/dist/runtime/query/context.js +1 -0
- package/dist/runtime/query/context.js.map +1 -1
- package/dist/runtime/query/index.d.ts +32 -1
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +5 -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 +2 -1
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/index.js +2 -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/disk.d.ts +10 -8
- package/dist/store/run/disk.d.ts.map +1 -1
- package/dist/store/run/disk.js.map +1 -1
- 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/store/run/memory.d.ts +46 -0
- package/dist/store/run/memory.d.ts.map +1 -0
- package/dist/store/run/memory.js +104 -0
- package/dist/store/run/memory.js.map +1 -0
- package/dist/types/run/checkpoint-store.d.ts +178 -2
- package/dist/types/run/checkpoint-store.d.ts.map +1 -1
- package/dist/types/run/config.d.ts +12 -0
- package/dist/types/run/config.d.ts.map +1 -1
- package/dist/types/run/index.d.ts +1 -0
- package/dist/types/run/index.d.ts.map +1 -1
- package/dist/types/run/index.js +1 -0
- package/dist/types/run/index.js.map +1 -1
- package/dist/types/run/store.d.ts +103 -0
- package/dist/types/run/store.d.ts.map +1 -0
- package/dist/types/run/store.js +30 -0
- package/dist/types/run/store.js.map +1 -0
- package/package.json +1 -1
- package/src/manager/run/persistence.ts +17 -7
- package/src/public-runtime.ts +6 -0
- package/src/runtime/query/checkpoint.ts +42 -5
- package/src/runtime/query/context.ts +3 -0
- package/src/runtime/query/index.ts +39 -1
- package/src/runtime/query/resume-run.ts +12 -2
- package/src/store/index.ts +5 -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/disk.ts +10 -8
- package/src/store/run/listing.ts +116 -1
- package/src/store/run/memory.ts +121 -0
- package/src/types/run/checkpoint-store.ts +189 -2
- package/src/types/run/config.ts +13 -0
- package/src/types/run/index.ts +1 -0
- package/src/types/run/store.ts +112 -0
|
@@ -3,11 +3,20 @@ import type {
|
|
|
3
3
|
CheckpointListingScope,
|
|
4
4
|
CheckpointRunScope,
|
|
5
5
|
CheckpointStore,
|
|
6
|
+
ClaimFence,
|
|
7
|
+
ClaimRunOptions,
|
|
6
8
|
DurableRunEntry,
|
|
7
9
|
DurableRunPage,
|
|
8
10
|
ListDurableRunsOptions,
|
|
11
|
+
RunClaim,
|
|
9
12
|
} from '../../types/run/checkpoint-store.js'
|
|
10
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
assertContiguousListingScope,
|
|
15
|
+
fencedOut,
|
|
16
|
+
paginateDurableRuns,
|
|
17
|
+
toClaimSummary,
|
|
18
|
+
toDurableRunEntry,
|
|
19
|
+
} from './listing.js'
|
|
11
20
|
|
|
12
21
|
/**
|
|
13
22
|
* Process-local {@link CheckpointStore}, keyed by the full five-layer scope.
|
|
@@ -35,7 +44,111 @@ export class InMemoryCheckpointStore implements CheckpointStore {
|
|
|
35
44
|
return [scope.tenantId, scope.projectId, scope.sessionId, scope.runId].join('/')
|
|
36
45
|
}
|
|
37
46
|
|
|
38
|
-
|
|
47
|
+
/** `tenant/project/session/run` → the run's current holding, if any. */
|
|
48
|
+
private readonly claims = new Map<string, RunClaim>()
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The highest fence ever issued per run, kept separately from the claim.
|
|
52
|
+
*
|
|
53
|
+
* The claim is removed on release; this is not. That separation is the
|
|
54
|
+
* whole point: the first version deleted the claim and then computed the
|
|
55
|
+
* next fence from it, so releasing rewound the counter to 1 and a worker
|
|
56
|
+
* stalled at fence 1 could write beside a new holder also at fence 1 —
|
|
57
|
+
* and the documented `finally { releaseRun() }` did it on every pass.
|
|
58
|
+
*
|
|
59
|
+
* The disk store gets this property from file names that persist. In
|
|
60
|
+
* memory the equivalent is a high-water mark nothing clears, and the two
|
|
61
|
+
* must agree, because this class is what a host reads when writing a
|
|
62
|
+
* backend of its own.
|
|
63
|
+
*/
|
|
64
|
+
private readonly highWater = new Map<string, ClaimFence>()
|
|
65
|
+
|
|
66
|
+
async claimRun(scope: CheckpointRunScope, options: ClaimRunOptions): Promise<RunClaim | null> {
|
|
67
|
+
const key = this.key(scope)
|
|
68
|
+
const now = options.now ?? Date.now()
|
|
69
|
+
const held = this.claims.get(key)
|
|
70
|
+
|
|
71
|
+
// Held by somebody else and still live. Not an error: two readers on
|
|
72
|
+
// one queue is the ordinary case.
|
|
73
|
+
if (held && now < held.expiresAt && held.holder !== options.holder) return null
|
|
74
|
+
|
|
75
|
+
// A reclaim of an expired holding and a renewal by the current holder
|
|
76
|
+
// are the same write. The fence advances either way, so a previous
|
|
77
|
+
// holder that wakes up is fenced out in both cases — a renewal that
|
|
78
|
+
// kept the fence would leave a stalled twin able to write.
|
|
79
|
+
// Counted from the high-water mark, never from the live claim. A
|
|
80
|
+
// released run has no claim, and computing from that absence is what
|
|
81
|
+
// rewound the counter to 1 on every release.
|
|
82
|
+
const fence = (this.highWater.get(key) ?? 0) + 1
|
|
83
|
+
const claim: RunClaim = { holder: options.holder, fence, expiresAt: now + options.ttlMs }
|
|
84
|
+
this.highWater.set(key, fence)
|
|
85
|
+
this.claims.set(key, claim)
|
|
86
|
+
return claim
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async releaseRun(scope: CheckpointRunScope, fence: ClaimFence): Promise<void> {
|
|
90
|
+
const key = this.key(scope)
|
|
91
|
+
const held = this.claims.get(key)
|
|
92
|
+
// A stale fence releases nothing. A worker that stalled past its lease
|
|
93
|
+
// must not be able to hand away a run somebody else now holds.
|
|
94
|
+
//
|
|
95
|
+
// The high-water mark deliberately survives this. Dropping the claim
|
|
96
|
+
// returns the run to the queue; forgetting the number it reached would
|
|
97
|
+
// re-issue a fence a stalled worker still believes it holds.
|
|
98
|
+
if (!held || held.fence !== fence) return
|
|
99
|
+
this.claims.delete(key)
|
|
100
|
+
// And the counter steps past the released holding, because the disk
|
|
101
|
+
// store's release does: it appends a TOMBSTONE at `fence + 1`, which
|
|
102
|
+
// raises the maximum name and therefore the fence every later write is
|
|
103
|
+
// checked against. Without this step a holder that released could still
|
|
104
|
+
// write with the fence it just gave up — refused on disk, accepted here.
|
|
105
|
+
//
|
|
106
|
+
// The consequence is visible in the numbering: a released run's next
|
|
107
|
+
// claim is `fence + 2` in both stores, because the tombstone consumed
|
|
108
|
+
// one. That is parity, not an off-by-one.
|
|
109
|
+
this.highWater.set(key, fence + 1)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async writeCheckpoint(
|
|
113
|
+
scope: CheckpointRunScope,
|
|
114
|
+
checkpoint: IterationCheckpoint,
|
|
115
|
+
fence?: ClaimFence,
|
|
116
|
+
): Promise<void> {
|
|
117
|
+
const key = this.key(scope)
|
|
118
|
+
// An unfenced write is allowed even on a claimed run: a host adopting
|
|
119
|
+
// claims on one worker must not break the workers that have not
|
|
120
|
+
// adopted them. A fenced write is checked, and that check is what
|
|
121
|
+
// makes the lease real.
|
|
122
|
+
if (fence !== undefined) {
|
|
123
|
+
// Against the HIGH-WATER MARK, not the live claim, and the difference
|
|
124
|
+
// is a silent loss rather than a duplicate.
|
|
125
|
+
//
|
|
126
|
+
// `releaseRun` deletes the claim. Reading `claims` here meant that
|
|
127
|
+
// between a release and the next take there was no holding to
|
|
128
|
+
// compare against, so `held` was `undefined` and every fence was
|
|
129
|
+
// accepted however stale. w1 stalls at fence 1; w2 reclaims at 2,
|
|
130
|
+
// finishes the work, releases cleanly; w1 wakes and writes with
|
|
131
|
+
// fence 1 — accepted — and its checkpoint carries a fresh
|
|
132
|
+
// `createdAt`, so it sorts newest and the next resume restores w1's
|
|
133
|
+
// stale history. w2's completed work is gone, with no error
|
|
134
|
+
// anywhere.
|
|
135
|
+
//
|
|
136
|
+
// The disk store refuses that write, because `currentFence` reads
|
|
137
|
+
// file names and the release tombstone raised the maximum. The
|
|
138
|
+
// minting side here already counted from the high-water mark; only
|
|
139
|
+
// the enforcement side was left reading the claim, so the two
|
|
140
|
+
// shipped stores disagreed at the one point that decides whether a
|
|
141
|
+
// lease is real.
|
|
142
|
+
const current = this.highWater.get(key) ?? 0
|
|
143
|
+
if (fence < current) throw fencedOut(scope, fence, current)
|
|
144
|
+
}
|
|
145
|
+
return this.writeUnchecked(scope, checkpoint)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private async writeUnchecked(
|
|
149
|
+
scope: CheckpointRunScope,
|
|
150
|
+
checkpoint: IterationCheckpoint,
|
|
151
|
+
): Promise<void> {
|
|
39
152
|
const key = this.key(scope)
|
|
40
153
|
let run = this.runs.get(key)
|
|
41
154
|
if (!run) {
|
|
@@ -93,7 +206,9 @@ export class InMemoryCheckpointStore implements CheckpointStore {
|
|
|
93
206
|
if (scope.sessionId !== undefined && runScope.sessionId !== scope.sessionId) continue
|
|
94
207
|
|
|
95
208
|
const entry = toDurableRunEntry(runScope, [...checkpoints.values()], now)
|
|
96
|
-
if (entry)
|
|
209
|
+
if (!entry) continue
|
|
210
|
+
const claim = this.claims.get(key)
|
|
211
|
+
entries.push(claim ? { ...entry, claim: toClaimSummary(claim, now) } : entry)
|
|
97
212
|
}
|
|
98
213
|
|
|
99
214
|
return paginateDurableRuns(entries, options)
|