@opendatalabs/personal-server-ts-core 1.35.2 → 1.35.3
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/additions/version-inventory.d.ts +2 -0
- package/dist/additions/version-inventory.d.ts.map +1 -1
- package/dist/additions/version-inventory.js +2 -0
- package/dist/additions/version-inventory.js.map +1 -1
- package/dist/ports/index.d.ts +79 -1
- package/dist/ports/index.d.ts.map +1 -1
- package/dist/ports/index.js.map +1 -1
- package/dist/sync/delete-data-port.d.ts +2 -0
- package/dist/sync/delete-data-port.d.ts.map +1 -1
- package/dist/sync/delete-data-port.js +22 -1
- package/dist/sync/delete-data-port.js.map +1 -1
- package/dist/sync/engine/sync-manager.d.ts +6 -1
- package/dist/sync/engine/sync-manager.d.ts.map +1 -1
- package/dist/sync/engine/sync-manager.js +62 -9
- package/dist/sync/engine/sync-manager.js.map +1 -1
- package/dist/sync/index.d.ts +1 -0
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +1 -0
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/pending-blob-deletions.d.ts.map +1 -1
- package/dist/sync/pending-blob-deletions.js +28 -0
- package/dist/sync/pending-blob-deletions.js.map +1 -1
- package/dist/sync/tombstone-finality.d.ts +36 -0
- package/dist/sync/tombstone-finality.d.ts.map +1 -0
- package/dist/sync/tombstone-finality.js +142 -0
- package/dist/sync/tombstone-finality.js.map +1 -0
- package/dist/sync/tombstone-resubmits.d.ts +61 -0
- package/dist/sync/tombstone-resubmits.d.ts.map +1 -0
- package/dist/sync/tombstone-resubmits.js +85 -0
- package/dist/sync/tombstone-resubmits.js.map +1 -0
- package/dist/sync/types.d.ts +8 -0
- package/dist/sync/types.d.ts.map +1 -1
- package/dist/sync/workers/delete.d.ts +54 -5
- package/dist/sync/workers/delete.d.ts.map +1 -1
- package/dist/sync/workers/delete.js +278 -44
- package/dist/sync/workers/delete.js.map +1 -1
- package/package.json +1 -1
|
@@ -15,6 +15,24 @@ function markerId(key) {
|
|
|
15
15
|
const range = key.range ? `${key.range.from}-${key.range.to}` : "";
|
|
16
16
|
return `${key.scope}\u0000${key.version ?? ""}\u0000${range}`;
|
|
17
17
|
}
|
|
18
|
+
function normalizeHead(value) {
|
|
19
|
+
if (typeof value !== "object" || value === null)
|
|
20
|
+
return undefined;
|
|
21
|
+
const { expectedVersion, dataHash, metadataHash } = value;
|
|
22
|
+
if (typeof expectedVersion !== "string" ||
|
|
23
|
+
!/^\d+$/.test(expectedVersion) ||
|
|
24
|
+
typeof dataHash !== "string" ||
|
|
25
|
+
!/^0x[0-9a-fA-F]{64}$/.test(dataHash) ||
|
|
26
|
+
typeof metadataHash !== "string" ||
|
|
27
|
+
!/^0x[0-9a-fA-F]{64}$/.test(metadataHash)) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
expectedVersion: BigInt(expectedVersion).toString(),
|
|
32
|
+
dataHash: dataHash.toLowerCase(),
|
|
33
|
+
metadataHash: metadataHash.toLowerCase(),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
18
36
|
function normalizeRange(value) {
|
|
19
37
|
if (typeof value !== "object" || value === null)
|
|
20
38
|
return undefined;
|
|
@@ -48,10 +66,16 @@ export function normalizePendingBlobDeletions(stored) {
|
|
|
48
66
|
const range = typeof version === "string"
|
|
49
67
|
? undefined
|
|
50
68
|
: normalizeRange(item.range);
|
|
69
|
+
const gate = item.tombstoneVersion;
|
|
70
|
+
const replacedHead = normalizeHead(item.replacedHead);
|
|
51
71
|
keys.push({
|
|
52
72
|
scope: item.scope,
|
|
53
73
|
version: typeof version === "string" ? version : null,
|
|
54
74
|
...(range ? { range } : {}),
|
|
75
|
+
...(typeof gate === "string" && /^\d+$/.test(gate)
|
|
76
|
+
? { tombstoneVersion: BigInt(gate).toString() }
|
|
77
|
+
: {}),
|
|
78
|
+
...(replacedHead ? { replacedHead } : {}),
|
|
55
79
|
});
|
|
56
80
|
}
|
|
57
81
|
}
|
|
@@ -92,6 +116,10 @@ export function createPendingBlobDeletionStore(kv) {
|
|
|
92
116
|
scope: key.scope,
|
|
93
117
|
version: key.version,
|
|
94
118
|
...(key.range ? { range: { ...key.range } } : {}),
|
|
119
|
+
...(key.tombstoneVersion !== undefined
|
|
120
|
+
? { tombstoneVersion: key.tombstoneVersion }
|
|
121
|
+
: {}),
|
|
122
|
+
...(key.replacedHead ? { replacedHead: { ...key.replacedHead } } : {}),
|
|
95
123
|
});
|
|
96
124
|
}
|
|
97
125
|
return next;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pending-blob-deletions.js","sourceRoot":"","sources":["../../src/sync/pending-blob-deletions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pending-blob-deletions.js","sourceRoot":"","sources":["../../src/sync/pending-blob-deletions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { VersionInventoryClient } from "../additions/version-inventory.js";
|
|
2
|
+
import type { TombstoneFinalityPort } from "../ports/index.js";
|
|
3
|
+
export interface InventoryTombstoneFinalityOptions {
|
|
4
|
+
/** Clock seam for tests; defaults to `Date.now`. */
|
|
5
|
+
now?: () => number;
|
|
6
|
+
/** First wait after a scope reports nothing settling (default 1 min). */
|
|
7
|
+
minRecheckMs?: number;
|
|
8
|
+
/** Longest wait between reads for one scope (default 15 min). */
|
|
9
|
+
maxRecheckMs?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Tombstone finality read from the gateway's version inventory
|
|
13
|
+
* (`GET /v1/data/:id/versions`, owner or registered-server signed).
|
|
14
|
+
*
|
|
15
|
+
* Only `finalized` counts. The gateway moves a data version
|
|
16
|
+
* pending → submitting → confirmed → finalized, and can still push a
|
|
17
|
+
* `confirmed` version back to `pending` on a reorg
|
|
18
|
+
* (data-gateway `reconcileConfirmedDataPoints`); until a tombstone is
|
|
19
|
+
* finalized its settlement can fail, the gateway rolls the head back, and
|
|
20
|
+
* a writer may recover the failed slot.
|
|
21
|
+
*
|
|
22
|
+
* While a tombstone at or above the asked version is settling, every call
|
|
23
|
+
* reads the inventory, so a sync cycle sees finality on its next pass. The
|
|
24
|
+
* route has no rate limit; a call costs one signed read of at most
|
|
25
|
+
* `MAX_PAGES` pages.
|
|
26
|
+
*
|
|
27
|
+
* When nothing is settling and nothing is finalized (a scope that was never
|
|
28
|
+
* registered, a rolled-back tombstone, a newer write above it), the scope is
|
|
29
|
+
* not read again until its recheck time, which doubles from `minRecheckMs`
|
|
30
|
+
* up to `maxRecheckMs`. Such keys may never become free, so they cost a few
|
|
31
|
+
* reads an hour, not one per sync cycle. `recheckNow` drops the wait when a
|
|
32
|
+
* new tombstone is queued. The backoff only delays a deletion, never brings
|
|
33
|
+
* one forward.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createInventoryTombstoneFinality(inventory: Pick<VersionInventoryClient, "page">, options?: InventoryTombstoneFinalityOptions): TombstoneFinalityPort;
|
|
36
|
+
//# sourceMappingURL=tombstone-finality.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tombstone-finality.d.ts","sourceRoot":"","sources":["../../src/sync/tombstone-finality.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAChF,OAAO,KAAK,EACV,qBAAqB,EAEtB,MAAM,mBAAmB,CAAC;AAM3B,MAAM,WAAW,iCAAiC;IAChD,oDAAoD;IACpD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAID;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,gCAAgC,CAC9C,SAAS,EAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,EAC/C,OAAO,GAAE,iCAAsC,GAC9C,qBAAqB,CAoHvB"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { isTombstoneRecord } from "./tombstone.js";
|
|
2
|
+
const PAGE_LIMIT = 1000;
|
|
3
|
+
const MAX_PAGES = 20;
|
|
4
|
+
const SETTLED = new Set(["finalized", "failed"]);
|
|
5
|
+
/**
|
|
6
|
+
* Tombstone finality read from the gateway's version inventory
|
|
7
|
+
* (`GET /v1/data/:id/versions`, owner or registered-server signed).
|
|
8
|
+
*
|
|
9
|
+
* Only `finalized` counts. The gateway moves a data version
|
|
10
|
+
* pending → submitting → confirmed → finalized, and can still push a
|
|
11
|
+
* `confirmed` version back to `pending` on a reorg
|
|
12
|
+
* (data-gateway `reconcileConfirmedDataPoints`); until a tombstone is
|
|
13
|
+
* finalized its settlement can fail, the gateway rolls the head back, and
|
|
14
|
+
* a writer may recover the failed slot.
|
|
15
|
+
*
|
|
16
|
+
* While a tombstone at or above the asked version is settling, every call
|
|
17
|
+
* reads the inventory, so a sync cycle sees finality on its next pass. The
|
|
18
|
+
* route has no rate limit; a call costs one signed read of at most
|
|
19
|
+
* `MAX_PAGES` pages.
|
|
20
|
+
*
|
|
21
|
+
* When nothing is settling and nothing is finalized (a scope that was never
|
|
22
|
+
* registered, a rolled-back tombstone, a newer write above it), the scope is
|
|
23
|
+
* not read again until its recheck time, which doubles from `minRecheckMs`
|
|
24
|
+
* up to `maxRecheckMs`. Such keys may never become free, so they cost a few
|
|
25
|
+
* reads an hour, not one per sync cycle. `recheckNow` drops the wait when a
|
|
26
|
+
* new tombstone is queued. The backoff only delays a deletion, never brings
|
|
27
|
+
* one forward.
|
|
28
|
+
*/
|
|
29
|
+
export function createInventoryTombstoneFinality(inventory, options = {}) {
|
|
30
|
+
const now = options.now ?? Date.now;
|
|
31
|
+
const minRecheck = options.minRecheckMs ?? 60_000;
|
|
32
|
+
const maxRecheck = options.maxRecheckMs ?? 15 * 60_000;
|
|
33
|
+
const recheck = new Map();
|
|
34
|
+
const idle = {
|
|
35
|
+
finalized: null,
|
|
36
|
+
settling: false,
|
|
37
|
+
rolledBack: null,
|
|
38
|
+
};
|
|
39
|
+
async function read(scope, from) {
|
|
40
|
+
let after = (from > 0n ? from - 1n : 0n).toString();
|
|
41
|
+
let finalized = null;
|
|
42
|
+
let settling = false;
|
|
43
|
+
// The highest listed row at or above `from`, and the head as of the last
|
|
44
|
+
// page (each page is its own snapshot; the last one is the newest).
|
|
45
|
+
let top = null;
|
|
46
|
+
let head = null;
|
|
47
|
+
let complete = false;
|
|
48
|
+
for (let count = 0; count < MAX_PAGES; count++) {
|
|
49
|
+
const result = await inventory.page(scope, { after, limit: PAGE_LIMIT });
|
|
50
|
+
head = result.data.head ?? null;
|
|
51
|
+
for (const version of result.data.versions) {
|
|
52
|
+
const value = BigInt(version.expectedVersion);
|
|
53
|
+
if (value < from)
|
|
54
|
+
continue;
|
|
55
|
+
if (top === null || value > top.version) {
|
|
56
|
+
top = {
|
|
57
|
+
version: value,
|
|
58
|
+
failedTombstone: version.isTombstone && version.status === "failed",
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (!version.isTombstone)
|
|
62
|
+
continue;
|
|
63
|
+
if (version.status === "finalized") {
|
|
64
|
+
if (finalized === null || value > finalized)
|
|
65
|
+
finalized = value;
|
|
66
|
+
}
|
|
67
|
+
else if (!SETTLED.has(version.status)) {
|
|
68
|
+
settling = true;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (!result.pagination.hasMore) {
|
|
72
|
+
complete = true;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
const next = result.pagination.nextAfter;
|
|
76
|
+
if (next === null)
|
|
77
|
+
break;
|
|
78
|
+
after = next;
|
|
79
|
+
}
|
|
80
|
+
// The head counts as a tombstone row too: a re-submitted delete can take
|
|
81
|
+
// a slot below the asked version (the gateway rolls back to the latest
|
|
82
|
+
// non-failed row, which may sit below a failed data row), and its
|
|
83
|
+
// finality frees the keys at or below it.
|
|
84
|
+
if (head !== null && (head.deletedAt !== null || isTombstoneRecord(head))) {
|
|
85
|
+
const value = BigInt(head.expectedVersion);
|
|
86
|
+
if (head.status === "finalized") {
|
|
87
|
+
if (finalized === null || value > finalized)
|
|
88
|
+
finalized = value;
|
|
89
|
+
}
|
|
90
|
+
else if (!SETTLED.has(head.status)) {
|
|
91
|
+
settling = true;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// An unfinished traversal can only under-report: keys wait longer, and
|
|
95
|
+
// it never reports a rollback it did not see to the end.
|
|
96
|
+
let rolledBack = null;
|
|
97
|
+
if (complete && !settling && top?.failedTombstone) {
|
|
98
|
+
const restored = head !== null &&
|
|
99
|
+
head.deletedAt === null &&
|
|
100
|
+
!isTombstoneRecord(head) &&
|
|
101
|
+
BigInt(head.expectedVersion) < top.version
|
|
102
|
+
? head
|
|
103
|
+
: null;
|
|
104
|
+
if (restored) {
|
|
105
|
+
rolledBack = {
|
|
106
|
+
tombstoneVersion: top.version.toString(),
|
|
107
|
+
head: {
|
|
108
|
+
expectedVersion: restored.expectedVersion,
|
|
109
|
+
dataHash: restored.dataHash,
|
|
110
|
+
metadataHash: restored.metadataHash,
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
finalized: finalized === null ? null : finalized.toString(),
|
|
117
|
+
settling,
|
|
118
|
+
rolledBack,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
async tombstoneState(scope, atOrAbove) {
|
|
123
|
+
const waiting = recheck.get(scope);
|
|
124
|
+
if (waiting && now() < waiting.at)
|
|
125
|
+
return idle;
|
|
126
|
+
const state = await read(scope, BigInt(atOrAbove));
|
|
127
|
+
if (state.finalized !== null || state.settling) {
|
|
128
|
+
recheck.delete(scope);
|
|
129
|
+
return state;
|
|
130
|
+
}
|
|
131
|
+
const interval = waiting
|
|
132
|
+
? Math.min(waiting.interval * 2, maxRecheck)
|
|
133
|
+
: minRecheck;
|
|
134
|
+
recheck.set(scope, { at: now() + interval, interval });
|
|
135
|
+
return state;
|
|
136
|
+
},
|
|
137
|
+
recheckNow(scope) {
|
|
138
|
+
recheck.delete(scope);
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=tombstone-finality.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tombstone-finality.js","sourceRoot":"","sources":["../../src/sync/tombstone-finality.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded re-submission of a user's delete whose gateway tombstone failed.
|
|
3
|
+
*
|
|
4
|
+
* A tombstone the gateway queued can fail to settle. The gateway then rolls
|
|
5
|
+
* the head back and reports the scope live again, though the user asked to
|
|
6
|
+
* delete it. The retry pass re-submits the delete through the ordinary delete
|
|
7
|
+
* path; this tracker bounds how often. Attempt n waits
|
|
8
|
+
* `baseDelayMs * 2^(n-2)` after attempt n-1 (the first runs at once), and
|
|
9
|
+
* after `maxAttempts` failed re-submissions the scope is reported as failed
|
|
10
|
+
* and left alone until the user deletes it again.
|
|
11
|
+
*
|
|
12
|
+
* In memory per sync manager: a restart starts the count again, so the bound
|
|
13
|
+
* is per boot.
|
|
14
|
+
*/
|
|
15
|
+
export interface TombstoneResubmitState {
|
|
16
|
+
scope: string;
|
|
17
|
+
/** Re-submissions made so far (the user's own delete is not counted). */
|
|
18
|
+
attempts: number;
|
|
19
|
+
/** The failed tombstone version last seen for the scope. */
|
|
20
|
+
tombstoneVersion: string;
|
|
21
|
+
/** ISO time of the last re-submission. */
|
|
22
|
+
lastAttemptAt: string;
|
|
23
|
+
/**
|
|
24
|
+
* Why the delete stopped (failed entries only): every attempt failed, or
|
|
25
|
+
* the gateway restored a head other than the one the delete replaced, so
|
|
26
|
+
* it is not re-submitted automatically.
|
|
27
|
+
*/
|
|
28
|
+
reason?: TombstoneResubmitFailure;
|
|
29
|
+
}
|
|
30
|
+
export type TombstoneResubmitFailure = "attempts-exhausted" | "head-changed";
|
|
31
|
+
export interface TombstoneResubmitSnapshot {
|
|
32
|
+
/** Scopes with a re-submission in progress or due. */
|
|
33
|
+
retrying: TombstoneResubmitState[];
|
|
34
|
+
/** Scopes whose delete did not complete after the last allowed attempt. */
|
|
35
|
+
failed: Array<TombstoneResubmitState & {
|
|
36
|
+
failedAt: string;
|
|
37
|
+
}>;
|
|
38
|
+
}
|
|
39
|
+
export interface TombstoneResubmitTracker {
|
|
40
|
+
/** Whether another re-submission is allowed now. */
|
|
41
|
+
due(scope: string): boolean;
|
|
42
|
+
/** True once the scope has used every attempt and been reported. */
|
|
43
|
+
exhausted(scope: string): boolean;
|
|
44
|
+
/** True when the scope has used every attempt but is not reported yet. */
|
|
45
|
+
outOfAttempts(scope: string): boolean;
|
|
46
|
+
/** Count one re-submission; returns the attempt number. */
|
|
47
|
+
recordAttempt(scope: string, tombstoneVersion: string): number;
|
|
48
|
+
/** Report the scope as failed (the default reason: attempts exhausted). */
|
|
49
|
+
giveUp(scope: string, tombstoneVersion: string, reason?: TombstoneResubmitFailure): TombstoneResubmitState;
|
|
50
|
+
/** Forget the scope: the delete converged, or the user deleted it again. */
|
|
51
|
+
reset(scope: string): void;
|
|
52
|
+
snapshot(): TombstoneResubmitSnapshot;
|
|
53
|
+
}
|
|
54
|
+
export declare const DEFAULT_TOMBSTONE_RESUBMIT_MAX_ATTEMPTS = 5;
|
|
55
|
+
export declare const DEFAULT_TOMBSTONE_RESUBMIT_BASE_DELAY_MS = 60000;
|
|
56
|
+
export declare function createTombstoneResubmitTracker(options?: {
|
|
57
|
+
maxAttempts?: number;
|
|
58
|
+
baseDelayMs?: number;
|
|
59
|
+
now?: () => number;
|
|
60
|
+
}): TombstoneResubmitTracker;
|
|
61
|
+
//# sourceMappingURL=tombstone-resubmits.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tombstone-resubmits.d.ts","sourceRoot":"","sources":["../../src/sync/tombstone-resubmits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,QAAQ,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,gBAAgB,EAAE,MAAM,CAAC;IACzB,0CAA0C;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,MAAM,CAAC,EAAE,wBAAwB,CAAC;CACnC;AAED,MAAM,MAAM,wBAAwB,GAAG,oBAAoB,GAAG,cAAc,CAAC;AAE7E,MAAM,WAAW,yBAAyB;IACxC,sDAAsD;IACtD,QAAQ,EAAE,sBAAsB,EAAE,CAAC;IACnC,2EAA2E;IAC3E,MAAM,EAAE,KAAK,CAAC,sBAAsB,GAAG;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC9D;AAED,MAAM,WAAW,wBAAwB;IACvC,oDAAoD;IACpD,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5B,oEAAoE;IACpE,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,0EAA0E;IAC1E,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IACtC,2DAA2D;IAC3D,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/D,2EAA2E;IAC3E,MAAM,CACJ,KAAK,EAAE,MAAM,EACb,gBAAgB,EAAE,MAAM,EACxB,MAAM,CAAC,EAAE,wBAAwB,GAChC,sBAAsB,CAAC;IAC1B,4EAA4E;IAC5E,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,IAAI,yBAAyB,CAAC;CACvC;AAED,eAAO,MAAM,uCAAuC,IAAI,CAAC;AACzD,eAAO,MAAM,wCAAwC,QAAS,CAAC;AAE/D,wBAAgB,8BAA8B,CAC5C,OAAO,GAAE;IACP,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACf,GACL,wBAAwB,CA8F1B"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export const DEFAULT_TOMBSTONE_RESUBMIT_MAX_ATTEMPTS = 5;
|
|
2
|
+
export const DEFAULT_TOMBSTONE_RESUBMIT_BASE_DELAY_MS = 60_000;
|
|
3
|
+
export function createTombstoneResubmitTracker(options = {}) {
|
|
4
|
+
const maxAttempts = options.maxAttempts ?? DEFAULT_TOMBSTONE_RESUBMIT_MAX_ATTEMPTS;
|
|
5
|
+
const baseDelayMs = options.baseDelayMs ?? DEFAULT_TOMBSTONE_RESUBMIT_BASE_DELAY_MS;
|
|
6
|
+
const now = options.now ?? Date.now;
|
|
7
|
+
const states = new Map();
|
|
8
|
+
const view = (scope) => {
|
|
9
|
+
const state = states.get(scope);
|
|
10
|
+
return state
|
|
11
|
+
? {
|
|
12
|
+
scope,
|
|
13
|
+
attempts: state.attempts,
|
|
14
|
+
tombstoneVersion: state.tombstoneVersion,
|
|
15
|
+
lastAttemptAt: new Date(state.lastAttemptMs).toISOString(),
|
|
16
|
+
...(state.reason ? { reason: state.reason } : {}),
|
|
17
|
+
}
|
|
18
|
+
: null;
|
|
19
|
+
};
|
|
20
|
+
return {
|
|
21
|
+
due(scope) {
|
|
22
|
+
const state = states.get(scope);
|
|
23
|
+
if (!state)
|
|
24
|
+
return true;
|
|
25
|
+
if (state.failedAtMs !== null || state.attempts >= maxAttempts)
|
|
26
|
+
return false;
|
|
27
|
+
const wait = baseDelayMs * 2 ** (state.attempts - 1);
|
|
28
|
+
return now() - state.lastAttemptMs >= wait;
|
|
29
|
+
},
|
|
30
|
+
exhausted(scope) {
|
|
31
|
+
return states.get(scope)?.failedAtMs != null;
|
|
32
|
+
},
|
|
33
|
+
outOfAttempts(scope) {
|
|
34
|
+
const state = states.get(scope);
|
|
35
|
+
return (state !== undefined &&
|
|
36
|
+
state.failedAtMs === null &&
|
|
37
|
+
state.attempts >= maxAttempts);
|
|
38
|
+
},
|
|
39
|
+
recordAttempt(scope, tombstoneVersion) {
|
|
40
|
+
const state = states.get(scope);
|
|
41
|
+
const attempts = (state?.attempts ?? 0) + 1;
|
|
42
|
+
states.set(scope, {
|
|
43
|
+
attempts,
|
|
44
|
+
tombstoneVersion,
|
|
45
|
+
lastAttemptMs: now(),
|
|
46
|
+
failedAtMs: null,
|
|
47
|
+
});
|
|
48
|
+
return attempts;
|
|
49
|
+
},
|
|
50
|
+
giveUp(scope, tombstoneVersion, reason = "attempts-exhausted") {
|
|
51
|
+
const state = states.get(scope) ?? {
|
|
52
|
+
attempts: 0,
|
|
53
|
+
tombstoneVersion,
|
|
54
|
+
lastAttemptMs: now(),
|
|
55
|
+
failedAtMs: null,
|
|
56
|
+
};
|
|
57
|
+
states.set(scope, {
|
|
58
|
+
...state,
|
|
59
|
+
tombstoneVersion,
|
|
60
|
+
failedAtMs: now(),
|
|
61
|
+
reason,
|
|
62
|
+
});
|
|
63
|
+
return view(scope);
|
|
64
|
+
},
|
|
65
|
+
reset(scope) {
|
|
66
|
+
states.delete(scope);
|
|
67
|
+
},
|
|
68
|
+
snapshot() {
|
|
69
|
+
const retrying = [];
|
|
70
|
+
const failed = [];
|
|
71
|
+
for (const [scope, state] of states) {
|
|
72
|
+
const entry = view(scope);
|
|
73
|
+
if (state.failedAtMs === null)
|
|
74
|
+
retrying.push(entry);
|
|
75
|
+
else
|
|
76
|
+
failed.push({
|
|
77
|
+
...entry,
|
|
78
|
+
failedAt: new Date(state.failedAtMs).toISOString(),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return { retrying, failed };
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=tombstone-resubmits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tombstone-resubmits.js","sourceRoot":"","sources":["../../src/sync/tombstone-resubmits.ts"],"names":[],"mappings":"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"}
|
package/dist/sync/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TombstoneResubmitSnapshot } from "./tombstone-resubmits.js";
|
|
1
2
|
/** Sync engine status for GET /v1/sync/status */
|
|
2
3
|
export interface SyncStatus {
|
|
3
4
|
enabled: boolean;
|
|
@@ -13,6 +14,13 @@ export interface SyncStatus {
|
|
|
13
14
|
/** Exact scopes hydrated independently of the registry cursor. */
|
|
14
15
|
hydratedScopes: string[];
|
|
15
16
|
errors: SyncError[];
|
|
17
|
+
/**
|
|
18
|
+
* Deletes whose gateway tombstone failed and rolled back (the data is live
|
|
19
|
+
* again). `retrying`: re-submitted automatically, a new tombstone is due or
|
|
20
|
+
* settling. `failed`: every allowed re-submission failed; the user must
|
|
21
|
+
* delete the scope again. Both lists are empty when nothing is owed.
|
|
22
|
+
*/
|
|
23
|
+
deletions?: TombstoneResubmitSnapshot;
|
|
16
24
|
}
|
|
17
25
|
export interface SyncBlockedReason {
|
|
18
26
|
reason: string;
|
package/dist/sync/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,+CAA+C;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,OAAO,EAAE,OAAO,CAAC;IACjB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACnC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,kEAAkE;IAClE,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,MAAM,EAAE,SAAS,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAC1E,iDAAiD;AACjD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,+CAA+C;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,OAAO,EAAE,OAAO,CAAC;IACjB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACnC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,kEAAkE;IAClE,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,yBAAyB,CAAC;CACvC;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Logger } from "../../logger/index.js";
|
|
2
|
-
import type { DataPointFeedPort, DataStoragePort, DeleteDataPort, PendingBlobDeletion, PendingBlobDeletionStore } from "../../ports/index.js";
|
|
2
|
+
import type { DataPointFeedPort, DataStoragePort, DeleteDataPort, PendingBlobDeletion, PendingBlobDeletionStore, TombstoneFence, TombstoneFinalityPort } from "../../ports/index.js";
|
|
3
|
+
import { type TombstoneResubmitState, type TombstoneResubmitTracker } from "../tombstone-resubmits.js";
|
|
3
4
|
import { type ScopeDeletionTracker } from "../scope-deletions.js";
|
|
4
5
|
/**
|
|
5
6
|
* Exact blob deletes sent per pass. vana-storage allows 30 DELETE requests
|
|
@@ -32,6 +33,15 @@ export interface DeleteScopeDeps {
|
|
|
32
33
|
* expand a marker whose tombstone version was not known.
|
|
33
34
|
*/
|
|
34
35
|
dataPointFeed?: DataPointFeedPort;
|
|
36
|
+
/**
|
|
37
|
+
* When set, a covered storage key is deleted only once a tombstone at or
|
|
38
|
+
* above the one that covers it is finalized at the gateway, never on the
|
|
39
|
+
* tombstone's queue acknowledgement. Until then settlement can fail, the
|
|
40
|
+
* gateway rolls the head back, and a writer may recover the failed slot: a
|
|
41
|
+
* key freed earlier could take another writer's bytes under that slot.
|
|
42
|
+
* Without it keys are deleted as soon as the tombstone is acknowledged.
|
|
43
|
+
*/
|
|
44
|
+
tombstoneFinality?: TombstoneFinalityPort;
|
|
35
45
|
logger: Logger;
|
|
36
46
|
now?: () => Date;
|
|
37
47
|
}
|
|
@@ -125,13 +135,37 @@ export declare function planBlobDeletions(storage: Pick<DataStoragePort, "listVe
|
|
|
125
135
|
* markers; step 3 does not wait for them because the tombstone already makes
|
|
126
136
|
* the deletion stick.
|
|
127
137
|
*
|
|
138
|
+
* With `tombstoneFinality` wired, step 2 queues every covered key and sends
|
|
139
|
+
* only keys whose covering tombstone the gateway reports as finalized. The
|
|
140
|
+
* gateway acknowledges a tombstone when it queues it; settlement can still
|
|
141
|
+
* fail, roll the head back and let a writer recover the failed slot with the
|
|
142
|
+
* ciphertext it uploaded there. A key freed before finality could take a
|
|
143
|
+
* second writer's bytes, and the recovered registration would then commit to
|
|
144
|
+
* bytes that are not stored. Reads refuse the scope from step 1 on, so the
|
|
145
|
+
* wait only keeps owner-encrypted ciphertext in storage until finality.
|
|
146
|
+
*
|
|
128
147
|
* Not serialised here: the sync manager runs this under the same lock as its
|
|
129
148
|
* upload/download cycles so a delete cannot interleave with an upload of the
|
|
130
149
|
* same scope on this replica (see engine/sync-manager.ts). Uploads racing on
|
|
131
150
|
* another replica are handled by the upload worker's deletion guard and the
|
|
132
151
|
* download worker's reconcile.
|
|
133
152
|
*/
|
|
134
|
-
export
|
|
153
|
+
export interface DeleteScopeOptions {
|
|
154
|
+
/**
|
|
155
|
+
* Automatic re-submission of a rolled-back delete: the tombstone is queued
|
|
156
|
+
* only over exactly this head (the one the failed tombstone replaced), so a
|
|
157
|
+
* write that landed after the rollback is never deleted.
|
|
158
|
+
*/
|
|
159
|
+
fence?: {
|
|
160
|
+
head: TombstoneFence;
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* False records the storage work but sends no batch in this call; the
|
|
164
|
+
* caller's own pass already used this cycle's storage budget.
|
|
165
|
+
*/
|
|
166
|
+
storagePass?: boolean;
|
|
167
|
+
}
|
|
168
|
+
export declare function deleteScope(deps: DeleteScopeDeps, scope: string, options?: DeleteScopeOptions): Promise<DeleteScopeResult>;
|
|
135
169
|
export interface RetryPendingBlobDeletionsResult {
|
|
136
170
|
/** Exact keys finished this pass (deleted, or already absent). */
|
|
137
171
|
completed: PendingBlobDeletion[];
|
|
@@ -154,10 +188,25 @@ export interface RetryPendingBlobDeletionsResult {
|
|
|
154
188
|
* are expanded from the registry first; if the registry shows the scope live
|
|
155
189
|
* again the old tombstone's version is unknowable, so the marker is dropped
|
|
156
190
|
* and the pre-tombstone ciphertext stays (owner-decryptable only) until the
|
|
157
|
-
* next delete of that scope.
|
|
191
|
+
* next delete of that scope. With `tombstoneFinality` wired, a key is sent
|
|
192
|
+
* only once a tombstone at or above its gate is finalized; a failed
|
|
193
|
+
* tombstone's keys stay queued (the deletion did not happen) until a later
|
|
194
|
+
* deletion of the scope is finalized.
|
|
158
195
|
*/
|
|
159
|
-
export declare
|
|
196
|
+
export declare const TOMBSTONE_RESUBMIT_MAX_ATTEMPTS = 5;
|
|
197
|
+
export type RetryPendingBlobDeletionsDeps = Pick<DeleteScopeDeps, "deleteData" | "pendingBlobDeletions" | "dataPointFeed" | "serverOwner" | "tombstoneFinality" | "logger"> & {
|
|
160
198
|
/** Local index, for expanding unexpanded markers into exact keys. */
|
|
161
199
|
storage?: Pick<DataStoragePort, "listVersions">;
|
|
162
|
-
|
|
200
|
+
/**
|
|
201
|
+
* Re-submits the user's delete of a scope whose tombstone failed, through
|
|
202
|
+
* the ordinary delete path. The caller runs it inside the same mutation
|
|
203
|
+
* queue as this pass.
|
|
204
|
+
*/
|
|
205
|
+
resubmitDeletion?: (scope: string, options: DeleteScopeOptions) => Promise<DeleteScopeResult>;
|
|
206
|
+
/** Bounds the re-submissions; required with `resubmitDeletion`. */
|
|
207
|
+
tombstoneResubmits?: TombstoneResubmitTracker;
|
|
208
|
+
/** Called once when a scope's re-submissions are used up. */
|
|
209
|
+
onDeletionAbandoned?: (state: TombstoneResubmitState) => void;
|
|
210
|
+
};
|
|
211
|
+
export declare function retryPendingBlobDeletions(deps: RetryPendingBlobDeletionsDeps): Promise<RetryPendingBlobDeletionsResult>;
|
|
163
212
|
//# sourceMappingURL=delete.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../../src/sync/workers/delete.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAGpD,OAAO,KAAK,EACV,iBAAiB,EAEjB,eAAe,EACf,cAAc,EACd,mBAAmB,EACnB,wBAAwB,
|
|
1
|
+
{"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../../src/sync/workers/delete.ts"],"names":[],"mappings":"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"}
|