@statewalker/cellb 0.5.2 → 0.6.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.
@@ -51,7 +51,14 @@ export function ltxCellStore(files, sab, opts = {}) {
51
51
  * fails instead. (Across machines, celld's epochs and ownership records do this.)
52
52
  */
53
53
  const owned = new Set();
54
+ /** Ends the held Web Locks (`close()`). */
55
+ const releases = [];
56
+ /** Set by `close()`: no request served, no lock taken, no swap made after it. */
57
+ let closed = false;
58
+ const closing = new AbortController();
54
59
  const own = async (dir) => {
60
+ if (closed)
61
+ throw new Error("ltx: the store is closed");
55
62
  const locks = globalThis.navigator?.locks;
56
63
  if (!locks || owned.has(dir))
57
64
  return;
@@ -59,14 +66,19 @@ export function ltxCellStore(files, sab, opts = {}) {
59
66
  // goes, a moment later (failing at once broke the cell's Durable Object on every reload).
60
67
  const got = await new Promise((resolve) => {
61
68
  void locks
62
- .request(`cellb-ltx:${opts.lockScope ?? ""}:${dir}`, { signal: AbortSignal.timeout(20_000) }, () => {
69
+ .request(`cellb-ltx:${opts.lockScope ?? ""}:${dir}`, { signal: AbortSignal.any([AbortSignal.timeout(20_000), closing.signal]) }, () => {
63
70
  resolve(true);
64
- return new Promise(() => { }); // held while this store lives
71
+ // Held until close(); one granted after close() (a request was waiting) goes at once.
72
+ return closed
73
+ ? Promise.resolve()
74
+ : new Promise((release) => releases.push(release));
65
75
  })
66
76
  .catch(() => resolve(false));
67
77
  });
68
78
  if (!got)
69
79
  throw new Error(`ltx: cell ${dir} is open in another tab or worker (waited 20 s)`);
80
+ if (closed)
81
+ throw new Error("ltx: the store is closed");
70
82
  owned.add(dir);
71
83
  };
72
84
  const cellOf = async (cell) => {
@@ -75,18 +87,43 @@ export function ltxCellStore(files, sab, opts = {}) {
75
87
  const first = !owned.has(dir);
76
88
  await own(dir);
77
89
  const paths = await list(dir, first);
78
- cells.set(cell, paths.length ? await openPagedCell(files, paths) : undefined);
90
+ const opened = paths.length ? await openPagedCell(files, paths) : undefined;
91
+ // An interrupted compaction's leftovers, which its snapshot covers: their space back
92
+ // (this store owns the cell, so no one else reads them).
93
+ if (opened?.superseded.length)
94
+ for (const p of opened.superseded)
95
+ await files.remove(p).catch(() => false);
96
+ if (opened)
97
+ opened.superseded = [];
98
+ cells.set(cell, opened);
79
99
  }
80
100
  return cells.get(cell);
81
101
  };
102
+ /**
103
+ * The request being answered's sequence number (the VFS's `seq`), tagged onto the state
104
+ * word: a VFS that gave up on a request (its 30 s wait) and sent the next one ignores the
105
+ * late answer instead of reading it as the next one's.
106
+ */
107
+ let answering = 0;
108
+ /**
109
+ * The number of the newest request received (set as it arrives, before its turn in the
110
+ * queue). The VFS has one request in flight unless it gave up on one, so a request whose
111
+ * number is not the newest was abandoned: not served, and a commit not kept (SQLite rolled
112
+ * that transaction back when the VFS reported the failure).
113
+ */
114
+ let newestSeq;
82
115
  const answer = (bytes, state = 1) => {
83
116
  payload.set(bytes);
84
117
  ctl[1] = bytes.length;
85
- Atomics.store(ctl, 0, state);
118
+ Atomics.store(ctl, 0, answering ? (answering << 2) | state : state);
86
119
  Atomics.notify(ctl, 0);
87
120
  };
88
121
  const enc = new TextEncoder();
89
122
  let queue = Promise.resolve();
123
+ /** `task` in the request queue (between two requests): the cell views change only there. */
124
+ const inQueue = (task) => new Promise((resolve, reject) => {
125
+ queue = queue.then(() => task().then(resolve, reject));
126
+ });
90
127
  /** The request being served and its stage: a watchdog names the one that does not end. */
91
128
  const current = { op: "", cell: "", stage: "", since: 0 };
92
129
  const stage = (s) => {
@@ -109,7 +146,7 @@ export function ltxCellStore(files, sab, opts = {}) {
109
146
  if (running)
110
147
  return running;
111
148
  const job = (async () => {
112
- const c = await cellOf(cell);
149
+ const c = await inQueue(() => cellOf(cell));
113
150
  if (!c || c.files.length < 2)
114
151
  return c?.files.length ?? 0;
115
152
  const dir = dirOf(cell);
@@ -117,11 +154,13 @@ export function ltxCellStore(files, sab, opts = {}) {
117
154
  // The new snapshot exists beside the files it replaces until the swap: without the room
118
155
  // for it, the compaction would only fill the disk (and fail the commits after it).
119
156
  const needed = pageBytes(c);
120
- const free = await room();
157
+ const free = await (opts.room ?? room)();
121
158
  if (free !== undefined && free < needed * 1.25 + (64 << 20)) {
122
159
  console.warn(`cellb ltx-store: ${cell}: no compaction, ${Math.round(free / 1e6)} MB free for a snapshot of up to ${Math.round(needed / 1e6)} MB`);
123
160
  return c.files.length;
124
161
  }
162
+ if (closed)
163
+ throw new Error("ltx: the store closed during a compaction");
125
164
  const frozen = await openPagedCell(files, replaced);
126
165
  const final = `${dir}/${ltxFileName(1n, frozen.maxTXID)}`;
127
166
  const tmp = `${final}.tmp`;
@@ -132,21 +171,28 @@ export function ltxCellStore(files, sab, opts = {}) {
132
171
  await files.remove(tmp).catch(() => false);
133
172
  throw err;
134
173
  }
135
- await new Promise((resolve, reject) => {
136
- queue = queue.then(async () => {
137
- try {
138
- for (const p of replaced)
139
- await files.remove(p);
140
- await files.move(tmp, final);
141
- cells.delete(cell);
142
- resolve();
143
- }
144
- catch (err) {
145
- reject(err);
146
- }
147
- });
174
+ // The swap and the reopen in one turn of the queue: a reopen outside it could finish
175
+ // after a commit that came between, and put back a view without that commit's file (the
176
+ // next commit would then take its TXID and overwrite it).
177
+ return inQueue(async () => {
178
+ if (closed) {
179
+ await files.remove(tmp).catch(() => false);
180
+ throw new Error("ltx: the store closed during a compaction");
181
+ }
182
+ // The snapshot in place FIRST, then the files it replaces removed: stopped between the
183
+ // two (a closed tab, a lost connection to a remote FilesApi), the cell still opens from
184
+ // the snapshot (openPagedCell skips the files it covers). The other order could leave
185
+ // only the `.tmp`, which the next open deletes: the cell lost.
186
+ await files.move(tmp, final);
187
+ try {
188
+ for (const p of replaced)
189
+ await files.remove(p);
190
+ }
191
+ finally {
192
+ cells.delete(cell); // the moved snapshot is the cell now, whatever the removes did
193
+ }
194
+ return (await cellOf(cell))?.files.length ?? 0;
148
195
  });
149
- return (await cellOf(cell))?.files.length ?? 0;
150
196
  })().finally(() => compacting.delete(cell));
151
197
  compacting.set(cell, job);
152
198
  return job;
@@ -168,7 +214,15 @@ export function ltxCellStore(files, sab, opts = {}) {
168
214
  return {
169
215
  /** Answers one request of a VFS (requests are served in order). */
170
216
  handle(msg) {
217
+ if (msg.seq !== undefined)
218
+ newestSeq = msg.seq;
219
+ const abandoned = () => msg.seq !== undefined && msg.seq !== newestSeq;
171
220
  queue = queue.then(async () => {
221
+ if (abandoned())
222
+ return;
223
+ answering = msg.seq ?? 0;
224
+ if (closed)
225
+ return answer(enc.encode("ltx: the store is closed"), 2);
172
226
  Object.assign(current, {
173
227
  op: msg.op,
174
228
  cell: msg.cell,
@@ -233,6 +287,16 @@ export function ltxCellStore(files, sab, opts = {}) {
233
287
  yield* pages;
234
288
  })(), 0n, written));
235
289
  const t1 = performance.now();
290
+ // Given up on while it was written (a newer request came), or written too late
291
+ // for the VFS to still be waiting (its `until`, less a second for the append and
292
+ // the answer): not a transaction of the cell, since SQLite rolled it back.
293
+ const late = msg.until !== undefined && Date.now() > msg.until - 1000;
294
+ if (abandoned() || late) {
295
+ await files.remove(path).catch(() => false);
296
+ if (abandoned())
297
+ return;
298
+ throw new Error(`ltx: commit of ${msg.cell} took too long; not kept`);
299
+ }
236
300
  stage("append");
237
301
  // The first commit is the snapshot; later ones extend the overlay by one file.
238
302
  if (c)
@@ -272,8 +336,24 @@ export function ltxCellStore(files, sab, opts = {}) {
272
336
  * own turn there to swap the files); answers the cell's file count after. */
273
337
  compact: (cell) => compact(cell),
274
338
  stats,
339
+ /**
340
+ * Stops the store: its Web Locks released (another store may open its cells), its open
341
+ * cells forgotten. A store in a worker of its own ends with the worker instead.
342
+ */
343
+ async close() {
344
+ closed = true;
345
+ closing.abort(); // a request waiting for a cell's Web Lock stops waiting
346
+ // What is under way ends first (a compaction's swap is refused), then the locks go.
347
+ await Promise.allSettled([queue, ...compacting.values()]);
348
+ await queue.catch(() => { });
349
+ clearInterval(watchdog);
350
+ for (const release of releases.splice(0))
351
+ release();
352
+ owned.clear();
353
+ cells.clear();
354
+ },
275
355
  /** The paths of a cell's current files. */
276
- files: async (cell) => (await cellOf(cell))?.files.map((f) => f.path) ?? [],
356
+ files: (cell) => inQueue(async () => (await cellOf(cell))?.files.map((f) => f.path) ?? []),
277
357
  };
278
358
  }
279
359
  /** One cell at `dir` (the earlier single-cell form): messages may omit `cell`. */
@@ -1 +1 @@
1
- {"version":3,"file":"ltx-page-server.js","sourceRoot":"","sources":["../../src/ltx/ltx-page-server.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The asynchronous side of the LTX VFS (src/adapters/ltx-vfs.ts): serves cells' pages from their\n * LTX files on a FilesApi and turns each commit into one LTX file there, in celld's layout\n * (`cells/<cell>/ltx/e1/0000/<min>-<max>.ltx`, a snapshot 1..N first). Runs in any thread that\n * is not one blocked on reads (a worker of its own, or a page): `handle(msg)` answers into the\n * shared buffer. A cell whose files pass `compactAfter` is folded into one snapshot (streamed).\n */\nimport type { FilesApi } from \"@statewalker/webrun-files\";\nimport { scopeOf } from \"../core/celld-schema.js\";\nimport { ltxFileName } from \"./ltx.js\";\nimport {\n compactPagedCellTo,\n encodeLtxStream,\n LtxShortReadError,\n openPagedCell,\n type PagedCell,\n} from \"./ltx-pages.js\";\n\n/** Bytes of page data a cell's files hold (what a compaction may write, at most). */\nconst pageBytes = (c: PagedCell) =>\n c.files.reduce((n, f) => n + [...f.index.values()].reduce((m, [, size]) => m + size, 0), 0);\n\n/** Free bytes in this origin's storage, or undefined where the estimate is not available. */\nasync function room(): Promise<number | undefined> {\n const storage = (globalThis.navigator as { storage?: StorageManager } | undefined)?.storage;\n if (!storage?.estimate) return undefined;\n const { usage = 0, quota } = await storage.estimate();\n return quota === undefined ? undefined : quota - usage;\n}\n\nexport interface LtxStoreMessage {\n op: \"meta\" | \"read\" | \"readRun\" | \"commit\" | \"stats\";\n cell: string;\n pgno?: number;\n count?: number;\n pageSize?: number;\n pageCount?: number;\n pages?: [number, Uint8Array][];\n}\n\nexport function ltxCellStore(\n files: FilesApi,\n sab: SharedArrayBuffer,\n opts: {\n root?: string;\n compactAfter?: number;\n autoCompact?: boolean;\n /**\n * What the files are (the OPFS directory's name, say): part of each cell's Web Lock, so the\n * stores of two directories (two projects in one tab) do not take each other's locks.\n */\n lockScope?: string;\n } = {},\n) {\n const ctl = new Int32Array(sab, 0, 2);\n const payload = new Uint8Array(sab, 8);\n const root = (opts.root ?? \"\").replace(/\\/$/, \"\");\n const compactAfter = opts.compactAfter ?? 64;\n // celld's cell directory: `<Class>:<idHex>` (scopeOf), as the LTX log and celld name it.\n const dirOf = (cell: string) =>\n `${root}/cells/${scopeOf(cell).replace(/[\\\\/]/g, \"_\")}/ltx/e1/0000`;\n const cells = new Map<string, PagedCell | undefined>();\n /** Where the store's time goes (ms), for traces. */\n const stats = {\n commits: 0,\n writeMs: 0,\n appendMs: 0,\n compactions: 0,\n compactMs: 0,\n reads: 0,\n readMs: 0,\n };\n const list = async (dir: string, cleanUp = false) => {\n const out: string[] = [];\n if (!(await files.exists(dir))) return out;\n for await (const e of files.list(dir)) {\n if (e.kind !== \"file\") continue;\n if (e.name.endsWith(\".ltx\")) out.push(e.path);\n // On this store's first open of the cell: a compaction or a write that did not finish\n // (a closed tab, a full disk), its space back.\n else if (cleanUp && /\\.ltx\\.(tmp|part-\\w+)$/.test(e.name))\n await files.remove(e.path).catch(() => false);\n }\n return out;\n };\n /**\n * One writer per cell in the origin: the store holds a Web Lock per cell directory while the\n * cell is open, so another tab's store (or worker) cannot write the same cell meanwhile; it\n * fails instead. (Across machines, celld's epochs and ownership records do this.)\n */\n const owned = new Set<string>();\n const own = async (dir: string) => {\n const locks = (globalThis.navigator as { locks?: LockManager } | undefined)?.locks;\n if (!locks || owned.has(dir)) return;\n // Waits a while for it: on a reload, the previous page's store holds it until its worker\n // goes, a moment later (failing at once broke the cell's Durable Object on every reload).\n const got = await new Promise<boolean>((resolve) => {\n void locks\n .request(\n `cellb-ltx:${opts.lockScope ?? \"\"}:${dir}`,\n { signal: AbortSignal.timeout(20_000) },\n () => {\n resolve(true);\n return new Promise<void>(() => {}); // held while this store lives\n },\n )\n .catch(() => resolve(false));\n });\n if (!got) throw new Error(`ltx: cell ${dir} is open in another tab or worker (waited 20 s)`);\n owned.add(dir);\n };\n const cellOf = async (cell: string) => {\n if (!cells.has(cell)) {\n const dir = dirOf(cell);\n const first = !owned.has(dir);\n await own(dir);\n const paths = await list(dir, first);\n cells.set(cell, paths.length ? await openPagedCell(files, paths) : undefined);\n }\n return cells.get(cell);\n };\n const answer = (bytes: Uint8Array, state = 1) => {\n payload.set(bytes);\n ctl[1] = bytes.length;\n Atomics.store(ctl, 0, state);\n Atomics.notify(ctl, 0);\n };\n const enc = new TextEncoder();\n let queue = Promise.resolve();\n /** The request being served and its stage: a watchdog names the one that does not end. */\n const current = { op: \"\", cell: \"\", stage: \"\", since: 0 };\n const stage = (s: string) => {\n current.stage = s;\n };\n const watchdog = setInterval(() => {\n if (current.op && performance.now() - current.since > 10_000)\n console.warn(\n `cellb ltx-store: ${current.op} of ${current.cell} at \"${current.stage}\" for ${Math.round((performance.now() - current.since) / 1000)} s`,\n );\n }, 10_000);\n (watchdog as { unref?: () => void }).unref?.();\n\n /**\n * Folds a cell's files into one snapshot WITHOUT holding up its requests: the snapshot is\n * written from a frozen view of the files as they are now (later commits add files after\n * them), then, between two requests, the files it replaces are removed and the cell reopened\n * (the snapshot plus the files written meanwhile). One compaction per cell at a time.\n */\n const compacting = new Map<string, Promise<number>>();\n const compact = (cell: string): Promise<number> => {\n const running = compacting.get(cell);\n if (running) return running;\n const job = (async () => {\n const c = await cellOf(cell);\n if (!c || c.files.length < 2) return c?.files.length ?? 0;\n const dir = dirOf(cell);\n const replaced = c.files.map((f) => f.path);\n // The new snapshot exists beside the files it replaces until the swap: without the room\n // for it, the compaction would only fill the disk (and fail the commits after it).\n const needed = pageBytes(c);\n const free = await room();\n if (free !== undefined && free < needed * 1.25 + (64 << 20)) {\n console.warn(\n `cellb ltx-store: ${cell}: no compaction, ${Math.round(free / 1e6)} MB free for a snapshot of up to ${Math.round(needed / 1e6)} MB`,\n );\n return c.files.length;\n }\n const frozen = await openPagedCell(files, replaced);\n const final = `${dir}/${ltxFileName(1n, frozen.maxTXID)}`;\n const tmp = `${final}.tmp`;\n try {\n await compactPagedCellTo(frozen, files, tmp); // page runs: memory stays at a run + index\n } catch (err) {\n await files.remove(tmp).catch(() => false);\n throw err;\n }\n await new Promise<void>((resolve, reject) => {\n queue = queue.then(async () => {\n try {\n for (const p of replaced) await files.remove(p);\n await files.move(tmp, final);\n cells.delete(cell);\n resolve();\n } catch (err) {\n reject(err);\n }\n });\n });\n return (await cellOf(cell))?.files.length ?? 0;\n })().finally(() => compacting.delete(cell));\n compacting.set(cell, job);\n return job;\n };\n\n /**\n * When to compact: past `compactAfter` files, or once the files after the snapshot hold more\n * bytes than half of it (a large cell is not rewritten for a few small commits).\n */\n const deltaBytes = new Map<string, number>();\n const wantsCompaction = (cell: string, c: PagedCell) => {\n if (opts.autoCompact === false) return false;\n if (c.files.length >= compactAfter) return true;\n const snapshot = c.files[0];\n const snapshotBytes = [...snapshot.index.values()].reduce((n, [, size]) => n + size, 0);\n return c.files.length > 1 && (deltaBytes.get(cell) ?? 0) > Math.max(1 << 20, snapshotBytes / 2);\n };\n\n return {\n /** Answers one request of a VFS (requests are served in order). */\n handle(msg: LtxStoreMessage) {\n queue = queue.then(async () => {\n Object.assign(current, {\n op: msg.op,\n cell: msg.cell,\n stage: \"open\",\n since: performance.now(),\n });\n try {\n const c = await cellOf(msg.cell);\n stage(\"serve\");\n if (msg.op === \"meta\")\n return answer(\n enc.encode(\n JSON.stringify({ pageSize: c?.pageSize ?? 0, pageCount: c?.pageCount ?? 0 }),\n ),\n );\n // A read of a file that changed under the cell's view: reopen the cell, read again.\n const retried = async <T>(read: (c: PagedCell) => Promise<T>): Promise<T> => {\n try {\n return await read(c as PagedCell);\n } catch (err) {\n if (!(err instanceof LtxShortReadError)) throw err;\n console.warn(`cellb ltx-store: ${msg.cell}: ${err.message}; reopening the cell`);\n cells.delete(msg.cell);\n stage(\"reopen\");\n return read((await cellOf(msg.cell)) as PagedCell);\n }\n };\n if (msg.op === \"read\")\n return answer(await retried((c) => c.readPage(msg.pgno as number)));\n if (msg.op === \"readRun\") {\n stats.reads++;\n const tr = performance.now();\n try {\n const run = await retried((c) => c.readRun(msg.pgno as number, msg.count as number));\n const size = (c as PagedCell).pageSize;\n const out = new Uint8Array(run.size * size);\n const pgnos = [...run.keys()].sort((a, b) => a - b);\n for (const [i, p] of pgnos.entries()) out.set(run.get(p) as Uint8Array, i * size);\n return answer(out);\n } finally {\n stats.readMs += performance.now() - tr;\n }\n }\n if (msg.op === \"stats\") return answer(enc.encode(JSON.stringify(stats)));\n if (msg.op === \"commit\") {\n const t0 = performance.now();\n const txid = (c?.maxTXID ?? 0n) + 1n;\n const path = `${dirOf(msg.cell)}/${ltxFileName(txid, txid)}`;\n const pages = msg.pages as [number, Uint8Array][];\n const written = { index: new Map<number, [number, number]>() };\n const header = {\n pageSize: msg.pageSize as number,\n commit: msg.pageCount as number,\n minTXID: txid,\n maxTXID: txid,\n timestamp: Date.now(),\n preApplyChecksum: 0n,\n };\n stage(`write ${path}`);\n await files.write(\n path,\n encodeLtxStream(\n header,\n (async function* () {\n yield* pages;\n })(),\n 0n,\n written,\n ),\n );\n const t1 = performance.now();\n stage(\"append\");\n // The first commit is the snapshot; later ones extend the overlay by one file.\n if (c) await c.append(path, { header: { flags: 2, ...header }, index: written.index });\n else cells.delete(msg.cell);\n answer(new Uint8Array());\n stats.commits++;\n stats.writeMs += t1 - t0;\n stats.appendMs += performance.now() - t1;\n // Past the threshold, fold the cell into one snapshot (after answering the commit).\n deltaBytes.set(\n msg.cell,\n (deltaBytes.get(msg.cell) ?? 0) +\n [...written.index.values()].reduce((n, [, size]) => n + size, 0),\n );\n if (c && wantsCompaction(msg.cell, c) && !compacting.has(msg.cell)) {\n const t2 = performance.now();\n deltaBytes.set(msg.cell, 0);\n void compact(msg.cell).then(\n () => {\n stats.compactMs += performance.now() - t2;\n },\n (err) => console.warn(`cellb ltx-store: compaction of ${msg.cell} failed:`, err),\n );\n stats.compactions++;\n }\n return;\n }\n throw new Error(`unknown op ${msg.op}`);\n } catch (err) {\n console.warn(\n `cellb ltx-store: ${msg.op} of ${msg.cell} failed at \"${current.stage}\":`,\n err,\n );\n answer(enc.encode(String((err as Error)?.message ?? err)), 2);\n } finally {\n current.op = \"\";\n }\n });\n },\n /** Folds a cell's files into one snapshot; answers its file count after. */\n /** Folds a cell's files into one snapshot (not through the request queue: it takes its\n * own turn there to swap the files); answers the cell's file count after. */\n compact: (cell: string) => compact(cell),\n stats,\n /** The paths of a cell's current files. */\n files: async (cell: string) => (await cellOf(cell))?.files.map((f) => f.path) ?? [],\n };\n}\n\n/** One cell at `dir` (the earlier single-cell form): messages may omit `cell`. */\nexport function ltxPageServer(files: FilesApi, dir: string, sab: SharedArrayBuffer) {\n const m = /^(.*)\\/cells\\/([^/]+)\\/ltx\\/e1\\/0000$/.exec(dir);\n const root = m ? m[1] : dir;\n const name = m ? m[2] : \"cell\";\n const store = ltxCellStore(files, sab, { root, autoCompact: false });\n let count = 0;\n const refresh = async () => {\n count = (await store.files(name)).length;\n };\n return {\n // A cell another store owns fails its requests (the VFS gets the error), not the start.\n ready: refresh().catch(() => {}),\n handle: (msg: Omit<LtxStoreMessage, \"cell\"> & { cell?: string }) => {\n store.handle({ ...msg, cell: name } as LtxStoreMessage);\n void refresh();\n },\n compact: async () => {\n const n = await store.compact(name);\n count = n;\n return n;\n },\n get files() {\n return Array.from({ length: count });\n },\n stats: store.stats,\n /** The cell's file count, after the requests sent so far. */\n fileCount: async () => (await store.files(name)).length,\n };\n}\n"]}
1
+ {"version":3,"file":"ltx-page-server.js","sourceRoot":"","sources":["../../src/ltx/ltx-page-server.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The asynchronous side of the LTX VFS (src/adapters/ltx-vfs.ts): serves cells' pages from their\n * LTX files on a FilesApi and turns each commit into one LTX file there, in celld's layout\n * (`cells/<cell>/ltx/e1/0000/<min>-<max>.ltx`, a snapshot 1..N first). Runs in any thread that\n * is not one blocked on reads (a worker of its own, or a page): `handle(msg)` answers into the\n * shared buffer. A cell whose files pass `compactAfter` is folded into one snapshot (streamed).\n */\nimport type { FilesApi } from \"@statewalker/webrun-files\";\nimport { scopeOf } from \"../core/celld-schema.js\";\nimport { ltxFileName } from \"./ltx.js\";\nimport {\n compactPagedCellTo,\n encodeLtxStream,\n LtxShortReadError,\n openPagedCell,\n type PagedCell,\n} from \"./ltx-pages.js\";\n\n/** Bytes of page data a cell's files hold (what a compaction may write, at most). */\nconst pageBytes = (c: PagedCell) =>\n c.files.reduce((n, f) => n + [...f.index.values()].reduce((m, [, size]) => m + size, 0), 0);\n\n/** Free bytes in this origin's storage, or undefined where the estimate is not available. */\nasync function room(): Promise<number | undefined> {\n const storage = (globalThis.navigator as { storage?: StorageManager } | undefined)?.storage;\n if (!storage?.estimate) return undefined;\n const { usage = 0, quota } = await storage.estimate();\n return quota === undefined ? undefined : quota - usage;\n}\n\n/**\n * Where the store's FilesApi comes from. An instance cannot cross to this worker, so it is:\n * - a preset by name, on OPFS directory `dir`: \"opfs-sync\" (default: synchronous handles; each\n * file written under a temporary name and moved into place, so a file being written is never\n * read), \"opfs\" (OPFS's asynchronous API), \"memory\" (nothing kept);\n * - `{ port }`: a FilesApi served elsewhere (`serveFilesApi`, on the page or in a worker of\n * the app's own): File System Access, IndexedDB, S3, anything;\n * - `{ module, args }`: a module this worker imports, whose default export (or\n * `createFilesApi`) builds the FilesApi here from `args` (structured-cloneable: a\n * FileSystemDirectoryHandle, S3 settings…), and whose optional `room(args)` says the free\n * bytes there.\n */\nexport type LtxStoreFiles =\n | \"opfs\"\n | \"opfs-sync\"\n | \"memory\"\n | { port: MessagePort }\n | { module: string; args?: unknown };\n\nexport interface LtxStoreInit {\n op: \"init\";\n sab: SharedArrayBuffer;\n /** The OPFS directory of a preset; else the name of the storage (the Web Locks' scope). */\n dir: string;\n compactAfter?: number;\n files?: LtxStoreFiles;\n}\n\nexport interface LtxStoreMessage {\n op: \"meta\" | \"read\" | \"readRun\" | \"commit\" | \"stats\";\n cell: string;\n pgno?: number;\n count?: number;\n pageSize?: number;\n pageCount?: number;\n pages?: [number, Uint8Array][];\n}\n\nexport function ltxCellStore(\n files: FilesApi,\n sab: SharedArrayBuffer,\n opts: {\n root?: string;\n compactAfter?: number;\n autoCompact?: boolean;\n /**\n * What the files are (the OPFS directory's name, say): part of each cell's Web Lock, so the\n * stores of two directories (two projects in one tab) do not take each other's locks.\n */\n lockScope?: string;\n /**\n * Free bytes where the files are, checked before a compaction writes its snapshot beside\n * the files it replaces; undefined: unknown, compact anyway. Defaults to this origin's\n * storage estimate, which is right for OPFS only: give another FilesApi its own (or\n * `async () => undefined`).\n */\n room?: () => Promise<number | undefined>;\n } = {},\n) {\n const ctl = new Int32Array(sab, 0, 2);\n const payload = new Uint8Array(sab, 8);\n const root = (opts.root ?? \"\").replace(/\\/$/, \"\");\n const compactAfter = opts.compactAfter ?? 64;\n // celld's cell directory: `<Class>:<idHex>` (scopeOf), as the LTX log and celld name it.\n const dirOf = (cell: string) =>\n `${root}/cells/${scopeOf(cell).replace(/[\\\\/]/g, \"_\")}/ltx/e1/0000`;\n const cells = new Map<string, PagedCell | undefined>();\n /** Where the store's time goes (ms), for traces. */\n const stats = {\n commits: 0,\n writeMs: 0,\n appendMs: 0,\n compactions: 0,\n compactMs: 0,\n reads: 0,\n readMs: 0,\n };\n const list = async (dir: string, cleanUp = false) => {\n const out: string[] = [];\n if (!(await files.exists(dir))) return out;\n for await (const e of files.list(dir)) {\n if (e.kind !== \"file\") continue;\n if (e.name.endsWith(\".ltx\")) out.push(e.path);\n // On this store's first open of the cell: a compaction or a write that did not finish\n // (a closed tab, a full disk), its space back.\n else if (cleanUp && /\\.ltx\\.(tmp|part-\\w+)$/.test(e.name))\n await files.remove(e.path).catch(() => false);\n }\n return out;\n };\n /**\n * One writer per cell in the origin: the store holds a Web Lock per cell directory while the\n * cell is open, so another tab's store (or worker) cannot write the same cell meanwhile; it\n * fails instead. (Across machines, celld's epochs and ownership records do this.)\n */\n const owned = new Set<string>();\n /** Ends the held Web Locks (`close()`). */\n const releases: (() => void)[] = [];\n /** Set by `close()`: no request served, no lock taken, no swap made after it. */\n let closed = false;\n const closing = new AbortController();\n const own = async (dir: string) => {\n if (closed) throw new Error(\"ltx: the store is closed\");\n const locks = (globalThis.navigator as { locks?: LockManager } | undefined)?.locks;\n if (!locks || owned.has(dir)) return;\n // Waits a while for it: on a reload, the previous page's store holds it until its worker\n // goes, a moment later (failing at once broke the cell's Durable Object on every reload).\n const got = await new Promise<boolean>((resolve) => {\n void locks\n .request(\n `cellb-ltx:${opts.lockScope ?? \"\"}:${dir}`,\n { signal: AbortSignal.any([AbortSignal.timeout(20_000), closing.signal]) },\n () => {\n resolve(true);\n // Held until close(); one granted after close() (a request was waiting) goes at once.\n return closed\n ? Promise.resolve()\n : new Promise<void>((release) => releases.push(release));\n },\n )\n .catch(() => resolve(false));\n });\n if (!got) throw new Error(`ltx: cell ${dir} is open in another tab or worker (waited 20 s)`);\n if (closed) throw new Error(\"ltx: the store is closed\");\n owned.add(dir);\n };\n const cellOf = async (cell: string) => {\n if (!cells.has(cell)) {\n const dir = dirOf(cell);\n const first = !owned.has(dir);\n await own(dir);\n const paths = await list(dir, first);\n const opened = paths.length ? await openPagedCell(files, paths) : undefined;\n // An interrupted compaction's leftovers, which its snapshot covers: their space back\n // (this store owns the cell, so no one else reads them).\n if (opened?.superseded.length)\n for (const p of opened.superseded) await files.remove(p).catch(() => false);\n if (opened) opened.superseded = [];\n cells.set(cell, opened);\n }\n return cells.get(cell);\n };\n /**\n * The request being answered's sequence number (the VFS's `seq`), tagged onto the state\n * word: a VFS that gave up on a request (its 30 s wait) and sent the next one ignores the\n * late answer instead of reading it as the next one's.\n */\n let answering = 0;\n /**\n * The number of the newest request received (set as it arrives, before its turn in the\n * queue). The VFS has one request in flight unless it gave up on one, so a request whose\n * number is not the newest was abandoned: not served, and a commit not kept (SQLite rolled\n * that transaction back when the VFS reported the failure).\n */\n let newestSeq: number | undefined;\n const answer = (bytes: Uint8Array, state = 1) => {\n payload.set(bytes);\n ctl[1] = bytes.length;\n Atomics.store(ctl, 0, answering ? (answering << 2) | state : state);\n Atomics.notify(ctl, 0);\n };\n const enc = new TextEncoder();\n let queue = Promise.resolve();\n /** `task` in the request queue (between two requests): the cell views change only there. */\n const inQueue = <T>(task: () => Promise<T>): Promise<T> =>\n new Promise<T>((resolve, reject) => {\n queue = queue.then(() => task().then(resolve, reject));\n });\n /** The request being served and its stage: a watchdog names the one that does not end. */\n const current = { op: \"\", cell: \"\", stage: \"\", since: 0 };\n const stage = (s: string) => {\n current.stage = s;\n };\n const watchdog = setInterval(() => {\n if (current.op && performance.now() - current.since > 10_000)\n console.warn(\n `cellb ltx-store: ${current.op} of ${current.cell} at \"${current.stage}\" for ${Math.round((performance.now() - current.since) / 1000)} s`,\n );\n }, 10_000);\n (watchdog as { unref?: () => void }).unref?.();\n\n /**\n * Folds a cell's files into one snapshot WITHOUT holding up its requests: the snapshot is\n * written from a frozen view of the files as they are now (later commits add files after\n * them), then, between two requests, the files it replaces are removed and the cell reopened\n * (the snapshot plus the files written meanwhile). One compaction per cell at a time.\n */\n const compacting = new Map<string, Promise<number>>();\n const compact = (cell: string): Promise<number> => {\n const running = compacting.get(cell);\n if (running) return running;\n const job = (async () => {\n const c = await inQueue(() => cellOf(cell));\n if (!c || c.files.length < 2) return c?.files.length ?? 0;\n const dir = dirOf(cell);\n const replaced = c.files.map((f) => f.path);\n // The new snapshot exists beside the files it replaces until the swap: without the room\n // for it, the compaction would only fill the disk (and fail the commits after it).\n const needed = pageBytes(c);\n const free = await (opts.room ?? room)();\n if (free !== undefined && free < needed * 1.25 + (64 << 20)) {\n console.warn(\n `cellb ltx-store: ${cell}: no compaction, ${Math.round(free / 1e6)} MB free for a snapshot of up to ${Math.round(needed / 1e6)} MB`,\n );\n return c.files.length;\n }\n if (closed) throw new Error(\"ltx: the store closed during a compaction\");\n const frozen = await openPagedCell(files, replaced);\n const final = `${dir}/${ltxFileName(1n, frozen.maxTXID)}`;\n const tmp = `${final}.tmp`;\n try {\n await compactPagedCellTo(frozen, files, tmp); // page runs: memory stays at a run + index\n } catch (err) {\n await files.remove(tmp).catch(() => false);\n throw err;\n }\n // The swap and the reopen in one turn of the queue: a reopen outside it could finish\n // after a commit that came between, and put back a view without that commit's file (the\n // next commit would then take its TXID and overwrite it).\n return inQueue(async () => {\n if (closed) {\n await files.remove(tmp).catch(() => false);\n throw new Error(\"ltx: the store closed during a compaction\");\n }\n // The snapshot in place FIRST, then the files it replaces removed: stopped between the\n // two (a closed tab, a lost connection to a remote FilesApi), the cell still opens from\n // the snapshot (openPagedCell skips the files it covers). The other order could leave\n // only the `.tmp`, which the next open deletes: the cell lost.\n await files.move(tmp, final);\n try {\n for (const p of replaced) await files.remove(p);\n } finally {\n cells.delete(cell); // the moved snapshot is the cell now, whatever the removes did\n }\n return (await cellOf(cell))?.files.length ?? 0;\n });\n })().finally(() => compacting.delete(cell));\n compacting.set(cell, job);\n return job;\n };\n\n /**\n * When to compact: past `compactAfter` files, or once the files after the snapshot hold more\n * bytes than half of it (a large cell is not rewritten for a few small commits).\n */\n const deltaBytes = new Map<string, number>();\n const wantsCompaction = (cell: string, c: PagedCell) => {\n if (opts.autoCompact === false) return false;\n if (c.files.length >= compactAfter) return true;\n const snapshot = c.files[0];\n const snapshotBytes = [...snapshot.index.values()].reduce((n, [, size]) => n + size, 0);\n return c.files.length > 1 && (deltaBytes.get(cell) ?? 0) > Math.max(1 << 20, snapshotBytes / 2);\n };\n\n return {\n /** Answers one request of a VFS (requests are served in order). */\n handle(msg: LtxStoreMessage & { seq?: number; until?: number }) {\n if (msg.seq !== undefined) newestSeq = msg.seq;\n const abandoned = () => msg.seq !== undefined && msg.seq !== newestSeq;\n queue = queue.then(async () => {\n if (abandoned()) return;\n answering = msg.seq ?? 0;\n if (closed) return answer(enc.encode(\"ltx: the store is closed\"), 2);\n Object.assign(current, {\n op: msg.op,\n cell: msg.cell,\n stage: \"open\",\n since: performance.now(),\n });\n try {\n const c = await cellOf(msg.cell);\n stage(\"serve\");\n if (msg.op === \"meta\")\n return answer(\n enc.encode(\n JSON.stringify({ pageSize: c?.pageSize ?? 0, pageCount: c?.pageCount ?? 0 }),\n ),\n );\n // A read of a file that changed under the cell's view: reopen the cell, read again.\n const retried = async <T>(read: (c: PagedCell) => Promise<T>): Promise<T> => {\n try {\n return await read(c as PagedCell);\n } catch (err) {\n if (!(err instanceof LtxShortReadError)) throw err;\n console.warn(`cellb ltx-store: ${msg.cell}: ${err.message}; reopening the cell`);\n cells.delete(msg.cell);\n stage(\"reopen\");\n return read((await cellOf(msg.cell)) as PagedCell);\n }\n };\n if (msg.op === \"read\")\n return answer(await retried((c) => c.readPage(msg.pgno as number)));\n if (msg.op === \"readRun\") {\n stats.reads++;\n const tr = performance.now();\n try {\n const run = await retried((c) => c.readRun(msg.pgno as number, msg.count as number));\n const size = (c as PagedCell).pageSize;\n const out = new Uint8Array(run.size * size);\n const pgnos = [...run.keys()].sort((a, b) => a - b);\n for (const [i, p] of pgnos.entries()) out.set(run.get(p) as Uint8Array, i * size);\n return answer(out);\n } finally {\n stats.readMs += performance.now() - tr;\n }\n }\n if (msg.op === \"stats\") return answer(enc.encode(JSON.stringify(stats)));\n if (msg.op === \"commit\") {\n const t0 = performance.now();\n const txid = (c?.maxTXID ?? 0n) + 1n;\n const path = `${dirOf(msg.cell)}/${ltxFileName(txid, txid)}`;\n const pages = msg.pages as [number, Uint8Array][];\n const written = { index: new Map<number, [number, number]>() };\n const header = {\n pageSize: msg.pageSize as number,\n commit: msg.pageCount as number,\n minTXID: txid,\n maxTXID: txid,\n timestamp: Date.now(),\n preApplyChecksum: 0n,\n };\n stage(`write ${path}`);\n await files.write(\n path,\n encodeLtxStream(\n header,\n (async function* () {\n yield* pages;\n })(),\n 0n,\n written,\n ),\n );\n const t1 = performance.now();\n // Given up on while it was written (a newer request came), or written too late\n // for the VFS to still be waiting (its `until`, less a second for the append and\n // the answer): not a transaction of the cell, since SQLite rolled it back.\n const late = msg.until !== undefined && Date.now() > msg.until - 1000;\n if (abandoned() || late) {\n await files.remove(path).catch(() => false);\n if (abandoned()) return;\n throw new Error(`ltx: commit of ${msg.cell} took too long; not kept`);\n }\n stage(\"append\");\n // The first commit is the snapshot; later ones extend the overlay by one file.\n if (c) await c.append(path, { header: { flags: 2, ...header }, index: written.index });\n else cells.delete(msg.cell);\n answer(new Uint8Array());\n stats.commits++;\n stats.writeMs += t1 - t0;\n stats.appendMs += performance.now() - t1;\n // Past the threshold, fold the cell into one snapshot (after answering the commit).\n deltaBytes.set(\n msg.cell,\n (deltaBytes.get(msg.cell) ?? 0) +\n [...written.index.values()].reduce((n, [, size]) => n + size, 0),\n );\n if (c && wantsCompaction(msg.cell, c) && !compacting.has(msg.cell)) {\n const t2 = performance.now();\n deltaBytes.set(msg.cell, 0);\n void compact(msg.cell).then(\n () => {\n stats.compactMs += performance.now() - t2;\n },\n (err) => console.warn(`cellb ltx-store: compaction of ${msg.cell} failed:`, err),\n );\n stats.compactions++;\n }\n return;\n }\n throw new Error(`unknown op ${msg.op}`);\n } catch (err) {\n console.warn(\n `cellb ltx-store: ${msg.op} of ${msg.cell} failed at \"${current.stage}\":`,\n err,\n );\n answer(enc.encode(String((err as Error)?.message ?? err)), 2);\n } finally {\n current.op = \"\";\n }\n });\n },\n /** Folds a cell's files into one snapshot; answers its file count after. */\n /** Folds a cell's files into one snapshot (not through the request queue: it takes its\n * own turn there to swap the files); answers the cell's file count after. */\n compact: (cell: string) => compact(cell),\n stats,\n /**\n * Stops the store: its Web Locks released (another store may open its cells), its open\n * cells forgotten. A store in a worker of its own ends with the worker instead.\n */\n async close() {\n closed = true;\n closing.abort(); // a request waiting for a cell's Web Lock stops waiting\n // What is under way ends first (a compaction's swap is refused), then the locks go.\n await Promise.allSettled([queue, ...compacting.values()]);\n await queue.catch(() => {});\n clearInterval(watchdog);\n for (const release of releases.splice(0)) release();\n owned.clear();\n cells.clear();\n },\n /** The paths of a cell's current files. */\n files: (cell: string) =>\n inQueue(async () => (await cellOf(cell))?.files.map((f) => f.path) ?? []),\n };\n}\n\n/** One cell at `dir` (the earlier single-cell form): messages may omit `cell`. */\nexport function ltxPageServer(files: FilesApi, dir: string, sab: SharedArrayBuffer) {\n const m = /^(.*)\\/cells\\/([^/]+)\\/ltx\\/e1\\/0000$/.exec(dir);\n const root = m ? m[1] : dir;\n const name = m ? m[2] : \"cell\";\n const store = ltxCellStore(files, sab, { root, autoCompact: false });\n let count = 0;\n const refresh = async () => {\n count = (await store.files(name)).length;\n };\n return {\n // A cell another store owns fails its requests (the VFS gets the error), not the start.\n ready: refresh().catch(() => {}),\n handle: (msg: Omit<LtxStoreMessage, \"cell\"> & { cell?: string }) => {\n store.handle({ ...msg, cell: name } as LtxStoreMessage);\n void refresh();\n },\n compact: async () => {\n const n = await store.compact(name);\n count = n;\n return n;\n },\n get files() {\n return Array.from({ length: count });\n },\n stats: store.stats,\n /** The cell's file count, after the requests sent so far. */\n fileCount: async () => (await store.files(name)).length,\n };\n}\n"]}
@@ -39,6 +39,11 @@ export interface PagedCell {
39
39
  maxTXID: bigint;
40
40
  /** The files, oldest first (the snapshot, then the incremental ones). */
41
41
  files: LtxPages[];
42
+ /**
43
+ * Older files the newest snapshot already holds (an interrupted compaction's): never read;
44
+ * a writer that owns the cell removes them.
45
+ */
46
+ superseded: string[];
42
47
  /** The page as of the newest file holding it (zeros past the database end). */
43
48
  readPage(pgno: number): Promise<Uint8Array>;
44
49
  /** Pages `from`..`from+count-1` (within the database), one range read per file they live in. */
@@ -113,16 +113,44 @@ function pagesOf(files, path, header, index) {
113
113
  * in TXID order. Only headers and page indexes are read.
114
114
  */
115
115
  export async function openPagedCell(files, paths) {
116
- const opened = await Promise.all(paths.map((p) => openLtxPages(files, p)));
117
- opened.sort((a, b) => a.header.maxTXID < b.header.maxTXID ? -1 : a.header.maxTXID > b.header.maxTXID ? 1 : 0);
116
+ const settled = await Promise.all(paths.map((p) => openLtxPages(files, p).then((f) => ({ f }), (error) => ({ p, error }))));
117
+ const opened = [];
118
+ const skipped = [];
119
+ for (const r of settled) {
120
+ if ("f" in r)
121
+ opened.push(r.f);
122
+ // A snapshot that does not open (a compaction's move done as a copy, cut short): the
123
+ // files it was made from are all still there, so the cell is read from them.
124
+ else if (/(^|\/)0{15}1-[0-9a-f]{16}\.ltx$/.test(r.p)) {
125
+ console.warn(`cellb ltx: skipping ${r.p}, which does not open:`, r.error);
126
+ skipped.push(r.error);
127
+ }
128
+ else
129
+ throw r.error;
130
+ }
131
+ // By maxTXID; at the same maxTXID, a snapshot (minTXID 1) after the incremental file it
132
+ // folded in: a compaction interrupted between writing its snapshot and removing the files it
133
+ // replaces leaves both, and the snapshot is what the cell is.
134
+ opened.sort((a, b) => a.header.maxTXID !== b.header.maxTXID
135
+ ? a.header.maxTXID < b.header.maxTXID
136
+ ? -1
137
+ : 1
138
+ : a.header.minTXID > b.header.minTXID
139
+ ? -1
140
+ : a.header.minTXID < b.header.minTXID
141
+ ? 1
142
+ : 0);
118
143
  let snapshotAt = -1;
119
144
  opened.forEach((f, i) => {
120
145
  if (f.header.minTXID === 1n)
121
146
  snapshotAt = i;
122
147
  });
148
+ // No other snapshot to read the cell from: the skipped one's own error is the reason.
123
149
  if (snapshotAt < 0)
124
- throw new Error("ltx: no snapshot among the cell's files");
150
+ throw skipped[0] ?? new Error("ltx: no snapshot among the cell's files");
125
151
  const chain = opened.slice(snapshotAt);
152
+ /** Files the newest snapshot covers (left by an interrupted compaction): not read. */
153
+ const superseded = opened.slice(0, snapshotAt).map((f) => f.path);
126
154
  for (let i = 1; i < chain.length; i++)
127
155
  if (chain[i].header.minTXID !== chain[i - 1].header.maxTXID + 1n)
128
156
  throw new Error(`ltx: gap before ${chain[i].path}`);
@@ -136,6 +164,7 @@ export async function openPagedCell(files, paths) {
136
164
  pageCount: newest.commit,
137
165
  maxTXID: newest.maxTXID,
138
166
  files: chain,
167
+ superseded,
139
168
  source: (pgno) => owner.get(pgno)?.path,
140
169
  async readPage(pgno) {
141
170
  const page = pgno <= cell.pageCount ? await owner.get(pgno)?.readPage(pgno) : undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"ltx-pages.js","sourceRoot":"","sources":["../../src/ltx/ltx-pages.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Page-level random access to a cell kept as celld keeps it: LTX files in a bucket\n * (`…/ltx/e<epoch>/<level>/<min>-<max>.ltx`), a snapshot (TXID 1..N) and the incremental files\n * after it. Nothing is read whole: an LTX file ends with a page index (pgno → offset, size), so\n * opening a file reads its 100-byte header and its index, and a page is ONE range read plus an\n * LZ4 decode. The newest file holding a page wins; `compact` folds them into a new snapshot.\n *\n * This is the read side a SQLite VFS needs to serve a database from any FilesApi (OPFS, File\n * System Access, IndexedDB, S3…) without loading it: xRead → `readPage` (through a page cache);\n * the write side is the existing LTX log (one incremental file per commit).\n */\nimport type { FilesApi } from \"@statewalker/webrun-files\";\nimport { Crc64 } from \"./crc64.js\";\nimport { CHECKSUM_FLAG, type LtxHeader, lockPgno } from \"./ltx.js\";\nimport { lz4Compress, lz4Decompress, lz4FrameDecompress } from \"./lz4.js\";\n\nconst HEADER_SIZE = 100;\nconst PAGE_HEADER_FLAG_SIZE = 1;\n\nasync function readRange(\n files: FilesApi,\n path: string,\n start: number,\n length: number,\n): Promise<Uint8Array> {\n const chunks: Uint8Array[] = [];\n for await (const c of files.read(path, { start, length })) chunks.push(c);\n const got = chunks.reduce((n, c) => n + c.length, 0);\n // A short read is a file that changed or went away under its reader: say which, rather than\n // decode past its end (\"Offset is outside the bounds of the DataView\").\n if (got !== length) {\n const st = await files.stats(path).catch(() => undefined);\n throw new LtxShortReadError(\n `ltx: short read of ${path}: ${got} of ${length} bytes at ${start} (file ${st?.kind === \"file\" ? `${(st as { size: number }).size} bytes` : \"missing\"})`,\n );\n }\n if (chunks.length === 1) return chunks[0];\n const out = new Uint8Array(got);\n let o = 0;\n for (const c of chunks) out.set(c, o), (o += c.length);\n return out;\n}\n\n/** A range read that got fewer bytes than asked: the file changed or was removed meanwhile. */\nexport class LtxShortReadError extends Error {\n name = \"LtxShortReadError\";\n}\n\nexport interface LtxPages {\n path: string;\n header: LtxHeader;\n /** pgno → [offset of the page's 6-byte header, size including it]. */\n index: Map<number, [number, number]>;\n /** One range read and an LZ4 decode. */\n readPage(pgno: number): Promise<Uint8Array | undefined>;\n /** Several pages of this file with ONE range read (they are stored in pgno order). */\n readPages(pgnos: number[]): Promise<Map<number, Uint8Array>>;\n}\n\n/** Opens an LTX file for page reads: reads its header and its page index only. */\nexport async function openLtxPages(\n files: FilesApi,\n path: string,\n /** The header and index of a file this process just wrote: nothing is read back. */\n known?: { header: LtxHeader; index: Map<number, [number, number]> },\n): Promise<LtxPages> {\n if (known) return pagesOf(files, path, known.header, known.index);\n const size =\n (await files.stats(path))?.kind === \"file\"\n ? ((await files.stats(path)) as { size: number }).size\n : 0;\n if (size < HEADER_SIZE + 6 + 24) throw new Error(`ltx: ${path} is too small`);\n const head = await readRange(files, path, 0, HEADER_SIZE);\n const hv = new DataView(head.buffer, head.byteOffset, head.byteLength);\n if (new TextDecoder().decode(head.subarray(0, 4)) !== \"LTX1\")\n throw new Error(`ltx: ${path}: bad magic`);\n const header: LtxHeader = {\n flags: hv.getUint32(4),\n pageSize: hv.getUint32(8),\n commit: hv.getUint32(12),\n minTXID: hv.getBigUint64(16),\n maxTXID: hv.getBigUint64(24),\n timestamp: Number(hv.getBigInt64(32)),\n preApplyChecksum: hv.getBigUint64(40),\n };\n // Trailer: … index | u64 index length | u64 post-apply checksum | u64 file checksum.\n const tail = await readRange(files, path, size - 24, 24);\n const idxLen = Number(new DataView(tail.buffer, tail.byteOffset, 24).getBigUint64(0));\n const idx = await readRange(files, path, size - 24 - idxLen, idxLen);\n const index = new Map<number, [number, number]>();\n let o = 0;\n const varint = () => {\n let x = 0;\n let shift = 0;\n let b: number;\n do {\n b = idx[o++];\n x += (b & 0x7f) * 2 ** shift;\n shift += 7;\n } while (b & 0x80);\n return x;\n };\n for (;;) {\n const pgno = varint();\n if (pgno === 0) break;\n const offset = varint();\n index.set(pgno, [offset, varint()]);\n }\n return pagesOf(files, path, header, index);\n}\n\nfunction pagesOf(\n files: FilesApi,\n path: string,\n header: LtxHeader,\n index: Map<number, [number, number]>,\n): LtxPages {\n const decodePage = (b: Uint8Array) => {\n const v = new DataView(b.buffer, b.byteOffset, b.byteLength);\n if (v.getUint16(4) & PAGE_HEADER_FLAG_SIZE)\n return lz4Decompress(b.subarray(10, 10 + v.getUint32(6)), header.pageSize);\n return lz4FrameDecompress(b.subarray(6), header.pageSize);\n };\n return {\n path,\n header,\n index,\n async readPage(pgno) {\n const at = index.get(pgno);\n if (!at) return undefined;\n return decodePage(await readRange(files, path, at[0], at[1]));\n },\n async readPages(pgnos) {\n const out = new Map<number, Uint8Array>();\n const wanted = pgnos.filter((p) => index.has(p));\n if (!wanted.length) return out;\n const spans = wanted.map((p) => index.get(p) as [number, number]);\n const start = Math.min(...spans.map((s) => s[0]));\n const end = Math.max(...spans.map((s) => s[0] + s[1]));\n const b = await readRange(files, path, start, end - start);\n for (const [i, p] of wanted.entries())\n out.set(p, decodePage(b.subarray(spans[i][0] - start, spans[i][0] - start + spans[i][1])));\n return out;\n },\n };\n}\n\nexport interface PagedCell {\n pageSize: number;\n /** Database size in pages, after the newest file. */\n pageCount: number;\n maxTXID: bigint;\n /** The files, oldest first (the snapshot, then the incremental ones). */\n files: LtxPages[];\n /** The page as of the newest file holding it (zeros past the database end). */\n readPage(pgno: number): Promise<Uint8Array>;\n /** Pages `from`..`from+count-1` (within the database), one range read per file they live in. */\n readRun(from: number, count: number): Promise<Map<number, Uint8Array>>;\n /** Which file serves each page (for tests and traces). */\n source(pgno: number): string | undefined;\n /**\n * Adds the cell's next file (the one just written after `maxTXID`): reads its header and index\n * only, so a commit costs one file whatever the number of files before it.\n */\n append(\n path: string,\n known?: { header: LtxHeader; index: Map<number, [number, number]> },\n ): Promise<void>;\n}\n\n/**\n * A cell's pages over its LTX files: the newest snapshot (min TXID 1) and the files after it,\n * in TXID order. Only headers and page indexes are read.\n */\nexport async function openPagedCell(files: FilesApi, paths: string[]): Promise<PagedCell> {\n const opened = await Promise.all(paths.map((p) => openLtxPages(files, p)));\n opened.sort((a, b) =>\n a.header.maxTXID < b.header.maxTXID ? -1 : a.header.maxTXID > b.header.maxTXID ? 1 : 0,\n );\n let snapshotAt = -1;\n opened.forEach((f, i) => {\n if (f.header.minTXID === 1n) snapshotAt = i;\n });\n if (snapshotAt < 0) throw new Error(\"ltx: no snapshot among the cell's files\");\n const chain = opened.slice(snapshotAt);\n for (let i = 1; i < chain.length; i++)\n if (chain[i].header.minTXID !== chain[i - 1].header.maxTXID + 1n)\n throw new Error(`ltx: gap before ${chain[i].path}`);\n let newest = chain[chain.length - 1].header;\n const owner = new Map<number, LtxPages>();\n for (const f of chain) for (const pgno of f.index.keys()) owner.set(pgno, f);\n const cell: PagedCell = {\n pageSize: newest.pageSize,\n pageCount: newest.commit,\n maxTXID: newest.maxTXID,\n files: chain,\n source: (pgno) => owner.get(pgno)?.path,\n async readPage(pgno) {\n const page = pgno <= cell.pageCount ? await owner.get(pgno)?.readPage(pgno) : undefined;\n return page ?? new Uint8Array(cell.pageSize);\n },\n async readRun(from, count) {\n const byFile = new Map<LtxPages, number[]>();\n const out = new Map<number, Uint8Array>();\n for (let p = from; p < from + count && p <= cell.pageCount; p++) {\n const f = owner.get(p);\n if (!f) out.set(p, new Uint8Array(cell.pageSize));\n else byFile.set(f, [...(byFile.get(f) ?? []), p]);\n }\n for (const [f, pgnos] of byFile) for (const [p, d] of await f.readPages(pgnos)) out.set(p, d);\n return out;\n },\n async append(path, known) {\n const f = await openLtxPages(files, path, known);\n if (f.header.minTXID !== cell.maxTXID + 1n)\n throw new Error(`ltx: ${path} does not follow TXID ${cell.maxTXID}`);\n chain.push(f);\n for (const pgno of f.index.keys()) owner.set(pgno, f);\n newest = f.header;\n cell.pageCount = newest.commit;\n cell.maxTXID = newest.maxTXID;\n for (const [pgno] of owner) if (pgno > cell.pageCount) owner.delete(pgno);\n },\n };\n return cell;\n}\n\n/**\n * An LTX file produced page by page, as celld writes it (block format: size-prefixed LZ4 page\n * blocks, page index, trailer, CRC-64/ISO over the uncompressed pages): the bytes come out as an\n * async stream while `pages` is consumed, so a writer holds one page at a time plus the index\n * (a few bytes per page), never the database.\n */\nexport async function* encodeLtxStream(\n header: LtxHeader,\n pages: AsyncIterable<[number, Uint8Array]>,\n postApplyChecksum = 0n,\n /** Filled while encoding: the file's page index (what `openLtxPages` would read back). */\n written?: { index: Map<number, [number, number]> },\n): AsyncGenerator<Uint8Array> {\n // Coalesced into 256 KiB chunks: a writer pays per call (an OPFS SyncAccessHandle write, a\n // stream write), and a page makes three small pieces.\n yield* coalesce(encodePieces(header, pages, postApplyChecksum, written), 256 * 1024);\n}\n\nasync function* coalesce(src: AsyncIterable<Uint8Array>, size: number): AsyncGenerator<Uint8Array> {\n let buf = new Uint8Array(size);\n let n = 0;\n for await (const piece of src) {\n if (piece.length >= size) {\n if (n) yield buf.slice(0, n), (n = 0);\n yield piece;\n continue;\n }\n if (n + piece.length > size) {\n yield buf.slice(0, n);\n buf = new Uint8Array(size);\n n = 0;\n }\n buf.set(piece, n);\n n += piece.length;\n }\n if (n) yield buf.slice(0, n);\n}\n\nasync function* encodePieces(\n header: LtxHeader,\n pages: AsyncIterable<[number, Uint8Array]>,\n postApplyChecksum: bigint,\n written?: { index: Map<number, [number, number]> },\n): AsyncGenerator<Uint8Array> {\n const crc = new Crc64();\n const head = new Uint8Array(HEADER_SIZE);\n const v = new DataView(head.buffer);\n head.set(new TextEncoder().encode(\"LTX1\"), 0);\n const flags =\n header.flags ?? (header.preApplyChecksum === 0n && postApplyChecksum === 0n ? 2 : 0);\n v.setUint32(4, flags);\n v.setUint32(8, header.pageSize);\n v.setUint32(12, header.commit);\n v.setBigUint64(16, header.minTXID);\n v.setBigUint64(24, header.maxTXID);\n v.setBigInt64(32, BigInt(header.timestamp));\n v.setBigUint64(40, header.preApplyChecksum);\n v.setBigUint64(72, header.nodeID ?? 0n);\n crc.update(head);\n yield head;\n const index: number[] = [];\n const uvarint = (n: number) => {\n while (n >= 0x80) index.push((n & 0x7f) | 0x80), (n = Math.floor(n / 128));\n index.push(n);\n };\n let offset = HEADER_SIZE;\n const lock = lockPgno(header.pageSize);\n let last = 0;\n for await (const [pgno, data] of pages) {\n if (pgno === lock) continue;\n if (pgno <= last) throw new Error(\"ltx: pages must come in ascending order\");\n last = pgno;\n const compressed = lz4Compress(data);\n const ph = new Uint8Array(10);\n const pv = new DataView(ph.buffer);\n pv.setUint32(0, pgno);\n pv.setUint16(4, PAGE_HEADER_FLAG_SIZE);\n pv.setUint32(6, compressed.length);\n crc.update(ph).update(data); // the checksum covers uncompressed pages (encodeLtx's `feed`)\n uvarint(pgno), uvarint(offset), uvarint(10 + compressed.length);\n written?.index.set(pgno, [offset, 10 + compressed.length]);\n offset += 10 + compressed.length;\n yield ph;\n yield compressed;\n }\n const end = new Uint8Array(6);\n uvarint(0);\n const idx = Uint8Array.from(index);\n const tail = new Uint8Array(16);\n const tv = new DataView(tail.buffer);\n tv.setBigUint64(0, BigInt(idx.length));\n tv.setBigUint64(8, postApplyChecksum);\n crc.update(end).update(idx).update(tail);\n const sum = new Uint8Array(8);\n new DataView(sum.buffer).setBigUint64(0, CHECKSUM_FLAG | crc.digest());\n yield end;\n yield idx;\n yield tail;\n yield sum;\n}\n\n/**\n * Folds a cell's files into one snapshot (TXID 1..maxTXID) written to `files` at `path`, page by\n * page: each page is read by range from the file that holds it and streamed into the new file,\n * so memory stays at one page (plus the index), whatever the database size. The caller removes\n * the files it replaces once this resolves.\n */\nexport async function compactPagedCellTo(\n cell: PagedCell,\n files: FilesApi,\n path: string,\n timestamp = Date.now(),\n): Promise<void> {\n // In runs of 256 pages: one range read per file a run touches (not one read per page).\n async function* pages(): AsyncGenerator<[number, Uint8Array]> {\n for (let from = 1; from <= cell.pageCount; from += 256) {\n const run = await cell.readRun(from, 256);\n for (let pgno = from; pgno < from + 256 && pgno <= cell.pageCount; pgno++)\n yield [pgno, run.get(pgno) ?? new Uint8Array(cell.pageSize)];\n }\n }\n await files.write(\n path,\n encodeLtxStream(\n {\n pageSize: cell.pageSize,\n commit: cell.pageCount,\n minTXID: 1n,\n maxTXID: cell.maxTXID,\n timestamp,\n preApplyChecksum: 0n,\n },\n pages(),\n ),\n );\n}\n\n/** The compacted snapshot as bytes (small cells, tests); compactPagedCellTo streams it. */\nexport async function compactPagedCell(\n cell: PagedCell,\n timestamp = Date.now(),\n): Promise<Uint8Array> {\n const chunks: Uint8Array[] = [];\n async function* pages(): AsyncGenerator<[number, Uint8Array]> {\n for (let pgno = 1; pgno <= cell.pageCount; pgno++) yield [pgno, await cell.readPage(pgno)];\n }\n for await (const c of encodeLtxStream(\n {\n pageSize: cell.pageSize,\n commit: cell.pageCount,\n minTXID: 1n,\n maxTXID: cell.maxTXID,\n timestamp,\n preApplyChecksum: 0n,\n },\n pages(),\n ))\n chunks.push(c);\n const out = new Uint8Array(chunks.reduce((n, c) => n + c.length, 0));\n let o = 0;\n for (const c of chunks) out.set(c, o), (o += c.length);\n return out;\n}\n"]}
1
+ {"version":3,"file":"ltx-pages.js","sourceRoot":"","sources":["../../src/ltx/ltx-pages.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Page-level random access to a cell kept as celld keeps it: LTX files in a bucket\n * (`…/ltx/e<epoch>/<level>/<min>-<max>.ltx`), a snapshot (TXID 1..N) and the incremental files\n * after it. Nothing is read whole: an LTX file ends with a page index (pgno → offset, size), so\n * opening a file reads its 100-byte header and its index, and a page is ONE range read plus an\n * LZ4 decode. The newest file holding a page wins; `compact` folds them into a new snapshot.\n *\n * This is the read side a SQLite VFS needs to serve a database from any FilesApi (OPFS, File\n * System Access, IndexedDB, S3…) without loading it: xRead → `readPage` (through a page cache);\n * the write side is the existing LTX log (one incremental file per commit).\n */\nimport type { FilesApi } from \"@statewalker/webrun-files\";\nimport { Crc64 } from \"./crc64.js\";\nimport { CHECKSUM_FLAG, type LtxHeader, lockPgno } from \"./ltx.js\";\nimport { lz4Compress, lz4Decompress, lz4FrameDecompress } from \"./lz4.js\";\n\nconst HEADER_SIZE = 100;\nconst PAGE_HEADER_FLAG_SIZE = 1;\n\nasync function readRange(\n files: FilesApi,\n path: string,\n start: number,\n length: number,\n): Promise<Uint8Array> {\n const chunks: Uint8Array[] = [];\n for await (const c of files.read(path, { start, length })) chunks.push(c);\n const got = chunks.reduce((n, c) => n + c.length, 0);\n // A short read is a file that changed or went away under its reader: say which, rather than\n // decode past its end (\"Offset is outside the bounds of the DataView\").\n if (got !== length) {\n const st = await files.stats(path).catch(() => undefined);\n throw new LtxShortReadError(\n `ltx: short read of ${path}: ${got} of ${length} bytes at ${start} (file ${st?.kind === \"file\" ? `${(st as { size: number }).size} bytes` : \"missing\"})`,\n );\n }\n if (chunks.length === 1) return chunks[0];\n const out = new Uint8Array(got);\n let o = 0;\n for (const c of chunks) out.set(c, o), (o += c.length);\n return out;\n}\n\n/** A range read that got fewer bytes than asked: the file changed or was removed meanwhile. */\nexport class LtxShortReadError extends Error {\n name = \"LtxShortReadError\";\n}\n\nexport interface LtxPages {\n path: string;\n header: LtxHeader;\n /** pgno → [offset of the page's 6-byte header, size including it]. */\n index: Map<number, [number, number]>;\n /** One range read and an LZ4 decode. */\n readPage(pgno: number): Promise<Uint8Array | undefined>;\n /** Several pages of this file with ONE range read (they are stored in pgno order). */\n readPages(pgnos: number[]): Promise<Map<number, Uint8Array>>;\n}\n\n/** Opens an LTX file for page reads: reads its header and its page index only. */\nexport async function openLtxPages(\n files: FilesApi,\n path: string,\n /** The header and index of a file this process just wrote: nothing is read back. */\n known?: { header: LtxHeader; index: Map<number, [number, number]> },\n): Promise<LtxPages> {\n if (known) return pagesOf(files, path, known.header, known.index);\n const size =\n (await files.stats(path))?.kind === \"file\"\n ? ((await files.stats(path)) as { size: number }).size\n : 0;\n if (size < HEADER_SIZE + 6 + 24) throw new Error(`ltx: ${path} is too small`);\n const head = await readRange(files, path, 0, HEADER_SIZE);\n const hv = new DataView(head.buffer, head.byteOffset, head.byteLength);\n if (new TextDecoder().decode(head.subarray(0, 4)) !== \"LTX1\")\n throw new Error(`ltx: ${path}: bad magic`);\n const header: LtxHeader = {\n flags: hv.getUint32(4),\n pageSize: hv.getUint32(8),\n commit: hv.getUint32(12),\n minTXID: hv.getBigUint64(16),\n maxTXID: hv.getBigUint64(24),\n timestamp: Number(hv.getBigInt64(32)),\n preApplyChecksum: hv.getBigUint64(40),\n };\n // Trailer: … index | u64 index length | u64 post-apply checksum | u64 file checksum.\n const tail = await readRange(files, path, size - 24, 24);\n const idxLen = Number(new DataView(tail.buffer, tail.byteOffset, 24).getBigUint64(0));\n const idx = await readRange(files, path, size - 24 - idxLen, idxLen);\n const index = new Map<number, [number, number]>();\n let o = 0;\n const varint = () => {\n let x = 0;\n let shift = 0;\n let b: number;\n do {\n b = idx[o++];\n x += (b & 0x7f) * 2 ** shift;\n shift += 7;\n } while (b & 0x80);\n return x;\n };\n for (;;) {\n const pgno = varint();\n if (pgno === 0) break;\n const offset = varint();\n index.set(pgno, [offset, varint()]);\n }\n return pagesOf(files, path, header, index);\n}\n\nfunction pagesOf(\n files: FilesApi,\n path: string,\n header: LtxHeader,\n index: Map<number, [number, number]>,\n): LtxPages {\n const decodePage = (b: Uint8Array) => {\n const v = new DataView(b.buffer, b.byteOffset, b.byteLength);\n if (v.getUint16(4) & PAGE_HEADER_FLAG_SIZE)\n return lz4Decompress(b.subarray(10, 10 + v.getUint32(6)), header.pageSize);\n return lz4FrameDecompress(b.subarray(6), header.pageSize);\n };\n return {\n path,\n header,\n index,\n async readPage(pgno) {\n const at = index.get(pgno);\n if (!at) return undefined;\n return decodePage(await readRange(files, path, at[0], at[1]));\n },\n async readPages(pgnos) {\n const out = new Map<number, Uint8Array>();\n const wanted = pgnos.filter((p) => index.has(p));\n if (!wanted.length) return out;\n const spans = wanted.map((p) => index.get(p) as [number, number]);\n const start = Math.min(...spans.map((s) => s[0]));\n const end = Math.max(...spans.map((s) => s[0] + s[1]));\n const b = await readRange(files, path, start, end - start);\n for (const [i, p] of wanted.entries())\n out.set(p, decodePage(b.subarray(spans[i][0] - start, spans[i][0] - start + spans[i][1])));\n return out;\n },\n };\n}\n\nexport interface PagedCell {\n pageSize: number;\n /** Database size in pages, after the newest file. */\n pageCount: number;\n maxTXID: bigint;\n /** The files, oldest first (the snapshot, then the incremental ones). */\n files: LtxPages[];\n /**\n * Older files the newest snapshot already holds (an interrupted compaction's): never read;\n * a writer that owns the cell removes them.\n */\n superseded: string[];\n /** The page as of the newest file holding it (zeros past the database end). */\n readPage(pgno: number): Promise<Uint8Array>;\n /** Pages `from`..`from+count-1` (within the database), one range read per file they live in. */\n readRun(from: number, count: number): Promise<Map<number, Uint8Array>>;\n /** Which file serves each page (for tests and traces). */\n source(pgno: number): string | undefined;\n /**\n * Adds the cell's next file (the one just written after `maxTXID`): reads its header and index\n * only, so a commit costs one file whatever the number of files before it.\n */\n append(\n path: string,\n known?: { header: LtxHeader; index: Map<number, [number, number]> },\n ): Promise<void>;\n}\n\n/**\n * A cell's pages over its LTX files: the newest snapshot (min TXID 1) and the files after it,\n * in TXID order. Only headers and page indexes are read.\n */\nexport async function openPagedCell(files: FilesApi, paths: string[]): Promise<PagedCell> {\n const settled = await Promise.all(\n paths.map((p) =>\n openLtxPages(files, p).then(\n (f) => ({ f }),\n (error: unknown) => ({ p, error }),\n ),\n ),\n );\n const opened: LtxPages[] = [];\n const skipped: unknown[] = [];\n for (const r of settled) {\n if (\"f\" in r) opened.push(r.f);\n // A snapshot that does not open (a compaction's move done as a copy, cut short): the\n // files it was made from are all still there, so the cell is read from them.\n else if (/(^|\\/)0{15}1-[0-9a-f]{16}\\.ltx$/.test(r.p)) {\n console.warn(`cellb ltx: skipping ${r.p}, which does not open:`, r.error);\n skipped.push(r.error);\n } else throw r.error;\n }\n // By maxTXID; at the same maxTXID, a snapshot (minTXID 1) after the incremental file it\n // folded in: a compaction interrupted between writing its snapshot and removing the files it\n // replaces leaves both, and the snapshot is what the cell is.\n opened.sort((a, b) =>\n a.header.maxTXID !== b.header.maxTXID\n ? a.header.maxTXID < b.header.maxTXID\n ? -1\n : 1\n : a.header.minTXID > b.header.minTXID\n ? -1\n : a.header.minTXID < b.header.minTXID\n ? 1\n : 0,\n );\n let snapshotAt = -1;\n opened.forEach((f, i) => {\n if (f.header.minTXID === 1n) snapshotAt = i;\n });\n // No other snapshot to read the cell from: the skipped one's own error is the reason.\n if (snapshotAt < 0) throw skipped[0] ?? new Error(\"ltx: no snapshot among the cell's files\");\n const chain = opened.slice(snapshotAt);\n /** Files the newest snapshot covers (left by an interrupted compaction): not read. */\n const superseded = opened.slice(0, snapshotAt).map((f) => f.path);\n for (let i = 1; i < chain.length; i++)\n if (chain[i].header.minTXID !== chain[i - 1].header.maxTXID + 1n)\n throw new Error(`ltx: gap before ${chain[i].path}`);\n let newest = chain[chain.length - 1].header;\n const owner = new Map<number, LtxPages>();\n for (const f of chain) for (const pgno of f.index.keys()) owner.set(pgno, f);\n const cell: PagedCell = {\n pageSize: newest.pageSize,\n pageCount: newest.commit,\n maxTXID: newest.maxTXID,\n files: chain,\n superseded,\n source: (pgno) => owner.get(pgno)?.path,\n async readPage(pgno) {\n const page = pgno <= cell.pageCount ? await owner.get(pgno)?.readPage(pgno) : undefined;\n return page ?? new Uint8Array(cell.pageSize);\n },\n async readRun(from, count) {\n const byFile = new Map<LtxPages, number[]>();\n const out = new Map<number, Uint8Array>();\n for (let p = from; p < from + count && p <= cell.pageCount; p++) {\n const f = owner.get(p);\n if (!f) out.set(p, new Uint8Array(cell.pageSize));\n else byFile.set(f, [...(byFile.get(f) ?? []), p]);\n }\n for (const [f, pgnos] of byFile) for (const [p, d] of await f.readPages(pgnos)) out.set(p, d);\n return out;\n },\n async append(path, known) {\n const f = await openLtxPages(files, path, known);\n if (f.header.minTXID !== cell.maxTXID + 1n)\n throw new Error(`ltx: ${path} does not follow TXID ${cell.maxTXID}`);\n chain.push(f);\n for (const pgno of f.index.keys()) owner.set(pgno, f);\n newest = f.header;\n cell.pageCount = newest.commit;\n cell.maxTXID = newest.maxTXID;\n for (const [pgno] of owner) if (pgno > cell.pageCount) owner.delete(pgno);\n },\n };\n return cell;\n}\n\n/**\n * An LTX file produced page by page, as celld writes it (block format: size-prefixed LZ4 page\n * blocks, page index, trailer, CRC-64/ISO over the uncompressed pages): the bytes come out as an\n * async stream while `pages` is consumed, so a writer holds one page at a time plus the index\n * (a few bytes per page), never the database.\n */\nexport async function* encodeLtxStream(\n header: LtxHeader,\n pages: AsyncIterable<[number, Uint8Array]>,\n postApplyChecksum = 0n,\n /** Filled while encoding: the file's page index (what `openLtxPages` would read back). */\n written?: { index: Map<number, [number, number]> },\n): AsyncGenerator<Uint8Array> {\n // Coalesced into 256 KiB chunks: a writer pays per call (an OPFS SyncAccessHandle write, a\n // stream write), and a page makes three small pieces.\n yield* coalesce(encodePieces(header, pages, postApplyChecksum, written), 256 * 1024);\n}\n\nasync function* coalesce(src: AsyncIterable<Uint8Array>, size: number): AsyncGenerator<Uint8Array> {\n let buf = new Uint8Array(size);\n let n = 0;\n for await (const piece of src) {\n if (piece.length >= size) {\n if (n) yield buf.slice(0, n), (n = 0);\n yield piece;\n continue;\n }\n if (n + piece.length > size) {\n yield buf.slice(0, n);\n buf = new Uint8Array(size);\n n = 0;\n }\n buf.set(piece, n);\n n += piece.length;\n }\n if (n) yield buf.slice(0, n);\n}\n\nasync function* encodePieces(\n header: LtxHeader,\n pages: AsyncIterable<[number, Uint8Array]>,\n postApplyChecksum: bigint,\n written?: { index: Map<number, [number, number]> },\n): AsyncGenerator<Uint8Array> {\n const crc = new Crc64();\n const head = new Uint8Array(HEADER_SIZE);\n const v = new DataView(head.buffer);\n head.set(new TextEncoder().encode(\"LTX1\"), 0);\n const flags =\n header.flags ?? (header.preApplyChecksum === 0n && postApplyChecksum === 0n ? 2 : 0);\n v.setUint32(4, flags);\n v.setUint32(8, header.pageSize);\n v.setUint32(12, header.commit);\n v.setBigUint64(16, header.minTXID);\n v.setBigUint64(24, header.maxTXID);\n v.setBigInt64(32, BigInt(header.timestamp));\n v.setBigUint64(40, header.preApplyChecksum);\n v.setBigUint64(72, header.nodeID ?? 0n);\n crc.update(head);\n yield head;\n const index: number[] = [];\n const uvarint = (n: number) => {\n while (n >= 0x80) index.push((n & 0x7f) | 0x80), (n = Math.floor(n / 128));\n index.push(n);\n };\n let offset = HEADER_SIZE;\n const lock = lockPgno(header.pageSize);\n let last = 0;\n for await (const [pgno, data] of pages) {\n if (pgno === lock) continue;\n if (pgno <= last) throw new Error(\"ltx: pages must come in ascending order\");\n last = pgno;\n const compressed = lz4Compress(data);\n const ph = new Uint8Array(10);\n const pv = new DataView(ph.buffer);\n pv.setUint32(0, pgno);\n pv.setUint16(4, PAGE_HEADER_FLAG_SIZE);\n pv.setUint32(6, compressed.length);\n crc.update(ph).update(data); // the checksum covers uncompressed pages (encodeLtx's `feed`)\n uvarint(pgno), uvarint(offset), uvarint(10 + compressed.length);\n written?.index.set(pgno, [offset, 10 + compressed.length]);\n offset += 10 + compressed.length;\n yield ph;\n yield compressed;\n }\n const end = new Uint8Array(6);\n uvarint(0);\n const idx = Uint8Array.from(index);\n const tail = new Uint8Array(16);\n const tv = new DataView(tail.buffer);\n tv.setBigUint64(0, BigInt(idx.length));\n tv.setBigUint64(8, postApplyChecksum);\n crc.update(end).update(idx).update(tail);\n const sum = new Uint8Array(8);\n new DataView(sum.buffer).setBigUint64(0, CHECKSUM_FLAG | crc.digest());\n yield end;\n yield idx;\n yield tail;\n yield sum;\n}\n\n/**\n * Folds a cell's files into one snapshot (TXID 1..maxTXID) written to `files` at `path`, page by\n * page: each page is read by range from the file that holds it and streamed into the new file,\n * so memory stays at one page (plus the index), whatever the database size. The caller removes\n * the files it replaces once this resolves.\n */\nexport async function compactPagedCellTo(\n cell: PagedCell,\n files: FilesApi,\n path: string,\n timestamp = Date.now(),\n): Promise<void> {\n // In runs of 256 pages: one range read per file a run touches (not one read per page).\n async function* pages(): AsyncGenerator<[number, Uint8Array]> {\n for (let from = 1; from <= cell.pageCount; from += 256) {\n const run = await cell.readRun(from, 256);\n for (let pgno = from; pgno < from + 256 && pgno <= cell.pageCount; pgno++)\n yield [pgno, run.get(pgno) ?? new Uint8Array(cell.pageSize)];\n }\n }\n await files.write(\n path,\n encodeLtxStream(\n {\n pageSize: cell.pageSize,\n commit: cell.pageCount,\n minTXID: 1n,\n maxTXID: cell.maxTXID,\n timestamp,\n preApplyChecksum: 0n,\n },\n pages(),\n ),\n );\n}\n\n/** The compacted snapshot as bytes (small cells, tests); compactPagedCellTo streams it. */\nexport async function compactPagedCell(\n cell: PagedCell,\n timestamp = Date.now(),\n): Promise<Uint8Array> {\n const chunks: Uint8Array[] = [];\n async function* pages(): AsyncGenerator<[number, Uint8Array]> {\n for (let pgno = 1; pgno <= cell.pageCount; pgno++) yield [pgno, await cell.readPage(pgno)];\n }\n for await (const c of encodeLtxStream(\n {\n pageSize: cell.pageSize,\n commit: cell.pageCount,\n minTXID: 1n,\n maxTXID: cell.maxTXID,\n timestamp,\n preApplyChecksum: 0n,\n },\n pages(),\n ))\n chunks.push(c);\n const out = new Uint8Array(chunks.reduce((n, c) => n + c.length, 0));\n let o = 0;\n for (const c of chunks) out.set(c, o), (o += c.length);\n return out;\n}\n"]}