orez 0.4.11 → 0.4.12
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.
|
@@ -21,6 +21,7 @@ export declare class DoBackend {
|
|
|
21
21
|
private triggerFunctions;
|
|
22
22
|
private schemaMetadata;
|
|
23
23
|
private publications;
|
|
24
|
+
private lastEmptyPublicationReloadAt;
|
|
24
25
|
private rewriteCache;
|
|
25
26
|
private preparedStatements;
|
|
26
27
|
private portals;
|
|
@@ -47,6 +48,7 @@ export declare class DoBackend {
|
|
|
47
48
|
private publicationToJSON;
|
|
48
49
|
private publicationFromJSON;
|
|
49
50
|
private loadDurableMetadata;
|
|
51
|
+
private reloadPublicationsIfEmpty;
|
|
50
52
|
private repairShardMetadataPublications;
|
|
51
53
|
private persistDurableMetadata;
|
|
52
54
|
close(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pg-proxy-do-backend.d.ts","sourceRoot":"","sources":["../src/pg-proxy-do-backend.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pg-proxy-do-backend.d.ts","sourceRoot":"","sources":["../src/pg-proxy-do-backend.ts"],"names":[],"mappings":"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"}
|
|
@@ -41,6 +41,11 @@ const PG_TYPE_BYTEA = 17;
|
|
|
41
41
|
const PG_TYPE_INT2 = 21;
|
|
42
42
|
const MAX_REWRITE_CACHE_ENTRIES = 2048;
|
|
43
43
|
const METADATA_TABLE = '_orez_pg_metadata';
|
|
44
|
+
// how long reloadPublicationsIfEmpty waits between re-reads while publications
|
|
45
|
+
// stay empty — short enough that the first write after a concurrently-created
|
|
46
|
+
// publication picks it up quickly, long enough that a genuinely
|
|
47
|
+
// publication-less db doesn't re-query on every write.
|
|
48
|
+
const EMPTY_PUBLICATION_RELOAD_THROTTLE_MS = 1000;
|
|
44
49
|
// ── Utilities ─────────────────────────────────────────────────────────────
|
|
45
50
|
function concat(...parts) {
|
|
46
51
|
let total = 0;
|
|
@@ -4567,6 +4572,9 @@ export class DoBackend {
|
|
|
4567
4572
|
triggerFunctions;
|
|
4568
4573
|
schemaMetadata;
|
|
4569
4574
|
publications;
|
|
4575
|
+
// throttle for reloadPublicationsIfEmpty (self-heal of a stale-empty
|
|
4576
|
+
// publication cache). undefined until the first empty-publication write.
|
|
4577
|
+
lastEmptyPublicationReloadAt;
|
|
4570
4578
|
rewriteCache;
|
|
4571
4579
|
preparedStatements = new Map();
|
|
4572
4580
|
portals = new Map();
|
|
@@ -4715,6 +4723,39 @@ export class DoBackend {
|
|
|
4715
4723
|
}
|
|
4716
4724
|
catch { }
|
|
4717
4725
|
}
|
|
4726
|
+
// self-heal a stale-empty publication cache. publications are durable
|
|
4727
|
+
// (_orez_pg_metadata) and SHARED across every DoBackend instance pointed at
|
|
4728
|
+
// one DO, but each instance only loads them once at init(). when this
|
|
4729
|
+
// backend's init() ran BEFORE another instance (e.g. the schema-migration pg
|
|
4730
|
+
// pool) created the publication, this.publications is empty and stays empty —
|
|
4731
|
+
// so a public write's change-capture is skipped (trackingForStatement) and the
|
|
4732
|
+
// row never reaches _zero_changes / the replica / a client. on CF this is the
|
|
4733
|
+
// per-project namespace's empty-fileTree bug: the project's /__soot_pg write
|
|
4734
|
+
// backend is constructed by a read/write that races provisioning's CREATE
|
|
4735
|
+
// PUBLICATION, caches zero publications, and never recovers. re-read just the
|
|
4736
|
+
// publication rows when empty so the first write after the publication exists
|
|
4737
|
+
// picks it up. throttled (a genuinely publication-less db must not re-query on
|
|
4738
|
+
// every write); cleared whenever a publication appears.
|
|
4739
|
+
async reloadPublicationsIfEmpty() {
|
|
4740
|
+
if (this.dbName !== 'postgres' || this.publications.size > 0)
|
|
4741
|
+
return;
|
|
4742
|
+
const now = Date.now();
|
|
4743
|
+
if (this.lastEmptyPublicationReloadAt &&
|
|
4744
|
+
now - this.lastEmptyPublicationReloadAt < EMPTY_PUBLICATION_RELOAD_THROTTLE_MS) {
|
|
4745
|
+
return;
|
|
4746
|
+
}
|
|
4747
|
+
this.lastEmptyPublicationReloadAt = now;
|
|
4748
|
+
try {
|
|
4749
|
+
await this.ensureMetadataTable();
|
|
4750
|
+
const result = await this.doExecResult(`SELECT key, value FROM ${quoteIdentifier(METADATA_TABLE)} WHERE kind = 'publication'`);
|
|
4751
|
+
for (const row of result.rows) {
|
|
4752
|
+
const publication = this.publicationFromJSON(String(row.value ?? ''));
|
|
4753
|
+
if (publication)
|
|
4754
|
+
this.publications.set(String(row.key ?? ''), publication);
|
|
4755
|
+
}
|
|
4756
|
+
}
|
|
4757
|
+
catch { }
|
|
4758
|
+
}
|
|
4718
4759
|
async repairShardMetadataPublications() {
|
|
4719
4760
|
let changed = false;
|
|
4720
4761
|
const tables = await this.listSqliteTables();
|
|
@@ -5521,6 +5562,12 @@ export class DoBackend {
|
|
|
5521
5562
|
const statement = statements.length === 1 ? statements[0] : undefined;
|
|
5522
5563
|
if (statement)
|
|
5523
5564
|
await this.snapshotTransactionWrite(statement);
|
|
5565
|
+
// a public DML write whose publication cache is empty may have been built
|
|
5566
|
+
// before another backend instance created the publication — self-heal the
|
|
5567
|
+
// stale-empty cache so its change-capture isn't silently skipped.
|
|
5568
|
+
if (statement?.changeTracking?.table.schema === 'public') {
|
|
5569
|
+
await this.reloadPublicationsIfEmpty();
|
|
5570
|
+
}
|
|
5524
5571
|
const tracking = statement ? this.trackingForStatement(statement) : undefined;
|
|
5525
5572
|
const execBound = tracking
|
|
5526
5573
|
? this.sqliteBoundSQL(tracking.returningSQL, params, arrayParamNumbers, jsonParamNumbers, timestampParamNumbers, epochMillisParamNumbers, booleanParamNumbers)
|
|
@@ -6671,8 +6718,23 @@ export class DoBackend {
|
|
|
6671
6718
|
return i16(Number(value));
|
|
6672
6719
|
if (oid === PG_TYPE_INT4)
|
|
6673
6720
|
return i32(Number(value));
|
|
6674
|
-
if (oid === PG_TYPE_INT8)
|
|
6675
|
-
|
|
6721
|
+
if (oid === PG_TYPE_INT8) {
|
|
6722
|
+
try {
|
|
6723
|
+
return i64(typeof value === 'bigint' ? value : BigInt(value));
|
|
6724
|
+
}
|
|
6725
|
+
catch {
|
|
6726
|
+
// value is not a valid integer for this int8 column — e.g. a timestamp
|
|
6727
|
+
// string written into a bigint column, which sqlite's dynamic typing
|
|
6728
|
+
// accepts but real pg would reject. encode a best-effort int rather than
|
|
6729
|
+
// throw: a thrown error mid binary-COPY emits an ErrorResponse into the
|
|
6730
|
+
// copy-out stream, which the consumer's COPY parser cannot recover from
|
|
6731
|
+
// and hangs on — wedging zero-cache's entire initial sync (the embed
|
|
6732
|
+
// never reaches ready, every /sync gets 0 frames). 0 keeps the stream
|
|
6733
|
+
// well-formed so the rest of the snapshot completes.
|
|
6734
|
+
const n = Number(value);
|
|
6735
|
+
return i64(Number.isFinite(n) ? BigInt(Math.trunc(n)) : 0n);
|
|
6736
|
+
}
|
|
6737
|
+
}
|
|
6676
6738
|
if (oid === PG_TYPE_FLOAT8) {
|
|
6677
6739
|
const buf = new ArrayBuffer(8);
|
|
6678
6740
|
new DataView(buf).setFloat64(0, Number(value));
|
|
@@ -6875,6 +6937,20 @@ export class DoBackend {
|
|
|
6875
6937
|
return (await this.handleCatalogQueries(sql))[0] ?? { rows: [], fields: [] };
|
|
6876
6938
|
}
|
|
6877
6939
|
async handleCatalogSelect(select) {
|
|
6940
|
+
// zero-cache's initial sync validates the publication via pg_publication /
|
|
6941
|
+
// pg_publication_tables. those answers come from in-memory this.publications,
|
|
6942
|
+
// loaded once at backend init. a backend instance constructed during early
|
|
6943
|
+
// embed boot (migrateOnly, before CREATE PUBLICATION persisted) caches an
|
|
6944
|
+
// empty set, so the catalog query reports "Found: []" → setupTablesAndReplication
|
|
6945
|
+
// throws "Unknown or invalid publications" → initial sync aborts → the embed
|
|
6946
|
+
// never reaches ready (120s timeout, /sync sends 0 frames). the write path
|
|
6947
|
+
// already self-heals (reloadPublicationsIfEmpty); the catalog-read path the
|
|
6948
|
+
// change-streamer uses did not. reload from durable _orez_pg_metadata before
|
|
6949
|
+
// answering so the publication created concurrently on another instance is seen.
|
|
6950
|
+
if (selectReferencesTable(select, 'pg_publication') ||
|
|
6951
|
+
selectReferencesTable(select, 'pg_publication_tables')) {
|
|
6952
|
+
await this.reloadPublicationsIfEmpty();
|
|
6953
|
+
}
|
|
6878
6954
|
return (catalogCurrentSettingResultFromSelect(select) ??
|
|
6879
6955
|
(await this.informationSchemaKeyColumnsResult(select)) ??
|
|
6880
6956
|
(await this.informationSchemaColumnsResult(select)) ??
|