@statewalker/cellb 0.5.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -6
- package/dist/adapters/ltx-vfs.js +26 -4
- package/dist/adapters/ltx-vfs.js.map +1 -1
- package/dist/browser.d.ts +4 -0
- package/dist/browser.js +2 -0
- package/dist/browser.js.map +1 -1
- package/dist/bundle.d.ts +2 -0
- package/dist/bundle.js +5 -0
- package/dist/bundle.js.map +1 -0
- package/dist/core/object-host.d.ts +7 -0
- package/dist/core/object-host.js +40 -7
- package/dist/core/object-host.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/ltx/files-rpc.d.ts +30 -0
- package/dist/ltx/files-rpc.js +129 -0
- package/dist/ltx/files-rpc.js.map +1 -0
- package/dist/ltx/ltx-host.d.ts +63 -0
- package/dist/ltx/ltx-host.js +46 -0
- package/dist/ltx/ltx-host.js.map +1 -0
- package/dist/ltx/ltx-page-server.d.ts +42 -1
- package/dist/ltx/ltx-page-server.js +101 -21
- package/dist/ltx/ltx-page-server.js.map +1 -1
- package/dist/ltx/ltx-pages.d.ts +5 -0
- package/dist/ltx/ltx-pages.js +32 -3
- package/dist/ltx/ltx-pages.js.map +1 -1
- package/dist/ltx/ltx-store-worker.js +35 -22
- package/dist/ltx/ltx-store-worker.js.map +1 -1
- package/dist/node-compat/browser-registry.d.ts +1 -0
- package/dist/node-compat/browser-registry.js +5 -0
- package/dist/node-compat/browser-registry.js.map +1 -1
- package/dist/node-compat/util-guard.d.ts +2 -0
- package/dist/node-compat/util-guard.js +14 -0
- package/dist/node-compat/util-guard.js.map +1 -0
- package/dist/project/browser-host.d.ts +32 -3
- package/dist/project/browser-host.js +47 -20
- package/dist/project/browser-host.js.map +1 -1
- package/dist/project/browser-load.d.ts +16 -4
- package/dist/project/browser-load.js +46 -9
- package/dist/project/browser-load.js.map +1 -1
- package/dist/project/build.js +2 -2
- package/dist/project/build.js.map +1 -1
- package/dist/project/bundle-compiler.d.ts +31 -0
- package/dist/project/bundle-compiler.js +73 -0
- package/dist/project/bundle-compiler.js.map +1 -0
- package/dist/project/node-project.js +3 -3
- package/dist/project/node-project.js.map +1 -1
- package/dist/project/site-compiler.d.ts +67 -0
- package/dist/project/site-compiler.js +134 -0
- package/dist/project/site-compiler.js.map +1 -0
- package/dist/project/source-project.d.ts +46 -0
- package/dist/project/source-project.js +45 -0
- package/dist/project/source-project.js.map +1 -0
- package/dist/project/wrangler.d.ts +4 -0
- package/dist/project/wrangler.js +7 -1
- package/dist/project/wrangler.js.map +1 -1
- package/dist/site.d.ts +2 -0
- package/dist/site.js +4 -3
- package/dist/site.js.map +1 -1
- package/package.json +11 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ltx-host.js","sourceRoot":"","sources":["../../src/ltx/ltx-host.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,gFAAgF;AAChF,MAAM,UAAU,YAAY,CAAC,KAAe;IAC1C,MAAM,EAAE,GAAG,IAAI,cAAc,EAAE,CAAC;IAChC,MAAM,IAAI,GAAG,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC5C,OAAO;QACL,IAAI,EAAE,EAAE,CAAC,KAAK;QACd,KAAK;YACH,IAAI,EAAE,CAAC;YACP,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC;AAaD;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAe,EAAE,IAA0B;IACvE,0FAA0F;IAC1F,gBAAgB;IAChB,MAAM,GAAG,GAAG,IAAI,iBAAiB,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,YAAY,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC;IACxF,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE;QACrC,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,SAAS,CAAC;KAC3C,CAAC,CAAC;IACH,MAAM,EAAE,GAAG,IAAI,cAAc,EAAE,CAAC;IAChC,EAAE,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACjD,OAAO;QACL,GAAG;QACH,IAAI,EAAE,EAAE,CAAC,KAAK;QACd,KAAK;QACL;;;;WAIG;QACH,KAAK;YACH,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACjB,OAAO,KAAK,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Cells' storage chosen by the app: it opens a FilesApi (File System Access, memory, OPFS, a\n * key/value store, S3…) BEFORE starting cellb's workers, then hands it to them in one of two\n * ways (a FilesApi instance cannot be posted to a worker; a port can):\n *\n * - `filesApiPort(files)`: the FilesApi stays in this thread and is served over a port; the\n * shard worker passes the port to its LTX store worker (`bucket: { ltx, files: { port } }`),\n * which runs the store (LZ4, compaction) and calls the FilesApi by message.\n * - `serveLtxStore(files)`: the LTX store itself runs in this thread on the instance (no\n * FilesApi messages at all); the shard worker's VFS sends it its page reads and commits\n * (`bucket: { ltx, ltxStore: { sab, port } }`). The store's work then shares this thread.\n *\n * Either way, one call per shard worker: a port answers one worker, a store one VFS.\n */\nimport type { FilesApi } from \"@statewalker/webrun-files\";\nimport { serveFilesApi } from \"./files-rpc.js\";\nimport { ltxCellStore } from \"./ltx-page-server.js\";\n\n/** A port to `files` for a worker (transfer it); `close()` stops serving it. */\nexport function filesApiPort(files: FilesApi): { port: MessagePort; close(): void } {\n const ch = new MessageChannel();\n const stop = serveFilesApi(ch.port1, files);\n return {\n port: ch.port2,\n close() {\n stop();\n ch.port1.close();\n },\n };\n}\n\nexport interface ServeLtxStoreOptions {\n /** Names the storage: two stores with one scope never write the same cell at once. */\n lockScope: string;\n /** Files per cell before it is folded into one snapshot (default 64). */\n compactAfter?: number;\n /** Free bytes where the files are (see ltxCellStore); unknown by default. */\n room?: () => Promise<number | undefined>;\n /** Bytes of the shared buffer's payload: a read-ahead run (default 1 MiB, at least 64 KiB). */\n payloadBytes?: number;\n}\n\n/**\n * The LTX store on `files` in this thread, for ONE shard worker's VFS: give the worker\n * `{ sab, port }` (transfer the port). This thread must not block (a page, or any worker that\n * does not run SQLite); it answers into `sab`.\n */\nexport function serveLtxStore(files: FilesApi, opts: ServeLtxStoreOptions) {\n // At least one page of SQLite's largest size: a smaller buffer would answer reads with no\n // pages at all.\n const sab = new SharedArrayBuffer(8 + Math.max(65536, opts.payloadBytes ?? 16 * 65536));\n const store = ltxCellStore(files, sab, {\n lockScope: opts.lockScope,\n compactAfter: opts.compactAfter,\n room: opts.room ?? (async () => undefined),\n });\n const ch = new MessageChannel();\n ch.port1.onmessage = (e) => store.handle(e.data);\n return {\n sab,\n port: ch.port2,\n store,\n /**\n * Stops serving: call it when the shard worker ends, so a store for its successor can\n * take the cells' Web Locks (it waits 20 s for them, then fails). Resolves once the\n * store's work under way has ended and its locks are released.\n */\n close() {\n ch.port1.close();\n return store.close();\n },\n };\n}\n"]}
|
|
@@ -6,6 +6,32 @@
|
|
|
6
6
|
* shared buffer. A cell whose files pass `compactAfter` is folded into one snapshot (streamed).
|
|
7
7
|
*/
|
|
8
8
|
import type { FilesApi } from "@statewalker/webrun-files";
|
|
9
|
+
/**
|
|
10
|
+
* Where the store's FilesApi comes from. An instance cannot cross to this worker, so it is:
|
|
11
|
+
* - a preset by name, on OPFS directory `dir`: "opfs-sync" (default: synchronous handles; each
|
|
12
|
+
* file written under a temporary name and moved into place, so a file being written is never
|
|
13
|
+
* read), "opfs" (OPFS's asynchronous API), "memory" (nothing kept);
|
|
14
|
+
* - `{ port }`: a FilesApi served elsewhere (`serveFilesApi`, on the page or in a worker of
|
|
15
|
+
* the app's own): File System Access, IndexedDB, S3, anything;
|
|
16
|
+
* - `{ module, args }`: a module this worker imports, whose default export (or
|
|
17
|
+
* `createFilesApi`) builds the FilesApi here from `args` (structured-cloneable: a
|
|
18
|
+
* FileSystemDirectoryHandle, S3 settings…), and whose optional `room(args)` says the free
|
|
19
|
+
* bytes there.
|
|
20
|
+
*/
|
|
21
|
+
export type LtxStoreFiles = "opfs" | "opfs-sync" | "memory" | {
|
|
22
|
+
port: MessagePort;
|
|
23
|
+
} | {
|
|
24
|
+
module: string;
|
|
25
|
+
args?: unknown;
|
|
26
|
+
};
|
|
27
|
+
export interface LtxStoreInit {
|
|
28
|
+
op: "init";
|
|
29
|
+
sab: SharedArrayBuffer;
|
|
30
|
+
/** The OPFS directory of a preset; else the name of the storage (the Web Locks' scope). */
|
|
31
|
+
dir: string;
|
|
32
|
+
compactAfter?: number;
|
|
33
|
+
files?: LtxStoreFiles;
|
|
34
|
+
}
|
|
9
35
|
export interface LtxStoreMessage {
|
|
10
36
|
op: "meta" | "read" | "readRun" | "commit" | "stats";
|
|
11
37
|
cell: string;
|
|
@@ -24,9 +50,19 @@ export declare function ltxCellStore(files: FilesApi, sab: SharedArrayBuffer, op
|
|
|
24
50
|
* stores of two directories (two projects in one tab) do not take each other's locks.
|
|
25
51
|
*/
|
|
26
52
|
lockScope?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Free bytes where the files are, checked before a compaction writes its snapshot beside
|
|
55
|
+
* the files it replaces; undefined: unknown, compact anyway. Defaults to this origin's
|
|
56
|
+
* storage estimate, which is right for OPFS only: give another FilesApi its own (or
|
|
57
|
+
* `async () => undefined`).
|
|
58
|
+
*/
|
|
59
|
+
room?: () => Promise<number | undefined>;
|
|
27
60
|
}): {
|
|
28
61
|
/** Answers one request of a VFS (requests are served in order). */
|
|
29
|
-
handle(msg: LtxStoreMessage
|
|
62
|
+
handle(msg: LtxStoreMessage & {
|
|
63
|
+
seq?: number;
|
|
64
|
+
until?: number;
|
|
65
|
+
}): void;
|
|
30
66
|
/** Folds a cell's files into one snapshot; answers its file count after. */
|
|
31
67
|
/** Folds a cell's files into one snapshot (not through the request queue: it takes its
|
|
32
68
|
* own turn there to swap the files); answers the cell's file count after. */
|
|
@@ -40,6 +76,11 @@ export declare function ltxCellStore(files: FilesApi, sab: SharedArrayBuffer, op
|
|
|
40
76
|
reads: number;
|
|
41
77
|
readMs: number;
|
|
42
78
|
};
|
|
79
|
+
/**
|
|
80
|
+
* Stops the store: its Web Locks released (another store may open its cells), its open
|
|
81
|
+
* cells forgotten. A store in a worker of its own ends with the worker instead.
|
|
82
|
+
*/
|
|
83
|
+
close(): Promise<void>;
|
|
43
84
|
/** The paths of a cell's current files. */
|
|
44
85
|
files: (cell: string) => Promise<string[]>;
|
|
45
86
|
};
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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 (
|
|
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"]}
|
package/dist/ltx/ltx-pages.d.ts
CHANGED
|
@@ -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. */
|
package/dist/ltx/ltx-pages.js
CHANGED
|
@@ -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
|
|
117
|
-
opened
|
|
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;
|