@sixb/core 0.1.14 → 0.1.15
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/actions/index.js +21 -21
- package/dist/agents/index.js +33 -33
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/streams/index.js +2 -2
- package/dist/agents/streams/internal.js +2 -2
- package/dist/auth/index.js +3 -3
- package/dist/authorization/index.js +2 -2
- package/dist/bootstrap/index.js +36 -36
- package/dist/{chunk-dv4ct8mp.js → chunk-1jvq82rp.js} +3 -3
- package/dist/{chunk-16jenmzw.js → chunk-2tz5vnvz.js} +28 -28
- package/dist/{chunk-16jenmzw.js.map → chunk-2tz5vnvz.js.map} +1 -1
- package/dist/{chunk-728fp090.js → chunk-3c90jvfj.js} +2 -2
- package/dist/{chunk-vn3yh259.js → chunk-4gmng3vf.js} +1 -1
- package/dist/{chunk-hxgj4kzd.js → chunk-4jph1t3v.js} +7 -7
- package/dist/{chunk-znw0zvy4.js → chunk-4wn7bxxa.js} +25 -7
- package/dist/chunk-4wn7bxxa.js.map +12 -0
- package/dist/{chunk-3qpcrcj8.js → chunk-6aawea28.js} +2 -4
- package/dist/chunk-6aawea28.js.map +10 -0
- package/dist/{chunk-rzkr70gr.js → chunk-6fsr7y93.js} +5 -1
- package/dist/{chunk-rzkr70gr.js.map → chunk-6fsr7y93.js.map} +3 -3
- package/dist/{chunk-y487ywrv.js → chunk-6q7c68yx.js} +4 -4
- package/dist/{chunk-ypj2wqtj.js → chunk-70jmg2zx.js} +2 -2
- package/dist/{chunk-wn7mznxc.js → chunk-7jc21943.js} +3 -3
- package/dist/{chunk-rzcjp5m2.js → chunk-8fjmqjyx.js} +1 -1
- package/dist/{chunk-m8zkvep1.js → chunk-8w27xf9e.js} +1 -1
- package/dist/{chunk-z9h698s8.js → chunk-a4g5yjm0.js} +1 -1
- package/dist/{chunk-4ypbe95r.js → chunk-adbve9zt.js} +1 -1
- package/dist/{chunk-hj0n3c2p.js → chunk-dqkd3kdj.js} +40 -22
- package/dist/{chunk-hj0n3c2p.js.map → chunk-dqkd3kdj.js.map} +3 -3
- package/dist/{chunk-wnxabtn4.js → chunk-ex8s8wsk.js} +6 -6
- package/dist/{chunk-4tnrzqqh.js → chunk-fj1dvp37.js} +1 -1
- package/dist/{chunk-3fpgxpsq.js → chunk-fjw417jf.js} +127 -69
- package/dist/{chunk-3fpgxpsq.js.map → chunk-fjw417jf.js.map} +4 -4
- package/dist/{chunk-ht4a7mjg.js → chunk-g4rbm4a6.js} +42 -8
- package/dist/{chunk-ht4a7mjg.js.map → chunk-g4rbm4a6.js.map} +3 -3
- package/dist/{chunk-qma80ych.js → chunk-hn2r7z41.js} +3 -2
- package/dist/chunk-hn2r7z41.js.map +11 -0
- package/dist/{chunk-bdhfn0ww.js → chunk-kkqwzy6h.js} +1 -1
- package/dist/{chunk-ww185kt6.js → chunk-mmac1mp6.js} +2 -2
- package/dist/{chunk-jfdggg1p.js → chunk-n5t4mq6z.js} +5 -5
- package/dist/{chunk-3a338wrp.js → chunk-p0nbkys4.js} +1 -1
- package/dist/{chunk-kxea54yg.js → chunk-py2dw8cw.js} +3 -3
- package/dist/{chunk-e2c5yxfe.js → chunk-q3grgzp1.js} +2 -2
- package/dist/{chunk-6rzb1g6h.js → chunk-qne45xqq.js} +1 -1
- package/dist/{chunk-cbn96bve.js → chunk-r5sc1d2b.js} +2 -2
- package/dist/{chunk-y6vnvayj.js → chunk-vjcd8d27.js} +1 -1
- package/dist/{chunk-ma1mhh7w.js → chunk-wd34wnsy.js} +6 -6
- package/dist/{chunk-pvgpckm1.js → chunk-wem5vqjw.js} +7 -7
- package/dist/{chunk-gct3m1da.js → chunk-xr7y35yj.js} +3 -3
- package/dist/{chunk-q2xghyft.js → chunk-y0zmae99.js} +5 -5
- package/dist/{chunk-xgptywp0.js → chunk-y2aq6ag9.js} +1 -1
- package/dist/{chunk-yvf2jqj8.js → chunk-yfppyaty.js} +1 -1
- package/dist/{chunk-00pfcqh4.js → chunk-z708gemf.js} +129 -70
- package/dist/{chunk-00pfcqh4.js.map → chunk-z708gemf.js.map} +5 -5
- package/dist/deploy/define.d.ts +4 -0
- package/dist/deploy/define.d.ts.map +1 -0
- package/dist/deploy/index.d.ts +3 -0
- package/dist/deploy/index.d.ts.map +1 -0
- package/dist/deploy/index.js +10 -0
- package/dist/deploy/index.js.map +10 -0
- package/dist/deploy/internal.d.ts +3 -0
- package/dist/deploy/internal.d.ts.map +1 -0
- package/dist/deploy/internal.js +244 -0
- package/dist/deploy/internal.js.map +11 -0
- package/dist/deploy/services.d.ts +8 -0
- package/dist/deploy/services.d.ts.map +1 -0
- package/dist/deploy/types.d.ts +309 -0
- package/dist/deploy/types.d.ts.map +1 -0
- package/dist/deploy/validation.d.ts +7 -0
- package/dist/deploy/validation.d.ts.map +1 -0
- package/dist/error-reporting/internal.js +3 -3
- package/dist/errors/catalog.d.ts +4 -0
- package/dist/errors/catalog.d.ts.map +1 -1
- package/dist/errors/internal.js +1 -1
- package/dist/events/index.js +3 -3
- package/dist/execution/agent.js +32 -32
- package/dist/execution/primitive.js +28 -28
- package/dist/execution/request.js +30 -30
- package/dist/execution/request.js.map +1 -1
- package/dist/index.js +48 -48
- package/dist/index.js.map +1 -1
- package/dist/maintenance/ontology-maintenance.d.ts +2 -0
- package/dist/maintenance/ontology-maintenance.d.ts.map +1 -1
- package/dist/maintenance/types.d.ts +8 -2
- package/dist/maintenance/types.d.ts.map +1 -1
- package/dist/materialization/dataset-watermark.d.ts +6 -1
- package/dist/materialization/dataset-watermark.d.ts.map +1 -1
- package/dist/materialization/index.js +1 -1
- package/dist/materialization/model.d.ts +6 -0
- package/dist/materialization/model.d.ts.map +1 -1
- package/dist/materializer/index.js +17 -17
- package/dist/materializer/index.js.map +1 -1
- package/dist/materializer/projections/finish-run.d.ts.map +1 -1
- package/dist/materializer/projections/replace.d.ts.map +1 -1
- package/dist/materializer/projections/source-materialization.d.ts +6 -2
- package/dist/materializer/projections/source-materialization.d.ts.map +1 -1
- package/dist/models/execution/index.js +13 -13
- package/dist/objects/index.js +20 -20
- package/dist/objects/query/index.js +14 -14
- package/dist/pipelines/index.js +18 -18
- package/dist/projections/internal.js +15 -15
- package/dist/projections/types.d.ts +1 -1
- package/dist/projections/types.d.ts.map +1 -1
- package/dist/queues/in-memory.d.ts +1 -1
- package/dist/queues/in-memory.d.ts.map +1 -1
- package/dist/runtime/internal.js +18 -18
- package/dist/shares/internal.js +13 -13
- package/dist/shares/internal.js.map +1 -1
- package/dist/storage/action-runs/failure.js +2 -2
- package/dist/storage/ai-limits/enforcement.js +2 -2
- package/dist/storage/connector-connections/provider.js +1 -1
- package/dist/storage/file-upload-sessions/provider.js +4 -4
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +28 -28
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/internal.js +1 -1
- package/dist/storage/ontology/in-memory/sources.d.ts +6 -3
- package/dist/storage/ontology/in-memory/sources.d.ts.map +1 -1
- package/dist/storage/ontology/index.d.ts +1 -1
- package/dist/storage/ontology/index.d.ts.map +1 -1
- package/dist/storage/ontology/provider.d.ts +16 -3
- package/dist/storage/ontology/provider.d.ts.map +1 -1
- package/dist/storage/ontology/provider.js +8 -4
- package/dist/storage/ontology/provider.js.map +1 -1
- package/dist/storage/ontology/sources.d.ts +36 -8
- package/dist/storage/ontology/sources.d.ts.map +1 -1
- package/dist/storage/projection-runs/in-memory.d.ts +3 -1
- package/dist/storage/projection-runs/in-memory.d.ts.map +1 -1
- package/dist/storage/projection-runs/index.d.ts +1 -1
- package/dist/storage/projection-runs/index.d.ts.map +1 -1
- package/dist/storage/projection-runs/lifecycle.d.ts +12 -0
- package/dist/storage/projection-runs/lifecycle.d.ts.map +1 -1
- package/dist/storage/projection-runs/provider.js +10 -4
- package/dist/storage/projection-runs/provider.js.map +1 -1
- package/dist/storage/projection-runs/types.d.ts +32 -1
- package/dist/storage/projection-runs/types.d.ts.map +1 -1
- package/dist/storage/workflow-runs/provider.js +4 -4
- package/dist/syncs/index.js +19 -19
- package/dist/testing/index.js +296 -54
- package/dist/testing/index.js.map +4 -4
- package/dist/testing/ontology-storage-contract.d.ts.map +1 -1
- package/dist/testing/projection-run-storage-contract.d.ts.map +1 -1
- package/dist/webhooks/internal.js +14 -14
- package/dist/webhooks/internal.js.map +1 -1
- package/dist/workers/index.js +1 -1
- package/dist/workflows/index.js +33 -33
- package/package.json +13 -1
- package/src/deploy/define.ts +6 -0
- package/src/deploy/index.ts +41 -0
- package/src/deploy/internal.ts +7 -0
- package/src/deploy/services.ts +51 -0
- package/src/deploy/types.ts +338 -0
- package/src/deploy/validation.ts +261 -0
- package/src/errors/catalog.ts +4 -0
- package/src/maintenance/ontology-maintenance.ts +32 -2
- package/src/maintenance/types.ts +8 -2
- package/src/materialization/dataset-watermark.ts +6 -4
- package/src/materialization/model.ts +7 -0
- package/src/materializer/README.md +26 -6
- package/src/materializer/projections/finish-run.ts +129 -26
- package/src/materializer/projections/replace.ts +67 -49
- package/src/materializer/projections/source-materialization.ts +11 -6
- package/src/projections/types.ts +1 -0
- package/src/storage/index.ts +6 -1
- package/src/storage/ontology/in-memory/sources.ts +119 -55
- package/src/storage/ontology/index.ts +4 -1
- package/src/storage/ontology/provider.ts +64 -7
- package/src/storage/ontology/sources.ts +42 -8
- package/src/storage/projection-runs/in-memory.ts +41 -2
- package/src/storage/projection-runs/index.ts +2 -0
- package/src/storage/projection-runs/lifecycle.ts +61 -4
- package/src/storage/projection-runs/types.ts +41 -1
- package/src/testing/ontology-storage-contract.ts +141 -15
- package/src/testing/projection-run-storage-contract.ts +174 -1
- package/dist/chunk-3qpcrcj8.js.map +0 -10
- package/dist/chunk-qma80ych.js.map +0 -11
- package/dist/chunk-znw0zvy4.js.map +0 -12
- /package/dist/{chunk-dv4ct8mp.js.map → chunk-1jvq82rp.js.map} +0 -0
- /package/dist/{chunk-728fp090.js.map → chunk-3c90jvfj.js.map} +0 -0
- /package/dist/{chunk-vn3yh259.js.map → chunk-4gmng3vf.js.map} +0 -0
- /package/dist/{chunk-hxgj4kzd.js.map → chunk-4jph1t3v.js.map} +0 -0
- /package/dist/{chunk-y487ywrv.js.map → chunk-6q7c68yx.js.map} +0 -0
- /package/dist/{chunk-ypj2wqtj.js.map → chunk-70jmg2zx.js.map} +0 -0
- /package/dist/{chunk-wn7mznxc.js.map → chunk-7jc21943.js.map} +0 -0
- /package/dist/{chunk-rzcjp5m2.js.map → chunk-8fjmqjyx.js.map} +0 -0
- /package/dist/{chunk-m8zkvep1.js.map → chunk-8w27xf9e.js.map} +0 -0
- /package/dist/{chunk-z9h698s8.js.map → chunk-a4g5yjm0.js.map} +0 -0
- /package/dist/{chunk-4ypbe95r.js.map → chunk-adbve9zt.js.map} +0 -0
- /package/dist/{chunk-wnxabtn4.js.map → chunk-ex8s8wsk.js.map} +0 -0
- /package/dist/{chunk-4tnrzqqh.js.map → chunk-fj1dvp37.js.map} +0 -0
- /package/dist/{chunk-bdhfn0ww.js.map → chunk-kkqwzy6h.js.map} +0 -0
- /package/dist/{chunk-ww185kt6.js.map → chunk-mmac1mp6.js.map} +0 -0
- /package/dist/{chunk-jfdggg1p.js.map → chunk-n5t4mq6z.js.map} +0 -0
- /package/dist/{chunk-3a338wrp.js.map → chunk-p0nbkys4.js.map} +0 -0
- /package/dist/{chunk-kxea54yg.js.map → chunk-py2dw8cw.js.map} +0 -0
- /package/dist/{chunk-e2c5yxfe.js.map → chunk-q3grgzp1.js.map} +0 -0
- /package/dist/{chunk-6rzb1g6h.js.map → chunk-qne45xqq.js.map} +0 -0
- /package/dist/{chunk-cbn96bve.js.map → chunk-r5sc1d2b.js.map} +0 -0
- /package/dist/{chunk-y6vnvayj.js.map → chunk-vjcd8d27.js.map} +0 -0
- /package/dist/{chunk-ma1mhh7w.js.map → chunk-wd34wnsy.js.map} +0 -0
- /package/dist/{chunk-pvgpckm1.js.map → chunk-wem5vqjw.js.map} +0 -0
- /package/dist/{chunk-gct3m1da.js.map → chunk-xr7y35yj.js.map} +0 -0
- /package/dist/{chunk-q2xghyft.js.map → chunk-y0zmae99.js.map} +0 -0
- /package/dist/{chunk-xgptywp0.js.map → chunk-y2aq6ag9.js.map} +0 -0
- /package/dist/{chunk-yvf2jqj8.js.map → chunk-yfppyaty.js.map} +0 -0
|
@@ -16,8 +16,11 @@ import {
|
|
|
16
16
|
sourceStageRoots,
|
|
17
17
|
} from "../provider"
|
|
18
18
|
import type {
|
|
19
|
+
AbandonRunSourceMaterializationInput,
|
|
19
20
|
AbandonSourceMaterializationCandidateInput,
|
|
20
21
|
AbandonSourceMaterializationInput,
|
|
22
|
+
AdoptedSourceMaterialization,
|
|
23
|
+
AdoptSourceMaterializationInput,
|
|
21
24
|
AssertSourceMaterializationExecution,
|
|
22
25
|
BeginSourceMaterializationInput,
|
|
23
26
|
CleanupTerminalSourceMaterializationsInput,
|
|
@@ -26,7 +29,7 @@ import type {
|
|
|
26
29
|
MarkSourceMaterializationReadyInput,
|
|
27
30
|
OntologySourceRecord,
|
|
28
31
|
OntologySourceStorage,
|
|
29
|
-
|
|
32
|
+
PurgeAbandonedSourceMaterializationsInput,
|
|
30
33
|
StageSourceAssertion,
|
|
31
34
|
StageSourceRowsInput,
|
|
32
35
|
StageSourceRowsResult,
|
|
@@ -38,6 +41,7 @@ import {
|
|
|
38
41
|
assertNonblank,
|
|
39
42
|
type InMemoryOntologyState,
|
|
40
43
|
type InMemorySourceMaterialization,
|
|
44
|
+
type InMemorySourceRoot,
|
|
41
45
|
insertBounded,
|
|
42
46
|
sourceMaterializationKey,
|
|
43
47
|
sourceMaterializationRecord,
|
|
@@ -78,7 +82,7 @@ export class InMemoryOntologySourceStorage implements OntologySourceStorage {
|
|
|
78
82
|
)
|
|
79
83
|
if (nonterminal.length > 0) {
|
|
80
84
|
throw sourceConflict(
|
|
81
|
-
`Projection run '${input.execution.projectionRunId}' already has a nonterminal source materialization;
|
|
85
|
+
`Projection run '${input.execution.projectionRunId}' already has a nonterminal source materialization; adopt it before beginning another.`
|
|
82
86
|
)
|
|
83
87
|
}
|
|
84
88
|
|
|
@@ -191,8 +195,42 @@ export class InMemoryOntologySourceStorage implements OntologySourceStorage {
|
|
|
191
195
|
})
|
|
192
196
|
}
|
|
193
197
|
|
|
198
|
+
async adopt(
|
|
199
|
+
input: AdoptSourceMaterializationInput
|
|
200
|
+
): Promise<AdoptedSourceMaterialization | null> {
|
|
201
|
+
return this.runRootOperation(async () => {
|
|
202
|
+
assertProjectAndSource(input)
|
|
203
|
+
assertSourceExecutionIdentity(input.execution.projectionRunId, input.execution.executionToken)
|
|
204
|
+
assertCanonicalTimestamp(input.adoptedAt, "Source adoptedAt")
|
|
205
|
+
await this.assertCurrentExecution(input)
|
|
206
|
+
const [candidate, ...others] = this.findRunCandidates(
|
|
207
|
+
input.projectId,
|
|
208
|
+
input.source.projectionId,
|
|
209
|
+
input.execution.projectionRunId
|
|
210
|
+
)
|
|
211
|
+
if (!candidate) return null
|
|
212
|
+
if (others.length > 0) throw multipleRunCandidates(input.execution.projectionRunId)
|
|
213
|
+
const [key, materialization] = candidate
|
|
214
|
+
const adopted: InMemorySourceMaterialization = {
|
|
215
|
+
...materialization,
|
|
216
|
+
executionToken: input.execution.executionToken,
|
|
217
|
+
// Workers' clocks differ; adoption never moves the record back in time.
|
|
218
|
+
updatedAt:
|
|
219
|
+
input.adoptedAt > materialization.updatedAt ? input.adoptedAt : materialization.updatedAt,
|
|
220
|
+
}
|
|
221
|
+
this.state.sourceMaterializations.set(key, adopted)
|
|
222
|
+
return {
|
|
223
|
+
record: sourceMaterializationRecord(adopted),
|
|
224
|
+
resumeStagingOrdinal:
|
|
225
|
+
adopted.status === "ready"
|
|
226
|
+
? (adopted.rootCount ?? 0)
|
|
227
|
+
: Math.max(0, ...adopted.ordinalRoots.keys()),
|
|
228
|
+
}
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
|
|
194
232
|
async abandon(input: AbandonSourceMaterializationCandidateInput): Promise<OntologySourceRecord>
|
|
195
|
-
async abandon(input:
|
|
233
|
+
async abandon(input: AbandonRunSourceMaterializationInput): Promise<OntologySourceRecord | null>
|
|
196
234
|
async abandon(input: AbandonSourceMaterializationInput): Promise<OntologySourceRecord | null> {
|
|
197
235
|
return this.runRootOperation(async () => {
|
|
198
236
|
assertProjectAndSource(input)
|
|
@@ -201,7 +239,7 @@ export class InMemoryOntologySourceStorage implements OntologySourceStorage {
|
|
|
201
239
|
await this.assertCurrentExecution(input)
|
|
202
240
|
return input.kind === "candidate"
|
|
203
241
|
? this.abandonCandidate(input)
|
|
204
|
-
: this.
|
|
242
|
+
: this.abandonRunCandidate(input)
|
|
205
243
|
})
|
|
206
244
|
}
|
|
207
245
|
|
|
@@ -211,18 +249,14 @@ export class InMemoryOntologySourceStorage implements OntologySourceStorage {
|
|
|
211
249
|
return this.runRootOperation(() => {
|
|
212
250
|
assertNonblank(input.projectId, "Terminal source cleanup project id")
|
|
213
251
|
assertCanonicalTimestamp(input.terminalBefore, "Terminal source cleanup cutoff")
|
|
214
|
-
|
|
215
|
-
throw new MaterializationValidationError(
|
|
216
|
-
"Terminal source cleanup limit must be a positive safe integer."
|
|
217
|
-
)
|
|
218
|
-
}
|
|
252
|
+
assertCleanupLimit(input.limit)
|
|
219
253
|
|
|
220
254
|
const candidates: [string, InMemorySourceMaterialization][] = []
|
|
221
255
|
for (const entry of this.state.sourceMaterializations.entries()) {
|
|
222
256
|
const materialization = entry[1]
|
|
223
257
|
if (
|
|
224
258
|
materialization.projectId !== input.projectId ||
|
|
225
|
-
|
|
259
|
+
materialization.status !== "superseded" ||
|
|
226
260
|
materialization.terminalAt === null ||
|
|
227
261
|
materialization.terminalAt >= input.terminalBefore
|
|
228
262
|
) {
|
|
@@ -239,42 +273,69 @@ export class InMemoryOntologySourceStorage implements OntologySourceStorage {
|
|
|
239
273
|
insertBounded(candidates, entry, input.limit, compareTerminalMaterializations)
|
|
240
274
|
}
|
|
241
275
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
276
|
+
return this.deleteRetiredRoots(
|
|
277
|
+
candidates,
|
|
278
|
+
input.limit,
|
|
279
|
+
(root) => !root.active && root.retiredAt !== null && root.retiredAt < input.terminalBefore
|
|
280
|
+
)
|
|
281
|
+
})
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async purgeAbandoned(
|
|
285
|
+
input: PurgeAbandonedSourceMaterializationsInput
|
|
286
|
+
): Promise<CleanupTerminalSourceMaterializationsResult> {
|
|
287
|
+
return this.runRootOperation(() => {
|
|
288
|
+
assertNonblank(input.projectId, "Abandoned source purge project id")
|
|
289
|
+
assertCleanupLimit(input.limit)
|
|
290
|
+
const abandoned = [...this.state.sourceMaterializations.entries()]
|
|
291
|
+
.filter(
|
|
292
|
+
([, materialization]) =>
|
|
293
|
+
materialization.projectId === input.projectId && materialization.status === "abandoned"
|
|
294
|
+
)
|
|
295
|
+
.sort(compareTerminalMaterializations)
|
|
296
|
+
return this.deleteRetiredRoots(abandoned, input.limit, () => true)
|
|
297
|
+
})
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private deleteRetiredRoots(
|
|
301
|
+
candidates: readonly [string, InMemorySourceMaterialization][],
|
|
302
|
+
limit: number,
|
|
303
|
+
retired: (root: InMemorySourceRoot) => boolean
|
|
304
|
+
): CleanupTerminalSourceMaterializationsResult {
|
|
305
|
+
let remaining = limit
|
|
306
|
+
let rowsDeleted = 0
|
|
307
|
+
let materializationsDeleted = 0
|
|
308
|
+
for (const [key, candidate] of candidates) {
|
|
309
|
+
if (remaining === 0) break
|
|
310
|
+
const current = this.state.sourceMaterializations.get(key)
|
|
311
|
+
if (current !== candidate) continue
|
|
312
|
+
current.rootOrdinals.clear()
|
|
313
|
+
current.ordinalRoots.clear()
|
|
314
|
+
for (const [rootKey, root] of current.roots) {
|
|
246
315
|
if (remaining === 0) break
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
current.rootOrdinals.clear()
|
|
250
|
-
current.ordinalRoots.clear()
|
|
251
|
-
for (const [rootKey, root] of current.roots) {
|
|
316
|
+
if (!retired(root)) continue
|
|
317
|
+
for (const entityKey of root.entityKeys) {
|
|
252
318
|
if (remaining === 0) break
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
current.rowsByEntity.delete(entityKey)
|
|
258
|
-
root.entityKeys.delete(entityKey)
|
|
259
|
-
rowsDeleted += 1
|
|
260
|
-
remaining -= 1
|
|
261
|
-
}
|
|
262
|
-
if (root.entityKeys.size === 0 && remaining > 0) {
|
|
263
|
-
current.roots.delete(rootKey)
|
|
264
|
-
current.rootOrdinals.delete(rootKey)
|
|
265
|
-
current.ordinalRoots.delete(root.stagingOrdinal)
|
|
266
|
-
rowsDeleted += 1
|
|
267
|
-
remaining -= 1
|
|
268
|
-
}
|
|
319
|
+
current.rowsByEntity.delete(entityKey)
|
|
320
|
+
root.entityKeys.delete(entityKey)
|
|
321
|
+
rowsDeleted += 1
|
|
322
|
+
remaining -= 1
|
|
269
323
|
}
|
|
270
|
-
if (
|
|
271
|
-
|
|
272
|
-
|
|
324
|
+
if (root.entityKeys.size === 0 && remaining > 0) {
|
|
325
|
+
current.roots.delete(rootKey)
|
|
326
|
+
current.rootOrdinals.delete(rootKey)
|
|
327
|
+
current.ordinalRoots.delete(root.stagingOrdinal)
|
|
328
|
+
rowsDeleted += 1
|
|
273
329
|
remaining -= 1
|
|
274
330
|
}
|
|
275
331
|
}
|
|
276
|
-
|
|
277
|
-
|
|
332
|
+
if (current.roots.size === 0 && current.rowsByEntity.size === 0 && remaining > 0) {
|
|
333
|
+
this.state.sourceMaterializations.delete(key)
|
|
334
|
+
materializationsDeleted += 1
|
|
335
|
+
remaining -= 1
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return { rowsDeleted, materializationsDeleted }
|
|
278
339
|
}
|
|
279
340
|
|
|
280
341
|
async summarizeTerminal(
|
|
@@ -356,26 +417,15 @@ export class InMemoryOntologySourceStorage implements OntologySourceStorage {
|
|
|
356
417
|
return this.transitionToAbandoned(materialization, input.abandonedAt)
|
|
357
418
|
}
|
|
358
419
|
|
|
359
|
-
private
|
|
420
|
+
private abandonRunCandidate(
|
|
421
|
+
input: AbandonRunSourceMaterializationInput
|
|
422
|
+
): OntologySourceRecord | null {
|
|
360
423
|
const candidates = this.findRunCandidates(
|
|
361
424
|
input.projectId,
|
|
362
425
|
input.source.projectionId,
|
|
363
426
|
input.execution.projectionRunId
|
|
364
427
|
)
|
|
365
|
-
if (
|
|
366
|
-
candidates.some(
|
|
367
|
-
([, materialization]) => materialization.executionToken === input.execution.executionToken
|
|
368
|
-
)
|
|
369
|
-
) {
|
|
370
|
-
throw sourceConflict(
|
|
371
|
-
`Projection run '${input.execution.projectionRunId}' already has a source materialization owned by the current execution.`
|
|
372
|
-
)
|
|
373
|
-
}
|
|
374
|
-
if (candidates.length > 1) {
|
|
375
|
-
throw sourceConflict(
|
|
376
|
-
`Projection run '${input.execution.projectionRunId}' has multiple nonterminal source materializations.`
|
|
377
|
-
)
|
|
378
|
-
}
|
|
428
|
+
if (candidates.length > 1) throw multipleRunCandidates(input.execution.projectionRunId)
|
|
379
429
|
const candidate = candidates[0]?.[1]
|
|
380
430
|
return candidate ? this.transitionToAbandoned(candidate, input.abandonedAt) : null
|
|
381
431
|
}
|
|
@@ -609,6 +659,20 @@ function assertNotBefore(
|
|
|
609
659
|
}
|
|
610
660
|
}
|
|
611
661
|
|
|
662
|
+
function assertCleanupLimit(limit: number): void {
|
|
663
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) {
|
|
664
|
+
throw new MaterializationValidationError(
|
|
665
|
+
"Terminal source cleanup limit must be a positive safe integer."
|
|
666
|
+
)
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
function multipleRunCandidates(projectionRunId: string) {
|
|
671
|
+
return sourceConflict(
|
|
672
|
+
`Projection run '${projectionRunId}' has multiple nonterminal source materializations.`
|
|
673
|
+
)
|
|
674
|
+
}
|
|
675
|
+
|
|
612
676
|
function compareTerminalMaterializations(
|
|
613
677
|
[, left]: readonly [string, InMemorySourceMaterialization],
|
|
614
678
|
[, right]: readonly [string, InMemorySourceMaterialization]
|
|
@@ -97,8 +97,11 @@ export type {
|
|
|
97
97
|
} from "./outbox"
|
|
98
98
|
export { ONTOLOGY_OUTBOX_FAILURE_CODES } from "./outbox"
|
|
99
99
|
export type {
|
|
100
|
+
AbandonRunSourceMaterializationInput,
|
|
100
101
|
AbandonSourceMaterializationCandidateInput,
|
|
101
102
|
AbandonSourceMaterializationInput,
|
|
103
|
+
AdoptedSourceMaterialization,
|
|
104
|
+
AdoptSourceMaterializationInput,
|
|
102
105
|
AssertSourceMaterializationExecution,
|
|
103
106
|
AssertSourceMaterializationExecutionInput,
|
|
104
107
|
BeginSourceMaterializationInput,
|
|
@@ -109,7 +112,7 @@ export type {
|
|
|
109
112
|
OntologySourceMaterializationStatus,
|
|
110
113
|
OntologySourceRecord,
|
|
111
114
|
OntologySourceStorage,
|
|
112
|
-
|
|
115
|
+
PurgeAbandonedSourceMaterializationsInput,
|
|
113
116
|
StageSourceAssertion,
|
|
114
117
|
StageSourceRoot,
|
|
115
118
|
StageSourceRowsInput,
|
|
@@ -11,6 +11,8 @@ import type {
|
|
|
11
11
|
ExpectedObjectRevision,
|
|
12
12
|
OntologyLinkRef,
|
|
13
13
|
OntologyObjectRef,
|
|
14
|
+
ProjectionEntityRef,
|
|
15
|
+
ProjectionSourceAssertion,
|
|
14
16
|
} from "../../materialization/model"
|
|
15
17
|
import { linkRefKey, objectRefKey, projectionEntityKey } from "../../materialization/refs"
|
|
16
18
|
import type { OntologyCommitOriginSelector, OntologyCommitWrite } from "./commits"
|
|
@@ -716,7 +718,6 @@ export function sourceStageRow(row: StageSourceAssertion): SourceStageRow {
|
|
|
716
718
|
export interface SourceStageRoot {
|
|
717
719
|
readonly root: StageSourceAssertion["root"]
|
|
718
720
|
readonly rootKey: string
|
|
719
|
-
readonly sortKey: string
|
|
720
721
|
readonly stagingOrdinal: number
|
|
721
722
|
readonly deleted: boolean
|
|
722
723
|
}
|
|
@@ -763,7 +764,6 @@ export function sourceStageRoots(
|
|
|
763
764
|
roots.set(key, {
|
|
764
765
|
root: row.root,
|
|
765
766
|
rootKey: key,
|
|
766
|
-
sortKey: utf8SortKey(key),
|
|
767
767
|
stagingOrdinal: row.stagingOrdinal,
|
|
768
768
|
deleted,
|
|
769
769
|
})
|
|
@@ -965,12 +965,69 @@ export function sourceEntityColumns(entity: StageSourceAssertion["root"]): {
|
|
|
965
965
|
}
|
|
966
966
|
}
|
|
967
967
|
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
968
|
+
/** The inverse of `projectionEntityKey`, for providers that store only the canonical key. */
|
|
969
|
+
export function sourceEntityFromKey(key: string): ProjectionEntityRef {
|
|
970
|
+
const parts: unknown = JSON.parse(key)
|
|
971
|
+
if (Array.isArray(parts) && parts.every((part) => typeof part === "string")) {
|
|
972
|
+
if (parts[0] === "object" && parts.length === 3) {
|
|
973
|
+
return { kind: "object", ref: { objectTypeId: parts[1]!, primaryId: parts[2]! } }
|
|
974
|
+
}
|
|
975
|
+
if (parts[0] === "link" && parts.length === 6) {
|
|
976
|
+
return {
|
|
977
|
+
kind: "link",
|
|
978
|
+
ref: {
|
|
979
|
+
source: { objectTypeId: parts[1]!, primaryId: parts[2]! },
|
|
980
|
+
linkId: parts[3]!,
|
|
981
|
+
target: { objectTypeId: parts[4]!, primaryId: parts[5]! },
|
|
982
|
+
},
|
|
983
|
+
}
|
|
984
|
+
}
|
|
972
985
|
}
|
|
973
|
-
|
|
986
|
+
throw new MaterializationConflictError(
|
|
987
|
+
"source-materialization",
|
|
988
|
+
`Stored source key ${key} is not a canonical entity key.`
|
|
989
|
+
)
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
/** What an assertion carries beyond its identity, or null when that is nothing. */
|
|
993
|
+
export function sourceAssertionPayload(
|
|
994
|
+
assertion: ProjectionSourceAssertion
|
|
995
|
+
): Readonly<Record<string, unknown>> | null {
|
|
996
|
+
const { kind: _kind, ref: _ref, ...payload } = assertion
|
|
997
|
+
return Object.keys(payload).length === 0 ? null : payload
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/** Rebuilds an assertion from its typed identity columns and its payload. */
|
|
1001
|
+
export function sourceAssertionFromColumns(
|
|
1002
|
+
columns: {
|
|
1003
|
+
readonly entity_kind: "object" | "link"
|
|
1004
|
+
readonly object_type_id: string | null
|
|
1005
|
+
readonly primary_id: string | null
|
|
1006
|
+
readonly source_type_id: string | null
|
|
1007
|
+
readonly source_primary_id: string | null
|
|
1008
|
+
readonly link_id: string | null
|
|
1009
|
+
readonly target_type_id: string | null
|
|
1010
|
+
readonly target_primary_id: string | null
|
|
1011
|
+
},
|
|
1012
|
+
payload: unknown
|
|
1013
|
+
): ProjectionSourceAssertion {
|
|
1014
|
+
const rest = (payload ?? {}) as Record<string, unknown>
|
|
1015
|
+
if (columns.entity_kind === "object") {
|
|
1016
|
+
return {
|
|
1017
|
+
kind: "object",
|
|
1018
|
+
ref: { objectTypeId: columns.object_type_id!, primaryId: columns.primary_id! },
|
|
1019
|
+
...rest,
|
|
1020
|
+
} as ProjectionSourceAssertion
|
|
1021
|
+
}
|
|
1022
|
+
return {
|
|
1023
|
+
kind: "link",
|
|
1024
|
+
ref: {
|
|
1025
|
+
source: { objectTypeId: columns.source_type_id!, primaryId: columns.source_primary_id! },
|
|
1026
|
+
linkId: columns.link_id!,
|
|
1027
|
+
target: { objectTypeId: columns.target_type_id!, primaryId: columns.target_primary_id! },
|
|
1028
|
+
},
|
|
1029
|
+
...rest,
|
|
1030
|
+
} as ProjectionSourceAssertion
|
|
974
1031
|
}
|
|
975
1032
|
|
|
976
1033
|
export function isExactStagingManifest(
|
|
@@ -128,7 +128,7 @@ interface BaseAbandonSourceMaterializationInput {
|
|
|
128
128
|
readonly abandonedAt: string
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
/** Abandon the current execution's exact candidate
|
|
131
|
+
/** Abandon the current execution's exact candidate, e.g. an adopted one whose delta base moved. */
|
|
132
132
|
export interface AbandonSourceMaterializationCandidateInput
|
|
133
133
|
extends BaseAbandonSourceMaterializationInput {
|
|
134
134
|
readonly kind: "candidate"
|
|
@@ -136,20 +136,38 @@ export interface AbandonSourceMaterializationCandidateInput
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
/**
|
|
139
|
-
*
|
|
140
|
-
*
|
|
139
|
+
* Release whatever candidate a run still holds, whichever execution staged it. Called in the same
|
|
140
|
+
* transaction as the run's terminal transition, so no candidate outlives its run.
|
|
141
141
|
*/
|
|
142
|
-
export interface
|
|
143
|
-
|
|
142
|
+
export interface AbandonRunSourceMaterializationInput
|
|
143
|
+
extends BaseAbandonSourceMaterializationInput {
|
|
144
|
+
readonly kind: "run"
|
|
144
145
|
}
|
|
145
146
|
|
|
146
147
|
export type AbandonSourceMaterializationInput =
|
|
147
148
|
| AbandonSourceMaterializationCandidateInput
|
|
148
|
-
|
|
|
149
|
+
| AbandonRunSourceMaterializationInput
|
|
150
|
+
|
|
151
|
+
export interface AdoptSourceMaterializationInput {
|
|
152
|
+
readonly projectId: string
|
|
153
|
+
readonly source: ProjectionSourceRef
|
|
154
|
+
readonly execution: ProjectionExecution
|
|
155
|
+
readonly adoptedAt: string
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface AdoptedSourceMaterialization {
|
|
159
|
+
readonly record: OntologySourceRecord
|
|
160
|
+
/**
|
|
161
|
+
* Where staging resumes. Every root before this ordinal is complete; the root at it may be
|
|
162
|
+
* partial and is staged again, which staging accepts for identical rows. Equals the root count
|
|
163
|
+
* once the candidate is ready.
|
|
164
|
+
*/
|
|
165
|
+
readonly resumeStagingOrdinal: number
|
|
166
|
+
}
|
|
149
167
|
|
|
150
168
|
export interface CleanupTerminalSourceMaterializationsInput {
|
|
151
169
|
readonly projectId: string
|
|
152
|
-
/** Exclusive cutoff for retired roots in
|
|
170
|
+
/** Exclusive cutoff for retired roots in superseded manifests, and for empty ones. */
|
|
153
171
|
readonly terminalBefore: string
|
|
154
172
|
/** Maximum total assertion, root-reference, and manifest deletions performed by one call. */
|
|
155
173
|
readonly limit: number
|
|
@@ -161,6 +179,12 @@ export interface CleanupTerminalSourceMaterializationsResult {
|
|
|
161
179
|
readonly materializationsDeleted: number
|
|
162
180
|
}
|
|
163
181
|
|
|
182
|
+
export interface PurgeAbandonedSourceMaterializationsInput {
|
|
183
|
+
readonly projectId: string
|
|
184
|
+
/** Maximum total assertion, root-reference, and manifest deletions performed by one call. */
|
|
185
|
+
readonly limit: number
|
|
186
|
+
}
|
|
187
|
+
|
|
164
188
|
export interface SummarizeTerminalSourceMaterializationsInput {
|
|
165
189
|
readonly projectId: string
|
|
166
190
|
}
|
|
@@ -195,11 +219,21 @@ export interface OntologySourceStorage {
|
|
|
195
219
|
stageRows(input: StageSourceRowsInput): Promise<StageSourceRowsResult>
|
|
196
220
|
markReady(input: MarkSourceMaterializationReadyInput): Promise<OntologySourceRecord>
|
|
197
221
|
getActive(input: GetActiveOntologySourceInput): Promise<OntologySourceRecord | null>
|
|
222
|
+
/** Hands a redelivered run's candidate to its current execution; null when it has none. */
|
|
223
|
+
adopt(input: AdoptSourceMaterializationInput): Promise<AdoptedSourceMaterialization | null>
|
|
198
224
|
abandon(input: AbandonSourceMaterializationCandidateInput): Promise<OntologySourceRecord>
|
|
199
|
-
abandon(input:
|
|
225
|
+
abandon(input: AbandonRunSourceMaterializationInput): Promise<OntologySourceRecord | null>
|
|
226
|
+
/** Retired roots of superseded manifests, after their retention. */
|
|
200
227
|
cleanupTerminal(
|
|
201
228
|
input: CleanupTerminalSourceMaterializationsInput
|
|
202
229
|
): Promise<CleanupTerminalSourceMaterializationsResult>
|
|
230
|
+
/**
|
|
231
|
+
* Abandoned candidates, whole and oldest first. Nothing reads an abandoned candidate, so it has
|
|
232
|
+
* no retention; deleting one manifest at a time keeps each delete on the source-version index.
|
|
233
|
+
*/
|
|
234
|
+
purgeAbandoned(
|
|
235
|
+
input: PurgeAbandonedSourceMaterializationsInput
|
|
236
|
+
): Promise<CleanupTerminalSourceMaterializationsResult>
|
|
203
237
|
summarizeTerminal(
|
|
204
238
|
input: SummarizeTerminalSourceMaterializationsInput
|
|
205
239
|
): Promise<TerminalSourceMaterializationSummary>
|
|
@@ -26,8 +26,10 @@ import {
|
|
|
26
26
|
finishProjectionRunRecord,
|
|
27
27
|
immutableDatasetVersionConflict,
|
|
28
28
|
mergeProjectionRunProgress,
|
|
29
|
+
parseProjectionAttemptFailure,
|
|
29
30
|
planProjectionRunReclaim,
|
|
30
31
|
projectionRunNotFound,
|
|
32
|
+
projectionRunSupersedes,
|
|
31
33
|
publicProjectionRunRecord,
|
|
32
34
|
requireTelemetryProjectionRun,
|
|
33
35
|
type StoredProjectionRunRecord,
|
|
@@ -36,6 +38,7 @@ import {
|
|
|
36
38
|
import {
|
|
37
39
|
type AdvanceProjectionTelemetryCheckpointInput,
|
|
38
40
|
type FailProjectionRunEnqueueInput,
|
|
41
|
+
type FindSupersedingProjectionRunInput,
|
|
39
42
|
type FinishProjectionRunInput,
|
|
40
43
|
type ListLatestProjectionRunsInput,
|
|
41
44
|
type ListLatestProjectionRunsResult,
|
|
@@ -47,6 +50,7 @@ import {
|
|
|
47
50
|
type ProjectionRunStorage,
|
|
48
51
|
projectionRunObjectTypesVisible,
|
|
49
52
|
type QueueProjectionRunInput,
|
|
53
|
+
type RecordProjectionAttemptFailureInput,
|
|
50
54
|
type RecordProjectionMissingTargetInput,
|
|
51
55
|
type StartOrReclaimProjectionRunInput,
|
|
52
56
|
type TelemetryProjectionRunRecord,
|
|
@@ -60,6 +64,10 @@ function projectionRunKey(projectId: string, id: string): string {
|
|
|
60
64
|
return JSON.stringify([projectId, id])
|
|
61
65
|
}
|
|
62
66
|
|
|
67
|
+
function compareText(left: string, right: string): number {
|
|
68
|
+
return left < right ? -1 : left > right ? 1 : 0
|
|
69
|
+
}
|
|
70
|
+
|
|
63
71
|
function compareRuns(a: ProjectionRunRecord, b: ProjectionRunRecord, order: "asc" | "desc") {
|
|
64
72
|
return compareStartedAt(a, b, order)
|
|
65
73
|
}
|
|
@@ -152,7 +160,6 @@ export class InMemoryProjectionRunStorage implements ProjectionRunStorage {
|
|
|
152
160
|
executionToken,
|
|
153
161
|
startedAt: existing.startedAt ?? new Date(input.startedAt ?? new Date()),
|
|
154
162
|
finishedAt: undefined,
|
|
155
|
-
error: undefined,
|
|
156
163
|
}
|
|
157
164
|
|
|
158
165
|
this.rows.set(key, structuredClone(record))
|
|
@@ -246,6 +253,17 @@ export class InMemoryProjectionRunStorage implements ProjectionRunStorage {
|
|
|
246
253
|
})
|
|
247
254
|
}
|
|
248
255
|
|
|
256
|
+
async recordAttemptFailure(
|
|
257
|
+
input: RecordProjectionAttemptFailureInput
|
|
258
|
+
): Promise<ProjectionRunRecord> {
|
|
259
|
+
return this.runRootOperation(() => {
|
|
260
|
+
const existing = this.requireExecution(input)
|
|
261
|
+
const next = { ...existing, error: parseProjectionAttemptFailure(existing, input.error) }
|
|
262
|
+
this.rows.set(projectionRunKey(input.projectId, input.id), structuredClone(next))
|
|
263
|
+
return publicProjectionRunRecord(next)
|
|
264
|
+
})
|
|
265
|
+
}
|
|
266
|
+
|
|
249
267
|
async finish(input: FinishProjectionRunInput): Promise<ProjectionRunRecord> {
|
|
250
268
|
return this.runRootOperation(() => {
|
|
251
269
|
const existing = this.requireExecution(input)
|
|
@@ -311,12 +329,33 @@ export class InMemoryProjectionRunStorage implements ProjectionRunStorage {
|
|
|
311
329
|
})
|
|
312
330
|
}
|
|
313
331
|
|
|
332
|
+
async findSupersedingRun(
|
|
333
|
+
input: FindSupersedingProjectionRunInput
|
|
334
|
+
): Promise<ProjectionRunRecord | null> {
|
|
335
|
+
return this.runRootOperation(() => {
|
|
336
|
+
const run = this.rows.get(projectionRunKey(input.projectId, input.id))
|
|
337
|
+
if (!run) throw projectionRunNotFound(input.projectId, input.id)
|
|
338
|
+
const newest = [...this.rows.values()]
|
|
339
|
+
.filter((candidate) => projectionRunSupersedes(candidate, run))
|
|
340
|
+
.sort(
|
|
341
|
+
(left, right) =>
|
|
342
|
+
compareText(
|
|
343
|
+
right.identity.datasetVersion.createdAt,
|
|
344
|
+
left.identity.datasetVersion.createdAt
|
|
345
|
+
) || compareText(right.id, left.id)
|
|
346
|
+
)[0]
|
|
347
|
+
return newest ? publicProjectionRunRecord(newest) : null
|
|
348
|
+
})
|
|
349
|
+
}
|
|
350
|
+
|
|
314
351
|
async listLatestByProjectionIds(
|
|
315
352
|
input: ListLatestProjectionRunsInput
|
|
316
353
|
): Promise<ListLatestProjectionRunsResult> {
|
|
317
354
|
return this.runRootOperation(() => {
|
|
318
355
|
const runs = latestStartedAtByOwnerId(
|
|
319
|
-
[...this.rows.values()].filter(
|
|
356
|
+
[...this.rows.values()].filter(
|
|
357
|
+
(record) => record.projectId === input.projectId && record.status !== "superseded"
|
|
358
|
+
),
|
|
320
359
|
input.projectionIds,
|
|
321
360
|
(record) => record.identity.projectionId
|
|
322
361
|
)
|
|
@@ -3,6 +3,7 @@ export { InMemoryProjectionRunStorage } from "./in-memory"
|
|
|
3
3
|
export type {
|
|
4
4
|
AdvanceProjectionTelemetryCheckpointInput,
|
|
5
5
|
FailProjectionRunEnqueueInput,
|
|
6
|
+
FindSupersedingProjectionRunInput,
|
|
6
7
|
FinishProjectionRunInput,
|
|
7
8
|
LinkProjectionRunRecord,
|
|
8
9
|
LinkProjectionTarget,
|
|
@@ -27,6 +28,7 @@ export type {
|
|
|
27
28
|
ProjectionTarget,
|
|
28
29
|
ProjectionTelemetryCheckpoint,
|
|
29
30
|
QueueProjectionRunInput,
|
|
31
|
+
RecordProjectionAttemptFailureInput,
|
|
30
32
|
RecordProjectionMissingTargetInput,
|
|
31
33
|
StartOrReclaimProjectionRunInput,
|
|
32
34
|
TelemetryProjectionRunRecord,
|