@peerbit/native-backbone 0.1.4 → 0.2.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/README.md +31 -0
- package/dist/src/durability/codec.d.ts +86 -0
- package/dist/src/durability/codec.d.ts.map +1 -0
- package/dist/src/durability/codec.js +365 -0
- package/dist/src/durability/codec.js.map +1 -0
- package/dist/src/durability/lease.d.ts +59 -0
- package/dist/src/durability/lease.d.ts.map +1 -0
- package/dist/src/durability/lease.js +48 -0
- package/dist/src/durability/lease.js.map +1 -0
- package/dist/src/durability/memory-storage.d.ts +37 -0
- package/dist/src/durability/memory-storage.d.ts.map +1 -0
- package/dist/src/durability/memory-storage.js +436 -0
- package/dist/src/durability/memory-storage.js.map +1 -0
- package/dist/src/durability/node-lease.d.ts +14 -0
- package/dist/src/durability/node-lease.d.ts.map +1 -0
- package/dist/src/durability/node-lease.js +214 -0
- package/dist/src/durability/node-lease.js.map +1 -0
- package/dist/src/durability/node-storage.d.ts +76 -0
- package/dist/src/durability/node-storage.d.ts.map +1 -0
- package/dist/src/durability/node-storage.js +1813 -0
- package/dist/src/durability/node-storage.js.map +1 -0
- package/dist/src/durability/storage.d.ts +224 -0
- package/dist/src/durability/storage.d.ts.map +1 -0
- package/dist/src/durability/storage.js +343 -0
- package/dist/src/durability/storage.js.map +1 -0
- package/dist/src/index.d.ts +93 -15
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +717 -199
- package/dist/src/index.js.map +1 -1
- package/dist/wasm/README.md +31 -0
- package/dist/wasm/native_backbone.d.ts +131 -115
- package/dist/wasm/native_backbone.js +96 -0
- package/dist/wasm/native_backbone_bg.wasm +0 -0
- package/dist/wasm/native_backbone_bg.wasm.d.ts +119 -115
- package/package.json +4 -3
- package/src/durability/codec.ts +683 -0
- package/src/durability/lease.ts +87 -0
- package/src/durability/memory-storage.ts +593 -0
- package/src/durability/node-lease.ts +293 -0
- package/src/durability/node-storage.ts +2798 -0
- package/src/durability/storage.ts +682 -0
- package/src/durability.rs +1872 -0
- package/src/index.ts +1392 -735
- package/src/lib.rs +1 -0
package/dist/src/index.js
CHANGED
|
@@ -7,6 +7,12 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
7
7
|
return path;
|
|
8
8
|
};
|
|
9
9
|
import { calculateRawCid, cidifyString } from "@peerbit/blocks-interface";
|
|
10
|
+
export * from "./durability/codec.js";
|
|
11
|
+
export * from "./durability/lease.js";
|
|
12
|
+
export * from "./durability/memory-storage.js";
|
|
13
|
+
export * from "./durability/node-lease.js";
|
|
14
|
+
export * from "./durability/node-storage.js";
|
|
15
|
+
export * from "./durability/storage.js";
|
|
10
16
|
const nativeBackboneHeadFlagsToBytes = (headFlags) => headFlags instanceof Uint8Array
|
|
11
17
|
? headFlags
|
|
12
18
|
: new Uint8Array(headFlags.map((head) => (head ? 1 : 0)));
|
|
@@ -170,8 +176,58 @@ const nativeBackboneCoordinatePersistenceFiles = {
|
|
|
170
176
|
documentSignerSnapshot: "document-signers.bin",
|
|
171
177
|
documentSignerJournal: "document-signers.wal",
|
|
172
178
|
};
|
|
179
|
+
export const nativeBackboneCoordinateDropTombstoneFile = "native-backbone-drop.tombstone";
|
|
173
180
|
export const defaultNativeBackboneCoordinateFlushMaxPendingBytes = 1024 * 1024;
|
|
181
|
+
/** @deprecated Built-in coordinate persistence compaction is currently disabled. */
|
|
174
182
|
export const defaultNativeBackboneCoordinateCompactMaxJournalBytes = 64 * 1024 * 1024;
|
|
183
|
+
const nativeBackboneCoordinateCompactionDisabledMessage = "Native backbone coordinate persistence compaction is disabled until snapshots use a crash-safe generation protocol";
|
|
184
|
+
const nativeBackboneCoordinateJournalMagic = Uint8Array.from([
|
|
185
|
+
0x50, 0x42, 0x52, 0x49, 0x44, 0x58, 0x57, 0x31,
|
|
186
|
+
]);
|
|
187
|
+
const readCoordinateJournalU32 = (bytes, offset) => (bytes[offset] |
|
|
188
|
+
(bytes[offset + 1] << 8) |
|
|
189
|
+
(bytes[offset + 2] << 16) |
|
|
190
|
+
(bytes[offset + 3] << 24)) >>>
|
|
191
|
+
0;
|
|
192
|
+
const coordinateJournalChecksum = (bytes) => {
|
|
193
|
+
let checksum = 0x811c9dc5;
|
|
194
|
+
for (const byte of bytes) {
|
|
195
|
+
checksum = Math.imul(checksum ^ byte, 0x01000193) >>> 0;
|
|
196
|
+
}
|
|
197
|
+
return checksum;
|
|
198
|
+
};
|
|
199
|
+
const hasCoordinateJournalMagic = (bytes) => bytes.byteLength >= nativeBackboneCoordinateJournalMagic.byteLength &&
|
|
200
|
+
nativeBackboneCoordinateJournalMagic.every((byte, index) => bytes[index] === byte);
|
|
201
|
+
/**
|
|
202
|
+
* The Rust decoder intentionally stops at a torn/corrupt tail. Persistence
|
|
203
|
+
* must reject that tail before hydrate, otherwise a later append can land
|
|
204
|
+
* behind it and remain permanently invisible to replay.
|
|
205
|
+
*/
|
|
206
|
+
const validateCoordinateJournal = (bytes, name) => {
|
|
207
|
+
if (!bytes || bytes.byteLength === 0) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
let offset = hasCoordinateJournalMagic(bytes)
|
|
211
|
+
? nativeBackboneCoordinateJournalMagic.byteLength
|
|
212
|
+
: 0;
|
|
213
|
+
while (offset < bytes.byteLength) {
|
|
214
|
+
if (bytes.byteLength - offset < 8) {
|
|
215
|
+
throw new Error(`Native backbone ${name} has a torn record header`);
|
|
216
|
+
}
|
|
217
|
+
const length = readCoordinateJournalU32(bytes, offset);
|
|
218
|
+
const expectedChecksum = readCoordinateJournalU32(bytes, offset + 4);
|
|
219
|
+
const payloadOffset = offset + 8;
|
|
220
|
+
const end = payloadOffset + length;
|
|
221
|
+
if (!Number.isSafeInteger(end) || end > bytes.byteLength) {
|
|
222
|
+
throw new Error(`Native backbone ${name} has a torn record payload`);
|
|
223
|
+
}
|
|
224
|
+
const payload = bytes.subarray(payloadOffset, end);
|
|
225
|
+
if (coordinateJournalChecksum(payload) !== expectedChecksum) {
|
|
226
|
+
throw new Error(`Native backbone ${name} has a checksum mismatch`);
|
|
227
|
+
}
|
|
228
|
+
offset = end;
|
|
229
|
+
}
|
|
230
|
+
};
|
|
175
231
|
const resolveCoordinateFlushMaxPendingBytes = (options) => options.flushMaxPendingBytes != null
|
|
176
232
|
? Math.max(0, options.flushMaxPendingBytes)
|
|
177
233
|
: options.flushOnAppend === false
|
|
@@ -181,6 +237,19 @@ const isNotFoundError = (error) => {
|
|
|
181
237
|
const maybeError = error;
|
|
182
238
|
return maybeError?.code === "ENOENT" || maybeError?.name === "NotFoundError";
|
|
183
239
|
};
|
|
240
|
+
const isUnsupportedDirectorySyncError = (error) => {
|
|
241
|
+
const code = error?.code;
|
|
242
|
+
return (code === "EISDIR" ||
|
|
243
|
+
code === "EINVAL" ||
|
|
244
|
+
code === "EPERM" ||
|
|
245
|
+
code === "ENOTSUP");
|
|
246
|
+
};
|
|
247
|
+
const validateCoordinatePersistenceWriteProgress = (written, remaining, target) => {
|
|
248
|
+
if (!Number.isSafeInteger(written) || written <= 0 || written > remaining) {
|
|
249
|
+
throw new Error(`Invalid ${target} write progress: ${String(written)} for ${remaining} remaining bytes`);
|
|
250
|
+
}
|
|
251
|
+
return written;
|
|
252
|
+
};
|
|
184
253
|
// Non-literal specifiers keep browser bundlers (esbuild statically resolves
|
|
185
254
|
// literal dynamic imports and hard-fails on node builtins) from following
|
|
186
255
|
// these node-only imports; they are only reached on node runtimes.
|
|
@@ -211,6 +280,62 @@ const validateCoordinatePersistenceName = (name) => {
|
|
|
211
280
|
}
|
|
212
281
|
return name;
|
|
213
282
|
};
|
|
283
|
+
const nativeBackboneCoordinateDropTombstoneBodyBytes = (body) => new TextEncoder().encode(JSON.stringify(body));
|
|
284
|
+
const nativeBackboneCoordinateDropChecksum = (bytes) => {
|
|
285
|
+
let checksum = 0xffffffff;
|
|
286
|
+
for (const byte of bytes) {
|
|
287
|
+
checksum ^= byte;
|
|
288
|
+
for (let bit = 0; bit < 8; bit++) {
|
|
289
|
+
checksum = (checksum >>> 1) ^ (checksum & 1 ? 0xedb88320 : 0);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return ((checksum ^ 0xffffffff) >>> 0).toString(16).padStart(8, "0");
|
|
293
|
+
};
|
|
294
|
+
const nativeBackboneCoordinateDropTombstoneBytes = (files) => {
|
|
295
|
+
const body = {
|
|
296
|
+
format: "peerbit-native-backbone-coordinate-drop",
|
|
297
|
+
version: 1,
|
|
298
|
+
files: [...files],
|
|
299
|
+
};
|
|
300
|
+
return new TextEncoder().encode(JSON.stringify({
|
|
301
|
+
...body,
|
|
302
|
+
checksum: nativeBackboneCoordinateDropChecksum(nativeBackboneCoordinateDropTombstoneBodyBytes(body)),
|
|
303
|
+
}));
|
|
304
|
+
};
|
|
305
|
+
const parseNativeBackboneCoordinateDropTombstone = (bytes) => {
|
|
306
|
+
let parsed;
|
|
307
|
+
try {
|
|
308
|
+
parsed = JSON.parse(new TextDecoder().decode(bytes));
|
|
309
|
+
}
|
|
310
|
+
catch (error) {
|
|
311
|
+
throw new Error("Invalid native backbone drop tombstone JSON", {
|
|
312
|
+
cause: error,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
const candidate = parsed;
|
|
316
|
+
if (!candidate ||
|
|
317
|
+
candidate.format !== "peerbit-native-backbone-coordinate-drop" ||
|
|
318
|
+
candidate.version !== 1 ||
|
|
319
|
+
!Array.isArray(candidate.files) ||
|
|
320
|
+
candidate.files.some((name) => typeof name !== "string") ||
|
|
321
|
+
typeof candidate.checksum !== "string") {
|
|
322
|
+
throw new Error("Invalid native backbone drop tombstone");
|
|
323
|
+
}
|
|
324
|
+
const files = candidate.files.map(validateCoordinatePersistenceName);
|
|
325
|
+
if (new Set(files).size !== files.length ||
|
|
326
|
+
files.includes(nativeBackboneCoordinateDropTombstoneFile)) {
|
|
327
|
+
throw new Error("Invalid native backbone drop tombstone file set");
|
|
328
|
+
}
|
|
329
|
+
const body = {
|
|
330
|
+
format: "peerbit-native-backbone-coordinate-drop",
|
|
331
|
+
version: 1,
|
|
332
|
+
files,
|
|
333
|
+
};
|
|
334
|
+
if (nativeBackboneCoordinateDropChecksum(nativeBackboneCoordinateDropTombstoneBodyBytes(body)) !== candidate.checksum) {
|
|
335
|
+
throw new Error("Native backbone drop tombstone checksum mismatch");
|
|
336
|
+
}
|
|
337
|
+
return body;
|
|
338
|
+
};
|
|
214
339
|
const concatBytes = (chunks) => {
|
|
215
340
|
const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
|
|
216
341
|
const out = new Uint8Array(total);
|
|
@@ -706,7 +831,9 @@ const validateNativeBackboneCoordinateCommitColumns = (columns) => {
|
|
|
706
831
|
columns.assignedToRangeBoundaries.length !== length) {
|
|
707
832
|
throw new Error("Expected equal native coordinate commit column lengths");
|
|
708
833
|
}
|
|
709
|
-
if (columns.hashNumberValues ||
|
|
834
|
+
if (columns.hashNumberValues ||
|
|
835
|
+
columns.coordinateCounts ||
|
|
836
|
+
columns.coordinateValues) {
|
|
710
837
|
if (!columns.hashNumberValues ||
|
|
711
838
|
!columns.coordinateCounts ||
|
|
712
839
|
!columns.coordinateValues ||
|
|
@@ -726,7 +853,9 @@ const validateNativeBackboneCoordinateCommitColumns = (columns) => {
|
|
|
726
853
|
columns.requestedReplicaValues.length !== length) {
|
|
727
854
|
throw new Error("Expected equal native coordinate replica column lengths");
|
|
728
855
|
}
|
|
729
|
-
if (columns.hashNumbers ||
|
|
856
|
+
if (columns.hashNumbers ||
|
|
857
|
+
columns.coordinateBatches ||
|
|
858
|
+
columns.requestedReplicas) {
|
|
730
859
|
if (!columns.hashNumbers ||
|
|
731
860
|
!columns.coordinateBatches ||
|
|
732
861
|
!columns.requestedReplicas ||
|
|
@@ -752,7 +881,9 @@ const hasNativeBackboneNumericCoordinateCommitColumns = (columns) => !!columns.h
|
|
|
752
881
|
!!columns.coordinateValues &&
|
|
753
882
|
!!columns.requestedReplicaValues;
|
|
754
883
|
const nativeBackboneCoordinateCommitStringColumns = (columns) => {
|
|
755
|
-
if (columns.hashNumbers &&
|
|
884
|
+
if (columns.hashNumbers &&
|
|
885
|
+
columns.coordinateBatches &&
|
|
886
|
+
columns.requestedReplicas) {
|
|
756
887
|
return {
|
|
757
888
|
hashNumbers: columns.hashNumbers,
|
|
758
889
|
coordinateBatches: columns.coordinateBatches,
|
|
@@ -1171,9 +1302,7 @@ class NativeBackboneLogGraph {
|
|
|
1171
1302
|
? ""
|
|
1172
1303
|
: integerString(documentIndex.existingCreated), documentIndex.byteElementIndexLimit ?? 0, documentIndex.deleteTrimmedHeads === true, this.options.documentProjectionPlanId(projection.plan), projection.encodedDocument, projection.signer));
|
|
1173
1304
|
}
|
|
1174
|
-
if (documentIndexArgs &&
|
|
1175
|
-
input.trimLengthTo == null &&
|
|
1176
|
-
hasNoNext) {
|
|
1305
|
+
if (documentIndexArgs && input.trimLengthTo == null && hasNoNext) {
|
|
1177
1306
|
return preparedCommitFactsFromRow(this.native.prepare_plain_entry_commit_no_next_facts_document_index_compact(wallTime, logical, input.gid, entryType, input.metaData, input.payloadData, ...documentIndexArgs));
|
|
1178
1307
|
}
|
|
1179
1308
|
if (documentIndexArgs &&
|
|
@@ -2137,7 +2266,7 @@ export class NativePeerbitBackbone {
|
|
|
2137
2266
|
});
|
|
2138
2267
|
}
|
|
2139
2268
|
commitEntryCoordinatesColumnsBatch(columns) {
|
|
2140
|
-
const { hashes, gids, nextHashBatches, assignedToRangeBoundaries
|
|
2269
|
+
const { hashes, gids, nextHashBatches, assignedToRangeBoundaries } = columns;
|
|
2141
2270
|
if (hashes.length === 0) {
|
|
2142
2271
|
return;
|
|
2143
2272
|
}
|
|
@@ -2628,9 +2757,7 @@ export class NativePeerbitBackbone {
|
|
|
2628
2757
|
];
|
|
2629
2758
|
const documentKeys = input.entries.map((entry) => entry.documentIndex.key);
|
|
2630
2759
|
const usePlainPutPayload = input.entries.every((entry) => entry.documentIndex.usePlainPutPayload === true);
|
|
2631
|
-
if (!requiredPreviousSignerPublicKey &&
|
|
2632
|
-
useCompact &&
|
|
2633
|
-
usePlainPutPayload) {
|
|
2760
|
+
if (!requiredPreviousSignerPublicKey && useCompact && usePlainPutPayload) {
|
|
2634
2761
|
const nativeCompactPlainPutPayloadBatch = this.native
|
|
2635
2762
|
.prepare_plain_committed_storage_append_document_index_latest_compact_plain_put_payload_batch_transaction;
|
|
2636
2763
|
if (nativeCompactPlainPutPayloadBatch) {
|
|
@@ -2774,11 +2901,8 @@ export class NativePeerbitBackbone {
|
|
|
2774
2901
|
new Uint32Array(input.entries.map((entry) => this.documentProjectionPlanId(entry.documentIndex.projection.plan))),
|
|
2775
2902
|
];
|
|
2776
2903
|
const rows = usePlainPutPayload
|
|
2777
|
-
? this.native
|
|
2778
|
-
|
|
2779
|
-
: this.native
|
|
2780
|
-
.prepare_plain_committed_no_next_storage_append_document_index_cached_plan_compact_batch_transaction?.call(this.native, ...baseArgs, input.entries.map((entry) => entry.documentIndex.projection
|
|
2781
|
-
.encodedDocument), input.entries.map((entry) => entry.documentIndex.projection.signer), input.trimLengthTo);
|
|
2904
|
+
? this.native.prepare_plain_committed_no_next_storage_append_document_index_cached_plan_compact_plain_put_payload_batch_transaction?.call(this.native, ...baseArgs, input.entries.map((entry) => entry.documentIndex.projection.signer), input.trimLengthTo)
|
|
2905
|
+
: this.native.prepare_plain_committed_no_next_storage_append_document_index_cached_plan_compact_batch_transaction?.call(this.native, ...baseArgs, input.entries.map((entry) => entry.documentIndex.projection.encodedDocument), input.entries.map((entry) => entry.documentIndex.projection.signer), input.trimLengthTo);
|
|
2782
2906
|
if (!rows) {
|
|
2783
2907
|
return undefined;
|
|
2784
2908
|
}
|
|
@@ -2827,6 +2951,7 @@ export class NativeBackboneMemoryCoordinatePersistenceStore {
|
|
|
2827
2951
|
async remove(name) {
|
|
2828
2952
|
this.files.delete(validateCoordinatePersistenceName(name));
|
|
2829
2953
|
}
|
|
2954
|
+
async flush(_name) { }
|
|
2830
2955
|
}
|
|
2831
2956
|
export class NativeBackboneNodeCoordinatePersistenceStore {
|
|
2832
2957
|
directory;
|
|
@@ -2834,9 +2959,17 @@ export class NativeBackboneNodeCoordinatePersistenceStore {
|
|
|
2834
2959
|
appendHandles = new Map();
|
|
2835
2960
|
filePaths = new Map();
|
|
2836
2961
|
directoryEnsured = false;
|
|
2962
|
+
appendFailure;
|
|
2963
|
+
durableBarrier;
|
|
2837
2964
|
constructor(directory, fs) {
|
|
2838
2965
|
this.directory = directory;
|
|
2839
2966
|
this.fs = fs;
|
|
2967
|
+
// The default Node backend has FileHandle.sync. Injected test/custom
|
|
2968
|
+
// backends only advertise durability when they at least expose open(); the
|
|
2969
|
+
// barrier itself verifies sync on every opened handle before ACK.
|
|
2970
|
+
if (!fs || typeof fs.open === "function") {
|
|
2971
|
+
this.durableBarrier = (name) => this.syncDurably(name);
|
|
2972
|
+
}
|
|
2840
2973
|
}
|
|
2841
2974
|
async nodeFs() {
|
|
2842
2975
|
return this.fs ?? (await importNodeFs());
|
|
@@ -2896,14 +3029,44 @@ export class NativeBackboneNodeCoordinatePersistenceStore {
|
|
|
2896
3029
|
await fs.writeFile(path, bytes);
|
|
2897
3030
|
}
|
|
2898
3031
|
async append(name, bytes) {
|
|
3032
|
+
if (this.appendFailure !== undefined) {
|
|
3033
|
+
throw this.appendFailure;
|
|
3034
|
+
}
|
|
2899
3035
|
const fs = await this.ensureDirectory();
|
|
2900
3036
|
const path = await this.filePath(name);
|
|
2901
3037
|
const handle = await this.appendHandle(fs, path);
|
|
2902
3038
|
if (handle) {
|
|
2903
|
-
|
|
3039
|
+
let offset = 0;
|
|
3040
|
+
while (offset < bytes.byteLength) {
|
|
3041
|
+
let result;
|
|
3042
|
+
try {
|
|
3043
|
+
result = await handle.write(bytes.subarray(offset));
|
|
3044
|
+
}
|
|
3045
|
+
catch (error) {
|
|
3046
|
+
// FileHandle.write does not expose progress on rejection, even for the
|
|
3047
|
+
// first call, so a partial tail can not be ruled out.
|
|
3048
|
+
this.appendFailure ??= error;
|
|
3049
|
+
throw this.appendFailure;
|
|
3050
|
+
}
|
|
3051
|
+
try {
|
|
3052
|
+
offset += validateCoordinatePersistenceWriteProgress(result.bytesWritten, bytes.byteLength - offset, "Node coordinate WAL");
|
|
3053
|
+
}
|
|
3054
|
+
catch (error) {
|
|
3055
|
+
this.appendFailure ??= error;
|
|
3056
|
+
throw this.appendFailure;
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
2904
3059
|
return;
|
|
2905
3060
|
}
|
|
2906
|
-
|
|
3061
|
+
try {
|
|
3062
|
+
await fs.appendFile(path, bytes);
|
|
3063
|
+
}
|
|
3064
|
+
catch (error) {
|
|
3065
|
+
// appendFile does not report partial progress. Conservatively prevent a
|
|
3066
|
+
// later append from landing behind a possibly torn tail.
|
|
3067
|
+
this.appendFailure ??= error;
|
|
3068
|
+
throw this.appendFailure;
|
|
3069
|
+
}
|
|
2907
3070
|
}
|
|
2908
3071
|
async remove(name) {
|
|
2909
3072
|
const fs = await this.nodeFs();
|
|
@@ -2918,6 +3081,67 @@ export class NativeBackboneNodeCoordinatePersistenceStore {
|
|
|
2918
3081
|
}
|
|
2919
3082
|
}
|
|
2920
3083
|
}
|
|
3084
|
+
async flush(name) {
|
|
3085
|
+
if (!this.durableBarrier) {
|
|
3086
|
+
return;
|
|
3087
|
+
}
|
|
3088
|
+
try {
|
|
3089
|
+
await this.durableBarrier(name);
|
|
3090
|
+
}
|
|
3091
|
+
catch (error) {
|
|
3092
|
+
// Ordinary buffering reads flush a named queue before discovering whether
|
|
3093
|
+
// the backing file exists. Missing files are expected there; strict ACK
|
|
3094
|
+
// paths call durableBarrier directly and therefore remain fail-closed.
|
|
3095
|
+
if (!name || !isNotFoundError(error)) {
|
|
3096
|
+
throw error;
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
async syncDurably(name) {
|
|
3101
|
+
const fs = await this.nodeFs();
|
|
3102
|
+
if (!fs.open) {
|
|
3103
|
+
throw new Error("Node coordinate persistence does not expose a durable FileHandle.sync barrier");
|
|
3104
|
+
}
|
|
3105
|
+
const syncHandle = async (handle, target) => {
|
|
3106
|
+
if (typeof handle.sync !== "function") {
|
|
3107
|
+
throw new Error(`Node coordinate persistence ${target} does not expose FileHandle.sync`);
|
|
3108
|
+
}
|
|
3109
|
+
await handle.sync();
|
|
3110
|
+
};
|
|
3111
|
+
if (name) {
|
|
3112
|
+
const path = await this.filePath(name);
|
|
3113
|
+
const appendHandle = this.appendHandles.get(path);
|
|
3114
|
+
if (appendHandle) {
|
|
3115
|
+
await syncHandle(await appendHandle, `file ${name}`);
|
|
3116
|
+
}
|
|
3117
|
+
else {
|
|
3118
|
+
let handle;
|
|
3119
|
+
try {
|
|
3120
|
+
handle = await fs.open(path, "r");
|
|
3121
|
+
await syncHandle(handle, `file ${name}`);
|
|
3122
|
+
}
|
|
3123
|
+
finally {
|
|
3124
|
+
await handle?.close();
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
else {
|
|
3129
|
+
await Promise.all([...this.appendHandles.entries()].map(async ([path, handle]) => syncHandle(await handle, `file ${path}`)));
|
|
3130
|
+
}
|
|
3131
|
+
let directoryHandle;
|
|
3132
|
+
try {
|
|
3133
|
+
directoryHandle = await fs.open(this.directory, "r");
|
|
3134
|
+
await syncHandle(directoryHandle, "directory");
|
|
3135
|
+
}
|
|
3136
|
+
catch (error) {
|
|
3137
|
+
if (!isNotFoundError(error) && !isUnsupportedDirectorySyncError(error)) {
|
|
3138
|
+
throw error;
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3141
|
+
finally {
|
|
3142
|
+
await directoryHandle?.close();
|
|
3143
|
+
}
|
|
3144
|
+
}
|
|
2921
3145
|
async close() {
|
|
2922
3146
|
this.directoryEnsured = false;
|
|
2923
3147
|
const handles = [...this.appendHandles.values()];
|
|
@@ -2927,6 +3151,7 @@ export class NativeBackboneNodeCoordinatePersistenceStore {
|
|
|
2927
3151
|
}
|
|
2928
3152
|
export class NativeBackboneOPFSCoordinatePersistenceStore {
|
|
2929
3153
|
directory;
|
|
3154
|
+
appendFailure;
|
|
2930
3155
|
constructor(directory) {
|
|
2931
3156
|
this.directory = directory;
|
|
2932
3157
|
}
|
|
@@ -2979,6 +3204,9 @@ export class NativeBackboneOPFSCoordinatePersistenceStore {
|
|
|
2979
3204
|
}
|
|
2980
3205
|
}
|
|
2981
3206
|
async append(name, bytes) {
|
|
3207
|
+
if (this.appendFailure !== undefined) {
|
|
3208
|
+
throw this.appendFailure;
|
|
3209
|
+
}
|
|
2982
3210
|
const handle = await this.directory.getFileHandle(validateCoordinatePersistenceName(name), { create: true });
|
|
2983
3211
|
if (handle.createSyncAccessHandle) {
|
|
2984
3212
|
let access;
|
|
@@ -2989,11 +3217,28 @@ export class NativeBackboneOPFSCoordinatePersistenceStore {
|
|
|
2989
3217
|
// Main-thread OPFS and some browser contexts do not expose sync handles.
|
|
2990
3218
|
}
|
|
2991
3219
|
if (access) {
|
|
3220
|
+
const originalSize = access.getSize();
|
|
3221
|
+
let offset = 0;
|
|
2992
3222
|
try {
|
|
2993
|
-
|
|
3223
|
+
while (offset < bytes.byteLength) {
|
|
3224
|
+
offset += validateCoordinatePersistenceWriteProgress(access.write(bytes.subarray(offset), {
|
|
3225
|
+
at: originalSize + offset,
|
|
3226
|
+
}), bytes.byteLength - offset, "OPFS coordinate WAL");
|
|
3227
|
+
}
|
|
2994
3228
|
access.flush?.();
|
|
2995
3229
|
return;
|
|
2996
3230
|
}
|
|
3231
|
+
catch (error) {
|
|
3232
|
+
try {
|
|
3233
|
+
access.truncate?.(originalSize);
|
|
3234
|
+
access.flush?.();
|
|
3235
|
+
}
|
|
3236
|
+
catch {
|
|
3237
|
+
// The sticky error below prevents appending behind a torn tail.
|
|
3238
|
+
}
|
|
3239
|
+
this.appendFailure ??= error;
|
|
3240
|
+
throw this.appendFailure;
|
|
3241
|
+
}
|
|
2997
3242
|
finally {
|
|
2998
3243
|
access.close();
|
|
2999
3244
|
}
|
|
@@ -3004,10 +3249,19 @@ export class NativeBackboneOPFSCoordinatePersistenceStore {
|
|
|
3004
3249
|
try {
|
|
3005
3250
|
await writable.seek(file.size);
|
|
3006
3251
|
await writable.write(bytes);
|
|
3007
|
-
}
|
|
3008
|
-
finally {
|
|
3009
3252
|
await writable.close();
|
|
3010
3253
|
}
|
|
3254
|
+
catch (error) {
|
|
3255
|
+
try {
|
|
3256
|
+
await writable.close();
|
|
3257
|
+
}
|
|
3258
|
+
catch {
|
|
3259
|
+
// Preserve the first error; the adapter is terminal either way.
|
|
3260
|
+
}
|
|
3261
|
+
// Async OPFS reports no write progress, so failure may leave a torn tail.
|
|
3262
|
+
this.appendFailure ??= error;
|
|
3263
|
+
throw this.appendFailure;
|
|
3264
|
+
}
|
|
3011
3265
|
}
|
|
3012
3266
|
async remove(name) {
|
|
3013
3267
|
try {
|
|
@@ -3019,15 +3273,45 @@ export class NativeBackboneOPFSCoordinatePersistenceStore {
|
|
|
3019
3273
|
}
|
|
3020
3274
|
}
|
|
3021
3275
|
}
|
|
3276
|
+
async flush(_name) { }
|
|
3277
|
+
async durableBarrier(name) {
|
|
3278
|
+
if (!name) {
|
|
3279
|
+
throw new Error("OPFS coordinate persistence requires a named durable barrier");
|
|
3280
|
+
}
|
|
3281
|
+
const handle = await this.directory.getFileHandle(validateCoordinatePersistenceName(name), { create: false });
|
|
3282
|
+
if (!handle.createSyncAccessHandle) {
|
|
3283
|
+
throw new Error("OPFS coordinate persistence durable barriers require createSyncAccessHandle");
|
|
3284
|
+
}
|
|
3285
|
+
const access = await handle.createSyncAccessHandle();
|
|
3286
|
+
try {
|
|
3287
|
+
if (typeof access.flush !== "function") {
|
|
3288
|
+
throw new Error("OPFS coordinate persistence sync handle does not expose flush");
|
|
3289
|
+
}
|
|
3290
|
+
access.flush();
|
|
3291
|
+
}
|
|
3292
|
+
finally {
|
|
3293
|
+
access.close();
|
|
3294
|
+
}
|
|
3295
|
+
}
|
|
3022
3296
|
}
|
|
3023
3297
|
export class NativeBackboneBufferedCoordinatePersistenceStore {
|
|
3024
3298
|
inner;
|
|
3025
3299
|
options;
|
|
3026
3300
|
buffers = new Map();
|
|
3027
3301
|
bufferedBytes = 0;
|
|
3302
|
+
supportsRemoval;
|
|
3303
|
+
durableBarrier;
|
|
3028
3304
|
constructor(inner, options = {}) {
|
|
3029
3305
|
this.inner = inner;
|
|
3030
3306
|
this.options = options;
|
|
3307
|
+
this.supportsRemoval =
|
|
3308
|
+
inner.supportsRemoval ?? typeof inner.remove === "function";
|
|
3309
|
+
if (typeof inner.durableBarrier === "function") {
|
|
3310
|
+
this.durableBarrier = async (name) => {
|
|
3311
|
+
await this.flush(name);
|
|
3312
|
+
await inner.durableBarrier(name);
|
|
3313
|
+
};
|
|
3314
|
+
}
|
|
3031
3315
|
}
|
|
3032
3316
|
buffer(name) {
|
|
3033
3317
|
const validName = validateCoordinatePersistenceName(name);
|
|
@@ -3067,7 +3351,10 @@ export class NativeBackboneBufferedCoordinatePersistenceStore {
|
|
|
3067
3351
|
this.bufferedBytes -= chunk.byteLength;
|
|
3068
3352
|
}
|
|
3069
3353
|
}
|
|
3070
|
-
|
|
3354
|
+
if (!this.inner.remove) {
|
|
3355
|
+
throw new Error("Native backbone coordinate persistence store does not support removal");
|
|
3356
|
+
}
|
|
3357
|
+
await this.inner.remove(name);
|
|
3071
3358
|
}
|
|
3072
3359
|
async flush(name) {
|
|
3073
3360
|
const names = name
|
|
@@ -3083,11 +3370,17 @@ export class NativeBackboneBufferedCoordinatePersistenceStore {
|
|
|
3083
3370
|
this.bufferedBytes -= bytes.byteLength;
|
|
3084
3371
|
await this.inner.append(fileName, bytes);
|
|
3085
3372
|
}
|
|
3086
|
-
await this.inner.flush?.();
|
|
3373
|
+
await this.inner.flush?.(name);
|
|
3087
3374
|
}
|
|
3088
|
-
async close() {
|
|
3089
|
-
|
|
3090
|
-
|
|
3375
|
+
async close(options) {
|
|
3376
|
+
if (options?.flush === false) {
|
|
3377
|
+
this.buffers.clear();
|
|
3378
|
+
this.bufferedBytes = 0;
|
|
3379
|
+
}
|
|
3380
|
+
else {
|
|
3381
|
+
await this.flush();
|
|
3382
|
+
}
|
|
3383
|
+
await this.inner.close?.(options);
|
|
3091
3384
|
}
|
|
3092
3385
|
}
|
|
3093
3386
|
export class NativeBackboneCoordinatePersistence {
|
|
@@ -3097,6 +3390,10 @@ export class NativeBackboneCoordinatePersistence {
|
|
|
3097
3390
|
flushIntervalMs;
|
|
3098
3391
|
compactMaxJournalBytes;
|
|
3099
3392
|
compactMaxJournalRecords;
|
|
3393
|
+
crashSafeCompaction = false;
|
|
3394
|
+
durableBarrier;
|
|
3395
|
+
supportsDrop;
|
|
3396
|
+
dropIsTerminal = true;
|
|
3100
3397
|
snapshotFile;
|
|
3101
3398
|
journalFile;
|
|
3102
3399
|
documentSnapshotFile;
|
|
@@ -3104,75 +3401,270 @@ export class NativeBackboneCoordinatePersistence {
|
|
|
3104
3401
|
documentSignerSnapshotFile;
|
|
3105
3402
|
documentSignerJournalFile;
|
|
3106
3403
|
journalInitialized;
|
|
3107
|
-
journalByteLength = 0;
|
|
3108
|
-
journalRecordCount = 0;
|
|
3109
3404
|
documentJournalInitialized;
|
|
3110
|
-
documentJournalByteLength = 0;
|
|
3111
|
-
documentJournalRecordCount = 0;
|
|
3112
3405
|
documentSignerJournalInitialized;
|
|
3113
|
-
documentSignerJournalByteLength = 0;
|
|
3114
|
-
documentSignerJournalRecordCount = 0;
|
|
3115
3406
|
lastFlushMs = Date.now();
|
|
3116
3407
|
persistenceQueue;
|
|
3408
|
+
persistenceLifecycle = "active";
|
|
3409
|
+
dropInitiatedOnGeneration = false;
|
|
3410
|
+
persistenceFailure;
|
|
3411
|
+
closeMode;
|
|
3412
|
+
closeFlushCompleted = false;
|
|
3413
|
+
closeStoreCompleted = false;
|
|
3414
|
+
closePromise;
|
|
3117
3415
|
constructor(store, options = {}) {
|
|
3118
3416
|
this.store = store;
|
|
3119
|
-
this.
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
this.
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
this.documentSignerSnapshotFile =
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3417
|
+
this.durableBarrier = typeof store.durableBarrier === "function";
|
|
3418
|
+
this.supportsDrop =
|
|
3419
|
+
(store.supportsRemoval ?? typeof store.remove === "function") &&
|
|
3420
|
+
typeof store.remove === "function";
|
|
3421
|
+
this.snapshotFile = validateCoordinatePersistenceName(options.snapshot ?? nativeBackboneCoordinatePersistenceFiles.snapshot);
|
|
3422
|
+
this.journalFile = validateCoordinatePersistenceName(options.journal ?? nativeBackboneCoordinatePersistenceFiles.journal);
|
|
3423
|
+
this.documentSnapshotFile = validateCoordinatePersistenceName(options.documentSnapshot ??
|
|
3424
|
+
nativeBackboneCoordinatePersistenceFiles.documentSnapshot);
|
|
3425
|
+
this.documentJournalFile = validateCoordinatePersistenceName(options.documentJournal ??
|
|
3426
|
+
nativeBackboneCoordinatePersistenceFiles.documentJournal);
|
|
3427
|
+
this.documentSignerSnapshotFile = validateCoordinatePersistenceName(options.documentSignerSnapshot ??
|
|
3428
|
+
nativeBackboneCoordinatePersistenceFiles.documentSignerSnapshot);
|
|
3429
|
+
this.documentSignerJournalFile = validateCoordinatePersistenceName(options.documentSignerJournal ??
|
|
3430
|
+
nativeBackboneCoordinatePersistenceFiles.documentSignerJournal);
|
|
3431
|
+
if (this.configuredFiles().includes(nativeBackboneCoordinateDropTombstoneFile)) {
|
|
3432
|
+
throw new Error("Native backbone coordinate persistence file conflicts with its drop tombstone");
|
|
3433
|
+
}
|
|
3135
3434
|
this.flushOnAppend = options.flushOnAppend ?? true;
|
|
3136
3435
|
this.flushMaxPendingBytes = resolveCoordinateFlushMaxPendingBytes(options);
|
|
3137
3436
|
if (options.flushIntervalMs != null) {
|
|
3138
3437
|
this.flushIntervalMs = Math.max(0, options.flushIntervalMs);
|
|
3139
3438
|
}
|
|
3140
|
-
if (options.compactMaxJournalBytes != null
|
|
3141
|
-
|
|
3439
|
+
if (options.compactMaxJournalBytes != null ||
|
|
3440
|
+
options.compactMaxJournalRecords != null) {
|
|
3441
|
+
throw new Error(nativeBackboneCoordinateCompactionDisabledMessage);
|
|
3142
3442
|
}
|
|
3143
|
-
|
|
3144
|
-
|
|
3443
|
+
}
|
|
3444
|
+
/** Durable operation-intent capability for strict shared-log transactions. */
|
|
3445
|
+
get intentStore() {
|
|
3446
|
+
return this.store;
|
|
3447
|
+
}
|
|
3448
|
+
configuredFiles() {
|
|
3449
|
+
return [
|
|
3450
|
+
this.snapshotFile,
|
|
3451
|
+
this.journalFile,
|
|
3452
|
+
this.documentSnapshotFile,
|
|
3453
|
+
this.documentJournalFile,
|
|
3454
|
+
this.documentSignerSnapshotFile,
|
|
3455
|
+
this.documentSignerJournalFile,
|
|
3456
|
+
];
|
|
3457
|
+
}
|
|
3458
|
+
assertLifecycleActive(operation) {
|
|
3459
|
+
if (this.persistenceLifecycle !== "active") {
|
|
3460
|
+
throw new Error(`Native backbone coordinate persistence can not ${operation} while ${this.persistenceLifecycle}`);
|
|
3145
3461
|
}
|
|
3146
3462
|
}
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
this.
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
this.
|
|
3160
|
-
this.journalByteLength = journal?.byteLength ?? 0;
|
|
3161
|
-
this.journalRecordCount = operations;
|
|
3162
|
-
this.documentJournalInitialized =
|
|
3163
|
-
!!documentJournal && documentJournal.byteLength > 0;
|
|
3164
|
-
this.documentJournalByteLength = documentJournal?.byteLength ?? 0;
|
|
3165
|
-
this.documentJournalRecordCount = documentOperations;
|
|
3166
|
-
this.documentSignerJournalInitialized =
|
|
3167
|
-
!!documentSignerJournal && documentSignerJournal.byteLength > 0;
|
|
3168
|
-
this.documentSignerJournalByteLength =
|
|
3169
|
-
documentSignerJournal?.byteLength ?? 0;
|
|
3170
|
-
this.documentSignerJournalRecordCount = documentSignerOperations;
|
|
3171
|
-
backbone.setCoordinateJournalEnabled(true);
|
|
3172
|
-
backbone.setDocumentJournalEnabled(true);
|
|
3173
|
-
backbone.setDocumentSignerJournalEnabled(true);
|
|
3463
|
+
assertActive(operation) {
|
|
3464
|
+
if (this.persistenceFailure !== undefined) {
|
|
3465
|
+
throw this.persistenceFailure;
|
|
3466
|
+
}
|
|
3467
|
+
this.assertLifecycleActive(operation);
|
|
3468
|
+
if (this.dropInitiatedOnGeneration) {
|
|
3469
|
+
throw new Error(`Native backbone coordinate persistence can not ${operation} after drop was initiated; retry drop or resume drop first`);
|
|
3470
|
+
}
|
|
3471
|
+
}
|
|
3472
|
+
resetJournalTracking() {
|
|
3473
|
+
this.journalInitialized = undefined;
|
|
3474
|
+
this.documentJournalInitialized = undefined;
|
|
3475
|
+
this.documentSignerJournalInitialized = undefined;
|
|
3174
3476
|
this.lastFlushMs = Date.now();
|
|
3175
|
-
|
|
3477
|
+
}
|
|
3478
|
+
async eraseDropFiles(files) {
|
|
3479
|
+
if (!this.supportsDrop || !this.store.remove) {
|
|
3480
|
+
throw new Error("Native backbone coordinate persistence store does not support removal");
|
|
3481
|
+
}
|
|
3482
|
+
const removals = await Promise.allSettled(files.map((name) => this.store.remove(name)));
|
|
3483
|
+
const failures = removals.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
3484
|
+
if (failures.length > 0) {
|
|
3485
|
+
throw new AggregateError(failures, "Failed to erase native backbone coordinate persistence namespace");
|
|
3486
|
+
}
|
|
3487
|
+
await this.store.flush?.();
|
|
3488
|
+
await this.store.remove(nativeBackboneCoordinateDropTombstoneFile);
|
|
3489
|
+
await this.store.flush?.();
|
|
3490
|
+
}
|
|
3491
|
+
async drop(additionalFiles = []) {
|
|
3492
|
+
// Drop is the recovery escape hatch for a persistence generation that has
|
|
3493
|
+
// already failed closed, so only the lifecycle (not persistenceFailure)
|
|
3494
|
+
// gates this destructive operation.
|
|
3495
|
+
this.assertLifecycleActive("drop");
|
|
3496
|
+
const files = [
|
|
3497
|
+
...new Set([
|
|
3498
|
+
...this.configuredFiles(),
|
|
3499
|
+
...additionalFiles.map(validateCoordinatePersistenceName),
|
|
3500
|
+
]),
|
|
3501
|
+
];
|
|
3502
|
+
if (files.includes(nativeBackboneCoordinateDropTombstoneFile)) {
|
|
3503
|
+
throw new Error("Native backbone drop targets can not include the drop tombstone");
|
|
3504
|
+
}
|
|
3505
|
+
if (!this.supportsDrop || !this.store.remove) {
|
|
3506
|
+
throw new Error("Native backbone coordinate persistence store does not support removal");
|
|
3507
|
+
}
|
|
3508
|
+
// Flip synchronously so no later append/compact can enqueue behind this
|
|
3509
|
+
// erase and recreate a file after its removal.
|
|
3510
|
+
this.dropInitiatedOnGeneration = true;
|
|
3511
|
+
this.persistenceLifecycle = "dropping";
|
|
3512
|
+
await this.enqueuePersistence(async () => {
|
|
3513
|
+
await this.store.write(nativeBackboneCoordinateDropTombstoneFile, nativeBackboneCoordinateDropTombstoneBytes(files));
|
|
3514
|
+
if (this.store.durableBarrier) {
|
|
3515
|
+
await this.store.durableBarrier(nativeBackboneCoordinateDropTombstoneFile);
|
|
3516
|
+
}
|
|
3517
|
+
else {
|
|
3518
|
+
await this.store.flush?.(nativeBackboneCoordinateDropTombstoneFile);
|
|
3519
|
+
}
|
|
3520
|
+
await this.eraseDropFiles(files);
|
|
3521
|
+
this.resetJournalTracking();
|
|
3522
|
+
this.persistenceLifecycle = "dropped";
|
|
3523
|
+
});
|
|
3524
|
+
}
|
|
3525
|
+
async resumeDrop() {
|
|
3526
|
+
if (this.persistenceLifecycle === "dropped") {
|
|
3527
|
+
return true;
|
|
3528
|
+
}
|
|
3529
|
+
if (this.closeMode !== undefined) {
|
|
3530
|
+
throw new Error("Native backbone coordinate persistence can not resume drop after close was initiated");
|
|
3531
|
+
}
|
|
3532
|
+
const resumesInProgress = this.persistenceLifecycle === "dropping";
|
|
3533
|
+
const completesInitiatedDrop = this.dropInitiatedOnGeneration;
|
|
3534
|
+
if (!resumesInProgress) {
|
|
3535
|
+
if (completesInitiatedDrop) {
|
|
3536
|
+
this.assertLifecycleActive("resume drop");
|
|
3537
|
+
}
|
|
3538
|
+
else {
|
|
3539
|
+
this.assertActive("resume drop");
|
|
3540
|
+
}
|
|
3541
|
+
}
|
|
3542
|
+
this.persistenceLifecycle = "dropping";
|
|
3543
|
+
return this.enqueuePersistence(async () => {
|
|
3544
|
+
// A concurrent drop may have completed before this queued recovery runs.
|
|
3545
|
+
// Preserve its terminal lifecycle instead of reactivating the generation.
|
|
3546
|
+
if (this.persistenceLifecycle === "dropped") {
|
|
3547
|
+
return true;
|
|
3548
|
+
}
|
|
3549
|
+
return this.resumeDropInternal(completesInitiatedDrop ? "dropped" : "active");
|
|
3550
|
+
});
|
|
3551
|
+
}
|
|
3552
|
+
async hydrate(backbone) {
|
|
3553
|
+
this.assertActive("hydrate");
|
|
3554
|
+
// Claim the lifecycle synchronously. close() queues after this complete
|
|
3555
|
+
// read/validate/load operation; drop() rejects before erasing anything.
|
|
3556
|
+
this.persistenceLifecycle = "hydrating";
|
|
3557
|
+
return this.enqueuePersistence(async () => {
|
|
3558
|
+
try {
|
|
3559
|
+
this.assertHydrating();
|
|
3560
|
+
await this.resumeDropInternal("hydrating");
|
|
3561
|
+
this.assertHydrating();
|
|
3562
|
+
const [snapshot, journal, documentSnapshot, documentJournal, documentSignerSnapshot, documentSignerJournal,] = await Promise.all([
|
|
3563
|
+
this.store.read(this.snapshotFile),
|
|
3564
|
+
this.store.read(this.journalFile),
|
|
3565
|
+
this.store.read(this.documentSnapshotFile),
|
|
3566
|
+
this.store.read(this.documentJournalFile),
|
|
3567
|
+
this.store.read(this.documentSignerSnapshotFile),
|
|
3568
|
+
this.store.read(this.documentSignerJournalFile),
|
|
3569
|
+
]);
|
|
3570
|
+
this.assertHydrating();
|
|
3571
|
+
try {
|
|
3572
|
+
validateCoordinateJournal(journal, this.journalFile);
|
|
3573
|
+
validateCoordinateJournal(documentJournal, this.documentJournalFile);
|
|
3574
|
+
validateCoordinateJournal(documentSignerJournal, this.documentSignerJournalFile);
|
|
3575
|
+
}
|
|
3576
|
+
catch (error) {
|
|
3577
|
+
this.persistenceFailure ??= error;
|
|
3578
|
+
throw this.persistenceFailure;
|
|
3579
|
+
}
|
|
3580
|
+
let operations;
|
|
3581
|
+
let documentOperations;
|
|
3582
|
+
let documentSignerOperations;
|
|
3583
|
+
try {
|
|
3584
|
+
this.assertHydrating();
|
|
3585
|
+
operations = backbone.loadCoordinateSnapshotAndJournal(snapshot, journal);
|
|
3586
|
+
this.assertHydrating();
|
|
3587
|
+
documentOperations = backbone.loadDocumentSnapshotAndJournal(documentSnapshot, documentJournal);
|
|
3588
|
+
this.assertHydrating();
|
|
3589
|
+
documentSignerOperations =
|
|
3590
|
+
backbone.loadDocumentSignerSnapshotAndJournal(documentSignerSnapshot, documentSignerJournal);
|
|
3591
|
+
}
|
|
3592
|
+
catch (error) {
|
|
3593
|
+
if (journal?.byteLength ||
|
|
3594
|
+
documentJournal?.byteLength ||
|
|
3595
|
+
documentSignerJournal?.byteLength) {
|
|
3596
|
+
this.persistenceFailure ??= error;
|
|
3597
|
+
throw this.persistenceFailure;
|
|
3598
|
+
}
|
|
3599
|
+
throw error;
|
|
3600
|
+
}
|
|
3601
|
+
this.assertHydrating();
|
|
3602
|
+
this.journalInitialized = !!journal && journal.byteLength > 0;
|
|
3603
|
+
this.documentJournalInitialized =
|
|
3604
|
+
!!documentJournal && documentJournal.byteLength > 0;
|
|
3605
|
+
this.documentSignerJournalInitialized =
|
|
3606
|
+
!!documentSignerJournal && documentSignerJournal.byteLength > 0;
|
|
3607
|
+
backbone.setCoordinateJournalEnabled(true);
|
|
3608
|
+
backbone.setDocumentJournalEnabled(true);
|
|
3609
|
+
backbone.setDocumentSignerJournalEnabled(true);
|
|
3610
|
+
this.lastFlushMs = Date.now();
|
|
3611
|
+
this.persistenceLifecycle = this.closePromise ? "closing" : "active";
|
|
3612
|
+
return operations + documentOperations + documentSignerOperations;
|
|
3613
|
+
}
|
|
3614
|
+
catch (error) {
|
|
3615
|
+
if (this.persistenceLifecycle === "hydrating") {
|
|
3616
|
+
this.persistenceLifecycle = this.closePromise ? "closing" : "active";
|
|
3617
|
+
}
|
|
3618
|
+
throw error;
|
|
3619
|
+
}
|
|
3620
|
+
});
|
|
3621
|
+
}
|
|
3622
|
+
assertHydrating() {
|
|
3623
|
+
if (this.persistenceLifecycle !== "hydrating") {
|
|
3624
|
+
throw new Error(`Native backbone coordinate persistence hydrate was interrupted while ${this.persistenceLifecycle}`);
|
|
3625
|
+
}
|
|
3626
|
+
}
|
|
3627
|
+
async resumeDropInternal(finalLifecycle) {
|
|
3628
|
+
const bytes = await this.store.read(nativeBackboneCoordinateDropTombstoneFile);
|
|
3629
|
+
if (!bytes) {
|
|
3630
|
+
// A failed drop can stop before its tombstone is durable. Restore the
|
|
3631
|
+
// generation to active so the caller can start a complete drop; only a
|
|
3632
|
+
// tombstone-backed erase may finish it as dropped.
|
|
3633
|
+
this.persistenceLifecycle =
|
|
3634
|
+
finalLifecycle === "hydrating"
|
|
3635
|
+
? "hydrating"
|
|
3636
|
+
: this.closePromise
|
|
3637
|
+
? "closing"
|
|
3638
|
+
: "active";
|
|
3639
|
+
return false;
|
|
3640
|
+
}
|
|
3641
|
+
let tombstone;
|
|
3642
|
+
try {
|
|
3643
|
+
tombstone = parseNativeBackboneCoordinateDropTombstone(bytes);
|
|
3644
|
+
for (const file of this.configuredFiles()) {
|
|
3645
|
+
if (!tombstone.files.includes(file)) {
|
|
3646
|
+
throw new Error("Native backbone drop tombstone does not cover the configured namespace");
|
|
3647
|
+
}
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3650
|
+
catch (error) {
|
|
3651
|
+
// Corruption must never hydrate stale files, but an explicit drop must
|
|
3652
|
+
// remain available to overwrite the bad marker and erase the namespace.
|
|
3653
|
+
this.persistenceFailure ??= error;
|
|
3654
|
+
this.persistenceLifecycle = this.closePromise ? "closing" : "active";
|
|
3655
|
+
throw this.persistenceFailure;
|
|
3656
|
+
}
|
|
3657
|
+
await this.eraseDropFiles(tombstone.files);
|
|
3658
|
+
this.resetJournalTracking();
|
|
3659
|
+
this.persistenceLifecycle =
|
|
3660
|
+
finalLifecycle === "dropped"
|
|
3661
|
+
? "dropped"
|
|
3662
|
+
: finalLifecycle === "hydrating"
|
|
3663
|
+
? "hydrating"
|
|
3664
|
+
: this.closePromise
|
|
3665
|
+
? "closing"
|
|
3666
|
+
: "active";
|
|
3667
|
+
return true;
|
|
3176
3668
|
}
|
|
3177
3669
|
shouldFlushJournalOnAppend(backbone, now = Date.now()) {
|
|
3178
3670
|
if (this.flushOnAppend !== false) {
|
|
@@ -3195,12 +3687,14 @@ export class NativeBackboneCoordinatePersistence {
|
|
|
3195
3687
|
now - this.lastFlushMs >= this.flushIntervalMs);
|
|
3196
3688
|
}
|
|
3197
3689
|
flushJournalOnAppend(backbone) {
|
|
3690
|
+
this.assertActive("flush on append");
|
|
3198
3691
|
if (!this.shouldFlushJournalOnAppend(backbone)) {
|
|
3199
3692
|
return 0;
|
|
3200
3693
|
}
|
|
3201
3694
|
return this.flushJournal(backbone);
|
|
3202
3695
|
}
|
|
3203
3696
|
flushJournal(backbone) {
|
|
3697
|
+
this.assertActive("flush");
|
|
3204
3698
|
// Serialized with compact() so a flush never clears records appended to
|
|
3205
3699
|
// the wasm journal while a previous flush was awaiting its disk write.
|
|
3206
3700
|
return this.enqueuePersistence(() => this.flushJournalInternal(backbone));
|
|
@@ -3209,9 +3703,7 @@ export class NativeBackboneCoordinatePersistence {
|
|
|
3209
3703
|
// Runs `fn` immediately when no other persistence operation is in
|
|
3210
3704
|
// flight so the common uncontended flush starts synchronously; queued
|
|
3211
3705
|
// operations still run strictly one at a time, in order.
|
|
3212
|
-
const next = this.persistenceQueue
|
|
3213
|
-
? this.persistenceQueue.then(fn)
|
|
3214
|
-
: fn();
|
|
3706
|
+
const next = this.persistenceQueue ? this.persistenceQueue.then(fn) : fn();
|
|
3215
3707
|
const tail = next.then(() => undefined, () => undefined);
|
|
3216
3708
|
this.persistenceQueue = tail;
|
|
3217
3709
|
void tail.then(() => {
|
|
@@ -3223,122 +3715,157 @@ export class NativeBackboneCoordinatePersistence {
|
|
|
3223
3715
|
}
|
|
3224
3716
|
async flushJournalInternal(backbone) {
|
|
3225
3717
|
let written = 0;
|
|
3718
|
+
let persistenceMutationStarted = false;
|
|
3719
|
+
let coordinateBytes;
|
|
3720
|
+
let documentBytes;
|
|
3721
|
+
let signerBytes;
|
|
3226
3722
|
const coordinateRecords = backbone.coordinateJournal();
|
|
3227
3723
|
const coordinateRecordCount = backbone.coordinatePendingJournalLength;
|
|
3228
|
-
if (coordinateRecords.byteLength > 0) {
|
|
3229
|
-
if (this.journalInitialized === undefined) {
|
|
3230
|
-
const existing = await this.store.read(this.journalFile);
|
|
3231
|
-
this.journalInitialized = !!existing && existing.byteLength > 0;
|
|
3232
|
-
}
|
|
3233
|
-
const bytes = this.journalInitialized
|
|
3234
|
-
? coordinateRecords
|
|
3235
|
-
: concatBytes([backbone.coordinateJournalHeader(), coordinateRecords]);
|
|
3236
|
-
await this.store.append(this.journalFile, bytes);
|
|
3237
|
-
this.journalInitialized = true;
|
|
3238
|
-
this.journalByteLength += bytes.byteLength;
|
|
3239
|
-
this.journalRecordCount += coordinateRecordCount;
|
|
3240
|
-
backbone.clearCoordinateJournalPrefix(coordinateRecords.byteLength, coordinateRecordCount);
|
|
3241
|
-
written += coordinateRecords.byteLength;
|
|
3242
|
-
}
|
|
3243
3724
|
const documentRecords = backbone.documentJournal();
|
|
3244
3725
|
const documentRecordCount = backbone.documentPendingJournalLength;
|
|
3245
|
-
if (documentRecords.byteLength > 0) {
|
|
3246
|
-
if (this.documentJournalInitialized === undefined) {
|
|
3247
|
-
const existing = await this.store.read(this.documentJournalFile);
|
|
3248
|
-
this.documentJournalInitialized = !!existing && existing.byteLength > 0;
|
|
3249
|
-
}
|
|
3250
|
-
const bytes = this.documentJournalInitialized
|
|
3251
|
-
? documentRecords
|
|
3252
|
-
: concatBytes([backbone.documentJournalHeader(), documentRecords]);
|
|
3253
|
-
await this.store.append(this.documentJournalFile, bytes);
|
|
3254
|
-
this.documentJournalInitialized = true;
|
|
3255
|
-
this.documentJournalByteLength += bytes.byteLength;
|
|
3256
|
-
this.documentJournalRecordCount += documentRecordCount;
|
|
3257
|
-
backbone.clearDocumentJournalPrefix(documentRecords.byteLength, documentRecordCount);
|
|
3258
|
-
written += documentRecords.byteLength;
|
|
3259
|
-
}
|
|
3260
3726
|
const signerRecords = backbone.documentSignerJournal();
|
|
3261
3727
|
const signerRecordCount = backbone.documentSignerPendingJournalLength;
|
|
3262
|
-
|
|
3263
|
-
if (
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
!!existing && existing.byteLength > 0;
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3728
|
+
try {
|
|
3729
|
+
if (coordinateRecords.byteLength > 0) {
|
|
3730
|
+
if (this.journalInitialized === undefined) {
|
|
3731
|
+
const existing = await this.store.read(this.journalFile);
|
|
3732
|
+
this.journalInitialized = !!existing && existing.byteLength > 0;
|
|
3733
|
+
}
|
|
3734
|
+
coordinateBytes = this.journalInitialized
|
|
3735
|
+
? coordinateRecords
|
|
3736
|
+
: concatBytes([
|
|
3737
|
+
backbone.coordinateJournalHeader(),
|
|
3738
|
+
coordinateRecords,
|
|
3739
|
+
]);
|
|
3740
|
+
await this.store.append(this.journalFile, coordinateBytes);
|
|
3741
|
+
persistenceMutationStarted = true;
|
|
3742
|
+
written += coordinateRecords.byteLength;
|
|
3743
|
+
}
|
|
3744
|
+
if (documentRecords.byteLength > 0) {
|
|
3745
|
+
if (this.documentJournalInitialized === undefined) {
|
|
3746
|
+
const existing = await this.store.read(this.documentJournalFile);
|
|
3747
|
+
this.documentJournalInitialized =
|
|
3748
|
+
!!existing && existing.byteLength > 0;
|
|
3749
|
+
}
|
|
3750
|
+
documentBytes = this.documentJournalInitialized
|
|
3751
|
+
? documentRecords
|
|
3752
|
+
: concatBytes([backbone.documentJournalHeader(), documentRecords]);
|
|
3753
|
+
await this.store.append(this.documentJournalFile, documentBytes);
|
|
3754
|
+
persistenceMutationStarted = true;
|
|
3755
|
+
written += documentRecords.byteLength;
|
|
3756
|
+
}
|
|
3757
|
+
if (signerRecords.byteLength > 0) {
|
|
3758
|
+
if (this.documentSignerJournalInitialized === undefined) {
|
|
3759
|
+
const existing = await this.store.read(this.documentSignerJournalFile);
|
|
3760
|
+
this.documentSignerJournalInitialized =
|
|
3761
|
+
!!existing && existing.byteLength > 0;
|
|
3762
|
+
}
|
|
3763
|
+
signerBytes = this.documentSignerJournalInitialized
|
|
3764
|
+
? signerRecords
|
|
3765
|
+
: concatBytes([
|
|
3766
|
+
backbone.documentSignerJournalHeader(),
|
|
3767
|
+
signerRecords,
|
|
3768
|
+
]);
|
|
3769
|
+
await this.store.append(this.documentSignerJournalFile, signerBytes);
|
|
3770
|
+
persistenceMutationStarted = true;
|
|
3771
|
+
written += signerRecords.byteLength;
|
|
3772
|
+
}
|
|
3773
|
+
if (written === 0) {
|
|
3774
|
+
this.lastFlushMs = Date.now();
|
|
3775
|
+
return 0;
|
|
3776
|
+
}
|
|
3777
|
+
// `append` may only enqueue bytes in a buffered store. Drain and fsync
|
|
3778
|
+
// every affected WAL before clearing its wasm prefix or returning an ACK.
|
|
3779
|
+
for (const file of [
|
|
3780
|
+
coordinateBytes ? this.journalFile : undefined,
|
|
3781
|
+
documentBytes ? this.documentJournalFile : undefined,
|
|
3782
|
+
signerBytes ? this.documentSignerJournalFile : undefined,
|
|
3783
|
+
]) {
|
|
3784
|
+
if (file) {
|
|
3785
|
+
if (this.store.durableBarrier) {
|
|
3786
|
+
await this.store.durableBarrier(file);
|
|
3787
|
+
}
|
|
3788
|
+
else {
|
|
3789
|
+
await this.store.flush?.(file);
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
}
|
|
3793
|
+
if (coordinateBytes) {
|
|
3794
|
+
this.journalInitialized = true;
|
|
3795
|
+
}
|
|
3796
|
+
if (documentBytes) {
|
|
3797
|
+
this.documentJournalInitialized = true;
|
|
3798
|
+
}
|
|
3799
|
+
if (signerBytes) {
|
|
3800
|
+
this.documentSignerJournalInitialized = true;
|
|
3801
|
+
}
|
|
3802
|
+
backbone.clearCoordinateJournalPrefix(coordinateRecords.byteLength, coordinateRecordCount);
|
|
3803
|
+
backbone.clearDocumentJournalPrefix(documentRecords.byteLength, documentRecordCount);
|
|
3275
3804
|
backbone.clearDocumentSignerJournalPrefix(signerRecords.byteLength, signerRecordCount);
|
|
3276
|
-
written += signerRecords.byteLength;
|
|
3277
|
-
}
|
|
3278
|
-
if (written === 0) {
|
|
3279
3805
|
this.lastFlushMs = Date.now();
|
|
3280
|
-
return
|
|
3806
|
+
return written;
|
|
3807
|
+
}
|
|
3808
|
+
catch (error) {
|
|
3809
|
+
if (persistenceMutationStarted) {
|
|
3810
|
+
this.persistenceFailure ??= error;
|
|
3811
|
+
throw this.persistenceFailure;
|
|
3812
|
+
}
|
|
3813
|
+
throw error;
|
|
3281
3814
|
}
|
|
3282
|
-
this.lastFlushMs = Date.now();
|
|
3283
|
-
if (this.shouldCompactJournal()) {
|
|
3284
|
-
await this.compactInternal(backbone);
|
|
3285
|
-
}
|
|
3286
|
-
return written;
|
|
3287
|
-
}
|
|
3288
|
-
shouldCompactJournal() {
|
|
3289
|
-
return ((this.compactMaxJournalBytes != null &&
|
|
3290
|
-
this.journalByteLength +
|
|
3291
|
-
this.documentJournalByteLength +
|
|
3292
|
-
this.documentSignerJournalByteLength >=
|
|
3293
|
-
this.compactMaxJournalBytes) ||
|
|
3294
|
-
(this.compactMaxJournalRecords != null &&
|
|
3295
|
-
this.journalRecordCount +
|
|
3296
|
-
this.documentJournalRecordCount +
|
|
3297
|
-
this.documentSignerJournalRecordCount >=
|
|
3298
|
-
this.compactMaxJournalRecords));
|
|
3299
|
-
}
|
|
3300
|
-
compact(backbone) {
|
|
3301
|
-
return this.enqueuePersistence(() => this.compactInternal(backbone));
|
|
3302
|
-
}
|
|
3303
|
-
async compactInternal(backbone) {
|
|
3304
|
-
// The snapshots below cover exactly the journal records pending right
|
|
3305
|
-
// now; records appended during the awaited writes must survive the
|
|
3306
|
-
// clears at the end, so only this prefix is dropped.
|
|
3307
|
-
const coordinateJournalByteLength = backbone.coordinatePendingJournalByteLength;
|
|
3308
|
-
const coordinateJournalRecordCount = backbone.coordinatePendingJournalLength;
|
|
3309
|
-
const documentJournalByteLength = backbone.documentPendingJournalByteLength;
|
|
3310
|
-
const documentJournalRecordCount = backbone.documentPendingJournalLength;
|
|
3311
|
-
const documentSignerJournalByteLength = backbone.documentSignerPendingJournalByteLength;
|
|
3312
|
-
const documentSignerJournalRecordCount = backbone.documentSignerPendingJournalLength;
|
|
3313
|
-
await Promise.all([
|
|
3314
|
-
this.store.write(this.snapshotFile, backbone.coordinateSnapshot()),
|
|
3315
|
-
this.store.write(this.documentSnapshotFile, backbone.documentSnapshot()),
|
|
3316
|
-
this.store.write(this.documentSignerSnapshotFile, backbone.documentSignerSnapshot()),
|
|
3317
|
-
]);
|
|
3318
|
-
await Promise.all([
|
|
3319
|
-
this.store.remove?.(this.journalFile),
|
|
3320
|
-
this.store.remove?.(this.documentJournalFile),
|
|
3321
|
-
this.store.remove?.(this.documentSignerJournalFile),
|
|
3322
|
-
]);
|
|
3323
|
-
this.journalInitialized = false;
|
|
3324
|
-
this.journalByteLength = 0;
|
|
3325
|
-
this.journalRecordCount = 0;
|
|
3326
|
-
this.documentJournalInitialized = false;
|
|
3327
|
-
this.documentJournalByteLength = 0;
|
|
3328
|
-
this.documentJournalRecordCount = 0;
|
|
3329
|
-
this.documentSignerJournalInitialized = false;
|
|
3330
|
-
this.documentSignerJournalByteLength = 0;
|
|
3331
|
-
this.documentSignerJournalRecordCount = 0;
|
|
3332
|
-
backbone.clearCoordinateJournalPrefix(coordinateJournalByteLength, coordinateJournalRecordCount);
|
|
3333
|
-
backbone.clearDocumentJournalPrefix(documentJournalByteLength, documentJournalRecordCount);
|
|
3334
|
-
backbone.clearDocumentSignerJournalPrefix(documentSignerJournalByteLength, documentSignerJournalRecordCount);
|
|
3335
|
-
// Compact persists the full pending state, so it restarts the
|
|
3336
|
-
// flushIntervalMs pacing window like a flush does.
|
|
3337
|
-
this.lastFlushMs = Date.now();
|
|
3338
3815
|
}
|
|
3339
|
-
async
|
|
3340
|
-
|
|
3341
|
-
|
|
3816
|
+
async compact(_backbone) {
|
|
3817
|
+
this.assertActive("compact");
|
|
3818
|
+
throw new Error(nativeBackboneCoordinateCompactionDisabledMessage);
|
|
3819
|
+
}
|
|
3820
|
+
close() {
|
|
3821
|
+
if (this.closePromise) {
|
|
3822
|
+
return this.closePromise;
|
|
3823
|
+
}
|
|
3824
|
+
const closeMode = this.closeMode ??
|
|
3825
|
+
(this.persistenceLifecycle === "active" && !this.dropInitiatedOnGeneration
|
|
3826
|
+
? "flush"
|
|
3827
|
+
: "withoutFlush");
|
|
3828
|
+
this.closeMode = closeMode;
|
|
3829
|
+
// Close wins once invoked from an active generation. The synchronous
|
|
3830
|
+
// transition rejects any later drop/hydrate/flush before the terminal
|
|
3831
|
+
// store close starts, including for stores that forbid all post-close I/O.
|
|
3832
|
+
if (this.persistenceLifecycle === "active") {
|
|
3833
|
+
this.persistenceLifecycle = "closing";
|
|
3834
|
+
}
|
|
3835
|
+
const closeAttempt = this.enqueuePersistence(async () => {
|
|
3836
|
+
if (closeMode === "flush") {
|
|
3837
|
+
this.persistenceLifecycle = "closing";
|
|
3838
|
+
if (!this.closeFlushCompleted) {
|
|
3839
|
+
await this.store.flush?.();
|
|
3840
|
+
this.closeFlushCompleted = true;
|
|
3841
|
+
}
|
|
3842
|
+
if (!this.closeStoreCompleted) {
|
|
3843
|
+
await this.store.close?.();
|
|
3844
|
+
this.closeStoreCompleted = true;
|
|
3845
|
+
}
|
|
3846
|
+
this.persistenceLifecycle = "closed";
|
|
3847
|
+
return;
|
|
3848
|
+
}
|
|
3849
|
+
// A drop already in flight wins. Drain no buffered bytes after its
|
|
3850
|
+
// tombstone erase and preserve the dropped terminal lifecycle.
|
|
3851
|
+
if (!this.closeStoreCompleted) {
|
|
3852
|
+
await this.store.close?.({ flush: false });
|
|
3853
|
+
this.closeStoreCompleted = true;
|
|
3854
|
+
}
|
|
3855
|
+
if (this.persistenceLifecycle !== "dropped") {
|
|
3856
|
+
this.persistenceLifecycle = "closed";
|
|
3857
|
+
}
|
|
3858
|
+
});
|
|
3859
|
+
this.closePromise = closeAttempt;
|
|
3860
|
+
void closeAttempt.catch(() => {
|
|
3861
|
+
// A rejected close is only the failed attempt, not terminal progress.
|
|
3862
|
+
// Retain completed stages above, but let the exact owner retry the first
|
|
3863
|
+
// incomplete store operation.
|
|
3864
|
+
if (this.closePromise === closeAttempt) {
|
|
3865
|
+
this.closePromise = undefined;
|
|
3866
|
+
}
|
|
3867
|
+
});
|
|
3868
|
+
return closeAttempt;
|
|
3342
3869
|
}
|
|
3343
3870
|
}
|
|
3344
3871
|
const resolveNodeWriteBufferMaxBytes = (options) => options.writeBufferMaxBytes != null
|
|
@@ -3375,18 +3902,12 @@ export const createNativeBackboneCoordinatePersistence = (config) => {
|
|
|
3375
3902
|
return config;
|
|
3376
3903
|
}
|
|
3377
3904
|
const { store, buffered, ...options } = config;
|
|
3378
|
-
const isBuffered = buffered === true || !!buffered;
|
|
3379
3905
|
const resolvedStore = buffered === true
|
|
3380
3906
|
? new NativeBackboneBufferedCoordinatePersistenceStore(store)
|
|
3381
3907
|
: buffered
|
|
3382
3908
|
? new NativeBackboneBufferedCoordinatePersistenceStore(store, buffered)
|
|
3383
3909
|
: store;
|
|
3384
|
-
return new NativeBackboneCoordinatePersistence(resolvedStore,
|
|
3385
|
-
...options,
|
|
3386
|
-
compactMaxJournalBytes: isBuffered && options.compactMaxJournalBytes == null
|
|
3387
|
-
? defaultNativeBackboneCoordinateCompactMaxJournalBytes
|
|
3388
|
-
: options.compactMaxJournalBytes,
|
|
3389
|
-
});
|
|
3910
|
+
return new NativeBackboneCoordinatePersistence(resolvedStore, options);
|
|
3390
3911
|
};
|
|
3391
3912
|
export const createBufferedNativeBackboneCoordinatePersistence = (store, options = {}) => {
|
|
3392
3913
|
const maxBufferedBytes = options.maxBufferedBytes ??
|
|
@@ -3405,8 +3926,7 @@ export const createBufferedNativeBackboneCoordinatePersistence = (store, options
|
|
|
3405
3926
|
flushOnAppend: false,
|
|
3406
3927
|
flushMaxPendingBytes,
|
|
3407
3928
|
flushIntervalMs: options.flushIntervalMs,
|
|
3408
|
-
compactMaxJournalBytes: options.compactMaxJournalBytes
|
|
3409
|
-
defaultNativeBackboneCoordinateCompactMaxJournalBytes,
|
|
3929
|
+
compactMaxJournalBytes: options.compactMaxJournalBytes,
|
|
3410
3930
|
compactMaxJournalRecords: options.compactMaxJournalRecords,
|
|
3411
3931
|
});
|
|
3412
3932
|
};
|
|
@@ -3418,8 +3938,6 @@ export const createBufferedNativeBackboneNodeCoordinatePersistence = (directory,
|
|
|
3418
3938
|
flushOnAppend: false,
|
|
3419
3939
|
flushMaxPendingBytes,
|
|
3420
3940
|
writeBufferMaxBytes: options.writeBufferMaxBytes ?? flushMaxPendingBytes,
|
|
3421
|
-
compactMaxJournalBytes: options.compactMaxJournalBytes ??
|
|
3422
|
-
defaultNativeBackboneCoordinateCompactMaxJournalBytes,
|
|
3423
3941
|
});
|
|
3424
3942
|
};
|
|
3425
3943
|
export const createNativePeerbitBackbone = NativePeerbitBackbone.create;
|