orez 0.2.26 → 0.2.29
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/cf-do/worker.d.ts.map +1 -1
- package/dist/cf-do/worker.js +9 -1
- package/dist/cf-do/worker.js.map +1 -1
- package/dist/pg-proxy-do-backend.d.ts +2 -0
- package/dist/pg-proxy-do-backend.d.ts.map +1 -1
- package/dist/pg-proxy-do-backend.js +49 -7
- package/dist/pg-proxy-do-backend.js.map +1 -1
- package/dist/pg-sqlite-compiler/catalog/seed.d.ts +67 -0
- package/dist/pg-sqlite-compiler/catalog/seed.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/catalog/seed.js +436 -0
- package/dist/pg-sqlite-compiler/catalog/seed.js.map +1 -0
- package/dist/pg-sqlite-compiler/index.d.ts +12 -0
- package/dist/pg-sqlite-compiler/index.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/index.js +59 -0
- package/dist/pg-sqlite-compiler/index.js.map +1 -0
- package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts +48 -0
- package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/passes/ast-utils.js +93 -0
- package/dist/pg-sqlite-compiler/passes/ast-utils.js.map +1 -0
- package/dist/pg-sqlite-compiler/passes/catalog.d.ts +34 -0
- package/dist/pg-sqlite-compiler/passes/catalog.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/passes/catalog.js +30 -0
- package/dist/pg-sqlite-compiler/passes/catalog.js.map +1 -0
- package/dist/pg-sqlite-compiler/passes/datetime.d.ts +21 -0
- package/dist/pg-sqlite-compiler/passes/datetime.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/passes/datetime.js +53 -0
- package/dist/pg-sqlite-compiler/passes/datetime.js.map +1 -0
- package/dist/pg-sqlite-compiler/passes/index.d.ts +21 -0
- package/dist/pg-sqlite-compiler/passes/index.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/passes/index.js +39 -0
- package/dist/pg-sqlite-compiler/passes/index.js.map +1 -0
- package/dist/pg-sqlite-compiler/passes/types.d.ts +41 -0
- package/dist/pg-sqlite-compiler/passes/types.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/passes/types.js +103 -0
- package/dist/pg-sqlite-compiler/passes/types.js.map +1 -0
- package/dist/pg-sqlite-compiler/test/oracle.d.ts +34 -0
- package/dist/pg-sqlite-compiler/test/oracle.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/test/oracle.js +204 -0
- package/dist/pg-sqlite-compiler/test/oracle.js.map +1 -0
- package/dist/pg-sqlite-compiler/types.d.ts +55 -0
- package/dist/pg-sqlite-compiler/types.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/types.js +2 -0
- package/dist/pg-sqlite-compiler/types.js.map +1 -0
- package/package.json +8 -4
- package/src/admin/admin-data.test.ts +0 -348
- package/src/admin/http-proxy.ts +0 -252
- package/src/admin/log-store.ts +0 -192
- package/src/admin/server.ts +0 -471
- package/src/admin/ui.ts +0 -1322
- package/src/bench/proxy-throughput.bench.ts +0 -343
- package/src/bench/serial-mutations.bench.ts +0 -270
- package/src/browser.ts +0 -203
- package/src/cf-do/.wrangler/cache/cf.json +0 -1
- package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-shm +0 -0
- package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-wal +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/0f0f3bdf0abda097eb6f1246db4657d9fc622081362d894d82c1a1ce067b05b6.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/1ddd3a4a48a11b51658444f5458a1fb175194b1d5b6a5bda20ef3fe3205b900c.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/204a39120310d37e972c5914cfd71ad55c151bdb9e8ed289a5f8c5b052dd60e4.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/3835f242df9728adba3d127a238793fd054ed3e51df3f60749ee744c469bf2a2.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/4aa9c80eb716cf55b8995ccf7afab0b36c683e6da07d7c37a3f9c570136036df.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/533e2fd1d6ea46e7a9a0017916ef341802d438d72583462755f2c1f8225e9bf2.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/5ffa1aced1225ecaeac6366f7586aa3de92761cdff8711d81fbd81f248076abd.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/686c3a9f0d7e59ed2ab607efd4b76d779c97cafeb3818380033bf7c7eb86c819.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/6e8214e8dcfadd0deb52d64e5e9ca85c6b329ace11193909845995396914c473.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/78d9ec9ff873d3fe3507ff53c2a6f6dfc408b4268eb0db3f2a146c0678965366.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/7eff9f0ed7e27ad0d3f9d923de0682fab1928591172c1ba336c5f79a134a5d85.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/836cda5b995b25867d722ed4f4c2292167e80351a3c6038db626648eb247dd8b.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/91ef63b112209ab30172763acd8a0935106c248f7f1bcae5545ce37a9f201551.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/a66ea4293a5f5938bc6d116edfa2522bb85bc37aea3541fbc09c3b613b9b32c0.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/ceb2ab26b80590840b65651deb6e948d3bf81565c6751f3a58752cf4bf4aecae.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-shm +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-wal +0 -0
- package/src/cf-do/ARCHITECTURE.md +0 -83
- package/src/cf-do/watermark.test.ts +0 -103
- package/src/cf-do/watermark.ts +0 -118
- package/src/cf-do/worker.ts +0 -1033
- package/src/cf-do/wrangler.toml +0 -11
- package/src/cf-pglite/README.md +0 -19
- package/src/change-tracking.ts +0 -25
- package/src/child-process.test.ts +0 -147
- package/src/child-process.ts +0 -90
- package/src/cli-entry.ts +0 -72
- package/src/cli.test.ts +0 -38
- package/src/cli.ts +0 -1214
- package/src/config.ts +0 -150
- package/src/do-sql-tracking.test.ts +0 -19
- package/src/do-sql-tracking.ts +0 -19
- package/src/index.ts +0 -1215
- package/src/integration/integration.test.ts +0 -517
- package/src/integration/native-binary.guard.test.ts +0 -13
- package/src/integration/native-startup.test.ts +0 -44
- package/src/integration/replication-latency.test.ts +0 -428
- package/src/integration/restore-live-stress.test.ts +0 -433
- package/src/integration/restore-reset.test.ts +0 -400
- package/src/integration/restore.test.ts +0 -274
- package/src/integration/test-permissions.ts +0 -147
- package/src/load-config.ts +0 -46
- package/src/log.ts +0 -96
- package/src/mutex.ts +0 -47
- package/src/pg-proxy-browser.singledb.test.ts +0 -233
- package/src/pg-proxy-browser.ts +0 -2022
- package/src/pg-proxy-do-backend.test.ts +0 -3890
- package/src/pg-proxy-do-backend.ts +0 -7157
- package/src/pg-proxy.ts +0 -1087
- package/src/pglite-ipc.test.ts +0 -116
- package/src/pglite-ipc.ts +0 -266
- package/src/pglite-manager.ts +0 -557
- package/src/pglite-web-proxy.test.ts +0 -57
- package/src/pglite-web-proxy.ts +0 -221
- package/src/pglite-web-worker.ts +0 -152
- package/src/pglite-worker-thread.ts +0 -253
- package/src/port.ts +0 -25
- package/src/process-title.ts +0 -9
- package/src/recovery.ts +0 -155
- package/src/replication/change-tracker.test.ts +0 -357
- package/src/replication/change-tracker.ts +0 -279
- package/src/replication/handler.test.ts +0 -511
- package/src/replication/handler.ts +0 -1190
- package/src/replication/pgoutput-encoder.test.ts +0 -697
- package/src/replication/pgoutput-encoder.ts +0 -373
- package/src/replication/tcp-replication.test.ts +0 -876
- package/src/replication/zero-compat.test.ts +0 -1150
- package/src/restore-stress.test.ts +0 -188
- package/src/s3-local.ts +0 -203
- package/src/shim/hooks.mjs +0 -120
- package/src/shim/register.mjs +0 -4
- package/src/sqlite-mode/apply-mode.ts +0 -224
- package/src/sqlite-mode/index.ts +0 -15
- package/src/sqlite-mode/native-binary.ts +0 -89
- package/src/sqlite-mode/package-resolve.ts +0 -17
- package/src/sqlite-mode/resolve-mode.ts +0 -80
- package/src/sqlite-mode/shim-template.ts +0 -159
- package/src/sqlite-mode/sqlite-mode.test.ts +0 -427
- package/src/sqlite-mode/types.ts +0 -30
- package/src/vite-plugin.ts +0 -67
- package/src/wasm-sqlite.test.ts +0 -537
- package/src/worker/browser-admin.ts +0 -52
- package/src/worker/browser-build-config.test.ts +0 -71
- package/src/worker/browser-build-config.ts +0 -109
- package/src/worker/browser-embed-admin.test.ts +0 -75
- package/src/worker/browser-embed.ts +0 -345
- package/src/worker/cf-patches.ts +0 -384
- package/src/worker/embed-integration.test.ts +0 -321
- package/src/worker/index.ts +0 -138
- package/src/worker/shims/fastify.test.ts +0 -255
- package/src/worker/shims/fastify.ts +0 -306
- package/src/worker/shims/http-service.test.ts +0 -355
- package/src/worker/shims/http-service.ts +0 -293
- package/src/worker/shims/node-stub.ts +0 -290
- package/src/worker/shims/oxfmt.ts +0 -3
- package/src/worker/shims/postgres-browser.ts +0 -59
- package/src/worker/shims/postgres-socket.test.ts +0 -576
- package/src/worker/shims/postgres-socket.ts +0 -310
- package/src/worker/shims/postgres.test.ts +0 -364
- package/src/worker/shims/postgres.ts +0 -1454
- package/src/worker/shims/sqlite-browser.test.ts +0 -233
- package/src/worker/shims/sqlite-browser.ts +0 -175
- package/src/worker/shims/sqlite.test.ts +0 -786
- package/src/worker/shims/sqlite.ts +0 -978
- package/src/worker/shims/stream-browser.ts +0 -15
- package/src/worker/shims/ws-browser.test.ts +0 -205
- package/src/worker/shims/ws-browser.ts +0 -248
- package/src/worker/shims/ws.test.ts +0 -288
- package/src/worker/shims/ws.ts +0 -467
- package/src/worker/shims/zero-process-env.ts +0 -11
- package/src/worker/types.ts +0 -75
- package/src/worker/worker-integration.test.ts +0 -223
- package/src/worker/worker.test.ts +0 -136
- package/src/worker/zero-cache-embed-cf.ts +0 -463
- package/src/worker/zero-cache-embed.ts +0 -277
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* integration test for orez/worker.
|
|
3
|
-
*
|
|
4
|
-
* tests the full pipeline available without zero-cache:
|
|
5
|
-
* PGlite → change tracking → replication encoding → InProcessWriter
|
|
6
|
-
*
|
|
7
|
-
* mirrors the existing integration test patterns but uses the worker
|
|
8
|
-
* API instead of startZeroLite(). validates that the worker entry
|
|
9
|
-
* point produces the same replication stream that zero-cache expects.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
|
13
|
-
|
|
14
|
-
import { InProcessWriter } from '../replication/handler.js'
|
|
15
|
-
import { resetReplicationState, signalReplicationChange } from '../replication/handler.js'
|
|
16
|
-
import { createOrezWorker } from './index'
|
|
17
|
-
|
|
18
|
-
import type { OrezWorker } from './types'
|
|
19
|
-
|
|
20
|
-
// extract pgoutput message types from CopyData(XLogData(...)) buffers
|
|
21
|
-
function extractPayloadTypes(buf: Uint8Array): number[] {
|
|
22
|
-
const types: number[] = []
|
|
23
|
-
let pos = 0
|
|
24
|
-
while (pos < buf.length) {
|
|
25
|
-
if (buf[pos] !== 0x64) break // CopyData
|
|
26
|
-
const view = new DataView(buf.buffer, buf.byteOffset + pos + 1)
|
|
27
|
-
const len = view.getInt32(0)
|
|
28
|
-
// XLogData starts at pos+5, payload type at pos+5+1+8+8+8 = pos+30
|
|
29
|
-
if (pos + 30 < buf.length) {
|
|
30
|
-
types.push(buf[pos + 30])
|
|
31
|
-
}
|
|
32
|
-
pos += 1 + len
|
|
33
|
-
}
|
|
34
|
-
return types
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
describe('orez/worker integration', { timeout: 30000 }, () => {
|
|
38
|
-
let worker: OrezWorker
|
|
39
|
-
|
|
40
|
-
beforeEach(async () => {
|
|
41
|
-
resetReplicationState()
|
|
42
|
-
worker = await createOrezWorker({
|
|
43
|
-
pgliteOptions: { dataDir: 'memory://' },
|
|
44
|
-
})
|
|
45
|
-
// create test table
|
|
46
|
-
await worker.exec(`
|
|
47
|
-
CREATE TABLE public.foo (
|
|
48
|
-
id TEXT PRIMARY KEY,
|
|
49
|
-
value TEXT,
|
|
50
|
-
num INTEGER
|
|
51
|
-
)
|
|
52
|
-
`)
|
|
53
|
-
// reinstall triggers after table creation
|
|
54
|
-
await worker.installChangeTracking()
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
afterEach(async () => {
|
|
58
|
-
resetReplicationState()
|
|
59
|
-
await worker.close()
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
it('change tracking captures insert/update/delete cycle', async () => {
|
|
63
|
-
await worker.query('INSERT INTO foo (id, value, num) VALUES ($1, $2, $3)', [
|
|
64
|
-
'row1',
|
|
65
|
-
'hello',
|
|
66
|
-
42,
|
|
67
|
-
])
|
|
68
|
-
await worker.query('UPDATE foo SET value = $1 WHERE id = $2', ['updated', 'row1'])
|
|
69
|
-
await worker.query('DELETE FROM foo WHERE id = $1', ['row1'])
|
|
70
|
-
|
|
71
|
-
const changes = await worker.getChangesSince(0)
|
|
72
|
-
expect(changes).toHaveLength(3)
|
|
73
|
-
expect(changes[0].op).toBe('INSERT')
|
|
74
|
-
expect(changes[0].row_data).toMatchObject({ id: 'row1', value: 'hello', num: 42 })
|
|
75
|
-
expect(changes[1].op).toBe('UPDATE')
|
|
76
|
-
expect(changes[1].row_data).toMatchObject({ id: 'row1', value: 'updated' })
|
|
77
|
-
expect(changes[1].old_data).toMatchObject({ id: 'row1', value: 'hello' })
|
|
78
|
-
expect(changes[2].op).toBe('DELETE')
|
|
79
|
-
expect(changes[2].old_data).toMatchObject({ id: 'row1', value: 'updated' })
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
it('InProcessWriter receives pgoutput stream from replication', async () => {
|
|
83
|
-
const received: Uint8Array[] = []
|
|
84
|
-
const writer = new InProcessWriter((data) => received.push(new Uint8Array(data)))
|
|
85
|
-
|
|
86
|
-
// start replication in background
|
|
87
|
-
const replPromise = worker.startReplication(writer)
|
|
88
|
-
|
|
89
|
-
// wait for handler to set up
|
|
90
|
-
await new Promise((r) => setTimeout(r, 200))
|
|
91
|
-
|
|
92
|
-
// insert data
|
|
93
|
-
await worker.query('INSERT INTO foo (id, value, num) VALUES ($1, $2, $3)', [
|
|
94
|
-
'streamed-1',
|
|
95
|
-
'live',
|
|
96
|
-
99,
|
|
97
|
-
])
|
|
98
|
-
signalReplicationChange()
|
|
99
|
-
|
|
100
|
-
// wait for replication to deliver
|
|
101
|
-
await new Promise((r) => setTimeout(r, 1500))
|
|
102
|
-
|
|
103
|
-
// close writer to stop replication
|
|
104
|
-
writer.close()
|
|
105
|
-
await replPromise.catch(() => {}) // may reject on close
|
|
106
|
-
|
|
107
|
-
// should have received pgoutput messages
|
|
108
|
-
expect(received.length).toBeGreaterThan(0)
|
|
109
|
-
|
|
110
|
-
// extract message types from all received buffers
|
|
111
|
-
const allTypes = received.flatMap(extractPayloadTypes)
|
|
112
|
-
|
|
113
|
-
// first message is CopyBothResponse (0x57), then pgoutput messages
|
|
114
|
-
// check we got the core pgoutput types
|
|
115
|
-
expect(allTypes).toContain(0x42) // BEGIN
|
|
116
|
-
expect(allTypes).toContain(0x52) // RELATION
|
|
117
|
-
expect(allTypes).toContain(0x49) // INSERT
|
|
118
|
-
expect(allTypes).toContain(0x43) // COMMIT
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
it('watermarks advance monotonically', async () => {
|
|
122
|
-
const watermarks: number[] = []
|
|
123
|
-
|
|
124
|
-
for (let i = 0; i < 5; i++) {
|
|
125
|
-
await worker.query('INSERT INTO foo (id, value, num) VALUES ($1, $2, $3)', [
|
|
126
|
-
`wm-${i}`,
|
|
127
|
-
`val-${i}`,
|
|
128
|
-
i,
|
|
129
|
-
])
|
|
130
|
-
watermarks.push(await worker.getCurrentWatermark())
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// each watermark should be strictly greater than the previous
|
|
134
|
-
for (let i = 1; i < watermarks.length; i++) {
|
|
135
|
-
expect(watermarks[i]).toBeGreaterThan(watermarks[i - 1])
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// getChangesSince with earlier watermark returns only newer changes
|
|
139
|
-
const midpoint = watermarks[2]
|
|
140
|
-
const laterChanges = await worker.getChangesSince(midpoint)
|
|
141
|
-
expect(laterChanges).toHaveLength(2)
|
|
142
|
-
expect(laterChanges[0].row_data).toMatchObject({ id: 'wm-3' })
|
|
143
|
-
expect(laterChanges[1].row_data).toMatchObject({ id: 'wm-4' })
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
it('purge removes consumed changes', async () => {
|
|
147
|
-
for (let i = 0; i < 10; i++) {
|
|
148
|
-
await worker.query('INSERT INTO foo (id, value, num) VALUES ($1, $2, $3)', [
|
|
149
|
-
`purge-${i}`,
|
|
150
|
-
'x',
|
|
151
|
-
i,
|
|
152
|
-
])
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const allChanges = await worker.getChangesSince(0)
|
|
156
|
-
expect(allChanges).toHaveLength(10)
|
|
157
|
-
|
|
158
|
-
// purge first 7
|
|
159
|
-
const purged = await worker.purgeChanges(allChanges[6].watermark)
|
|
160
|
-
expect(purged).toBe(7)
|
|
161
|
-
|
|
162
|
-
// only 3 remain
|
|
163
|
-
const remaining = await worker.getChangesSince(0)
|
|
164
|
-
expect(remaining).toHaveLength(3)
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
it('multiple tables each get tracked', async () => {
|
|
168
|
-
await worker.exec(`
|
|
169
|
-
CREATE TABLE public.bar (
|
|
170
|
-
id TEXT PRIMARY KEY,
|
|
171
|
-
foo_id TEXT
|
|
172
|
-
)
|
|
173
|
-
`)
|
|
174
|
-
await worker.installChangeTracking()
|
|
175
|
-
|
|
176
|
-
await worker.query('INSERT INTO foo VALUES ($1, $2, $3)', ['f1', 'a', 1])
|
|
177
|
-
await worker.query('INSERT INTO bar VALUES ($1, $2)', ['b1', 'f1'])
|
|
178
|
-
|
|
179
|
-
const changes = await worker.getChangesSince(0)
|
|
180
|
-
expect(changes).toHaveLength(2)
|
|
181
|
-
expect(changes[0].table_name).toBe('public.foo')
|
|
182
|
-
expect(changes[1].table_name).toBe('public.bar')
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
it('replication stream contains correct data for multiple operations', async () => {
|
|
186
|
-
const received: Uint8Array[] = []
|
|
187
|
-
const writer = new InProcessWriter((data) => received.push(new Uint8Array(data)))
|
|
188
|
-
|
|
189
|
-
const replPromise = worker.startReplication(writer)
|
|
190
|
-
await new Promise((r) => setTimeout(r, 200))
|
|
191
|
-
|
|
192
|
-
// insert
|
|
193
|
-
await worker.query('INSERT INTO foo VALUES ($1, $2, $3)', ['r1', 'initial', 1])
|
|
194
|
-
signalReplicationChange()
|
|
195
|
-
await new Promise((r) => setTimeout(r, 800))
|
|
196
|
-
|
|
197
|
-
// update
|
|
198
|
-
await worker.query('UPDATE foo SET value = $1 WHERE id = $2', ['modified', 'r1'])
|
|
199
|
-
signalReplicationChange()
|
|
200
|
-
await new Promise((r) => setTimeout(r, 800))
|
|
201
|
-
|
|
202
|
-
// delete
|
|
203
|
-
await worker.query('DELETE FROM foo WHERE id = $1', ['r1'])
|
|
204
|
-
signalReplicationChange()
|
|
205
|
-
await new Promise((r) => setTimeout(r, 800))
|
|
206
|
-
|
|
207
|
-
writer.close()
|
|
208
|
-
await replPromise.catch(() => {})
|
|
209
|
-
|
|
210
|
-
const allTypes = received.flatMap(extractPayloadTypes)
|
|
211
|
-
|
|
212
|
-
// should have all three operation types
|
|
213
|
-
expect(allTypes).toContain(0x49) // INSERT
|
|
214
|
-
expect(allTypes).toContain(0x55) // UPDATE
|
|
215
|
-
expect(allTypes).toContain(0x44) // DELETE
|
|
216
|
-
|
|
217
|
-
// should have BEGIN/COMMIT pairs
|
|
218
|
-
const begins = allTypes.filter((t) => t === 0x42).length
|
|
219
|
-
const commits = allTypes.filter((t) => t === 0x43).length
|
|
220
|
-
expect(begins).toBe(commits)
|
|
221
|
-
expect(begins).toBeGreaterThanOrEqual(1)
|
|
222
|
-
})
|
|
223
|
-
})
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { PGlite } from '@electric-sql/pglite'
|
|
2
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
|
3
|
-
|
|
4
|
-
import { createOrezWorker } from './index'
|
|
5
|
-
|
|
6
|
-
import type { OrezWorker } from './types'
|
|
7
|
-
|
|
8
|
-
describe('orez/worker', () => {
|
|
9
|
-
let worker: OrezWorker
|
|
10
|
-
|
|
11
|
-
beforeEach(async () => {
|
|
12
|
-
worker = await createOrezWorker({
|
|
13
|
-
pgliteOptions: { dataDir: 'memory://' },
|
|
14
|
-
})
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
afterEach(async () => {
|
|
18
|
-
await worker.close()
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
it('creates worker with pgliteOptions', () => {
|
|
22
|
-
expect(worker.db).toBeDefined()
|
|
23
|
-
expect(worker.ownsInstance).toBe(true)
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it('creates worker with pre-existing PGlite', async () => {
|
|
27
|
-
const pglite = new PGlite()
|
|
28
|
-
await pglite.waitReady
|
|
29
|
-
const w = await createOrezWorker({ pglite })
|
|
30
|
-
expect(w.db).toBe(pglite)
|
|
31
|
-
expect(w.ownsInstance).toBe(false)
|
|
32
|
-
await w.close()
|
|
33
|
-
// pglite should still be open since worker doesn't own it
|
|
34
|
-
expect(pglite.closed).toBe(false)
|
|
35
|
-
await pglite.close()
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
it('throws without pglite or pgliteOptions', async () => {
|
|
39
|
-
await expect(createOrezWorker({})).rejects.toThrow(
|
|
40
|
-
'provide either pglite or pgliteOptions'
|
|
41
|
-
)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
it('exec and query work', async () => {
|
|
45
|
-
await worker.exec(`
|
|
46
|
-
CREATE TABLE public.items (
|
|
47
|
-
id SERIAL PRIMARY KEY,
|
|
48
|
-
name TEXT NOT NULL
|
|
49
|
-
)
|
|
50
|
-
`)
|
|
51
|
-
await worker.installChangeTracking()
|
|
52
|
-
await worker.query('INSERT INTO public.items (name) VALUES ($1)', ['hello'])
|
|
53
|
-
const result = await worker.query<{ id: number; name: string }>(
|
|
54
|
-
'SELECT * FROM public.items'
|
|
55
|
-
)
|
|
56
|
-
expect(result.rows).toHaveLength(1)
|
|
57
|
-
expect(result.rows[0].name).toBe('hello')
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it('change tracking captures mutations', async () => {
|
|
61
|
-
await worker.exec(`
|
|
62
|
-
CREATE TABLE public.things (
|
|
63
|
-
id TEXT PRIMARY KEY,
|
|
64
|
-
val INTEGER
|
|
65
|
-
)
|
|
66
|
-
`)
|
|
67
|
-
// reinstall after creating table so triggers are on the new table
|
|
68
|
-
await worker.installChangeTracking()
|
|
69
|
-
|
|
70
|
-
await worker.exec(`INSERT INTO public.things VALUES ('a', 1)`)
|
|
71
|
-
await worker.exec(`UPDATE public.things SET val = 2 WHERE id = 'a'`)
|
|
72
|
-
await worker.exec(`DELETE FROM public.things WHERE id = 'a'`)
|
|
73
|
-
|
|
74
|
-
const changes = await worker.getChangesSince(0)
|
|
75
|
-
expect(changes).toHaveLength(3)
|
|
76
|
-
expect(changes[0].op).toBe('INSERT')
|
|
77
|
-
expect(changes[0].table_name).toBe('public.things')
|
|
78
|
-
expect(changes[0].row_data).toMatchObject({ id: 'a', val: 1 })
|
|
79
|
-
expect(changes[1].op).toBe('UPDATE')
|
|
80
|
-
expect(changes[1].row_data).toMatchObject({ id: 'a', val: 2 })
|
|
81
|
-
expect(changes[1].old_data).toMatchObject({ id: 'a', val: 1 })
|
|
82
|
-
expect(changes[2].op).toBe('DELETE')
|
|
83
|
-
expect(changes[2].old_data).toMatchObject({ id: 'a', val: 2 })
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
it('watermark tracking works', async () => {
|
|
87
|
-
await worker.exec(`
|
|
88
|
-
CREATE TABLE public.wm_test (id TEXT PRIMARY KEY)
|
|
89
|
-
`)
|
|
90
|
-
await worker.installChangeTracking()
|
|
91
|
-
|
|
92
|
-
const wm0 = await worker.getCurrentWatermark()
|
|
93
|
-
expect(wm0).toBe(0)
|
|
94
|
-
|
|
95
|
-
await worker.exec(`INSERT INTO public.wm_test VALUES ('x')`)
|
|
96
|
-
const wm1 = await worker.getCurrentWatermark()
|
|
97
|
-
expect(wm1).toBeGreaterThan(0)
|
|
98
|
-
|
|
99
|
-
await worker.exec(`INSERT INTO public.wm_test VALUES ('y')`)
|
|
100
|
-
const wm2 = await worker.getCurrentWatermark()
|
|
101
|
-
expect(wm2).toBeGreaterThan(wm1)
|
|
102
|
-
|
|
103
|
-
// getChangesSince with wm1 should only return the second insert
|
|
104
|
-
const changes = await worker.getChangesSince(wm1)
|
|
105
|
-
expect(changes).toHaveLength(1)
|
|
106
|
-
expect(changes[0].row_data).toMatchObject({ id: 'y' })
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
it('purgeChanges removes old entries', async () => {
|
|
110
|
-
await worker.exec(`CREATE TABLE public.purge_test (id TEXT PRIMARY KEY)`)
|
|
111
|
-
await worker.installChangeTracking()
|
|
112
|
-
|
|
113
|
-
await worker.exec(`INSERT INTO public.purge_test VALUES ('a')`)
|
|
114
|
-
await worker.exec(`INSERT INTO public.purge_test VALUES ('b')`)
|
|
115
|
-
await worker.exec(`INSERT INTO public.purge_test VALUES ('c')`)
|
|
116
|
-
|
|
117
|
-
const allChanges = await worker.getChangesSince(0)
|
|
118
|
-
expect(allChanges).toHaveLength(3)
|
|
119
|
-
|
|
120
|
-
// purge up to second change
|
|
121
|
-
const purged = await worker.purgeChanges(allChanges[1].watermark)
|
|
122
|
-
expect(purged).toBe(2)
|
|
123
|
-
|
|
124
|
-
// only third change remains
|
|
125
|
-
const remaining = await worker.getChangesSince(0)
|
|
126
|
-
expect(remaining).toHaveLength(1)
|
|
127
|
-
expect(remaining[0].row_data).toMatchObject({ id: 'c' })
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
it('close shuts down owned instance', async () => {
|
|
131
|
-
const w = await createOrezWorker({ pgliteOptions: { dataDir: 'memory://' } })
|
|
132
|
-
expect(w.db.closed).toBe(false)
|
|
133
|
-
await w.close()
|
|
134
|
-
expect(w.db.closed).toBe(true)
|
|
135
|
-
})
|
|
136
|
-
})
|