orez 0.0.18 → 0.0.20
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/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +46 -12
- package/dist/index.js.map +1 -1
- package/dist/mutex.d.ts +7 -0
- package/dist/mutex.d.ts.map +1 -0
- package/dist/mutex.js +24 -0
- package/dist/mutex.js.map +1 -0
- package/dist/pg-proxy.d.ts +6 -1
- package/dist/pg-proxy.d.ts.map +1 -1
- package/dist/pg-proxy.js +64 -76
- package/dist/pg-proxy.js.map +1 -1
- package/dist/pglite-manager.d.ts +14 -1
- package/dist/pglite-manager.d.ts.map +1 -1
- package/dist/pglite-manager.js +37 -18
- package/dist/pglite-manager.js.map +1 -1
- package/dist/replication/change-tracker.d.ts +7 -0
- package/dist/replication/change-tracker.d.ts.map +1 -1
- package/dist/replication/change-tracker.js +40 -3
- package/dist/replication/change-tracker.js.map +1 -1
- package/dist/replication/handler.d.ts +2 -1
- package/dist/replication/handler.d.ts.map +1 -1
- package/dist/replication/handler.js +136 -60
- package/dist/replication/handler.js.map +1 -1
- package/dist/replication/pgoutput-encoder.d.ts.map +1 -1
- package/dist/replication/pgoutput-encoder.js +11 -1
- package/dist/replication/pgoutput-encoder.js.map +1 -1
- package/package.json +3 -3
- package/src/cli.ts +1 -1
- package/src/config.ts +1 -1
- package/src/index.ts +45 -12
- package/src/integration/integration.test.ts +24 -14
- package/src/mutex.ts +24 -0
- package/src/pg-proxy.ts +78 -76
- package/src/pglite-manager.ts +48 -18
- package/src/replication/change-tracker.test.ts +4 -4
- package/src/replication/change-tracker.ts +49 -3
- package/src/replication/handler.test.ts +18 -8
- package/src/replication/handler.ts +179 -69
- package/src/replication/pgoutput-encoder.ts +9 -1
- package/src/replication/zero-compat.test.ts +7 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pgoutput-encoder.js","sourceRoot":"","sources":["../../src/replication/pgoutput-encoder.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"pgoutput-encoder.js","sourceRoot":"","sources":["../../src/replication/pgoutput-encoder.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orez",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.20",
|
|
4
4
|
"description": "PGlite-powered zero-sync development backend. No Docker required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"dev": "tsc --watch",
|
|
33
33
|
"test": "vitest run",
|
|
34
34
|
"test:watch": "vitest",
|
|
35
|
-
"lint": "oxlint --import-plugin",
|
|
35
|
+
"lint": "oxlint --import-plugin --ignore-pattern sqlite-wasm/",
|
|
36
36
|
"format": "oxfmt .",
|
|
37
37
|
"format:check": "oxfmt --check .",
|
|
38
38
|
"check": "tsc --noEmit",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@electric-sql/pglite": "^0.2.17",
|
|
45
45
|
"@rocicorp/zero": ">=0.1.0",
|
|
46
|
-
"bedrock-sqlite": "0.0.
|
|
46
|
+
"bedrock-sqlite": "0.0.12",
|
|
47
47
|
"citty": "^0.2.0",
|
|
48
48
|
"pg-gateway": "^0.3.0-beta.4"
|
|
49
49
|
},
|
package/src/cli.ts
CHANGED
package/src/config.ts
CHANGED
|
@@ -27,7 +27,7 @@ export function getConfig(overrides: Partial<ZeroLiteConfig> = {}): ZeroLiteConf
|
|
|
27
27
|
migrationsDir: overrides.migrationsDir || '',
|
|
28
28
|
seedFile: overrides.seedFile || 'src/database/seed.sql',
|
|
29
29
|
skipZeroCache: overrides.skipZeroCache || false,
|
|
30
|
-
disableWasmSqlite: overrides.disableWasmSqlite
|
|
30
|
+
disableWasmSqlite: overrides.disableWasmSqlite ?? true,
|
|
31
31
|
logLevel: overrides.logLevel || 'info',
|
|
32
32
|
pgliteOptions: overrides.pgliteOptions || {},
|
|
33
33
|
onDbReady: overrides.onDbReady || '',
|
package/src/index.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { basename, dirname, resolve } from 'node:path'
|
|
|
14
14
|
import { getConfig, getConnectionString } from './config.js'
|
|
15
15
|
import { log, setLogLevel } from './log.js'
|
|
16
16
|
import { startPgProxy } from './pg-proxy.js'
|
|
17
|
-
import {
|
|
17
|
+
import { createPGliteInstances, runMigrations } from './pglite-manager.js'
|
|
18
18
|
import { findPort } from './port.js'
|
|
19
19
|
import { installChangeTracking } from './replication/change-tracker.js'
|
|
20
20
|
|
|
@@ -57,18 +57,19 @@ export async function startZeroLite(overrides: Partial<ZeroLiteConfig> = {}) {
|
|
|
57
57
|
|
|
58
58
|
mkdirSync(config.dataDir, { recursive: true })
|
|
59
59
|
|
|
60
|
-
// start pglite
|
|
61
|
-
const
|
|
60
|
+
// start pglite (separate instances for postgres, zero_cvr, zero_cdb)
|
|
61
|
+
const instances = await createPGliteInstances(config)
|
|
62
|
+
const db = instances.postgres
|
|
62
63
|
|
|
63
|
-
// run migrations
|
|
64
|
+
// run migrations (on postgres instance only)
|
|
64
65
|
await runMigrations(db, config)
|
|
65
66
|
|
|
66
|
-
// install change tracking
|
|
67
|
+
// install change tracking (on postgres instance only)
|
|
67
68
|
log.debug.orez('installing change tracking')
|
|
68
69
|
await installChangeTracking(db)
|
|
69
70
|
|
|
70
|
-
// start tcp proxy
|
|
71
|
-
const pgServer = await startPgProxy(
|
|
71
|
+
// start tcp proxy (routes connections to correct instance by database name)
|
|
72
|
+
const pgServer = await startPgProxy(instances, config)
|
|
72
73
|
|
|
73
74
|
// seed data if needed
|
|
74
75
|
await seedIfNeeded(db, config)
|
|
@@ -143,12 +144,12 @@ export async function startZeroLite(overrides: Partial<ZeroLiteConfig> = {}) {
|
|
|
143
144
|
})
|
|
144
145
|
}
|
|
145
146
|
pgServer.close()
|
|
146
|
-
await
|
|
147
|
+
await Promise.all([instances.postgres.close(), instances.cvr.close(), instances.cdb.close()])
|
|
147
148
|
cleanupEnvLocal()
|
|
148
149
|
log.orez('stopped')
|
|
149
150
|
}
|
|
150
151
|
|
|
151
|
-
return { config, stop, db, pgPort: config.pgPort, zeroPort: config.zeroPort }
|
|
152
|
+
return { config, stop, db, instances, pgPort: config.pgPort, zeroPort: config.zeroPort }
|
|
152
153
|
}
|
|
153
154
|
|
|
154
155
|
// use .env.development.local so it overrides .env.development in vite's load order:
|
|
@@ -272,6 +273,15 @@ function patchSqliteForWasm(): void {
|
|
|
272
273
|
if (current.includes('OrigDatabase')) return // already patched
|
|
273
274
|
}
|
|
274
275
|
|
|
276
|
+
// back up originals before patching
|
|
277
|
+
if (existsSync(indexPath)) {
|
|
278
|
+
writeFileSync(indexPath + '.original', readFileSync(indexPath))
|
|
279
|
+
}
|
|
280
|
+
const dbBackupPath = resolve(libDir, 'database.js')
|
|
281
|
+
if (existsSync(dbBackupPath)) {
|
|
282
|
+
writeFileSync(dbBackupPath + '.original', readFileSync(dbBackupPath))
|
|
283
|
+
}
|
|
284
|
+
|
|
275
285
|
// resolve bedrock-sqlite's dist entry point
|
|
276
286
|
const bedrockEntry = resolvePackage('bedrock-sqlite')
|
|
277
287
|
if (!existsSync(bedrockEntry)) {
|
|
@@ -287,9 +297,6 @@ var OrigDatabase = mod.Database;
|
|
|
287
297
|
var SqliteError = mod.SqliteError;
|
|
288
298
|
|
|
289
299
|
// wrap constructor to set busy_timeout on every connection immediately.
|
|
290
|
-
// VFS nodejsLock retries with Atomics.wait, and nodejsSleep enables
|
|
291
|
-
// SQLite's busy handler - so busy_timeout works properly in WASM now.
|
|
292
|
-
// WAL mode is left to zero-cache to manage (needed for BEGIN CONCURRENT).
|
|
293
300
|
function Database() {
|
|
294
301
|
var db = new OrigDatabase(...arguments);
|
|
295
302
|
try {
|
|
@@ -358,6 +365,31 @@ module.exports.SqliteError = SqliteError;
|
|
|
358
365
|
}
|
|
359
366
|
}
|
|
360
367
|
|
|
368
|
+
function unpatchSqliteWasm(): void {
|
|
369
|
+
// restore native @rocicorp/zero-sqlite3 if it was previously patched
|
|
370
|
+
try {
|
|
371
|
+
const sqliteEntry = resolvePackage('@rocicorp/zero-sqlite3')
|
|
372
|
+
if (!sqliteEntry) return
|
|
373
|
+
const libDir = resolve(sqliteEntry, '..')
|
|
374
|
+
const indexPath = resolve(libDir, 'index.js')
|
|
375
|
+
if (!existsSync(indexPath)) return
|
|
376
|
+
const current = readFileSync(indexPath, 'utf-8')
|
|
377
|
+
if (!current.includes('OrigDatabase')) return // not patched
|
|
378
|
+
|
|
379
|
+
// restore from backups if available
|
|
380
|
+
for (const file of ['index.js', 'database.js']) {
|
|
381
|
+
const filePath = resolve(libDir, file)
|
|
382
|
+
const backupPath = filePath + '.original'
|
|
383
|
+
if (existsSync(backupPath)) {
|
|
384
|
+
writeFileSync(filePath, readFileSync(backupPath))
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
log.orez('restored native @rocicorp/zero-sqlite3 (removed wasm patch)')
|
|
388
|
+
} catch (e) {
|
|
389
|
+
log.orez(`failed to restore native sqlite: ${e}`)
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
361
393
|
async function startZeroCache(config: ZeroLiteConfig): Promise<ChildProcess> {
|
|
362
394
|
// resolve @rocicorp/zero entry for finding zero-cache modules
|
|
363
395
|
const zeroEntry = resolvePackage('@rocicorp/zero')
|
|
@@ -370,6 +402,7 @@ async function startZeroCache(config: ZeroLiteConfig): Promise<ChildProcess> {
|
|
|
370
402
|
if (!config.disableWasmSqlite) {
|
|
371
403
|
patchSqliteForWasm()
|
|
372
404
|
} else {
|
|
405
|
+
unpatchSqliteWasm()
|
|
373
406
|
log.debug.orez('wasm sqlite disabled, using native @rocicorp/zero-sqlite3')
|
|
374
407
|
}
|
|
375
408
|
|
|
@@ -9,13 +9,18 @@
|
|
|
9
9
|
|
|
10
10
|
import { describe, expect, test, beforeAll, afterAll, beforeEach } from 'vitest'
|
|
11
11
|
import WebSocket from 'ws'
|
|
12
|
+
|
|
12
13
|
import { startZeroLite } from '../index.js'
|
|
14
|
+
|
|
13
15
|
import type { PGlite } from '@electric-sql/pglite'
|
|
14
16
|
|
|
15
17
|
// simple async queue for collecting websocket messages
|
|
16
18
|
class Queue<T> {
|
|
17
19
|
private items: T[] = []
|
|
18
|
-
private waiters: Array<{
|
|
20
|
+
private waiters: Array<{
|
|
21
|
+
resolve: (v: T) => void
|
|
22
|
+
timer?: ReturnType<typeof setTimeout>
|
|
23
|
+
}> = []
|
|
19
24
|
|
|
20
25
|
enqueue(item: T) {
|
|
21
26
|
const waiter = this.waiters.shift()
|
|
@@ -113,7 +118,7 @@ describe('orez integration', { timeout: 120000 }, () => {
|
|
|
113
118
|
test('zero-cache starts and accepts websocket connections', async () => {
|
|
114
119
|
const ws = new WebSocket(
|
|
115
120
|
`ws://localhost:${zeroPort}/sync/v4/connect` +
|
|
116
|
-
`?clientGroupID=test-cg&clientID=test-client&wsid=ws1&schemaVersion=1&baseCookie=&ts=${Date.now()}&lmid=0
|
|
121
|
+
`?clientGroupID=test-cg&clientID=test-client&wsid=ws1&schemaVersion=1&baseCookie=&ts=${Date.now()}&lmid=0`
|
|
117
122
|
)
|
|
118
123
|
|
|
119
124
|
const connected = new Promise<void>((resolve, reject) => {
|
|
@@ -161,7 +166,7 @@ describe('orez integration', { timeout: 120000 }, () => {
|
|
|
161
166
|
value: 'hello',
|
|
162
167
|
}),
|
|
163
168
|
}),
|
|
164
|
-
])
|
|
169
|
+
])
|
|
165
170
|
)
|
|
166
171
|
|
|
167
172
|
ws.close()
|
|
@@ -196,7 +201,7 @@ describe('orez integration', { timeout: 120000 }, () => {
|
|
|
196
201
|
value: 'live-value',
|
|
197
202
|
}),
|
|
198
203
|
}),
|
|
199
|
-
])
|
|
204
|
+
])
|
|
200
205
|
)
|
|
201
206
|
|
|
202
207
|
ws.close()
|
|
@@ -236,7 +241,7 @@ describe('orez integration', { timeout: 120000 }, () => {
|
|
|
236
241
|
value: 'updated',
|
|
237
242
|
}),
|
|
238
243
|
}),
|
|
239
|
-
])
|
|
244
|
+
])
|
|
240
245
|
)
|
|
241
246
|
|
|
242
247
|
ws.close()
|
|
@@ -267,7 +272,7 @@ describe('orez integration', { timeout: 120000 }, () => {
|
|
|
267
272
|
op: 'del',
|
|
268
273
|
tableName: 'foo',
|
|
269
274
|
}),
|
|
270
|
-
])
|
|
275
|
+
])
|
|
271
276
|
)
|
|
272
277
|
|
|
273
278
|
ws.close()
|
|
@@ -289,8 +294,8 @@ describe('orez integration', { timeout: 120000 }, () => {
|
|
|
289
294
|
`concurrent-${i}`,
|
|
290
295
|
`value-${i}`,
|
|
291
296
|
i,
|
|
292
|
-
])
|
|
293
|
-
)
|
|
297
|
+
])
|
|
298
|
+
)
|
|
294
299
|
)
|
|
295
300
|
|
|
296
301
|
// collect all poke parts within a window
|
|
@@ -316,11 +321,11 @@ describe('orez integration', { timeout: 120000 }, () => {
|
|
|
316
321
|
function connectAndSubscribe(
|
|
317
322
|
port: number,
|
|
318
323
|
downstream: Queue<unknown>,
|
|
319
|
-
query: Record<string, unknown
|
|
324
|
+
query: Record<string, unknown>
|
|
320
325
|
): WebSocket {
|
|
321
326
|
const ws = new WebSocket(
|
|
322
327
|
`ws://localhost:${port}/sync/v4/connect` +
|
|
323
|
-
`?clientGroupID=test-cg-${Date.now()}&clientID=test-client&wsid=ws1&schemaVersion=1&baseCookie=&ts=${Date.now()}&lmid=0
|
|
328
|
+
`?clientGroupID=test-cg-${Date.now()}&clientID=test-client&wsid=ws1&schemaVersion=1&baseCookie=&ts=${Date.now()}&lmid=0`
|
|
324
329
|
)
|
|
325
330
|
|
|
326
331
|
ws.on('message', (data) => {
|
|
@@ -334,7 +339,7 @@ describe('orez integration', { timeout: 120000 }, () => {
|
|
|
334
339
|
{
|
|
335
340
|
desiredQueriesPatch: [{ op: 'put', hash: 'q1', ast: query }],
|
|
336
341
|
},
|
|
337
|
-
])
|
|
342
|
+
])
|
|
338
343
|
)
|
|
339
344
|
})
|
|
340
345
|
|
|
@@ -363,7 +368,7 @@ describe('orez integration', { timeout: 120000 }, () => {
|
|
|
363
368
|
|
|
364
369
|
async function waitForPokePart(
|
|
365
370
|
downstream: Queue<unknown>,
|
|
366
|
-
timeoutMs = 10000
|
|
371
|
+
timeoutMs = 10000
|
|
367
372
|
): Promise<Record<string, any>> {
|
|
368
373
|
const deadline = Date.now() + timeoutMs
|
|
369
374
|
while (Date.now() < deadline) {
|
|
@@ -379,7 +384,7 @@ describe('orez integration', { timeout: 120000 }, () => {
|
|
|
379
384
|
|
|
380
385
|
async function collectPokeRows(
|
|
381
386
|
downstream: Queue<unknown>,
|
|
382
|
-
windowMs = 5000
|
|
387
|
+
windowMs = 5000
|
|
383
388
|
): Promise<any[]> {
|
|
384
389
|
const rows: any[] = []
|
|
385
390
|
const deadline = Date.now() + windowMs
|
|
@@ -392,7 +397,12 @@ describe('orez integration', { timeout: 120000 }, () => {
|
|
|
392
397
|
rows.push(...msg[1].rowsPatch)
|
|
393
398
|
// check if more poke parts come quickly
|
|
394
399
|
const more = (await downstream.dequeue('timeout' as any, 2000)) as any
|
|
395
|
-
if (
|
|
400
|
+
if (
|
|
401
|
+
more !== 'timeout' &&
|
|
402
|
+
Array.isArray(more) &&
|
|
403
|
+
more[0] === 'pokePart' &&
|
|
404
|
+
more[1]?.rowsPatch
|
|
405
|
+
) {
|
|
396
406
|
rows.push(...more[1].rowsPatch)
|
|
397
407
|
}
|
|
398
408
|
break
|
package/src/mutex.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// simple mutex for serializing pglite access
|
|
2
|
+
export class Mutex {
|
|
3
|
+
private locked = false
|
|
4
|
+
private queue: Array<() => void> = []
|
|
5
|
+
|
|
6
|
+
async acquire(): Promise<void> {
|
|
7
|
+
if (!this.locked) {
|
|
8
|
+
this.locked = true
|
|
9
|
+
return
|
|
10
|
+
}
|
|
11
|
+
return new Promise<void>((resolve) => {
|
|
12
|
+
this.queue.push(resolve)
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
release(): void {
|
|
17
|
+
const next = this.queue.shift()
|
|
18
|
+
if (next) {
|
|
19
|
+
next()
|
|
20
|
+
} else {
|
|
21
|
+
this.locked = false
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/pg-proxy.ts
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
*
|
|
7
7
|
* regular connections: forwarded to pglite via execProtocolRaw()
|
|
8
8
|
* replication connections: intercepted, replication protocol faked
|
|
9
|
+
*
|
|
10
|
+
* each "database" (postgres, zero_cvr, zero_cdb) maps to its own pglite
|
|
11
|
+
* instance with independent transaction context, preventing cross-database
|
|
12
|
+
* query interleaving that causes CVR concurrent modification errors.
|
|
9
13
|
*/
|
|
10
14
|
|
|
11
15
|
import { createServer, type Server, type Socket } from 'node:net'
|
|
@@ -14,16 +18,11 @@ import { fromNodeSocket } from 'pg-gateway/node'
|
|
|
14
18
|
|
|
15
19
|
import { log } from './log.js'
|
|
16
20
|
import { handleReplicationQuery, handleStartReplication } from './replication/handler.js'
|
|
21
|
+
import { Mutex } from './mutex.js'
|
|
17
22
|
|
|
18
23
|
import type { ZeroLiteConfig } from './config.js'
|
|
19
24
|
import type { PGlite } from '@electric-sql/pglite'
|
|
20
|
-
|
|
21
|
-
// database name -> search_path mapping
|
|
22
|
-
const DB_SCHEMA_MAP: Record<string, string> = {
|
|
23
|
-
postgres: 'public',
|
|
24
|
-
zero_cvr: 'zero_cvr, public',
|
|
25
|
-
zero_cdb: 'zero_cdb, public',
|
|
26
|
-
}
|
|
25
|
+
import type { PGliteInstances } from './pglite-manager.js'
|
|
27
26
|
|
|
28
27
|
// query rewrites: make pglite look like real postgres with logical replication
|
|
29
28
|
const QUERY_REWRITES: Array<{ match: RegExp; replace: string }> = [
|
|
@@ -32,26 +31,33 @@ const QUERY_REWRITES: Array<{ match: RegExp; replace: string }> = [
|
|
|
32
31
|
match: /current_setting\s*\(\s*'wal_level'\s*\)/gi,
|
|
33
32
|
replace: "'logical'::text",
|
|
34
33
|
},
|
|
35
|
-
// strip READ ONLY from BEGIN
|
|
34
|
+
// strip READ ONLY from BEGIN (pglite is single-session, no read-only transactions)
|
|
36
35
|
{
|
|
37
36
|
match: /\bREAD\s+ONLY\b/gi,
|
|
38
37
|
replace: '',
|
|
39
38
|
},
|
|
39
|
+
// strip ISOLATION LEVEL from any query (pglite is single-session, isolation is meaningless)
|
|
40
|
+
// catches: SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, BEGIN ISOLATION LEVEL SERIALIZABLE, etc.
|
|
41
|
+
{
|
|
42
|
+
match:
|
|
43
|
+
/\bISOLATION\s+LEVEL\s+(SERIALIZABLE|REPEATABLE\s+READ|READ\s+COMMITTED|READ\s+UNCOMMITTED)\b/gi,
|
|
44
|
+
replace: '',
|
|
45
|
+
},
|
|
46
|
+
// strip bare SET TRANSACTION (after ISOLATION LEVEL is removed, this becomes a no-op statement)
|
|
47
|
+
{
|
|
48
|
+
match: /\bSET\s+TRANSACTION\s*;/gi,
|
|
49
|
+
replace: ';',
|
|
50
|
+
},
|
|
40
51
|
// redirect pg_replication_slots to our fake table
|
|
41
52
|
{
|
|
42
53
|
match: /\bpg_replication_slots\b/g,
|
|
43
54
|
replace: 'public._zero_replication_slots',
|
|
44
55
|
},
|
|
45
|
-
// SET TRANSACTION queries: rewrite to SELECT 1 (PGlite doesn't support them,
|
|
46
|
-
// and no-op interception doesn't work for extended protocol Bind/Execute)
|
|
47
|
-
{
|
|
48
|
-
match: /^\s*SET\s+TRANSACTION\b.*$/gis,
|
|
49
|
-
replace: 'SELECT 1',
|
|
50
|
-
},
|
|
51
56
|
]
|
|
52
57
|
|
|
53
|
-
// queries to intercept and return no-op success
|
|
54
|
-
|
|
58
|
+
// queries to intercept and return no-op success (synthetic SET response)
|
|
59
|
+
// pglite rejects SET TRANSACTION if any query (e.g. SET search_path) ran first
|
|
60
|
+
const NOOP_QUERY_PATTERNS: RegExp[] = [/^\s*SET\s+TRANSACTION\b/i, /^\s*SET\s+SESSION\b/i]
|
|
55
61
|
|
|
56
62
|
/**
|
|
57
63
|
* extract query text from a Parse message (0x50).
|
|
@@ -239,37 +245,30 @@ function stripReadyForQuery(data: Uint8Array): Uint8Array {
|
|
|
239
245
|
return result
|
|
240
246
|
}
|
|
241
247
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
248
|
+
export async function startPgProxy(
|
|
249
|
+
dbInput: PGlite | PGliteInstances,
|
|
250
|
+
config: ZeroLiteConfig
|
|
251
|
+
): Promise<Server> {
|
|
252
|
+
// normalize input: single PGlite instance = use it for all databases (backwards compat for tests)
|
|
253
|
+
const instances: PGliteInstances =
|
|
254
|
+
'postgres' in dbInput
|
|
255
|
+
? (dbInput as PGliteInstances)
|
|
256
|
+
: { postgres: dbInput as PGlite, cvr: dbInput as PGlite, cdb: dbInput as PGlite }
|
|
257
|
+
|
|
258
|
+
// per-instance mutexes for serializing pglite access
|
|
259
|
+
const mutexes = {
|
|
260
|
+
postgres: new Mutex(),
|
|
261
|
+
cvr: new Mutex(),
|
|
262
|
+
cdb: new Mutex(),
|
|
255
263
|
}
|
|
256
264
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if (
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
this.locked = false
|
|
263
|
-
}
|
|
265
|
+
// helper to get instance + mutex for a database name
|
|
266
|
+
function getDbContext(dbName: string): { db: PGlite; mutex: Mutex } {
|
|
267
|
+
if (dbName === 'zero_cvr') return { db: instances.cvr, mutex: mutexes.cvr }
|
|
268
|
+
if (dbName === 'zero_cdb') return { db: instances.cdb, mutex: mutexes.cdb }
|
|
269
|
+
return { db: instances.postgres, mutex: mutexes.postgres }
|
|
264
270
|
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const mutex = new Mutex()
|
|
268
271
|
|
|
269
|
-
// module-level search_path tracking
|
|
270
|
-
let currentSearchPath = 'public'
|
|
271
|
-
|
|
272
|
-
export async function startPgProxy(db: PGlite, config: ZeroLiteConfig): Promise<Server> {
|
|
273
272
|
const server = createServer(async (socket: Socket) => {
|
|
274
273
|
// prevent idle timeouts from killing connections
|
|
275
274
|
socket.setKeepAlive(true, 30000)
|
|
@@ -280,11 +279,12 @@ export async function startPgProxy(db: PGlite, config: ZeroLiteConfig): Promise<
|
|
|
280
279
|
|
|
281
280
|
// clean up pglite transaction state when a client disconnects
|
|
282
281
|
socket.on('close', async () => {
|
|
282
|
+
const { db, mutex } = getDbContext(dbName)
|
|
283
283
|
await mutex.acquire()
|
|
284
284
|
try {
|
|
285
285
|
await db.exec('ROLLBACK')
|
|
286
286
|
} catch {
|
|
287
|
-
// no transaction to rollback
|
|
287
|
+
// no transaction to rollback
|
|
288
288
|
} finally {
|
|
289
289
|
mutex.release()
|
|
290
290
|
}
|
|
@@ -318,13 +318,14 @@ export async function startPgProxy(db: PGlite, config: ZeroLiteConfig): Promise<
|
|
|
318
318
|
log.debug.proxy(
|
|
319
319
|
`connection: db=${dbName} user=${params?.user} replication=${params?.replication || 'none'}`
|
|
320
320
|
)
|
|
321
|
+
const { db } = getDbContext(dbName)
|
|
321
322
|
await db.waitReady
|
|
322
323
|
},
|
|
323
324
|
|
|
324
325
|
async onMessage(data, state) {
|
|
325
326
|
if (!state.isAuthenticated) return
|
|
326
327
|
|
|
327
|
-
// handle replication connections
|
|
328
|
+
// handle replication connections (always go to postgres instance)
|
|
328
329
|
if (isReplicationConnection) {
|
|
329
330
|
if (data[0] === 0x51) {
|
|
330
331
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength)
|
|
@@ -334,7 +335,13 @@ export async function startPgProxy(db: PGlite, config: ZeroLiteConfig): Promise<
|
|
|
334
335
|
.replace(/\0$/, '')
|
|
335
336
|
log.debug.proxy(`repl query: ${query.slice(0, 200)}`)
|
|
336
337
|
}
|
|
337
|
-
return handleReplicationMessage(
|
|
338
|
+
return handleReplicationMessage(
|
|
339
|
+
data,
|
|
340
|
+
socket,
|
|
341
|
+
instances.postgres,
|
|
342
|
+
mutexes.postgres,
|
|
343
|
+
connection
|
|
344
|
+
)
|
|
338
345
|
}
|
|
339
346
|
|
|
340
347
|
// check for no-op queries
|
|
@@ -349,31 +356,27 @@ export async function startPgProxy(db: PGlite, config: ZeroLiteConfig): Promise<
|
|
|
349
356
|
// intercept and rewrite queries
|
|
350
357
|
data = interceptQuery(data)
|
|
351
358
|
|
|
352
|
-
//
|
|
359
|
+
// message-level locking on the connection's pglite instance
|
|
360
|
+
const { db, mutex } = getDbContext(dbName)
|
|
353
361
|
await mutex.acquire()
|
|
362
|
+
|
|
363
|
+
let result: Uint8Array
|
|
354
364
|
try {
|
|
355
|
-
|
|
356
|
-
if (currentSearchPath !== searchPath) {
|
|
357
|
-
try {
|
|
358
|
-
await db.exec(`SET search_path TO ${searchPath}`)
|
|
359
|
-
} catch {
|
|
360
|
-
// pglite may be in aborted transaction state — reset it
|
|
361
|
-
await db.exec('ROLLBACK')
|
|
362
|
-
await db.exec(`SET search_path TO ${searchPath}`)
|
|
363
|
-
}
|
|
364
|
-
currentSearchPath = searchPath
|
|
365
|
-
}
|
|
366
|
-
let result = await db.execProtocolRaw(data, {
|
|
365
|
+
result = await db.execProtocolRaw(data, {
|
|
367
366
|
throwOnError: false,
|
|
368
367
|
})
|
|
369
|
-
|
|
370
|
-
if (data[0] !== 0x53 && data[0] !== 0x51) {
|
|
371
|
-
result = stripReadyForQuery(result)
|
|
372
|
-
}
|
|
373
|
-
return result
|
|
374
|
-
} finally {
|
|
368
|
+
} catch (err) {
|
|
375
369
|
mutex.release()
|
|
370
|
+
throw err
|
|
376
371
|
}
|
|
372
|
+
|
|
373
|
+
// strip ReadyForQuery from non-Sync/non-SimpleQuery responses
|
|
374
|
+
if (data[0] !== 0x53 && data[0] !== 0x51) {
|
|
375
|
+
result = stripReadyForQuery(result)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
mutex.release()
|
|
379
|
+
return result
|
|
377
380
|
},
|
|
378
381
|
})
|
|
379
382
|
} catch (err) {
|
|
@@ -396,6 +399,7 @@ async function handleReplicationMessage(
|
|
|
396
399
|
data: Uint8Array,
|
|
397
400
|
socket: Socket,
|
|
398
401
|
db: PGlite,
|
|
402
|
+
mutex: Mutex,
|
|
399
403
|
connection: Awaited<ReturnType<typeof fromNodeSocket>>
|
|
400
404
|
): Promise<Uint8Array | undefined> {
|
|
401
405
|
if (data[0] !== 0x51) return undefined
|
|
@@ -424,24 +428,22 @@ async function handleReplicationMessage(
|
|
|
424
428
|
socket.destroy()
|
|
425
429
|
})
|
|
426
430
|
|
|
427
|
-
handleStartReplication(query, writer, db).catch((err) => {
|
|
431
|
+
handleStartReplication(query, writer, db, mutex).catch((err) => {
|
|
428
432
|
log.debug.proxy(`replication stream ended: ${err}`)
|
|
429
433
|
})
|
|
430
434
|
return undefined
|
|
431
435
|
}
|
|
432
436
|
|
|
433
|
-
// handle
|
|
434
|
-
const response = await handleReplicationQuery(query, db)
|
|
435
|
-
if (response) return response
|
|
436
|
-
|
|
437
|
-
// fall through to pglite for unrecognized queries
|
|
437
|
+
// handle replication queries + fallthrough to pglite, all under mutex
|
|
438
438
|
await mutex.acquire()
|
|
439
439
|
try {
|
|
440
|
-
const
|
|
441
|
-
if (
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
440
|
+
const response = await handleReplicationQuery(query, db)
|
|
441
|
+
if (response) return response
|
|
442
|
+
|
|
443
|
+
// apply query rewrites before forwarding
|
|
444
|
+
data = interceptQuery(data)
|
|
445
|
+
|
|
446
|
+
// fall through to pglite for unrecognized queries
|
|
445
447
|
return await db.execProtocolRaw(data, {
|
|
446
448
|
throwOnError: false,
|
|
447
449
|
})
|