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,279 +0,0 @@
|
|
|
1
|
-
import { log } from '../log.js'
|
|
2
|
-
|
|
3
|
-
export interface ChangeRecord {
|
|
4
|
-
watermark: number
|
|
5
|
-
table_name: string
|
|
6
|
-
op: 'INSERT' | 'UPDATE' | 'DELETE'
|
|
7
|
-
row_data: Record<string, unknown> | null
|
|
8
|
-
old_data: Record<string, unknown> | null
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* minimal db interface change tracking needs. any sql executor that provides
|
|
13
|
-
* these two methods (e.g., a PGlite instance or a proxy wrapper) is accepted.
|
|
14
|
-
* using a structural type here keeps this module free of heavy imports so it
|
|
15
|
-
* can be loaded as the standalone `orez/change-tracking` entrypoint without
|
|
16
|
-
* pulling in pglite-manager or other server-only modules.
|
|
17
|
-
*/
|
|
18
|
-
export interface ChangeTrackingDb {
|
|
19
|
-
exec(sql: string): Promise<Array<{ affectedRows?: number }>>
|
|
20
|
-
query<T>(sql: string, params?: unknown[]): Promise<{ rows: T[] }>
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// PGlite returns JSONB columns as parsed objects; the DO backend returns them
|
|
24
|
-
// as JSON strings (it stores `row_data TEXT`). normalize once at the consumer
|
|
25
|
-
// boundary so callers always get an object.
|
|
26
|
-
function jsonRecord(value: unknown): Record<string, unknown> | null {
|
|
27
|
-
if (value === null || value === undefined) return null
|
|
28
|
-
if (typeof value === 'object') return value as Record<string, unknown>
|
|
29
|
-
if (typeof value !== 'string' || value === '') return null
|
|
30
|
-
return JSON.parse(value) as Record<string, unknown>
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export async function installChangeTracking(db: ChangeTrackingDb): Promise<void> {
|
|
34
|
-
// use _orez schema for internal tables - survives pg_restore of public schema
|
|
35
|
-
await db.exec(`CREATE SCHEMA IF NOT EXISTS _orez`)
|
|
36
|
-
|
|
37
|
-
// create changes table and watermark sequence
|
|
38
|
-
// watermark is the primary key - monotonically increasing, no separate id needed
|
|
39
|
-
await db.exec(`
|
|
40
|
-
CREATE SEQUENCE IF NOT EXISTS _orez._zero_watermark;
|
|
41
|
-
|
|
42
|
-
CREATE TABLE IF NOT EXISTS _orez._zero_changes (
|
|
43
|
-
watermark BIGINT NOT NULL DEFAULT nextval('_orez._zero_watermark') PRIMARY KEY,
|
|
44
|
-
table_name TEXT NOT NULL,
|
|
45
|
-
op TEXT NOT NULL CHECK (op IN ('INSERT', 'UPDATE', 'DELETE')),
|
|
46
|
-
row_data JSONB,
|
|
47
|
-
old_data JSONB
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
CREATE TABLE IF NOT EXISTS _orez._zero_replication_slots (
|
|
51
|
-
slot_name TEXT PRIMARY KEY,
|
|
52
|
-
restart_lsn TEXT NOT NULL DEFAULT '0/1000000',
|
|
53
|
-
confirmed_flush_lsn TEXT NOT NULL DEFAULT '0/1000000',
|
|
54
|
-
wal_status TEXT NOT NULL DEFAULT 'reserved',
|
|
55
|
-
plugin TEXT NOT NULL DEFAULT 'pgoutput',
|
|
56
|
-
slot_type TEXT NOT NULL DEFAULT 'logical',
|
|
57
|
-
active BOOLEAN NOT NULL DEFAULT false,
|
|
58
|
-
active_pid INTEGER DEFAULT NULL,
|
|
59
|
-
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
60
|
-
);
|
|
61
|
-
`)
|
|
62
|
-
|
|
63
|
-
// create trigger functions (writes to _orez schema)
|
|
64
|
-
// uses to_jsonb() directly instead of row_to_json()::jsonb to avoid double conversion.
|
|
65
|
-
// per-row trigger for single-row operations
|
|
66
|
-
await db.exec(`
|
|
67
|
-
CREATE OR REPLACE FUNCTION public._zero_track_change() RETURNS TRIGGER AS $$
|
|
68
|
-
BEGIN
|
|
69
|
-
IF TG_OP = 'DELETE' THEN
|
|
70
|
-
INSERT INTO _orez._zero_changes (table_name, op, old_data)
|
|
71
|
-
VALUES (TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME, 'DELETE', to_jsonb(OLD));
|
|
72
|
-
RETURN OLD;
|
|
73
|
-
ELSIF TG_OP = 'UPDATE' THEN
|
|
74
|
-
-- skip no-op updates where no columns actually changed
|
|
75
|
-
IF to_jsonb(NEW) = to_jsonb(OLD) THEN
|
|
76
|
-
RETURN NEW;
|
|
77
|
-
END IF;
|
|
78
|
-
INSERT INTO _orez._zero_changes (table_name, op, row_data, old_data)
|
|
79
|
-
VALUES (TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME, 'UPDATE', to_jsonb(NEW), to_jsonb(OLD));
|
|
80
|
-
RETURN NEW;
|
|
81
|
-
ELSE
|
|
82
|
-
INSERT INTO _orez._zero_changes (table_name, op, row_data)
|
|
83
|
-
VALUES (TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME, 'INSERT', to_jsonb(NEW));
|
|
84
|
-
RETURN NEW;
|
|
85
|
-
END IF;
|
|
86
|
-
END;
|
|
87
|
-
$$ LANGUAGE plpgsql;
|
|
88
|
-
`)
|
|
89
|
-
|
|
90
|
-
// install triggers on all public tables
|
|
91
|
-
await installTriggersOnAllTables(db)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function quoteIdent(name: string): string {
|
|
95
|
-
return '"' + name.replace(/"/g, '""') + '"'
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
async function installTriggersOnAllTables(db: ChangeTrackingDb): Promise<void> {
|
|
99
|
-
// If a publication is configured, respect it strictly. This avoids accidentally
|
|
100
|
-
// streaming private tables when publication membership is temporarily empty.
|
|
101
|
-
const pubName = process.env.ZERO_APP_PUBLICATIONS?.trim()
|
|
102
|
-
let tables: { tablename: string }[]
|
|
103
|
-
if (pubName) {
|
|
104
|
-
const result = await db.query<{ tablename: string }>(
|
|
105
|
-
`SELECT tablename FROM pg_publication_tables
|
|
106
|
-
WHERE pubname = $1
|
|
107
|
-
AND schemaname = 'public'
|
|
108
|
-
AND tablename NOT LIKE '_zero_%'`,
|
|
109
|
-
[pubName]
|
|
110
|
-
)
|
|
111
|
-
tables = result.rows
|
|
112
|
-
if (tables.length > 0) {
|
|
113
|
-
log.debug.pglite(`using publication "${pubName}" (${tables.length} tables)`)
|
|
114
|
-
} else {
|
|
115
|
-
log.pglite(`publication "${pubName}" is empty; installing no public table triggers`)
|
|
116
|
-
}
|
|
117
|
-
} else {
|
|
118
|
-
const all = await db.query<{ tablename: string }>(
|
|
119
|
-
`SELECT tablename FROM pg_tables
|
|
120
|
-
WHERE schemaname = 'public'
|
|
121
|
-
AND tablename NOT IN ('migrations')
|
|
122
|
-
AND tablename NOT LIKE '_zero_%'`
|
|
123
|
-
)
|
|
124
|
-
tables = all.rows
|
|
125
|
-
log.debug.pglite(`using all public tables (${tables.length})`)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// drop stale triggers from tables NOT in the publication
|
|
129
|
-
// (these may exist from a prior install before the publication was created)
|
|
130
|
-
const publishedSet = new Set(tables.map((t) => t.tablename))
|
|
131
|
-
const allTriggered = await db.query<{ event_object_table: string }>(
|
|
132
|
-
`SELECT DISTINCT event_object_table FROM information_schema.triggers
|
|
133
|
-
WHERE trigger_name = '_zero_change_trigger'
|
|
134
|
-
AND event_object_schema = 'public'`
|
|
135
|
-
)
|
|
136
|
-
for (const { event_object_table } of allTriggered.rows) {
|
|
137
|
-
if (!publishedSet.has(event_object_table)) {
|
|
138
|
-
const quoted = quoteIdent(event_object_table)
|
|
139
|
-
await db.exec(`DROP TRIGGER IF EXISTS _zero_change_trigger ON public.${quoted}`)
|
|
140
|
-
log.debug.pglite(
|
|
141
|
-
`removed stale trigger from non-published table: ${event_object_table}`
|
|
142
|
-
)
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
let count = 0
|
|
147
|
-
for (const { tablename } of tables) {
|
|
148
|
-
const quoted = quoteIdent(tablename)
|
|
149
|
-
await db.exec(`
|
|
150
|
-
DROP TRIGGER IF EXISTS _zero_change_trigger ON public.${quoted};
|
|
151
|
-
CREATE TRIGGER _zero_change_trigger
|
|
152
|
-
AFTER INSERT OR UPDATE OR DELETE ON public.${quoted}
|
|
153
|
-
FOR EACH ROW EXECUTE FUNCTION public._zero_track_change();
|
|
154
|
-
`)
|
|
155
|
-
count++
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (count > 0) {
|
|
159
|
-
log.debug.pglite(`installed change tracking triggers on ${count} tables`)
|
|
160
|
-
} else {
|
|
161
|
-
log.debug.pglite(`no tables to install change tracking triggers on`)
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* install change tracking triggers on tables in shard schemas.
|
|
167
|
-
* zero-cache creates shard schemas (e.g. chat_0) with clients/mutations
|
|
168
|
-
* tables that track mutation confirmations. these must be replicated
|
|
169
|
-
* for .server promises to resolve.
|
|
170
|
-
*
|
|
171
|
-
* caches already-tracked shard tables to avoid redundant DDL while still
|
|
172
|
-
* handling zero-cache creating a schema before the internal tables exist.
|
|
173
|
-
*/
|
|
174
|
-
const trackedShardTables = new Set<string>()
|
|
175
|
-
const TRACKED_SHARD_TABLES = new Set(['clients', 'mutations'])
|
|
176
|
-
|
|
177
|
-
/** reset shard schema cache (for tests) */
|
|
178
|
-
export function resetShardSchemaCache(): void {
|
|
179
|
-
trackedShardTables.clear()
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export async function installTriggersOnShardTables(db: ChangeTrackingDb): Promise<void> {
|
|
183
|
-
const result = await db.query<{ nspname: string }>(
|
|
184
|
-
`SELECT nspname FROM pg_namespace
|
|
185
|
-
WHERE nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast', 'public')
|
|
186
|
-
AND nspname NOT LIKE 'pg_%'
|
|
187
|
-
AND nspname NOT LIKE 'zero_%'
|
|
188
|
-
AND nspname NOT LIKE '_zero_%'
|
|
189
|
-
AND nspname NOT LIKE '%/%'`
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
if (result.rows.length === 0) return
|
|
193
|
-
|
|
194
|
-
// only track the shard tables zero-cache expects in the replication stream.
|
|
195
|
-
// `clients` advances LMID for successful .server promises; `mutations`
|
|
196
|
-
// carries per-mutation results for application errors. other shard tables
|
|
197
|
-
// like `replicas` are zero-cache internal state and streaming them back
|
|
198
|
-
// causes "Unknown table" crashes in zero-cache's change-processor.
|
|
199
|
-
let count = 0
|
|
200
|
-
for (const { nspname } of result.rows) {
|
|
201
|
-
// remove stale triggers from non-replicated shard tables.
|
|
202
|
-
const stale = await db.query<{ event_object_table: string }>(
|
|
203
|
-
`SELECT DISTINCT event_object_table FROM information_schema.triggers
|
|
204
|
-
WHERE trigger_name = '_zero_change_trigger'
|
|
205
|
-
AND event_object_schema = $1
|
|
206
|
-
AND event_object_table != ALL($2)`,
|
|
207
|
-
[nspname, [...TRACKED_SHARD_TABLES]]
|
|
208
|
-
)
|
|
209
|
-
for (const { event_object_table } of stale.rows) {
|
|
210
|
-
const qs = quoteIdent(nspname)
|
|
211
|
-
const qt = quoteIdent(event_object_table)
|
|
212
|
-
await db.exec(`DROP TRIGGER IF EXISTS _zero_change_trigger ON ${qs}.${qt}`)
|
|
213
|
-
log.debug.pglite(
|
|
214
|
-
`removed stale shard trigger from ${nspname}.${event_object_table}`
|
|
215
|
-
)
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const tables = await db.query<{ tablename: string }>(
|
|
219
|
-
`SELECT tablename FROM pg_tables WHERE schemaname = $1 AND tablename = ANY($2)`,
|
|
220
|
-
[nspname, [...TRACKED_SHARD_TABLES]]
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
for (const { tablename } of tables.rows) {
|
|
224
|
-
const key = `${nspname}.${tablename}`
|
|
225
|
-
if (trackedShardTables.has(key)) continue
|
|
226
|
-
|
|
227
|
-
const quotedSchema = quoteIdent(nspname)
|
|
228
|
-
const quotedTable = quoteIdent(tablename)
|
|
229
|
-
await db.exec(`
|
|
230
|
-
DROP TRIGGER IF EXISTS _zero_change_trigger ON ${quotedSchema}.${quotedTable};
|
|
231
|
-
CREATE TRIGGER _zero_change_trigger
|
|
232
|
-
AFTER INSERT OR UPDATE OR DELETE ON ${quotedSchema}.${quotedTable}
|
|
233
|
-
FOR EACH ROW EXECUTE FUNCTION public._zero_track_change();
|
|
234
|
-
`)
|
|
235
|
-
trackedShardTables.add(key)
|
|
236
|
-
count++
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if (count > 0) {
|
|
241
|
-
log.debug.pglite(`installed change tracking on ${count} shard tables`)
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
export async function getChangesSince(
|
|
246
|
-
db: ChangeTrackingDb,
|
|
247
|
-
watermark: number,
|
|
248
|
-
limit = 50000
|
|
249
|
-
): Promise<ChangeRecord[]> {
|
|
250
|
-
const result = await db.query<ChangeRecord>(
|
|
251
|
-
'SELECT watermark, table_name, op, row_data, old_data FROM _orez._zero_changes WHERE watermark > $1 ORDER BY watermark LIMIT $2',
|
|
252
|
-
[watermark, limit]
|
|
253
|
-
)
|
|
254
|
-
return result.rows.map((row) => ({
|
|
255
|
-
...row,
|
|
256
|
-
watermark: Number(row.watermark),
|
|
257
|
-
row_data: jsonRecord(row.row_data),
|
|
258
|
-
old_data: jsonRecord(row.old_data),
|
|
259
|
-
}))
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
export async function purgeConsumedChanges(
|
|
263
|
-
db: ChangeTrackingDb,
|
|
264
|
-
watermark: number
|
|
265
|
-
): Promise<number> {
|
|
266
|
-
const result = await db.exec(
|
|
267
|
-
`DELETE FROM _orez._zero_changes WHERE watermark <= ${Number(watermark)}`
|
|
268
|
-
)
|
|
269
|
-
return result[0]?.affectedRows ?? 0
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
export async function getCurrentWatermark(db: ChangeTrackingDb): Promise<number> {
|
|
273
|
-
const result = await db.query<{ last_value: string; is_called: boolean }>(
|
|
274
|
-
'SELECT last_value, is_called FROM _orez._zero_watermark'
|
|
275
|
-
)
|
|
276
|
-
const { last_value, is_called } = result.rows[0]
|
|
277
|
-
if (!is_called) return 0
|
|
278
|
-
return Number(last_value)
|
|
279
|
-
}
|