@minnowdb/core 0.9.1 → 0.10.1
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/engine/auto-store.d.ts +52 -0
- package/dist/engine/auto-store.js +157 -0
- package/dist/engine/buffered-writer.d.ts +2 -0
- package/dist/engine/buffered-writer.js +15 -2
- package/dist/engine/client-audit-harness.js +123 -0
- package/dist/engine/client.d.ts +55 -6
- package/dist/engine/client.js +176 -46
- package/dist/engine/database.d.ts +15 -1
- package/dist/engine/database.js +1276 -251
- package/dist/engine/errors.d.ts +61 -2
- package/dist/engine/errors.js +116 -3
- package/dist/engine/index.d.ts +1 -0
- package/dist/engine/index.js +2 -0
- package/dist/engine/live.d.ts +24 -1
- package/dist/engine/live.js +33 -9
- package/dist/engine/scope-write-set.js +36 -0
- package/dist/engine/worker-auto.d.ts +1 -0
- package/dist/engine/worker-auto.js +3 -0
- package/dist/engine/worker-host.d.ts +2 -1
- package/dist/engine/worker-host.js +19 -1
- package/dist/engine/worker-server.d.ts +53 -1
- package/dist/engine/worker-server.js +122 -19
- package/dist/engine/worker-store-auto.js +36 -0
- package/dist/engine/worker-store-opfs.js +3 -2
- package/dist/engine/write-coordinator.js +44 -2
- package/dist/storage/indexeddb-audit-helpers.js +269 -0
- package/dist/storage/indexeddb.js +599 -374
- package/dist/storage/opfs/coordination-helpers.js +54 -0
- package/dist/storage/opfs/index.d.ts +1 -1
- package/dist/storage/opfs/index.js +3 -2
- package/dist/storage/opfs/leader.js +243 -17
- package/dist/storage/opfs/power-loss-model.js +62 -0
- package/dist/storage/opfs/rpc.js +24 -43
- package/dist/storage/opfs/store.d.ts +32 -0
- package/dist/storage/opfs/store.js +531 -65
- package/dist/storage/toolkit/record-core.js +67 -38
- package/dist/storage/toolkit/wal.js +16 -0
- package/dist/storage/toolkit/wire.d.ts +1 -1
- package/dist/storage/toolkit/wire.js +4 -4
- package/dist/storage/types.d.ts +31 -10
- package/dist/storage/types.js +27 -16
- package/dist/testing/opfs-shim.js +14 -6
- package/dist/transactions/index.d.ts +19 -0
- package/dist/transactions/index.js +99 -25
- package/dist/worker-protocol/index.d.ts +50 -2
- package/dist/worker-protocol/index.js +106 -4
- package/package.json +7 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CompactionBacklogError, CompactionJobConflictError, assertCompactionOutputProvenance, compactionOutputSegmentIds, createManifest, createGarbageCollectionJobRecord, advanceGarbageCollectionJobRecord, collectFtsCandidates, collectFtsPostingsBounded, validateFtsCandidateLimit, validateFtsReadVersion, activePostingStorageColumnIds, invalidateUncoveredFtsColumns, invalidateUncoveredSecondaryIndexes, GarbageCollectionJobConflictError, LeaseConflictError, LeaseExpiredError, LeaseOwnerConflictError, MAX_LEASE_TTL_MS, MAX_ACTIVE_LEASES, MAX_ACTIVE_TEMP_OWNERS, MAX_ACTIVE_COMPACTION_JOBS, MAX_ACTIVE_UNIQUE_KEY_BUILDS, MAX_ACTIVE_TRANSACTIONS, MAX_TERMINAL_TRANSACTION_RECORDS, MAX_TERMINAL_COMPACTION_JOB_RECORDS, MAX_COMPLETED_GARBAGE_COLLECTION_JOB_RECORDS, MAX_PINNED_MANIFEST_VERSION_LAG, MAX_PINNED_RETIRED_BLOCKS, MAX_PINNED_RETIRED_BYTES, MAX_RETIRED_HISTORY_BYTES, MAX_GLOBAL_STAGED_ARTIFACT_BYTES, MAX_GLOBAL_STAGED_BLOCKS, MAX_GLOBAL_STAGED_SEGMENTS, MAX_TEMP_OWNER_TTL_MS, MAX_ROW_ID, MAX_ROW_ID_EXCLUSIVE_END, MAX_AUTO_INCREMENT_EXCLUSIVE_END, MAX_LEVEL_ZERO_SEGMENTS, MAX_MANIFEST_BLOCK_PRESENCE_IDS, MAX_STORAGE_ID_CHARACTERS, MAX_CATALOG_RECORDS, MAX_CATALOG_RETAINED_BYTES, MAX_MANIFEST_RECORDS, MAX_MANIFEST_RETAINED_BYTES, MAX_SEGMENT_RECORDS, MAX_SEGMENT_RETAINED_BYTES, MAX_FTS_CANDIDATE_ROW_IDS, MAX_FTS_POSTINGS_PER_CHUNK, MAX_FTS_POSTING_ROW_IDS_PER_CHUNK, MAX_FTS_POSTING_TERM_CHARACTERS, MAX_FTS_TOKENS_PER_DOCUMENT, MAX_FTS_BASE_CHUNKS, MAX_FTS_DELTA_CHUNKS, SnapshotManifestMissingError, StorageResourceLimitError, TableInUseError, TableRecordConflictError, TempOwnerConflictError, TransactionRecordConflictError, UniqueKeyConflictError, UniqueKeyBuildConflictError, UniqueIndexCoverageError, assertTransactionArtifactBatchLimits,
|
|
1
|
+
import { CompactionBacklogError, CompactionJobConflictError, assertCompactionOutputProvenance, compactionOutputSegmentIds, createManifest, createGarbageCollectionJobRecord, advanceGarbageCollectionJobRecord, collectFtsCandidates, collectFtsPostingsBounded, validateFtsCandidateLimit, validateFtsReadVersion, activePostingStorageColumnIds, invalidateUncoveredFtsColumns, invalidateUncoveredSecondaryIndexes, GarbageCollectionJobConflictError, LeaseConflictError, LeaseExpiredError, LeaseOwnerConflictError, MAX_LEASE_TTL_MS, MAX_ACTIVE_LEASES, MAX_ACTIVE_TEMP_OWNERS, MAX_ACTIVE_COMPACTION_JOBS, MAX_ACTIVE_UNIQUE_KEY_BUILDS, MAX_ACTIVE_TRANSACTIONS, MAX_TERMINAL_TRANSACTION_RECORDS, MAX_TERMINAL_COMPACTION_JOB_RECORDS, MAX_COMPLETED_GARBAGE_COLLECTION_JOB_RECORDS, MAX_PINNED_MANIFEST_VERSION_LAG, MAX_PINNED_RETIRED_BLOCKS, MAX_PINNED_RETIRED_BYTES, MAX_RETIRED_HISTORY_BYTES, MAX_GLOBAL_STAGED_ARTIFACT_BYTES, MAX_GLOBAL_STAGED_BLOCKS, MAX_GLOBAL_STAGED_SEGMENTS, MAX_TEMP_OWNER_TTL_MS, MAX_ROW_ID, MAX_ROW_ID_EXCLUSIVE_END, MAX_AUTO_INCREMENT_EXCLUSIVE_END, MAX_LEVEL_ZERO_SEGMENTS, MAX_MANIFEST_BLOCK_PRESENCE_IDS, MAX_STORAGE_ID_CHARACTERS, MAX_CATALOG_RECORDS, MAX_CATALOG_RETAINED_BYTES, MAX_MANIFEST_RECORDS, MAX_MANIFEST_RETAINED_BYTES, MAX_SEGMENT_RECORDS, MAX_SEGMENT_RETAINED_BYTES, MAX_FTS_CANDIDATE_ROW_IDS, MAX_FTS_POSTINGS_PER_CHUNK, MAX_FTS_POSTING_ROW_IDS_PER_CHUNK, MAX_FTS_POSTING_TERM_CHARACTERS, MAX_FTS_TOKENS_PER_DOCUMENT, MAX_FTS_BASE_CHUNKS, MAX_FTS_DELTA_CHUNKS, SnapshotManifestMissingError, StorageResourceLimitError, TableInUseError, TableRecordConflictError, TempOwnerConflictError, TransactionRecordConflictError, UniqueKeyConflictError, UniqueKeyBuildConflictError, UniqueIndexCoverageError, assertTransactionArtifactBatchLimits, assertTempRunPageBatchLimits, assertStorageBulkReadItems, boundedMaintenanceBatchItems, normalizeCompactionJobRecord, normalizeGarbageCollectionJobRecord, normalizeSegmentRecord, updateCompactionJobRecord, appendTransactionJournal, updateTransactionRecord, updateGarbageCollectionPlanningRecord, validateTableColumns, validateTableForeignKey, validateSecondaryIndexes, secondaryIndexColumnIds, secondaryIndexWriteContractChanged, secondaryUniqueKeyNamespace, transactionCommitDeltaRetainedBytes, uniqueKeyBuildChunkRetainedBytes, MAX_UNIQUE_KEY_BUILD_TTL_MS, MAX_UNIQUE_KEY_BUILD_STAGED_BYTES, MAX_UNIQUE_KEY_BUILD_STAGED_BYTES_TOTAL, MAX_UNIQUE_KEY_BUILD_TOKENS_PER_CHUNK, validateFtsPostingQueries, validateCanonicalManifestChangedTableIds, catalogRecordRetainedBytes, manifestRecordRetainedReservationBytes, segmentRecordRetainedBytes, snapshotAcceleratorItemRetainedUsage, assertSnapshotImportAcceleratorUsage, SchemaConflictError, WriteConflictError } from "../types.js";
|
|
2
2
|
import { dateIsoString } from "../../date-value.js";
|
|
3
3
|
import { assertWellFormedString, crc32, MAX_STORED_BLOCK_BYTE_LENGTH } from "../../block-format/index.js";
|
|
4
4
|
function cloneRecord(value) {
|
|
@@ -418,6 +418,14 @@ class RecordRootIndex {
|
|
|
418
418
|
else
|
|
419
419
|
index.delete(record.version);
|
|
420
420
|
}
|
|
421
|
+
appendTransactionArtifacts(record, appended) {
|
|
422
|
+
this.#change(this.#transactionBlocks, appended.blockIds, 1);
|
|
423
|
+
this.#change(this.#transactionSegments, appended.segmentIds, 1);
|
|
424
|
+
if (record.status !== "active")
|
|
425
|
+
return;
|
|
426
|
+
this.#change(this.#activeTransactionBlocks, appended.blockIds, 1);
|
|
427
|
+
this.#change(this.#activeTransactionSegments, appended.segmentIds, 1);
|
|
428
|
+
}
|
|
421
429
|
changeTransaction(record, delta) {
|
|
422
430
|
this.#change(this.#transactionBlocks, record.pendingBlockIds, delta);
|
|
423
431
|
this.#change(this.#transactionSegments, record.pendingSegmentIds, delta);
|
|
@@ -616,6 +624,9 @@ class RecordCore {
|
|
|
616
624
|
#activeStagedArtifactBytes = 0;
|
|
617
625
|
#terminalTransactionCount = 0;
|
|
618
626
|
#transactionBlockLengthOverrides;
|
|
627
|
+
#journalBlockSets = /* @__PURE__ */ new WeakMap();
|
|
628
|
+
#journalBytes = /* @__PURE__ */ new WeakMap();
|
|
629
|
+
#transactionAppendHint;
|
|
619
630
|
#activeTransactionExpiries = new OrderedKeyIndex(compareExpiryKeys);
|
|
620
631
|
#activeTransactionExpiryKeys = /* @__PURE__ */ new Map();
|
|
621
632
|
#transactions = new OrderedRecordMap((previous, next) => {
|
|
@@ -691,6 +702,11 @@ class RecordCore {
|
|
|
691
702
|
this.#activeStagedBlockCount += after.blocks - before.blocks;
|
|
692
703
|
this.#activeStagedSegmentCount += after.segments - before.segments;
|
|
693
704
|
this.#activeStagedArtifactBytes += after.bytes - before.bytes;
|
|
705
|
+
const hint = this.#transactionAppendHint;
|
|
706
|
+
if (hint !== void 0 && previous !== void 0 && next !== void 0 && hint.previous === previous && previous.status === "active" && next.status === "active" && next.snapshotVersion === previous.snapshotVersion && next.pendingBlockIds.length === previous.pendingBlockIds.length + hint.blockIds.length && next.pendingSegmentIds.length === previous.pendingSegmentIds.length + hint.segmentIds.length) {
|
|
707
|
+
this.#roots.appendTransactionArtifacts(next, hint);
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
694
710
|
if (previous !== void 0) {
|
|
695
711
|
const linkedJobs = this.#roots.liveJobTransactionCount(previous.id);
|
|
696
712
|
if (previous.status === "committed") {
|
|
@@ -829,15 +845,19 @@ class RecordCore {
|
|
|
829
845
|
#transactionResources(record, overrides) {
|
|
830
846
|
if (record?.status !== "active")
|
|
831
847
|
return { transactions: 0, blocks: 0, segments: 0, bytes: 0 };
|
|
832
|
-
let bytes =
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
848
|
+
let bytes = this.#journalBytes.get(record.pendingBlockIds);
|
|
849
|
+
if (bytes === void 0) {
|
|
850
|
+
bytes = 0;
|
|
851
|
+
for (const id of record.pendingBlockIds) {
|
|
852
|
+
const length = overrides?.get(id) ?? this.#physical.blockByteLength(id);
|
|
853
|
+
if (length === void 0)
|
|
854
|
+
throw new Error(`Active transaction references missing block: ${id}`);
|
|
855
|
+
bytes += length;
|
|
856
|
+
if (!Number.isSafeInteger(bytes)) {
|
|
857
|
+
throw new RangeError("Active transaction staged bytes exceed the safe integer range");
|
|
858
|
+
}
|
|
840
859
|
}
|
|
860
|
+
this.#journalBytes.set(record.pendingBlockIds, bytes);
|
|
841
861
|
}
|
|
842
862
|
return {
|
|
843
863
|
transactions: 1,
|
|
@@ -1943,10 +1963,6 @@ class RecordCore {
|
|
|
1943
1963
|
if (replacement.pendingSegmentIds.includes(desired.id)) {
|
|
1944
1964
|
throw new Error(`Replacement transaction already journals segment: ${desired.id}`);
|
|
1945
1965
|
}
|
|
1946
|
-
assertTransactionArtifactJournalLimits(replacement.pendingBlockIds, [
|
|
1947
|
-
...replacement.pendingSegmentIds,
|
|
1948
|
-
desired.id
|
|
1949
|
-
]);
|
|
1950
1966
|
for (const id of segmentBlockIds(stored)) {
|
|
1951
1967
|
const provenance = this.#manifestBlocks.get(id);
|
|
1952
1968
|
if (provenance !== void 0 && this.#manifestBlockHasReadableVersion(provenance)) {
|
|
@@ -2612,9 +2628,29 @@ class RecordCore {
|
|
|
2612
2628
|
]));
|
|
2613
2629
|
this.#assertTransactionResourceTransition(plan.updated, blockByteLengths);
|
|
2614
2630
|
this.#replaceSegments(plan.segments);
|
|
2615
|
-
this.#
|
|
2631
|
+
this.#transactionAppendHint = {
|
|
2632
|
+
previous: plan.current,
|
|
2633
|
+
blockIds: [...blockByteLengths.keys()],
|
|
2634
|
+
segmentIds: plan.segments.map((segment) => segment.id)
|
|
2635
|
+
};
|
|
2636
|
+
try {
|
|
2637
|
+
this.#setTransaction(plan.updated, blockByteLengths);
|
|
2638
|
+
} finally {
|
|
2639
|
+
this.#transactionAppendHint = void 0;
|
|
2640
|
+
}
|
|
2641
|
+
for (const id of blockByteLengths.keys())
|
|
2642
|
+
plan.journalBlocks.add(id);
|
|
2643
|
+
this.#journalBlockSets.set(plan.updated.pendingBlockIds, plan.journalBlocks);
|
|
2616
2644
|
return cloneRecord(plan.updated);
|
|
2617
2645
|
}
|
|
2646
|
+
#journalBlockSet(record) {
|
|
2647
|
+
let set = this.#journalBlockSets.get(record.pendingBlockIds);
|
|
2648
|
+
if (set === void 0) {
|
|
2649
|
+
set = new Set(record.pendingBlockIds);
|
|
2650
|
+
this.#journalBlockSets.set(record.pendingBlockIds, set);
|
|
2651
|
+
}
|
|
2652
|
+
return set;
|
|
2653
|
+
}
|
|
2618
2654
|
preflightTransactionArtifactStage(input) {
|
|
2619
2655
|
const plan = this.#planTransactionArtifactStage(input, {});
|
|
2620
2656
|
this.#assertTransactionResourceTransition(plan.updated, new Map(input.blocks.map((block) => [block.id, block.bytes.byteLength])));
|
|
@@ -2627,25 +2663,18 @@ class RecordCore {
|
|
|
2627
2663
|
assertGenericTransactionUpdateAllowed(current, { updatedAt: input.updatedAt });
|
|
2628
2664
|
validateTimestampRuntime(input.updatedAt, "Transaction staging timestamp");
|
|
2629
2665
|
assertTransactionArtifactBatchLimits(input.blocks, input.segments);
|
|
2630
|
-
const
|
|
2631
|
-
const pendingSegmentIds = [
|
|
2632
|
-
...current.pendingSegmentIds,
|
|
2633
|
-
...input.segments.map((segment) => segment.id)
|
|
2634
|
-
];
|
|
2635
|
-
assertTransactionArtifactJournalLimits(pendingBlockIds, pendingSegmentIds);
|
|
2666
|
+
const journalBlocks = this.#journalBlockSet(current);
|
|
2636
2667
|
const inputBlockIds = /* @__PURE__ */ new Set();
|
|
2668
|
+
let addedBytes = 0;
|
|
2637
2669
|
for (const block of input.blocks) {
|
|
2638
2670
|
validateId(block.id);
|
|
2639
2671
|
validateBlockWriteBytes(block.bytes);
|
|
2640
|
-
if (inputBlockIds.has(block.id) || options.blocksPrevalidated !== true && this.#physical.hasBlock(block.id)) {
|
|
2672
|
+
if (inputBlockIds.has(block.id) || journalBlocks.has(block.id) || options.blocksPrevalidated !== true && this.#physical.hasBlock(block.id)) {
|
|
2641
2673
|
throw new Error(`Block already exists: ${block.id}`);
|
|
2642
2674
|
}
|
|
2643
2675
|
inputBlockIds.add(block.id);
|
|
2676
|
+
addedBytes += options.blockByteLengths?.get(block.id) ?? block.bytes.byteLength;
|
|
2644
2677
|
}
|
|
2645
|
-
const stagedBlockIds = /* @__PURE__ */ new Set([
|
|
2646
|
-
...current.pendingBlockIds,
|
|
2647
|
-
...input.blocks.map((block) => block.id)
|
|
2648
|
-
]);
|
|
2649
2678
|
const segmentIds = /* @__PURE__ */ new Set();
|
|
2650
2679
|
const normalizedSegments = [];
|
|
2651
2680
|
for (const segment of input.segments) {
|
|
@@ -2661,19 +2690,16 @@ class RecordCore {
|
|
|
2661
2690
|
if (normalized.commitOrdinal !== expectedCommitOrdinal) {
|
|
2662
2691
|
throw new TypeError(`Segment ${segment.id} commit ordinal must be ${String(expectedCommitOrdinal)}`);
|
|
2663
2692
|
}
|
|
2664
|
-
const availableBlockIds = new Set(
|
|
2693
|
+
const availableBlockIds = /* @__PURE__ */ new Set();
|
|
2665
2694
|
for (const id of segmentBlockIds(normalized)) {
|
|
2666
|
-
if (this.#isManifestBlockVisible(current.snapshotVersion, id))
|
|
2695
|
+
if (inputBlockIds.has(id) || journalBlocks.has(id) || this.#isManifestBlockVisible(current.snapshotVersion, id)) {
|
|
2667
2696
|
availableBlockIds.add(id);
|
|
2697
|
+
}
|
|
2668
2698
|
}
|
|
2669
2699
|
this.#validateStagedSegment(normalized, input.transactionId, availableBlockIds);
|
|
2670
2700
|
normalizedSegments.push(normalized);
|
|
2671
2701
|
}
|
|
2672
|
-
|
|
2673
|
-
pendingBlockIds,
|
|
2674
|
-
pendingSegmentIds,
|
|
2675
|
-
updatedAt: input.updatedAt
|
|
2676
|
-
};
|
|
2702
|
+
let pendingTableNextRowId;
|
|
2677
2703
|
if (current.pendingTable !== void 0) {
|
|
2678
2704
|
let nextRowId = current.pendingTableNextRowId;
|
|
2679
2705
|
if (nextRowId === void 0)
|
|
@@ -2687,13 +2713,17 @@ class RecordCore {
|
|
|
2687
2713
|
}
|
|
2688
2714
|
nextRowId = segment.rowIdEndExclusive;
|
|
2689
2715
|
}
|
|
2690
|
-
|
|
2716
|
+
pendingTableNextRowId = nextRowId;
|
|
2691
2717
|
}
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2718
|
+
const updated = appendTransactionJournal(current, {
|
|
2719
|
+
blockIds: input.blocks.map((block) => block.id),
|
|
2720
|
+
segmentIds: normalizedSegments.map((segment) => segment.id),
|
|
2721
|
+
updatedAt: input.updatedAt,
|
|
2722
|
+
...pendingTableNextRowId === void 0 ? {} : { pendingTableNextRowId }
|
|
2723
|
+
});
|
|
2724
|
+
this.#journalBytes.set(updated.pendingBlockIds, this.#transactionResources(current, options.blockByteLengths).bytes + addedBytes);
|
|
2695
2725
|
this.#segmentUsageAfter(normalizedSegments);
|
|
2696
|
-
return { updated, segments: normalizedSegments };
|
|
2726
|
+
return { updated, segments: normalizedSegments, journalBlocks, current };
|
|
2697
2727
|
}
|
|
2698
2728
|
rollbackTransactionArtifacts(input) {
|
|
2699
2729
|
const current = this.#transactions.get(input.transactionId);
|
|
@@ -2878,7 +2908,6 @@ class RecordCore {
|
|
|
2878
2908
|
}
|
|
2879
2909
|
const pendingBlockIds = [...base.pendingBlockIds, ...blockIds];
|
|
2880
2910
|
const pendingSegmentIds = [...base.pendingSegmentIds, ...segments.keys()];
|
|
2881
|
-
assertTransactionArtifactJournalLimits(pendingBlockIds, pendingSegmentIds);
|
|
2882
2911
|
let segmentIndex = 0;
|
|
2883
2912
|
for (const segment of segments.values()) {
|
|
2884
2913
|
const expectedCommitOrdinal = base.pendingSegmentIds.length + segmentIndex;
|
|
@@ -79,6 +79,8 @@ function* iterateWalFrames(handle) {
|
|
|
79
79
|
while (offset + FRAME_HEADER_BYTES <= size) {
|
|
80
80
|
readFully(handle, header, offset, "reading a WAL frame header for recovery");
|
|
81
81
|
if (headerView.getUint32(0, true) !== FRAME_MAGIC) {
|
|
82
|
+
if (isZeroFilled(handle, offset, size))
|
|
83
|
+
break;
|
|
82
84
|
throw new Error(`WAL frame marker mismatch at offset ${String(offset)}`);
|
|
83
85
|
}
|
|
84
86
|
const length = headerView.getUint32(4, true);
|
|
@@ -92,12 +94,26 @@ function* iterateWalFrames(handle) {
|
|
|
92
94
|
const payloadBytes = new Uint8Array(length);
|
|
93
95
|
readFully(handle, payloadBytes, offset + FRAME_HEADER_BYTES, "reading a WAL frame payload for recovery");
|
|
94
96
|
if (crc32(payloadBytes) !== checksum) {
|
|
97
|
+
if (payloadBytes.every((byte) => byte === 0) && isZeroFilled(handle, end, size))
|
|
98
|
+
break;
|
|
95
99
|
throw new Error(`WAL frame checksum mismatch at offset ${String(offset)}`);
|
|
96
100
|
}
|
|
97
101
|
yield { payload: decodeRecordJson(payloadBytes), frameEnd: end };
|
|
98
102
|
offset = end;
|
|
99
103
|
}
|
|
100
104
|
}
|
|
105
|
+
function isZeroFilled(handle, offset, size) {
|
|
106
|
+
const chunk = new Uint8Array(Math.min(64 * 1024, Math.max(0, size - offset)));
|
|
107
|
+
for (let at = offset; at < size; at += chunk.byteLength) {
|
|
108
|
+
const length = Math.min(chunk.byteLength, size - at);
|
|
109
|
+
const window = length === chunk.byteLength ? chunk : chunk.subarray(0, length);
|
|
110
|
+
readFully(handle, window, at, "reading a WAL tail for recovery");
|
|
111
|
+
for (let index = 0; index < length; index += 1)
|
|
112
|
+
if (window[index] !== 0)
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
101
117
|
export {
|
|
102
118
|
MAX_WAL_FRAME_BYTES,
|
|
103
119
|
WalWriter,
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* can produce that object naturally — keys are ids and column names, values are JSON scalars —
|
|
10
10
|
* and the reviver only converts an object whose sole key is `$n`.
|
|
11
11
|
*/
|
|
12
|
-
export declare const LOG_FORMAT_VERSION =
|
|
12
|
+
export declare const LOG_FORMAT_VERSION = 6;
|
|
13
13
|
export declare function encodeRecordJson(value: unknown): Uint8Array;
|
|
14
14
|
export declare function decodeRecordJson(bytes: Uint8Array): unknown;
|
|
15
15
|
/** Immutable artifact payloads (full-text base chunks) inside extents. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { crc32 } from "../../block-format/index.js";
|
|
2
2
|
import { MAX_ROW_ID_EXCLUSIVE_END, StorageFormatVersionError } from "../types.js";
|
|
3
|
-
const LOG_FORMAT_VERSION =
|
|
3
|
+
const LOG_FORMAT_VERSION = 6;
|
|
4
4
|
const textEncoder = new TextEncoder();
|
|
5
5
|
const textDecoder = new TextDecoder("utf-8", { fatal: true });
|
|
6
6
|
function encodeRecordJson(value) {
|
|
@@ -58,15 +58,15 @@ function decodeEnvelope(magic, bytes) {
|
|
|
58
58
|
}
|
|
59
59
|
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
60
60
|
const version = view.getUint32(8, true);
|
|
61
|
-
if (version !== LOG_FORMAT_VERSION) {
|
|
62
|
-
throw new StorageFormatVersionError("opfs", `envelope/${magic}`, version, LOG_FORMAT_VERSION, version < LOG_FORMAT_VERSION ? "older" : "newer");
|
|
63
|
-
}
|
|
64
61
|
const payloadLength = view.getUint32(12, true);
|
|
65
62
|
if (bytes.byteLength !== ENVELOPE_HEADER_BYTES + payloadLength)
|
|
66
63
|
return void 0;
|
|
67
64
|
const payload = bytes.subarray(ENVELOPE_HEADER_BYTES, ENVELOPE_HEADER_BYTES + payloadLength);
|
|
68
65
|
if (crc32(payload) !== view.getUint32(16, true))
|
|
69
66
|
return void 0;
|
|
67
|
+
if (version !== LOG_FORMAT_VERSION) {
|
|
68
|
+
throw new StorageFormatVersionError("opfs", `envelope/${magic}`, version, LOG_FORMAT_VERSION, version < LOG_FORMAT_VERSION ? "older" : "newer");
|
|
69
|
+
}
|
|
70
70
|
return payload;
|
|
71
71
|
}
|
|
72
72
|
const CHUNK_MAGIC = "MNWCHNK1";
|
package/dist/storage/types.d.ts
CHANGED
|
@@ -480,8 +480,8 @@ export declare const MAX_ACCELERATOR_BUILD_STAGED_BYTES_TOTAL: number;
|
|
|
480
480
|
export declare const MAX_ACCELERATOR_BUILD_STAGED_ENTRIES_TOTAL = 16777216;
|
|
481
481
|
export declare const MAX_ACTIVE_TRANSACTIONS = 4096;
|
|
482
482
|
export declare const MAX_GLOBAL_STAGED_ARTIFACT_BYTES: number;
|
|
483
|
-
export declare const MAX_GLOBAL_STAGED_BLOCKS =
|
|
484
|
-
export declare const MAX_GLOBAL_STAGED_SEGMENTS =
|
|
483
|
+
export declare const MAX_GLOBAL_STAGED_BLOCKS = 1048576;
|
|
484
|
+
export declare const MAX_GLOBAL_STAGED_SEGMENTS = 1048576;
|
|
485
485
|
/** Catalog enumeration remains bounded even for cold public inspection APIs. */
|
|
486
486
|
export declare const MAX_CATALOG_RECORDS = 4096;
|
|
487
487
|
/** Total canonical UTF-8 record-wire bytes retained by the durable catalog. */
|
|
@@ -502,11 +502,11 @@ export declare const MAX_PINNED_RETIRED_BYTES: number;
|
|
|
502
502
|
export declare const MAX_RETIRED_HISTORY_BYTES: number;
|
|
503
503
|
/** A durable resource family reached its fixed corruption/growth safety ceiling. */
|
|
504
504
|
export declare class StorageResourceLimitError extends Error {
|
|
505
|
-
readonly resource: "lease" | "temp owner" | "temp run" | "temp page" | "temporary run total" | "temporary page total" | "temp owner byte" | "temporary byte" | "compaction job" | "garbage collection job" | "unique-key build" | "full-text build" | "secondary-index build" | "accelerator build byte" | "accelerator build entry" | "transaction" | "staged artifact byte" | "staged block" | "staged segment" | "catalog record" | "catalog byte" | "manifest record" | "manifest byte" | "segment record" | "segment byte" | "terminal transaction" | "terminal compaction job" | "completed garbage collection job" | "pinned manifest version lag" | "pinned retired block" | "pinned retired byte" | "snapshot accelerator byte" | "snapshot accelerator entry" | "retired history byte";
|
|
505
|
+
readonly resource: "lease" | "temp owner" | "temp run" | "temp page" | "temporary run total" | "temporary page total" | "temp owner byte" | "temporary byte" | "compaction job" | "garbage collection job" | "unique-key build" | "full-text build" | "secondary-index build" | "accelerator build byte" | "accelerator build entry" | "transaction" | "staged artifact byte" | "staged block" | "staged segment" | "catalog record" | "catalog byte" | "manifest record" | "manifest byte" | "segment record" | "segment byte" | "terminal transaction" | "terminal compaction job" | "completed garbage collection job" | "pinned manifest version lag" | "pinned retired block" | "pinned retired byte" | "snapshot accelerator byte" | "snapshot accelerator entry" | "retired history byte" | "checkpoint byte";
|
|
506
506
|
readonly count: number;
|
|
507
507
|
readonly limit: number;
|
|
508
508
|
readonly name = "StorageResourceLimitError";
|
|
509
|
-
constructor(resource: "lease" | "temp owner" | "temp run" | "temp page" | "temporary run total" | "temporary page total" | "temp owner byte" | "temporary byte" | "compaction job" | "garbage collection job" | "unique-key build" | "full-text build" | "secondary-index build" | "accelerator build byte" | "accelerator build entry" | "transaction" | "staged artifact byte" | "staged block" | "staged segment" | "catalog record" | "catalog byte" | "manifest record" | "manifest byte" | "segment record" | "segment byte" | "terminal transaction" | "terminal compaction job" | "completed garbage collection job" | "pinned manifest version lag" | "pinned retired block" | "pinned retired byte" | "snapshot accelerator byte" | "snapshot accelerator entry" | "retired history byte", count: number, limit: number);
|
|
509
|
+
constructor(resource: "lease" | "temp owner" | "temp run" | "temp page" | "temporary run total" | "temporary page total" | "temp owner byte" | "temporary byte" | "compaction job" | "garbage collection job" | "unique-key build" | "full-text build" | "secondary-index build" | "accelerator build byte" | "accelerator build entry" | "transaction" | "staged artifact byte" | "staged block" | "staged segment" | "catalog record" | "catalog byte" | "manifest record" | "manifest byte" | "segment record" | "segment byte" | "terminal transaction" | "terminal compaction job" | "completed garbage collection job" | "pinned manifest version lag" | "pinned retired block" | "pinned retired byte" | "snapshot accelerator byte" | "snapshot accelerator entry" | "retired history byte" | "checkpoint byte", count: number, limit: number);
|
|
510
510
|
}
|
|
511
511
|
export interface LeaseRecord {
|
|
512
512
|
id: string;
|
|
@@ -1043,13 +1043,8 @@ export declare const MAX_TRANSACTION_STAGE_SEGMENTS = 64;
|
|
|
1043
1043
|
* maximum-size physical block; callers split collections of smaller blocks into bounded calls.
|
|
1044
1044
|
*/
|
|
1045
1045
|
export declare const MAX_TRANSACTION_STAGE_BYTES: number;
|
|
1046
|
-
/** Durable journal ceilings prevent a forgotten or hostile transaction growing without bound. */
|
|
1047
|
-
export declare const MAX_TRANSACTION_PENDING_BLOCKS = 4096;
|
|
1048
|
-
export declare const MAX_TRANSACTION_PENDING_SEGMENTS = 4096;
|
|
1049
1046
|
/** Storage-boundary preflight shared by staging and single-shot write implementations. */
|
|
1050
1047
|
export declare function assertTransactionArtifactBatchLimits(blocks: readonly BlockWrite[], segments: readonly SegmentRecord[]): void;
|
|
1051
|
-
/** Preflight for the complete persisted journal after a bounded staging operation. */
|
|
1052
|
-
export declare function assertTransactionArtifactJournalLimits(pendingBlockIds: readonly string[], pendingSegmentIds: readonly string[]): void;
|
|
1053
1048
|
/**
|
|
1054
1049
|
* Atomically rewinds one active transaction's artifact journal to an earlier set and removes
|
|
1055
1050
|
* exactly the artifacts that are no longer reachable. Journals are canonically sorted, so an
|
|
@@ -1351,11 +1346,26 @@ export declare class StorageCorruptionError extends Error {
|
|
|
1351
1346
|
readonly name = "StorageCorruptionError";
|
|
1352
1347
|
constructor(backend: string, location: string, message: string);
|
|
1353
1348
|
}
|
|
1349
|
+
/**
|
|
1350
|
+
* The base of every error that means "the operation may have happened": the reply was lost,
|
|
1351
|
+
* not the request. Nothing that extends it may be retried blindly — reconcile a stable id or
|
|
1352
|
+
* revision first. `instanceof` survives the worker and follower hops.
|
|
1353
|
+
*/
|
|
1354
|
+
export declare class UnknownOutcomeError extends Error {
|
|
1355
|
+
readonly name: string;
|
|
1356
|
+
}
|
|
1357
|
+
/**
|
|
1358
|
+
* The base of every error that means "this connection is finished": every later call on it
|
|
1359
|
+
* fails the same way, and the remedy is a new connection (`client.reopen()`), not a retry.
|
|
1360
|
+
*/
|
|
1361
|
+
export declare class ConnectionLostError extends Error {
|
|
1362
|
+
readonly name: string;
|
|
1363
|
+
}
|
|
1354
1364
|
/**
|
|
1355
1365
|
* A remote OPFS leader may have committed a mutation whose acknowledgement was lost.
|
|
1356
1366
|
* Reconcile stable identities or revisions before retrying the named operation.
|
|
1357
1367
|
*/
|
|
1358
|
-
export declare class OpfsUncertainOutcomeError extends
|
|
1368
|
+
export declare class OpfsUncertainOutcomeError extends UnknownOutcomeError {
|
|
1359
1369
|
readonly method: string;
|
|
1360
1370
|
readonly name = "OpfsUncertainOutcomeError";
|
|
1361
1371
|
constructor(method: string);
|
|
@@ -2448,6 +2458,17 @@ export declare function createManifest(input: CreateManifestInput): Manifest;
|
|
|
2448
2458
|
/** Strictly clones the one canonical v1 segment shape. */
|
|
2449
2459
|
export declare function normalizeSegmentRecord(record: SegmentRecord): SegmentRecord;
|
|
2450
2460
|
export declare function updateTransactionRecord(record: TransactionRecord, update: TransactionRecordUpdate): TransactionRecord;
|
|
2461
|
+
/**
|
|
2462
|
+
* Appends artifacts to a transaction's journal without re-validating the journal it already
|
|
2463
|
+
* holds. The caller proves the additions are new; this checks only that they are well-formed and
|
|
2464
|
+
* distinct among themselves, so one staging call costs what it stages, not what came before.
|
|
2465
|
+
*/
|
|
2466
|
+
export declare function appendTransactionJournal(record: TransactionRecord, additions: {
|
|
2467
|
+
blockIds: readonly string[];
|
|
2468
|
+
segmentIds: readonly string[];
|
|
2469
|
+
updatedAt: string;
|
|
2470
|
+
pendingTableNextRowId?: bigint;
|
|
2471
|
+
}): TransactionRecord;
|
|
2451
2472
|
export declare function createGarbageCollectionJobRecord(input: CreateGarbageCollectionJobInput): GarbageCollectionJobRecord;
|
|
2452
2473
|
export declare function updateGarbageCollectionPlanningRecord(record: GarbageCollectionJobRecord, input: UpdateGarbageCollectionPlanningInput): GarbageCollectionJobRecord;
|
|
2453
2474
|
export declare function normalizeGarbageCollectionJobRecord(record: GarbageCollectionJobRecord): GarbageCollectionJobRecord;
|
package/dist/storage/types.js
CHANGED
|
@@ -553,8 +553,8 @@ const MAX_ACCELERATOR_BUILD_STAGED_BYTES_TOTAL = 1024 * 1024 * 1024;
|
|
|
553
553
|
const MAX_ACCELERATOR_BUILD_STAGED_ENTRIES_TOTAL = 16777216;
|
|
554
554
|
const MAX_ACTIVE_TRANSACTIONS = 4096;
|
|
555
555
|
const MAX_GLOBAL_STAGED_ARTIFACT_BYTES = 512 * 1024 * 1024;
|
|
556
|
-
const MAX_GLOBAL_STAGED_BLOCKS =
|
|
557
|
-
const MAX_GLOBAL_STAGED_SEGMENTS =
|
|
556
|
+
const MAX_GLOBAL_STAGED_BLOCKS = 1048576;
|
|
557
|
+
const MAX_GLOBAL_STAGED_SEGMENTS = 1048576;
|
|
558
558
|
const MAX_CATALOG_RECORDS = 4096;
|
|
559
559
|
const MAX_CATALOG_RETAINED_BYTES = 64 * 1024 * 1024;
|
|
560
560
|
const MAX_MANIFEST_RECORDS = 65536;
|
|
@@ -874,8 +874,6 @@ class LeaseOwnerConflictError extends Error {
|
|
|
874
874
|
const MAX_TRANSACTION_STAGE_BLOCKS = 64;
|
|
875
875
|
const MAX_TRANSACTION_STAGE_SEGMENTS = 64;
|
|
876
876
|
const MAX_TRANSACTION_STAGE_BYTES = MAX_STORED_BLOCK_BYTE_LENGTH;
|
|
877
|
-
const MAX_TRANSACTION_PENDING_BLOCKS = 4096;
|
|
878
|
-
const MAX_TRANSACTION_PENDING_SEGMENTS = 4096;
|
|
879
877
|
function assertTransactionArtifactBatchLimits(blocks, segments) {
|
|
880
878
|
if (blocks.length > MAX_TRANSACTION_STAGE_BLOCKS) {
|
|
881
879
|
throw new RangeError(`Transaction artifact batch exceeds ${String(MAX_TRANSACTION_STAGE_BLOCKS)} blocks`);
|
|
@@ -894,14 +892,6 @@ function assertTransactionArtifactBatchLimits(blocks, segments) {
|
|
|
894
892
|
}
|
|
895
893
|
}
|
|
896
894
|
}
|
|
897
|
-
function assertTransactionArtifactJournalLimits(pendingBlockIds, pendingSegmentIds) {
|
|
898
|
-
if (pendingBlockIds.length > MAX_TRANSACTION_PENDING_BLOCKS) {
|
|
899
|
-
throw new RangeError(`Transaction journal exceeds ${String(MAX_TRANSACTION_PENDING_BLOCKS)} pending blocks`);
|
|
900
|
-
}
|
|
901
|
-
if (pendingSegmentIds.length > MAX_TRANSACTION_PENDING_SEGMENTS) {
|
|
902
|
-
throw new RangeError(`Transaction journal exceeds ${String(MAX_TRANSACTION_PENDING_SEGMENTS)} pending segments`);
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
895
|
const MAX_SNAPSHOT_SESSION_TTL_MS = 60 * 60 * 1e3;
|
|
906
896
|
const SNAPSHOT_FRAME_KINDS = [
|
|
907
897
|
"catalog-page",
|
|
@@ -997,7 +987,13 @@ class StorageCorruptionError extends Error {
|
|
|
997
987
|
this.location = location;
|
|
998
988
|
}
|
|
999
989
|
}
|
|
1000
|
-
class
|
|
990
|
+
class UnknownOutcomeError extends Error {
|
|
991
|
+
name = "UnknownOutcomeError";
|
|
992
|
+
}
|
|
993
|
+
class ConnectionLostError extends Error {
|
|
994
|
+
name = "ConnectionLostError";
|
|
995
|
+
}
|
|
996
|
+
class OpfsUncertainOutcomeError extends UnknownOutcomeError {
|
|
1001
997
|
method;
|
|
1002
998
|
name = "OpfsUncertainOutcomeError";
|
|
1003
999
|
constructor(method) {
|
|
@@ -1641,6 +1637,21 @@ function updateTransactionRecord(record, update) {
|
|
|
1641
1637
|
}
|
|
1642
1638
|
return updated;
|
|
1643
1639
|
}
|
|
1640
|
+
function appendTransactionJournal(record, additions) {
|
|
1641
|
+
if (record.status !== "active") {
|
|
1642
|
+
throw new TypeError(`Only active transactions can be updated; found ${record.status}`);
|
|
1643
|
+
}
|
|
1644
|
+
const blockIds = orderedUniqueIds(additions.blockIds, "Transaction pending block ID");
|
|
1645
|
+
const segmentIds = orderedUniqueIds(additions.segmentIds, "Transaction pending segment ID");
|
|
1646
|
+
return {
|
|
1647
|
+
...record,
|
|
1648
|
+
pendingBlockIds: blockIds.length === 0 ? record.pendingBlockIds : [...record.pendingBlockIds, ...blockIds],
|
|
1649
|
+
pendingSegmentIds: segmentIds.length === 0 ? record.pendingSegmentIds : [...record.pendingSegmentIds, ...segmentIds],
|
|
1650
|
+
...additions.pendingTableNextRowId === void 0 ? {} : { pendingTableNextRowId: additions.pendingTableNextRowId },
|
|
1651
|
+
updatedAt: additions.updatedAt,
|
|
1652
|
+
revision: safeSum([record.revision, 1], "Transaction revision")
|
|
1653
|
+
};
|
|
1654
|
+
}
|
|
1644
1655
|
function createGarbageCollectionJobRecord(input) {
|
|
1645
1656
|
const candidateManifestVersions = uniqueWholeNumbers(input.candidateManifestVersions, "Garbage collection candidate manifest version");
|
|
1646
1657
|
const candidateSegmentIds = uniqueIds(input.candidateSegmentIds, "Garbage collection candidate segment ID", true);
|
|
@@ -2950,6 +2961,7 @@ export {
|
|
|
2950
2961
|
BlockReadBatchTooLargeError,
|
|
2951
2962
|
CompactionBacklogError,
|
|
2952
2963
|
CompactionJobConflictError,
|
|
2964
|
+
ConnectionLostError,
|
|
2953
2965
|
GarbageCollectionJobConflictError,
|
|
2954
2966
|
IndexedDbSchemaUpgradeBlockedError,
|
|
2955
2967
|
LeaseConflictError,
|
|
@@ -3033,8 +3045,6 @@ export {
|
|
|
3033
3045
|
MAX_TERMINAL_TRANSACTION_RECORDS,
|
|
3034
3046
|
MAX_TRANSACTION_COMMIT_DELTA_BYTES,
|
|
3035
3047
|
MAX_TRANSACTION_COMMIT_DELTA_ENTRIES,
|
|
3036
|
-
MAX_TRANSACTION_PENDING_BLOCKS,
|
|
3037
|
-
MAX_TRANSACTION_PENDING_SEGMENTS,
|
|
3038
3048
|
MAX_TRANSACTION_STAGE_BLOCKS,
|
|
3039
3049
|
MAX_TRANSACTION_STAGE_BYTES,
|
|
3040
3050
|
MAX_TRANSACTION_STAGE_SEGMENTS,
|
|
@@ -3062,15 +3072,16 @@ export {
|
|
|
3062
3072
|
UniqueIndexCoverageError,
|
|
3063
3073
|
UniqueKeyBuildConflictError,
|
|
3064
3074
|
UniqueKeyConflictError,
|
|
3075
|
+
UnknownOutcomeError,
|
|
3065
3076
|
WriteConflictError,
|
|
3066
3077
|
activePostingStorageColumnIds,
|
|
3067
3078
|
advanceGarbageCollectionJobRecord,
|
|
3079
|
+
appendTransactionJournal,
|
|
3068
3080
|
assertCompactionOutputProvenance,
|
|
3069
3081
|
assertSnapshotImportAcceleratorUsage,
|
|
3070
3082
|
assertStorageBulkReadItems,
|
|
3071
3083
|
assertTempRunPageBatchLimits,
|
|
3072
3084
|
assertTransactionArtifactBatchLimits,
|
|
3073
|
-
assertTransactionArtifactJournalLimits,
|
|
3074
3085
|
boundedMaintenanceBatchItems,
|
|
3075
3086
|
canonicalManifestChangedTableIds,
|
|
3076
3087
|
catalogRecordRetainedBytes,
|
|
@@ -181,6 +181,18 @@ class ShimDirectoryHandle {
|
|
|
181
181
|
return this.entries();
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
|
+
function grownFileBytes(bytes, length) {
|
|
185
|
+
const owned = bytes.byteOffset === 0 && bytes.buffer instanceof ArrayBuffer;
|
|
186
|
+
const capacity = owned ? bytes.buffer.byteLength : bytes.byteLength;
|
|
187
|
+
if (owned && length <= capacity) {
|
|
188
|
+
const grown = new Uint8Array(bytes.buffer, 0, length);
|
|
189
|
+
grown.fill(0, bytes.byteLength);
|
|
190
|
+
return grown;
|
|
191
|
+
}
|
|
192
|
+
const backing = new Uint8Array(Math.max(length, capacity * 2, 4096));
|
|
193
|
+
backing.set(bytes);
|
|
194
|
+
return new Uint8Array(backing.buffer, 0, length);
|
|
195
|
+
}
|
|
184
196
|
function assertSubtreeUnlocked(node, name) {
|
|
185
197
|
for (const child of node.children.values()) {
|
|
186
198
|
if (child.kind === "file") {
|
|
@@ -262,9 +274,7 @@ class ShimSyncAccessHandle {
|
|
|
262
274
|
const source = requested.subarray(0, limit);
|
|
263
275
|
const end = at + source.byteLength;
|
|
264
276
|
if (source.byteLength > 0 && end > this.#node.bytes.byteLength) {
|
|
265
|
-
|
|
266
|
-
grown.set(this.#node.bytes);
|
|
267
|
-
this.#node.bytes = grown;
|
|
277
|
+
this.#node.bytes = grownFileBytes(this.#node.bytes, end);
|
|
268
278
|
}
|
|
269
279
|
this.#node.bytes.set(source, at);
|
|
270
280
|
return source.byteLength;
|
|
@@ -276,9 +286,7 @@ class ShimSyncAccessHandle {
|
|
|
276
286
|
this.#node.bytes = this.#node.bytes.slice(0, size);
|
|
277
287
|
return;
|
|
278
288
|
}
|
|
279
|
-
|
|
280
|
-
grown.set(this.#node.bytes);
|
|
281
|
-
this.#node.bytes = grown;
|
|
289
|
+
this.#node.bytes = grownFileBytes(this.#node.bytes, size);
|
|
282
290
|
}
|
|
283
291
|
flush() {
|
|
284
292
|
this.#assertOpen();
|
|
@@ -112,6 +112,18 @@ export declare class DatabaseTransaction {
|
|
|
112
112
|
get snapshotVersion(): number | null;
|
|
113
113
|
get pendingBlockIds(): string[];
|
|
114
114
|
get pendingSegmentIds(): string[];
|
|
115
|
+
/** Whether the journal, persisted or process-local, holds the block. */
|
|
116
|
+
hasPendingBlock(id: string): boolean;
|
|
117
|
+
/**
|
|
118
|
+
* Every segment this transaction has staged, persisted or process-local, as the records it
|
|
119
|
+
* staged them with. One shared array, rebuilt only when the set changes: a scope reads it on
|
|
120
|
+
* every statement, and copying a long journal's records each time would cost more than the
|
|
121
|
+
* read. Callers must not mutate it.
|
|
122
|
+
*/
|
|
123
|
+
get stagedSegments(): readonly SegmentRecord[];
|
|
124
|
+
/** Journal sizes without materializing the id lists. */
|
|
125
|
+
get pendingBlockCount(): number;
|
|
126
|
+
get pendingSegmentCount(): number;
|
|
115
127
|
/** Process-local segments retained for a bounded single-shot commit. */
|
|
116
128
|
get deferredSegments(): SegmentRecord[];
|
|
117
129
|
get pendingTableNextRowId(): bigint | undefined;
|
|
@@ -133,6 +145,13 @@ export declare class DatabaseTransaction {
|
|
|
133
145
|
get compactionSourceBlockIds(): string[];
|
|
134
146
|
/** Extends durable ownership; scope owners call this while user code is legitimately waiting. */
|
|
135
147
|
renew(): Promise<void>;
|
|
148
|
+
/**
|
|
149
|
+
* Renews the lease and record only when a third of their lifetime has gone: an O(1) deadline
|
|
150
|
+
* check otherwise. A scope that buffers statement after statement stages nothing, so nothing
|
|
151
|
+
* renews inline, and a loop that never yields to the event loop never lets the heartbeat
|
|
152
|
+
* timer fire either — the buffered path calls this per statement instead.
|
|
153
|
+
*/
|
|
154
|
+
renewIfDue(): Promise<void>;
|
|
136
155
|
/**
|
|
137
156
|
* Counts every registration this transaction carries: staged blocks and segments, unique-key
|
|
138
157
|
* entries, full-text entries, and supersessions. A caller that fails part-way through a
|