orez 0.4.40 → 0.4.41
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.
|
@@ -1,29 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* neutralize zero-cache's litestream replica restore for orez.
|
|
3
3
|
*
|
|
4
|
-
* zero
|
|
5
|
-
* `restoreReplica()` on startup whenever the change-log already has rows
|
|
6
|
-
*
|
|
7
|
-
* backup
|
|
8
|
-
*
|
|
9
|
-
* "Missing
|
|
10
|
-
*
|
|
11
|
-
* re-syncing the entire replica from scratch.
|
|
4
|
+
* zero's dedicated change-streamer worker unconditionally calls
|
|
5
|
+
* `restoreReplica()` on startup whenever the change-log already has rows.
|
|
6
|
+
* it assumes a litestream backup exists to restore the SQLite replica from.
|
|
7
|
+
* orez has no litestream backup — it owns the replica on disk directly — so
|
|
8
|
+
* without a guard the restore throws "Missing --litestream-executable" (or
|
|
9
|
+
* "Missing required value" for the backup URL) after a retry, which zero
|
|
10
|
+
* logs at error level on every warm restart.
|
|
12
11
|
*
|
|
13
12
|
* the correct config gate doesn't exist for the dedicated change-streamer:
|
|
14
|
-
* - leaving litestream unconfigured -> the error above
|
|
13
|
+
* - leaving litestream unconfigured -> the noisy double-attempt error above.
|
|
15
14
|
* - setting ZERO_LITESTREAM_BACKUP_URL to satisfy the restore -> switches the
|
|
16
15
|
* replicator into "serving-copy" mode (VACUUM INTO a copy on every start)
|
|
17
16
|
* and spawns a backup worker + litestream `replicate` subprocess. wrong for
|
|
18
17
|
* an ephemeral dev backend.
|
|
19
18
|
*
|
|
20
|
-
* so we patch the compiled `restoreReplica`
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
19
|
+
* so we patch the compiled `restoreReplica` for the no-backup case. the guard
|
|
20
|
+
* must preserve upstream's LOCK-RELEASE semantics, not just silence the error:
|
|
21
|
+
* the change-streamer acquires a changeLog purge lock before restoring — an
|
|
22
|
+
* open `SELECT ... FOR SHARE` transaction, which holds a real xid — and its
|
|
23
|
+
* caller only releases that lock when restoreReplica THROWS. a plain
|
|
24
|
+
* `return` here (the original v1 guard) kept the lock held into
|
|
25
|
+
* `initializePostgresChangeSource`; whenever the local replica was missing
|
|
26
|
+
* (unclean shutdown, cache-only reset), initial sync then ran with the lock
|
|
27
|
+
* still open and `CREATE_REPLICATION_SLOT` waited on our own transaction's
|
|
28
|
+
* xid — a deterministic self-deadlock, crash-looping the change-streamer on
|
|
29
|
+
* `ensureSchemaMigrated`/55P03 lock timeouts until the change db was dropped.
|
|
30
|
+
*
|
|
31
|
+
* throwing `BackupNotFoundException` mirrors what unpatched zero does when
|
|
32
|
+
* litestream config is absent (its `must(backupURL)` throws and the caller
|
|
33
|
+
* releases the lock, logs at warn, and proceeds): a valid local replica is
|
|
34
|
+
* resumed exactly as before, an invalid/missing one initial-syncs lock-free.
|
|
35
|
+
* the purge lock only exists to fence multi-node purge races during a real
|
|
36
|
+
* backup restore; single-node orez has neither.
|
|
27
37
|
*
|
|
28
38
|
* this mirrors orez's existing in-place patching of @rocicorp/zero-sqlite3
|
|
29
39
|
* (see sqlite-mode/apply-mode.ts). it is idempotent and only touches orez's
|
|
@@ -33,7 +43,7 @@
|
|
|
33
43
|
/**
|
|
34
44
|
* apply the restore-guard to a specific compiled litestream commands.js —
|
|
35
45
|
* shared by the in-place node path and the CF overlay (cf-patches.ts).
|
|
36
|
-
* idempotent.
|
|
46
|
+
* idempotent; upgrades a v1 guard in place.
|
|
37
47
|
*/
|
|
38
48
|
export declare function applyLitestreamRestoreGuard(commandsPath: string): void;
|
|
39
49
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zero-litestream-patch.d.ts","sourceRoot":"","sources":["../src/zero-litestream-patch.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"zero-litestream-patch.d.ts","sourceRoot":"","sources":["../src/zero-litestream-patch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAmDH;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CA8BtE;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,IAAI,IAAI,CAInD"}
|
|
@@ -1,29 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* neutralize zero-cache's litestream replica restore for orez.
|
|
3
3
|
*
|
|
4
|
-
* zero
|
|
5
|
-
* `restoreReplica()` on startup whenever the change-log already has rows
|
|
6
|
-
*
|
|
7
|
-
* backup
|
|
8
|
-
*
|
|
9
|
-
* "Missing
|
|
10
|
-
*
|
|
11
|
-
* re-syncing the entire replica from scratch.
|
|
4
|
+
* zero's dedicated change-streamer worker unconditionally calls
|
|
5
|
+
* `restoreReplica()` on startup whenever the change-log already has rows.
|
|
6
|
+
* it assumes a litestream backup exists to restore the SQLite replica from.
|
|
7
|
+
* orez has no litestream backup — it owns the replica on disk directly — so
|
|
8
|
+
* without a guard the restore throws "Missing --litestream-executable" (or
|
|
9
|
+
* "Missing required value" for the backup URL) after a retry, which zero
|
|
10
|
+
* logs at error level on every warm restart.
|
|
12
11
|
*
|
|
13
12
|
* the correct config gate doesn't exist for the dedicated change-streamer:
|
|
14
|
-
* - leaving litestream unconfigured -> the error above
|
|
13
|
+
* - leaving litestream unconfigured -> the noisy double-attempt error above.
|
|
15
14
|
* - setting ZERO_LITESTREAM_BACKUP_URL to satisfy the restore -> switches the
|
|
16
15
|
* replicator into "serving-copy" mode (VACUUM INTO a copy on every start)
|
|
17
16
|
* and spawns a backup worker + litestream `replicate` subprocess. wrong for
|
|
18
17
|
* an ephemeral dev backend.
|
|
19
18
|
*
|
|
20
|
-
* so we patch the compiled `restoreReplica`
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
19
|
+
* so we patch the compiled `restoreReplica` for the no-backup case. the guard
|
|
20
|
+
* must preserve upstream's LOCK-RELEASE semantics, not just silence the error:
|
|
21
|
+
* the change-streamer acquires a changeLog purge lock before restoring — an
|
|
22
|
+
* open `SELECT ... FOR SHARE` transaction, which holds a real xid — and its
|
|
23
|
+
* caller only releases that lock when restoreReplica THROWS. a plain
|
|
24
|
+
* `return` here (the original v1 guard) kept the lock held into
|
|
25
|
+
* `initializePostgresChangeSource`; whenever the local replica was missing
|
|
26
|
+
* (unclean shutdown, cache-only reset), initial sync then ran with the lock
|
|
27
|
+
* still open and `CREATE_REPLICATION_SLOT` waited on our own transaction's
|
|
28
|
+
* xid — a deterministic self-deadlock, crash-looping the change-streamer on
|
|
29
|
+
* `ensureSchemaMigrated`/55P03 lock timeouts until the change db was dropped.
|
|
30
|
+
*
|
|
31
|
+
* throwing `BackupNotFoundException` mirrors what unpatched zero does when
|
|
32
|
+
* litestream config is absent (its `must(backupURL)` throws and the caller
|
|
33
|
+
* releases the lock, logs at warn, and proceeds): a valid local replica is
|
|
34
|
+
* resumed exactly as before, an invalid/missing one initial-syncs lock-free.
|
|
35
|
+
* the purge lock only exists to fence multi-node purge races during a real
|
|
36
|
+
* backup restore; single-node orez has neither.
|
|
27
37
|
*
|
|
28
38
|
* this mirrors orez's existing in-place patching of @rocicorp/zero-sqlite3
|
|
29
39
|
* (see sqlite-mode/apply-mode.ts). it is idempotent and only touches orez's
|
|
@@ -33,11 +43,21 @@
|
|
|
33
43
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
34
44
|
import { dirname, resolve } from 'node:path';
|
|
35
45
|
import { resolvePackage } from './sqlite-mode/package-resolve.js';
|
|
36
|
-
const OREZ_MARKER = '/* orez: litestream restore disabled (no backup configured) */';
|
|
37
|
-
// the
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
const
|
|
46
|
+
const OREZ_MARKER = '/* orez: litestream restore disabled (no backup configured) — v2 */';
|
|
47
|
+
// the previous guard generation: silenced the error but swallowed the throw
|
|
48
|
+
// the caller relies on to release the changeLog purge lock (see header).
|
|
49
|
+
// stripped on sight so upgrades replace it instead of stacking guards.
|
|
50
|
+
const OREZ_MARKER_V1 = '/* orez: litestream restore disabled (no backup configured) */';
|
|
51
|
+
const GUARD_V1 = `${OREZ_MARKER_V1}\n\tif (!config.litestream?.backupURL) return;`;
|
|
52
|
+
// the guard injected at the top of restoreReplica's body. when no backup URL
|
|
53
|
+
// is configured there is nothing to restore; surfacing not-found makes the
|
|
54
|
+
// caller release the purge lock before any initial sync (the deadlock fix),
|
|
55
|
+
// while callers without a lock (replicaConstraints == null) just proceed.
|
|
56
|
+
const GUARD = `${OREZ_MARKER}
|
|
57
|
+
if (!config.litestream?.backupURL) {
|
|
58
|
+
if (replicaConstraints) throw new BackupNotFoundException("(litestream disabled)");
|
|
59
|
+
return;
|
|
60
|
+
}`;
|
|
41
61
|
/** locate the compiled litestream commands.js inside the resolved @rocicorp/zero */
|
|
42
62
|
function findLitestreamCommands() {
|
|
43
63
|
const zeroEntry = resolvePackage('@rocicorp/zero');
|
|
@@ -58,12 +78,24 @@ function findLitestreamCommands() {
|
|
|
58
78
|
/**
|
|
59
79
|
* apply the restore-guard to a specific compiled litestream commands.js —
|
|
60
80
|
* shared by the in-place node path and the CF overlay (cf-patches.ts).
|
|
61
|
-
* idempotent.
|
|
81
|
+
* idempotent; upgrades a v1 guard in place.
|
|
62
82
|
*/
|
|
63
83
|
export function applyLitestreamRestoreGuard(commandsPath) {
|
|
64
|
-
|
|
65
|
-
|
|
84
|
+
let content = readFileSync(commandsPath, 'utf-8');
|
|
85
|
+
// strip the v1 guard BEFORE the already-patched check: a v1-era orez run
|
|
86
|
+
// against an already-v2-patched file re-injects v1 above the v2 guard
|
|
87
|
+
// (its marker check doesn't recognize v2), and v1's early `return` then
|
|
88
|
+
// shadows the fix. checking the v2 marker first would return early here
|
|
89
|
+
// and leave that stacked v1 in place. observed live on 2026-07-07.
|
|
90
|
+
const hadV1 = content.includes(GUARD_V1);
|
|
91
|
+
if (hadV1) {
|
|
92
|
+
content = content.replace(`\n\t${GUARD_V1}`, '');
|
|
93
|
+
}
|
|
94
|
+
if (content.includes(OREZ_MARKER)) {
|
|
95
|
+
if (hadV1)
|
|
96
|
+
writeFileSync(commandsPath, content);
|
|
66
97
|
return; // already patched
|
|
98
|
+
}
|
|
67
99
|
const anchor = 'async function restoreReplica(lc, config, replicaConstraints) {';
|
|
68
100
|
if (!content.includes(anchor)) {
|
|
69
101
|
// upstream shape changed — fail loudly rather than silently leaving the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zero-litestream-patch.js","sourceRoot":"","sources":["../src/zero-litestream-patch.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"zero-litestream-patch.js","sourceRoot":"","sources":["../src/zero-litestream-patch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACjE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAE5C,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAA;AAEjE,MAAM,WAAW,GAAG,qEAAqE,CAAA;AAEzF,4EAA4E;AAC5E,yEAAyE;AACzE,uEAAuE;AACvE,MAAM,cAAc,GAAG,gEAAgE,CAAA;AACvF,MAAM,QAAQ,GAAG,GAAG,cAAc,gDAAgD,CAAA;AAElF,6EAA6E;AAC7E,2EAA2E;AAC3E,4EAA4E;AAC5E,0EAA0E;AAC1E,MAAM,KAAK,GAAG,GAAG,WAAW;;;;GAIzB,CAAA;AAEH,oFAAoF;AACpF,SAAS,sBAAsB;IAC7B,MAAM,SAAS,GAAG,cAAc,CAAC,gBAAgB,CAAC,CAAA;IAClD,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAA;IAE3B,8EAA8E;IAC9E,sEAAsE;IACtE,IAAI,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,CAAA;IAC5B,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC;QACxD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;QACjC,IAAI,MAAM,KAAK,GAAG;YAAE,MAAK;QACzB,GAAG,GAAG,MAAM,CAAA;IACd,CAAC;IAED,MAAM,YAAY,GAAG,OAAO,CAC1B,GAAG,EACH,KAAK,EACL,YAAY,EACZ,KAAK,EACL,UAAU,EACV,YAAY,EACZ,aAAa,CACd,CAAA;IACD,OAAO,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAA;AACvD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,2BAA2B,CAAC,YAAoB;IAC9D,IAAI,OAAO,GAAG,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;IAEjD,yEAAyE;IACzE,sEAAsE;IACtE,wEAAwE;IACxE,wEAAwE;IACxE,mEAAmE;IACnE,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IACxC,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAA;IAClD,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QAClC,IAAI,KAAK;YAAE,aAAa,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;QAC/C,OAAM,CAAC,kBAAkB;IAC3B,CAAC;IAED,MAAM,MAAM,GAAG,iEAAiE,CAAA;IAChF,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9B,wEAAwE;QACxE,yDAAyD;QACzD,MAAM,IAAI,KAAK,CACb,+FAA+F,YAAY,IAAI;YAC7G,mEAAmE,CACtE,CAAA;IACH,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,MAAM,OAAO,KAAK,EAAE,CAAC,CAAA;IAChE,aAAa,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;AACtC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,4BAA4B;IAC1C,MAAM,YAAY,GAAG,sBAAsB,EAAE,CAAA;IAC7C,IAAI,CAAC,YAAY;QAAE,OAAM;IACzB,2BAA2B,CAAC,YAAY,CAAC,CAAA;AAC3C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orez",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.41",
|
|
4
4
|
"description": "PGlite-powered zero-sync development backend. No Docker required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"@electric-sql/pglite": "0.4.1",
|
|
88
88
|
"@electric-sql/pglite-tools": "^0.3.1",
|
|
89
89
|
"@pgsql/traverse": "17.2.6",
|
|
90
|
-
"bedrock-sqlite": "0.4.
|
|
90
|
+
"bedrock-sqlite": "0.4.41",
|
|
91
91
|
"citty": "^0.2.0",
|
|
92
92
|
"pg-gateway": "0.3.0-beta.4",
|
|
93
93
|
"pgsql-parser": "^17.9.11",
|