@rulvar/store-sqlite 1.47.0 → 1.49.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/dist/index.d.ts +11 -1
- package/dist/index.js +43 -3
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,16 @@ import { JournalEntry, LeasableStore, Lease, MetaLookupStore, RunFilter, RunMeta
|
|
|
3
3
|
//#region src/store.d.ts
|
|
4
4
|
/** Appendix A interim reference for the sqlite store. */
|
|
5
5
|
declare const DEFAULT_LEASE_TTL_MS = 6e4;
|
|
6
|
+
/**
|
|
7
|
+
* Total time the constructor keeps retrying its schema bootstrap
|
|
8
|
+
* through SQLITE_BUSY before giving up, so concurrent multi-process
|
|
9
|
+
* construction over one fresh file serializes instead of dying raw.
|
|
10
|
+
* The bound applies ONLY to boot; every runtime contention path keeps
|
|
11
|
+
* the documented fail-fast semantics (busy surfaces immediately). A
|
|
12
|
+
* boot still busy past the bound throws the driver's error: something
|
|
13
|
+
* is wedged, not merely concurrent.
|
|
14
|
+
*/
|
|
15
|
+
declare const BOOT_BUSY_TIMEOUT_MS = 5e3;
|
|
6
16
|
interface SqliteStoreOptions {
|
|
7
17
|
/** Database file path, or ':memory:' for an in-process store. */
|
|
8
18
|
path: string;
|
|
@@ -103,4 +113,4 @@ declare class SqliteStore implements MetaLookupStore, LeasableStore {
|
|
|
103
113
|
release(l: Lease): Promise<void>;
|
|
104
114
|
}
|
|
105
115
|
//#endregion
|
|
106
|
-
export { DEFAULT_LEASE_TTL_MS, SqliteStore, type SqliteStoreOptions, type SqliteTranscriptStore };
|
|
116
|
+
export { BOOT_BUSY_TIMEOUT_MS, DEFAULT_LEASE_TTL_MS, SqliteStore, type SqliteStoreOptions, type SqliteTranscriptStore };
|
package/dist/index.js
CHANGED
|
@@ -35,11 +35,43 @@ import { ConfigError, JournalOrderViolation, LeaseHeldError, metaMatchesFilter }
|
|
|
35
35
|
* - acquire on a held, unexpired lease rejects with LeaseHeldError; the
|
|
36
36
|
* holder MUST renew at an interval of at most ttl/3; an unrenewed
|
|
37
37
|
* lease is reclaimable after ttl and reclaiming advances the epoch.
|
|
38
|
+
* - Concurrent boot: constructing over one shared file from several
|
|
39
|
+
* processes at once serializes on a boot-scoped busy timeout
|
|
40
|
+
* (BOOT_BUSY_TIMEOUT_MS) instead of dying raw in the schema
|
|
41
|
+
* bootstrap; runtime contention stays fail fast (busy 0).
|
|
38
42
|
* - The lease ttl default is the Appendix A interim reference for this
|
|
39
43
|
* store (60000 ms; the committed value is decided before M8).
|
|
40
44
|
*/
|
|
41
45
|
/** Appendix A interim reference for the sqlite store. */
|
|
42
46
|
const DEFAULT_LEASE_TTL_MS = 6e4;
|
|
47
|
+
/**
|
|
48
|
+
* Total time the constructor keeps retrying its schema bootstrap
|
|
49
|
+
* through SQLITE_BUSY before giving up, so concurrent multi-process
|
|
50
|
+
* construction over one fresh file serializes instead of dying raw.
|
|
51
|
+
* The bound applies ONLY to boot; every runtime contention path keeps
|
|
52
|
+
* the documented fail-fast semantics (busy surfaces immediately). A
|
|
53
|
+
* boot still busy past the bound throws the driver's error: something
|
|
54
|
+
* is wedged, not merely concurrent.
|
|
55
|
+
*/
|
|
56
|
+
const BOOT_BUSY_TIMEOUT_MS = 5e3;
|
|
57
|
+
/**
|
|
58
|
+
* The SQLITE_BUSY family, the one boot error class worth a bounded
|
|
59
|
+
* retry. The driver reports EXTENDED result codes (a concurrent boot
|
|
60
|
+
* also yields SQLITE_BUSY_RECOVERY 261 while a sibling process is
|
|
61
|
+
* recovering the fresh WAL), so the primary code is the low byte.
|
|
62
|
+
*/
|
|
63
|
+
function isSqliteBusy(thrown) {
|
|
64
|
+
const errcode = thrown?.errcode;
|
|
65
|
+
return errcode !== void 0 && (errcode & 255) === 5;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Synchronous bounded sleep for the boot retry loop (the constructor is
|
|
69
|
+
* synchronous by SPI shape): Atomics.wait blocks the thread for the few
|
|
70
|
+
* milliseconds another process needs to finish the shared bootstrap.
|
|
71
|
+
*/
|
|
72
|
+
function sleepSync(ms) {
|
|
73
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
74
|
+
}
|
|
43
75
|
const wallClock = Date.now.bind(globalThis);
|
|
44
76
|
var SqliteStore = class {
|
|
45
77
|
/**
|
|
@@ -60,7 +92,7 @@ var SqliteStore = class {
|
|
|
60
92
|
this.db = new DatabaseSync(options.path);
|
|
61
93
|
this.ttlMs = ttlMs;
|
|
62
94
|
this.now = options.now ?? wallClock;
|
|
63
|
-
|
|
95
|
+
const schema = `
|
|
64
96
|
PRAGMA journal_mode = WAL;
|
|
65
97
|
CREATE TABLE IF NOT EXISTS entries (
|
|
66
98
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -94,7 +126,15 @@ var SqliteStore = class {
|
|
|
94
126
|
data BLOB NOT NULL
|
|
95
127
|
);
|
|
96
128
|
CREATE INDEX IF NOT EXISTS blobs_by_run ON blobs (run_id);
|
|
97
|
-
|
|
129
|
+
`;
|
|
130
|
+
const bootDeadline = wallClock() + BOOT_BUSY_TIMEOUT_MS;
|
|
131
|
+
for (let attempt = 0;; attempt += 1) try {
|
|
132
|
+
this.db.exec(schema);
|
|
133
|
+
break;
|
|
134
|
+
} catch (thrown) {
|
|
135
|
+
if (!isSqliteBusy(thrown) || wallClock() > bootDeadline) throw thrown;
|
|
136
|
+
sleepSync(2 + attempt % 7);
|
|
137
|
+
}
|
|
98
138
|
}
|
|
99
139
|
close() {
|
|
100
140
|
this.db.close();
|
|
@@ -314,4 +354,4 @@ var SqliteStore = class {
|
|
|
314
354
|
}
|
|
315
355
|
};
|
|
316
356
|
//#endregion
|
|
317
|
-
export { DEFAULT_LEASE_TTL_MS, SqliteStore };
|
|
357
|
+
export { BOOT_BUSY_TIMEOUT_MS, DEFAULT_LEASE_TTL_MS, SqliteStore };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/store-sqlite",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.49.0",
|
|
4
4
|
"description": "Rulvar SQLite store implementing JournalStore and LeasableStore with a fencing epoch.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
"access": "public"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@rulvar/core": "1.
|
|
25
|
+
"@rulvar/core": "1.49.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^22.20.0",
|
|
29
29
|
"tsdown": "^0.22.3",
|
|
30
30
|
"typescript": "~6.0.3",
|
|
31
|
-
"@rulvar/store-conformance": "1.
|
|
31
|
+
"@rulvar/store-conformance": "1.49.0"
|
|
32
32
|
},
|
|
33
33
|
"repository": {
|
|
34
34
|
"type": "git",
|