orez 0.5.9 → 0.5.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.
- package/dist/browser.js +1 -1
- package/dist/browser.js.map +1 -1
- package/dist/cf-do/tx-journal.d.ts +6 -5
- package/dist/cf-do/tx-journal.d.ts.map +1 -1
- package/dist/cf-do/tx-journal.js +116 -27
- package/dist/cf-do/tx-journal.js.map +1 -1
- package/dist/pg-proxy-browser.d.ts +3 -1
- package/dist/pg-proxy-browser.d.ts.map +1 -1
- package/dist/pg-proxy-browser.js +240 -210
- package/dist/pg-proxy-browser.js.map +1 -1
- package/dist/pg-proxy-do-backend.d.ts +8 -0
- package/dist/pg-proxy-do-backend.d.ts.map +1 -1
- package/dist/pg-proxy-do-backend.js +27 -9
- package/dist/pg-proxy-do-backend.js.map +1 -1
- package/dist/pg-sqlite-compiler/compiler.d.ts +17 -0
- package/dist/pg-sqlite-compiler/compiler.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/compiler.js +80 -0
- package/dist/pg-sqlite-compiler/compiler.js.map +1 -0
- package/dist/pg-sqlite-compiler/index.d.ts +1 -16
- package/dist/pg-sqlite-compiler/index.d.ts.map +1 -1
- package/dist/pg-sqlite-compiler/index.js +2 -79
- package/dist/pg-sqlite-compiler/index.js.map +1 -1
- package/dist/replication/handler.d.ts +8 -7
- package/dist/replication/handler.d.ts.map +1 -1
- package/dist/replication/handler.js +147 -116
- package/dist/replication/handler.js.map +1 -1
- package/dist/worker/browser-build-config.d.ts.map +1 -1
- package/dist/worker/browser-build-config.js +1 -0
- package/dist/worker/browser-build-config.js.map +1 -1
- package/dist/worker/browser-embed.d.ts +3 -2
- package/dist/worker/browser-embed.d.ts.map +1 -1
- package/dist/worker/browser-embed.js +187 -150
- package/dist/worker/browser-embed.js.map +1 -1
- package/dist/worker/cf-instance-runtime.d.ts +62 -0
- package/dist/worker/cf-instance-runtime.d.ts.map +1 -0
- package/dist/worker/cf-instance-runtime.js +260 -0
- package/dist/worker/cf-instance-runtime.js.map +1 -0
- package/dist/worker/cf-patches.d.ts +9 -6
- package/dist/worker/cf-patches.d.ts.map +1 -1
- package/dist/worker/cf-patches.js +117 -60
- package/dist/worker/cf-patches.js.map +1 -1
- package/dist/worker/durable-object-websocket-handoff.d.ts +2 -0
- package/dist/worker/durable-object-websocket-handoff.d.ts.map +1 -1
- package/dist/worker/durable-object-websocket-handoff.js +22 -41
- package/dist/worker/durable-object-websocket-handoff.js.map +1 -1
- package/dist/worker/embed-generation.d.ts +9 -6
- package/dist/worker/embed-generation.d.ts.map +1 -1
- package/dist/worker/embed-generation.js +27 -6
- package/dist/worker/embed-generation.js.map +1 -1
- package/dist/worker/shims/fastify-websocket.d.ts +3 -0
- package/dist/worker/shims/fastify-websocket.d.ts.map +1 -0
- package/dist/worker/shims/fastify-websocket.js +3 -0
- package/dist/worker/shims/fastify-websocket.js.map +1 -0
- package/dist/worker/shims/fastify.d.ts +2 -10
- package/dist/worker/shims/fastify.d.ts.map +1 -1
- package/dist/worker/shims/fastify.js +14 -21
- package/dist/worker/shims/fastify.js.map +1 -1
- package/dist/worker/shims/postgres-browser.d.ts +1 -1
- package/dist/worker/shims/postgres-browser.d.ts.map +1 -1
- package/dist/worker/shims/postgres-browser.js +29 -20
- package/dist/worker/shims/postgres-browser.js.map +1 -1
- package/dist/worker/shims/sqlite-browser.d.ts +2 -3
- package/dist/worker/shims/sqlite-browser.d.ts.map +1 -1
- package/dist/worker/shims/sqlite-browser.js +2 -3
- package/dist/worker/shims/sqlite-browser.js.map +1 -1
- package/dist/worker/shims/sqlite.d.ts.map +1 -1
- package/dist/worker/shims/sqlite.js +56 -35
- package/dist/worker/shims/sqlite.js.map +1 -1
- package/dist/worker/shims/ws.d.ts.map +1 -1
- package/dist/worker/shims/ws.js +54 -39
- package/dist/worker/shims/ws.js.map +1 -1
- package/dist/worker/shims/zero-process-env.d.ts +1 -1
- package/dist/worker/shims/zero-process-env.d.ts.map +1 -1
- package/dist/worker/shims/zero-process-env.js +79 -8
- package/dist/worker/shims/zero-process-env.js.map +1 -1
- package/dist/worker/zero-cache-embed-cf.d.ts +5 -2
- package/dist/worker/zero-cache-embed-cf.d.ts.map +1 -1
- package/dist/worker/zero-cache-embed-cf.js +339 -256
- package/dist/worker/zero-cache-embed-cf.js.map +1 -1
- package/dist/worker/zero-cache-run-worker.d.ts +2 -0
- package/dist/worker/zero-cache-run-worker.d.ts.map +1 -0
- package/dist/worker/zero-cache-run-worker.js +3 -0
- package/dist/worker/zero-cache-run-worker.js.map +1 -0
- package/package.json +6 -2
package/dist/pg-proxy-browser.js
CHANGED
|
@@ -14,29 +14,10 @@
|
|
|
14
14
|
import { PostgresConnection } from 'pg-gateway';
|
|
15
15
|
import { log } from './log.js';
|
|
16
16
|
import { Mutex } from './mutex.js';
|
|
17
|
-
import { createReplicationFeedbackParser, handleReplicationQuery, handleStartReplication, signalReplicationChange, } from './replication/handler.js';
|
|
17
|
+
import { createReplicationFeedbackParser, handleReplicationQuery, handleStartReplication, noteConfirmedFlushLsn, signalReplicationChange, } from './replication/handler.js';
|
|
18
18
|
// shared encoder/decoder instances
|
|
19
19
|
const textEncoder = new TextEncoder();
|
|
20
20
|
const textDecoder = new TextDecoder();
|
|
21
|
-
// verbose wire-protocol logging. off by default — every postgres message hits
|
|
22
|
-
// the hot path, so unconditional console.debug calls are surprisingly expensive
|
|
23
|
-
// even when devtools is set to hide them (the interpolated strings still get
|
|
24
|
-
// built, and devtools retains them).
|
|
25
|
-
//
|
|
26
|
-
// re-evaluated on every call so you can flip it at runtime from any iframe,
|
|
27
|
-
// worker, or console without restarting. toggle via either:
|
|
28
|
-
// - env: OREZ_DEBUG_WIRE=1 (picked up at process start)
|
|
29
|
-
// - global: globalThis.__OREZ_DEBUG_WIRE__ = true (works anywhere, anytime)
|
|
30
|
-
function isDebugWire() {
|
|
31
|
-
try {
|
|
32
|
-
if (globalThis.__OREZ_DEBUG_WIRE__ === true)
|
|
33
|
-
return true;
|
|
34
|
-
if (typeof process !== 'undefined' && process.env?.OREZ_DEBUG_WIRE)
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
catch { }
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
21
|
// postgres frontend/backend message type bytes — only used for debug-wire logging
|
|
41
22
|
const PG_MSG_TYPE_NAMES = {
|
|
42
23
|
0x50: 'Parse',
|
|
@@ -109,8 +90,6 @@ function extractQueryText(data) {
|
|
|
109
90
|
}
|
|
110
91
|
return null;
|
|
111
92
|
}
|
|
112
|
-
// abort previous replication handler when a new one starts
|
|
113
|
-
let abortPreviousReplication = null;
|
|
114
93
|
// clean version string: strip emscripten compiler info that breaks pg_restore/pg_dump
|
|
115
94
|
const PG_VERSION_STRING = "'PostgreSQL 17.4 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 12.2.0, 64-bit'";
|
|
116
95
|
// query rewrites: make pglite look like real postgres with logical replication
|
|
@@ -552,7 +531,6 @@ function stripResponseMessages(data, stripRfq) {
|
|
|
552
531
|
* readable receives Uint8Array messages from the port.
|
|
553
532
|
* writable sends Uint8Array messages via the port.
|
|
554
533
|
*/
|
|
555
|
-
let _globalWriteCount = 0;
|
|
556
534
|
function messagePortToDuplexWithInject(port) {
|
|
557
535
|
let readController;
|
|
558
536
|
let msgCount = 0;
|
|
@@ -593,52 +571,6 @@ function messagePortToDuplexWithInject(port) {
|
|
|
593
571
|
};
|
|
594
572
|
return { duplex: { readable, writable }, rawWrite, injectMessage };
|
|
595
573
|
}
|
|
596
|
-
function messagePortToDuplex(port) {
|
|
597
|
-
let msgCount = 0;
|
|
598
|
-
const readable = new ReadableStream({
|
|
599
|
-
start(controller) {
|
|
600
|
-
port.onmessage = (ev) => {
|
|
601
|
-
msgCount++;
|
|
602
|
-
if (isDebugWire() && msgCount <= 3) {
|
|
603
|
-
console.debug(`[pg-proxy-duplex] msg#${msgCount} type=${typeof ev.data} isAB=${ev.data instanceof ArrayBuffer} isU8=${ev.data instanceof Uint8Array} len=${ev.data?.byteLength ?? ev.data?.length ?? '?'}`);
|
|
604
|
-
}
|
|
605
|
-
if (ev.data instanceof ArrayBuffer) {
|
|
606
|
-
controller.enqueue(new Uint8Array(ev.data));
|
|
607
|
-
}
|
|
608
|
-
else if (ev.data instanceof Uint8Array) {
|
|
609
|
-
controller.enqueue(ev.data);
|
|
610
|
-
}
|
|
611
|
-
else {
|
|
612
|
-
console.warn(`[pg-proxy-duplex] unexpected data type:`, typeof ev.data, ev.data);
|
|
613
|
-
}
|
|
614
|
-
};
|
|
615
|
-
},
|
|
616
|
-
cancel() {
|
|
617
|
-
port.close();
|
|
618
|
-
},
|
|
619
|
-
});
|
|
620
|
-
const writable = new WritableStream({
|
|
621
|
-
write(chunk) {
|
|
622
|
-
_globalWriteCount++;
|
|
623
|
-
if (isDebugWire() && _globalWriteCount <= 200) {
|
|
624
|
-
console.debug(`[pg-proxy-ws-write] #${_globalWriteCount} len=${chunk.byteLength}`);
|
|
625
|
-
}
|
|
626
|
-
// CF workerd MessagePort does not support transfer lists.
|
|
627
|
-
const buf = chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength);
|
|
628
|
-
port.postMessage(buf);
|
|
629
|
-
},
|
|
630
|
-
close() {
|
|
631
|
-
port.close();
|
|
632
|
-
},
|
|
633
|
-
});
|
|
634
|
-
// raw write function for injecting data outside of pg-gateway's stream
|
|
635
|
-
// (e.g. parameter status messages during onAuthenticated)
|
|
636
|
-
const rawWrite = (data) => {
|
|
637
|
-
const buf = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
638
|
-
port.postMessage(buf);
|
|
639
|
-
};
|
|
640
|
-
return { duplex: { readable, writable }, rawWrite };
|
|
641
|
-
}
|
|
642
574
|
function hasProtocolSessionFactory(db) {
|
|
643
575
|
return (typeof db.createProtocolSession === 'function');
|
|
644
576
|
}
|
|
@@ -660,6 +592,8 @@ function createConnectionContext(ctx) {
|
|
|
660
592
|
};
|
|
661
593
|
}
|
|
662
594
|
export async function createBrowserProxy(dbInput, config) {
|
|
595
|
+
const debugWire = config.debugWire === true;
|
|
596
|
+
const isDebugWire = () => debugWire;
|
|
663
597
|
// normalize input: single PGlite instance = use it for all databases (backwards compat for tests)
|
|
664
598
|
const instances = 'postgres' in dbInput
|
|
665
599
|
? dbInput
|
|
@@ -767,16 +701,39 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
767
701
|
signalPending = true;
|
|
768
702
|
queueMicrotask(() => {
|
|
769
703
|
signalPending = false;
|
|
770
|
-
signalReplicationChange();
|
|
704
|
+
signalReplicationChange(config.instanceId);
|
|
771
705
|
});
|
|
772
706
|
}
|
|
773
707
|
let closed = false;
|
|
708
|
+
let closePromise = null;
|
|
774
709
|
const activeConnectionClosers = new Set();
|
|
710
|
+
const unreportedConnectionCleanupErrors = new Set();
|
|
711
|
+
let abortPreviousReplication = null;
|
|
712
|
+
const activeReplicationTasks = new Set();
|
|
713
|
+
const replicationRuntime = {
|
|
714
|
+
replace(abort) {
|
|
715
|
+
abortPreviousReplication?.();
|
|
716
|
+
abortPreviousReplication = abort;
|
|
717
|
+
},
|
|
718
|
+
track(promise, abort, onError) {
|
|
719
|
+
let tracked;
|
|
720
|
+
tracked = promise.catch(onError).finally(() => {
|
|
721
|
+
activeReplicationTasks.delete(tracked);
|
|
722
|
+
if (abortPreviousReplication === abort)
|
|
723
|
+
abortPreviousReplication = null;
|
|
724
|
+
});
|
|
725
|
+
activeReplicationTasks.add(tracked);
|
|
726
|
+
},
|
|
727
|
+
};
|
|
775
728
|
function handleConnection(port) {
|
|
776
729
|
if (closed) {
|
|
777
730
|
port.close();
|
|
778
731
|
return;
|
|
779
732
|
}
|
|
733
|
+
const closePendingConnection = async () => {
|
|
734
|
+
port.close();
|
|
735
|
+
};
|
|
736
|
+
activeConnectionClosers.add(closePendingConnection);
|
|
780
737
|
port.start();
|
|
781
738
|
// peek at the first message to detect replication connections.
|
|
782
739
|
// replication connections bypass pg-gateway entirely and are handled
|
|
@@ -786,6 +743,10 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
786
743
|
const buffered = [];
|
|
787
744
|
let handlerInstalled = false;
|
|
788
745
|
port.onmessage = (ev) => {
|
|
746
|
+
if (closed) {
|
|
747
|
+
port.close();
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
789
750
|
if (handlerInstalled)
|
|
790
751
|
return; // shouldn't happen — handler replaced port.onmessage
|
|
791
752
|
buffered.push(ev);
|
|
@@ -795,6 +756,7 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
795
756
|
if (!(data instanceof Uint8Array) || data.length < 8) {
|
|
796
757
|
handlerInstalled = true;
|
|
797
758
|
handleRegularConnection(port, ev);
|
|
759
|
+
activeConnectionClosers.delete(closePendingConnection);
|
|
798
760
|
// flush buffered messages to the new handler
|
|
799
761
|
for (let i = 1; i < buffered.length; i++) {
|
|
800
762
|
port.onmessage?.(buffered[i]);
|
|
@@ -813,6 +775,7 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
813
775
|
// in browser Web Workers (same root cause as patches #9, #18, #20).
|
|
814
776
|
handleRawConnection(port, data, params, getDbContext(dbName), isRepl);
|
|
815
777
|
handlerInstalled = true;
|
|
778
|
+
activeConnectionClosers.delete(closePendingConnection);
|
|
816
779
|
// flush any messages that arrived while we were processing the startup
|
|
817
780
|
for (let i = 1; i < buffered.length; i++) {
|
|
818
781
|
if (port.onmessage) {
|
|
@@ -855,43 +818,53 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
855
818
|
const connId = {};
|
|
856
819
|
const dbName = params.database || 'postgres';
|
|
857
820
|
let connClosed = false;
|
|
858
|
-
let
|
|
859
|
-
const closeSession =
|
|
860
|
-
if (
|
|
861
|
-
return;
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
821
|
+
let closeSessionPromise = null;
|
|
822
|
+
const closeSession = () => {
|
|
823
|
+
if (closeSessionPromise)
|
|
824
|
+
return closeSessionPromise;
|
|
825
|
+
connClosed = true;
|
|
826
|
+
closeSessionPromise = (async () => {
|
|
827
|
+
port.close();
|
|
828
|
+
// a connection torn down mid-pipeline still OWNS the db mutex. a dead
|
|
829
|
+
// pipeline can never send its Sync, so without this release the mutex
|
|
830
|
+
// is leaked forever and every later connection on this db (including
|
|
831
|
+
// the next embed generation after an idle-hibernation teardown) blocks
|
|
832
|
+
// in mutex.acquire() before its first statement. when a statement is
|
|
833
|
+
// still executing, the pipeline continuation performs this cleanup
|
|
834
|
+
// after it settles instead (pipelineExecInFlight).
|
|
835
|
+
if (pipelineExecInFlight)
|
|
836
|
+
await pipelineIdlePromise;
|
|
837
|
+
if (pipelineMutexHeld) {
|
|
838
|
+
pipelineMutexHeld = false;
|
|
839
|
+
try {
|
|
840
|
+
await rollbackOwnTransaction();
|
|
841
|
+
}
|
|
842
|
+
finally {
|
|
843
|
+
mutex.release();
|
|
844
|
+
}
|
|
875
845
|
}
|
|
876
|
-
|
|
877
|
-
mutex.
|
|
846
|
+
else {
|
|
847
|
+
await mutex.acquire();
|
|
848
|
+
try {
|
|
849
|
+
await rollbackOwnTransaction();
|
|
850
|
+
}
|
|
851
|
+
finally {
|
|
852
|
+
mutex.release();
|
|
853
|
+
}
|
|
878
854
|
}
|
|
879
|
-
}
|
|
880
|
-
try {
|
|
881
855
|
await scopedContext.close();
|
|
882
|
-
}
|
|
883
|
-
|
|
856
|
+
})();
|
|
857
|
+
void closeSessionPromise.then(() => activeConnectionClosers.delete(closeSession), (error) => {
|
|
858
|
+
activeConnectionClosers.delete(closeSession);
|
|
859
|
+
unreportedConnectionCleanupErrors.add(error);
|
|
860
|
+
});
|
|
861
|
+
return closeSessionPromise;
|
|
884
862
|
};
|
|
885
863
|
activeConnectionClosers.add(closeSession);
|
|
886
864
|
// roll back this connection's open transaction. caller must hold the mutex.
|
|
887
865
|
const rollbackOwnTransaction = async () => {
|
|
888
866
|
if (txState.owner === connId && txState.status !== 0x49) {
|
|
889
|
-
|
|
890
|
-
await db.exec('ROLLBACK');
|
|
891
|
-
}
|
|
892
|
-
catch {
|
|
893
|
-
// db is closed or rollback failed — ignore
|
|
894
|
-
}
|
|
867
|
+
await db.exec('ROLLBACK');
|
|
895
868
|
txState.status = 0x49;
|
|
896
869
|
txState.owner = null;
|
|
897
870
|
}
|
|
@@ -959,6 +932,18 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
959
932
|
// statement currently executing inside the held pipeline — closeSession
|
|
960
933
|
// must not release the mutex under it (see closeSession above)
|
|
961
934
|
let pipelineExecInFlight = false;
|
|
935
|
+
let resolvePipelineIdle = () => { };
|
|
936
|
+
let pipelineIdlePromise = Promise.resolve();
|
|
937
|
+
const beginPipelineExec = () => {
|
|
938
|
+
pipelineExecInFlight = true;
|
|
939
|
+
pipelineIdlePromise = new Promise((resolve) => {
|
|
940
|
+
resolvePipelineIdle = resolve;
|
|
941
|
+
});
|
|
942
|
+
};
|
|
943
|
+
const endPipelineExec = () => {
|
|
944
|
+
pipelineExecInFlight = false;
|
|
945
|
+
resolvePipelineIdle();
|
|
946
|
+
};
|
|
962
947
|
let extWritePending = false;
|
|
963
948
|
let pipelineBuffer = [];
|
|
964
949
|
// remember the most recent Parse / SimpleQuery for diagnostic logging when
|
|
@@ -1029,9 +1014,15 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
1029
1014
|
.replace(/\0$/, '');
|
|
1030
1015
|
const upper = query.trim().toUpperCase();
|
|
1031
1016
|
if (upper.startsWith('START_REPLICATION')) {
|
|
1032
|
-
if (abortPreviousReplication)
|
|
1033
|
-
abortPreviousReplication();
|
|
1034
1017
|
let aborted = false;
|
|
1018
|
+
const abort = () => {
|
|
1019
|
+
if (aborted)
|
|
1020
|
+
return;
|
|
1021
|
+
aborted = true;
|
|
1022
|
+
connClosed = true;
|
|
1023
|
+
port.close();
|
|
1024
|
+
void closeSession();
|
|
1025
|
+
};
|
|
1035
1026
|
const writer = {
|
|
1036
1027
|
write(chunk) {
|
|
1037
1028
|
if (!connClosed && !aborted) {
|
|
@@ -1047,20 +1038,15 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
1047
1038
|
return connClosed || aborted;
|
|
1048
1039
|
},
|
|
1049
1040
|
close() {
|
|
1050
|
-
|
|
1041
|
+
abort();
|
|
1051
1042
|
},
|
|
1052
1043
|
};
|
|
1053
|
-
|
|
1054
|
-
aborted = true;
|
|
1055
|
-
connClosed = true;
|
|
1056
|
-
port.close();
|
|
1057
|
-
void closeSession();
|
|
1058
|
-
};
|
|
1044
|
+
replicationRuntime.replace(abort);
|
|
1059
1045
|
// client→server traffic on a replication connection carries
|
|
1060
1046
|
// standby status updates: the consumer's confirmed-flush LSN
|
|
1061
1047
|
// drives purging of _zero_changes (rows are retained until the
|
|
1062
1048
|
// consumer durably commits them, like real pg WAL retention).
|
|
1063
|
-
const feedbackParser = createReplicationFeedbackParser();
|
|
1049
|
+
const feedbackParser = createReplicationFeedbackParser((lsn) => noteConfirmedFlushLsn(lsn, config.instanceId));
|
|
1064
1050
|
port.onmessage = (ev) => {
|
|
1065
1051
|
const incoming = ev.data instanceof ArrayBuffer
|
|
1066
1052
|
? new Uint8Array(ev.data)
|
|
@@ -1068,7 +1054,7 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
1068
1054
|
if (incoming instanceof Uint8Array)
|
|
1069
1055
|
feedbackParser(incoming);
|
|
1070
1056
|
};
|
|
1071
|
-
handleStartReplication(query, writer, db, transactionAwareMutex(db, mutex, txState, null))
|
|
1057
|
+
replicationRuntime.track(handleStartReplication(query, writer, db, transactionAwareMutex(db, mutex, txState, null), config.instanceId), abort, () => { });
|
|
1072
1058
|
return;
|
|
1073
1059
|
}
|
|
1074
1060
|
// replication queries (IDENTIFY_SYSTEM, SET ..., CREATE/DROP SLOT).
|
|
@@ -1086,7 +1072,7 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
1086
1072
|
// open tx and commits with it.
|
|
1087
1073
|
await mutex.acquire();
|
|
1088
1074
|
try {
|
|
1089
|
-
const response = await handleReplicationQuery(query, db);
|
|
1075
|
+
const response = await handleReplicationQuery(query, db, config.instanceId);
|
|
1090
1076
|
if (response) {
|
|
1091
1077
|
write(response);
|
|
1092
1078
|
return;
|
|
@@ -1163,7 +1149,7 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
1163
1149
|
const combined = concatUint8Arrays(pipelineBuffer);
|
|
1164
1150
|
pipelineBuffer = [];
|
|
1165
1151
|
let flushResult;
|
|
1166
|
-
|
|
1152
|
+
beginPipelineExec();
|
|
1167
1153
|
try {
|
|
1168
1154
|
flushResult = await db.execProtocolRaw(combined, { syncToFs: false });
|
|
1169
1155
|
}
|
|
@@ -1174,19 +1160,7 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
1174
1160
|
return;
|
|
1175
1161
|
}
|
|
1176
1162
|
finally {
|
|
1177
|
-
|
|
1178
|
-
}
|
|
1179
|
-
if (sessionClosed && pipelineMutexHeld) {
|
|
1180
|
-
// session torn down while this statement was executing —
|
|
1181
|
-
// closeSession skipped its cleanup, finish it here
|
|
1182
|
-
pipelineMutexHeld = false;
|
|
1183
|
-
try {
|
|
1184
|
-
await rollbackOwnTransaction();
|
|
1185
|
-
}
|
|
1186
|
-
finally {
|
|
1187
|
-
mutex.release();
|
|
1188
|
-
}
|
|
1189
|
-
return;
|
|
1163
|
+
endPipelineExec();
|
|
1190
1164
|
}
|
|
1191
1165
|
flushResult = stripResponseMessages(flushResult, true);
|
|
1192
1166
|
write(flushResult);
|
|
@@ -1199,7 +1173,7 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
1199
1173
|
pipelineBuffer = [];
|
|
1200
1174
|
let result;
|
|
1201
1175
|
const t0 = performance.now();
|
|
1202
|
-
|
|
1176
|
+
beginPipelineExec();
|
|
1203
1177
|
try {
|
|
1204
1178
|
result = await db.execProtocolRaw(combined, { syncToFs: false });
|
|
1205
1179
|
}
|
|
@@ -1210,7 +1184,7 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
1210
1184
|
return;
|
|
1211
1185
|
}
|
|
1212
1186
|
finally {
|
|
1213
|
-
|
|
1187
|
+
endPipelineExec();
|
|
1214
1188
|
}
|
|
1215
1189
|
const dt = performance.now() - t0;
|
|
1216
1190
|
if (isDebugWire() && dt > 100)
|
|
@@ -1290,8 +1264,9 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
1290
1264
|
// signal writes
|
|
1291
1265
|
if (dbName === 'postgres' && msgType === 0x51) {
|
|
1292
1266
|
const qn = extractQueryText(data)?.trimStart().toLowerCase();
|
|
1293
|
-
if (qn && isWriteNormalized(qn))
|
|
1294
|
-
signalReplicationChange();
|
|
1267
|
+
if (qn && isWriteNormalized(qn)) {
|
|
1268
|
+
signalReplicationChange(config.instanceId);
|
|
1269
|
+
}
|
|
1295
1270
|
}
|
|
1296
1271
|
write(result);
|
|
1297
1272
|
}
|
|
@@ -1327,17 +1302,25 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
1327
1302
|
// not release the mutex under it; the pipeline continuation finishes the
|
|
1328
1303
|
// cleanup after the statement settles.
|
|
1329
1304
|
let pipelineExecInFlight = false;
|
|
1305
|
+
let resolvePipelineIdle = () => { };
|
|
1306
|
+
let pipelineIdlePromise = Promise.resolve();
|
|
1307
|
+
const beginPipelineExec = () => {
|
|
1308
|
+
pipelineExecInFlight = true;
|
|
1309
|
+
pipelineIdlePromise = new Promise((resolve) => {
|
|
1310
|
+
resolvePipelineIdle = resolve;
|
|
1311
|
+
});
|
|
1312
|
+
};
|
|
1313
|
+
const endPipelineExec = () => {
|
|
1314
|
+
pipelineExecInFlight = false;
|
|
1315
|
+
resolvePipelineIdle();
|
|
1316
|
+
};
|
|
1330
1317
|
// connection closed flag
|
|
1331
1318
|
let connClosed = false;
|
|
1319
|
+
let cleanupPromise = null;
|
|
1332
1320
|
// roll back this connection's open transaction. caller must hold the mutex.
|
|
1333
1321
|
const rollbackOwnTransaction = async (db, txState) => {
|
|
1334
1322
|
if (txState.owner === connId && txState.status !== 0x49) {
|
|
1335
|
-
|
|
1336
|
-
await db.exec('ROLLBACK');
|
|
1337
|
-
}
|
|
1338
|
-
catch {
|
|
1339
|
-
// db is closed or rollback failed — ignore
|
|
1340
|
-
}
|
|
1323
|
+
await db.exec('ROLLBACK');
|
|
1341
1324
|
txState.status = 0x49;
|
|
1342
1325
|
txState.owner = null;
|
|
1343
1326
|
}
|
|
@@ -1350,32 +1333,30 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
1350
1333
|
// pollution that 2eb1873 fixed on the node side. browser cleanup was
|
|
1351
1334
|
// missed and showed up as "current transaction is aborted, commands
|
|
1352
1335
|
// ignored" when a foreign connection closed mid-someone-else's-txn.
|
|
1353
|
-
const cleanup =
|
|
1354
|
-
if (
|
|
1355
|
-
return;
|
|
1336
|
+
const cleanup = () => {
|
|
1337
|
+
if (cleanupPromise)
|
|
1338
|
+
return cleanupPromise;
|
|
1356
1339
|
connClosed = true;
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1340
|
+
cleanupPromise = (async () => {
|
|
1341
|
+
port.close();
|
|
1342
|
+
// replication connections don't own a transaction — skip ROLLBACK
|
|
1343
|
+
if (isReplicationConnection)
|
|
1344
|
+
return;
|
|
1362
1345
|
const { db, mutex, txState } = getDbContext(dbName);
|
|
1346
|
+
if (pipelineExecInFlight)
|
|
1347
|
+
await pipelineIdlePromise;
|
|
1363
1348
|
if (pipelineMutexHeld) {
|
|
1364
1349
|
// this connection died mid-pipeline and still OWNS the mutex. a
|
|
1365
1350
|
// dead pipeline can never send its Sync, so without this release
|
|
1366
1351
|
// the mutex is leaked forever and every later connection on this
|
|
1367
1352
|
// db (including the next embed generation after idle-hibernation
|
|
1368
1353
|
// teardown) blocks in mutex.acquire() before its first statement.
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
}
|
|
1376
|
-
finally {
|
|
1377
|
-
mutex.release();
|
|
1378
|
-
}
|
|
1354
|
+
pipelineMutexHeld = false;
|
|
1355
|
+
try {
|
|
1356
|
+
await rollbackOwnTransaction(db, txState);
|
|
1357
|
+
}
|
|
1358
|
+
finally {
|
|
1359
|
+
mutex.release();
|
|
1379
1360
|
}
|
|
1380
1361
|
return;
|
|
1381
1362
|
}
|
|
@@ -1386,10 +1367,12 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
1386
1367
|
finally {
|
|
1387
1368
|
mutex.release();
|
|
1388
1369
|
}
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1370
|
+
})();
|
|
1371
|
+
void cleanupPromise.then(() => activeConnectionClosers.delete(cleanup), (error) => {
|
|
1372
|
+
activeConnectionClosers.delete(cleanup);
|
|
1373
|
+
unreportedConnectionCleanupErrors.add(error);
|
|
1374
|
+
});
|
|
1375
|
+
return cleanupPromise;
|
|
1393
1376
|
};
|
|
1394
1377
|
activeConnectionClosers.add(cleanup);
|
|
1395
1378
|
try {
|
|
@@ -1452,7 +1435,7 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
1452
1435
|
return handleReplicationMessageBrowser(data, rawWrite, () => connClosed, () => {
|
|
1453
1436
|
connClosed = true;
|
|
1454
1437
|
port.close();
|
|
1455
|
-
}, instances.postgres, mutexes.postgres, connection);
|
|
1438
|
+
}, instances.postgres, mutexes.postgres, connection, config.instanceId, replicationRuntime, debugWire);
|
|
1456
1439
|
}
|
|
1457
1440
|
const msgType = data[0];
|
|
1458
1441
|
const { db, mutex, txState } = getDbContext(dbName);
|
|
@@ -1486,7 +1469,7 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
1486
1469
|
data = interceptQuery(data);
|
|
1487
1470
|
const t1 = performance.now();
|
|
1488
1471
|
let result;
|
|
1489
|
-
|
|
1472
|
+
beginPipelineExec();
|
|
1490
1473
|
try {
|
|
1491
1474
|
result = await db.execProtocolRaw(data, { syncToFs: false });
|
|
1492
1475
|
}
|
|
@@ -1496,7 +1479,7 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
1496
1479
|
throw err;
|
|
1497
1480
|
}
|
|
1498
1481
|
finally {
|
|
1499
|
-
|
|
1482
|
+
endPipelineExec();
|
|
1500
1483
|
}
|
|
1501
1484
|
const t2 = performance.now();
|
|
1502
1485
|
proxyStats.totalExecMs += t2 - t1;
|
|
@@ -1518,18 +1501,6 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
1518
1501
|
signalWrite();
|
|
1519
1502
|
}
|
|
1520
1503
|
}
|
|
1521
|
-
else if (connClosed) {
|
|
1522
|
-
// the connection died while this statement was executing, so
|
|
1523
|
-
// cleanup skipped its release (pipelineExecInFlight). no Sync
|
|
1524
|
-
// can ever arrive — roll back and release here instead.
|
|
1525
|
-
pipelineMutexHeld = false;
|
|
1526
|
-
try {
|
|
1527
|
-
await rollbackOwnTransaction(db, txState);
|
|
1528
|
-
}
|
|
1529
|
-
finally {
|
|
1530
|
-
mutex.release();
|
|
1531
|
-
}
|
|
1532
|
-
}
|
|
1533
1504
|
else {
|
|
1534
1505
|
// strip ReadyForQuery from non-Sync pipeline messages
|
|
1535
1506
|
// result = stripResponseMessages(result, true) // disabled for debugging
|
|
@@ -1627,7 +1598,7 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
1627
1598
|
result = stripResponseMessages(result, stripRfq);
|
|
1628
1599
|
// signal replication handler on postgres writes for instant sync
|
|
1629
1600
|
if (dbName === 'postgres' && queryNorm && isWriteNormalized(queryNorm)) {
|
|
1630
|
-
signalReplicationChange();
|
|
1601
|
+
signalReplicationChange(config.instanceId);
|
|
1631
1602
|
}
|
|
1632
1603
|
return result;
|
|
1633
1604
|
},
|
|
@@ -1707,11 +1678,59 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
1707
1678
|
txState.owner = owner;
|
|
1708
1679
|
}
|
|
1709
1680
|
const defaultExternalOwner = {};
|
|
1681
|
+
const closedExternalOwners = new WeakSet();
|
|
1682
|
+
function createExternalOwnerCloser(owner, dbNames) {
|
|
1683
|
+
let closing = null;
|
|
1684
|
+
const closeOwner = () => {
|
|
1685
|
+
if (closing)
|
|
1686
|
+
return closing;
|
|
1687
|
+
closedExternalOwners.add(owner);
|
|
1688
|
+
closing = (async () => {
|
|
1689
|
+
const contexts = new Map();
|
|
1690
|
+
for (const dbName of dbNames) {
|
|
1691
|
+
const context = getDbContext(dbName);
|
|
1692
|
+
contexts.set(context.txState, context);
|
|
1693
|
+
}
|
|
1694
|
+
const results = await Promise.allSettled([...contexts.values()].map(async ({ db, mutex, txState }) => {
|
|
1695
|
+
await mutex.acquire();
|
|
1696
|
+
try {
|
|
1697
|
+
if (txState.owner === owner && txState.status !== 0x49) {
|
|
1698
|
+
await db.exec('ROLLBACK');
|
|
1699
|
+
txState.status = 0x49;
|
|
1700
|
+
txState.owner = null;
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
finally {
|
|
1704
|
+
mutex.release();
|
|
1705
|
+
}
|
|
1706
|
+
}));
|
|
1707
|
+
const errors = results.flatMap((result) => result.status === 'rejected' ? [result.reason] : []);
|
|
1708
|
+
if (errors.length === 1)
|
|
1709
|
+
throw errors[0];
|
|
1710
|
+
if (errors.length > 1) {
|
|
1711
|
+
throw new AggregateError(errors, 'browser proxy external cleanup failed');
|
|
1712
|
+
}
|
|
1713
|
+
})();
|
|
1714
|
+
void closing.then(() => activeConnectionClosers.delete(closeOwner), (error) => {
|
|
1715
|
+
activeConnectionClosers.delete(closeOwner);
|
|
1716
|
+
unreportedConnectionCleanupErrors.add(error);
|
|
1717
|
+
});
|
|
1718
|
+
return closing;
|
|
1719
|
+
};
|
|
1720
|
+
activeConnectionClosers.add(closeOwner);
|
|
1721
|
+
return closeOwner;
|
|
1722
|
+
}
|
|
1723
|
+
createExternalOwnerCloser(defaultExternalOwner, ['postgres', 'zero_cvr', 'zero_cdb']);
|
|
1710
1724
|
async function externalQuery(dbName, sql, params, owner = defaultExternalOwner) {
|
|
1711
|
-
if (closed)
|
|
1725
|
+
if (closed || closedExternalOwners.has(owner)) {
|
|
1712
1726
|
throw new Error('pg-proxy is closed');
|
|
1727
|
+
}
|
|
1713
1728
|
const { db, mutex, txState } = getDbContext(dbName);
|
|
1714
1729
|
await acquireForOwner(db, mutex, txState, owner);
|
|
1730
|
+
if (closed || closedExternalOwners.has(owner)) {
|
|
1731
|
+
mutex.release();
|
|
1732
|
+
throw new Error('pg-proxy is closed');
|
|
1733
|
+
}
|
|
1715
1734
|
try {
|
|
1716
1735
|
const result = await db.query(sql, params);
|
|
1717
1736
|
updateExternalTxStateAfterSuccess(txState, owner, sql);
|
|
@@ -1730,10 +1749,15 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
1730
1749
|
}
|
|
1731
1750
|
}
|
|
1732
1751
|
async function externalExec(dbName, sql, owner = defaultExternalOwner) {
|
|
1733
|
-
if (closed)
|
|
1752
|
+
if (closed || closedExternalOwners.has(owner)) {
|
|
1734
1753
|
throw new Error('pg-proxy is closed');
|
|
1754
|
+
}
|
|
1735
1755
|
const { db, mutex, txState } = getDbContext(dbName);
|
|
1736
1756
|
await acquireForOwner(db, mutex, txState, owner);
|
|
1757
|
+
if (closed || closedExternalOwners.has(owner)) {
|
|
1758
|
+
mutex.release();
|
|
1759
|
+
throw new Error('pg-proxy is closed');
|
|
1760
|
+
}
|
|
1737
1761
|
try {
|
|
1738
1762
|
const result = await db.exec(sql);
|
|
1739
1763
|
updateExternalTxStateAfterSuccess(txState, owner, sql);
|
|
@@ -1752,59 +1776,68 @@ export async function createBrowserProxy(dbInput, config) {
|
|
|
1752
1776
|
}
|
|
1753
1777
|
function createExternalSession(dbName = 'postgres') {
|
|
1754
1778
|
const owner = {};
|
|
1779
|
+
let sessionClosed = false;
|
|
1780
|
+
const closeOwner = createExternalOwnerCloser(owner, [dbName]);
|
|
1755
1781
|
return {
|
|
1756
1782
|
query(sql, params) {
|
|
1783
|
+
if (sessionClosed)
|
|
1784
|
+
return Promise.reject(new Error('pg-proxy session is closed'));
|
|
1757
1785
|
return externalQuery(dbName, sql, params, owner);
|
|
1758
1786
|
},
|
|
1759
1787
|
exec(sql) {
|
|
1788
|
+
if (sessionClosed)
|
|
1789
|
+
return Promise.reject(new Error('pg-proxy session is closed'));
|
|
1760
1790
|
return externalExec(dbName, sql, owner);
|
|
1761
1791
|
},
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
try {
|
|
1766
|
-
if (txState.owner === owner && txState.status !== 0x49) {
|
|
1767
|
-
try {
|
|
1768
|
-
await db.exec('ROLLBACK');
|
|
1769
|
-
}
|
|
1770
|
-
catch { }
|
|
1771
|
-
txState.status = 0x49;
|
|
1772
|
-
txState.owner = null;
|
|
1773
|
-
}
|
|
1774
|
-
}
|
|
1775
|
-
finally {
|
|
1776
|
-
mutex.release();
|
|
1777
|
-
}
|
|
1792
|
+
close() {
|
|
1793
|
+
sessionClosed = true;
|
|
1794
|
+
return closeOwner();
|
|
1778
1795
|
},
|
|
1779
1796
|
};
|
|
1780
1797
|
}
|
|
1781
1798
|
return {
|
|
1782
1799
|
handleConnection,
|
|
1783
1800
|
close() {
|
|
1801
|
+
if (closePromise)
|
|
1802
|
+
return closePromise;
|
|
1784
1803
|
closed = true;
|
|
1785
1804
|
signalPending = false;
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1805
|
+
closePromise = (async () => {
|
|
1806
|
+
abortPreviousReplication?.();
|
|
1807
|
+
abortPreviousReplication = null;
|
|
1808
|
+
signalReplicationChange(config.instanceId);
|
|
1809
|
+
const results = await Promise.allSettled([...activeConnectionClosers].map((closeConnection) => Promise.resolve().then(() => closeConnection())));
|
|
1810
|
+
const errors = new Set(unreportedConnectionCleanupErrors);
|
|
1811
|
+
for (const result of results) {
|
|
1812
|
+
if (result.status === 'rejected')
|
|
1813
|
+
errors.add(result.reason);
|
|
1814
|
+
}
|
|
1815
|
+
await Promise.all([...activeReplicationTasks]);
|
|
1816
|
+
unreportedConnectionCleanupErrors.clear();
|
|
1817
|
+
if (errors.size > 0) {
|
|
1818
|
+
throw new AggregateError([...errors], 'browser proxy connection cleanup failed');
|
|
1819
|
+
}
|
|
1820
|
+
})();
|
|
1821
|
+
return closePromise;
|
|
1789
1822
|
},
|
|
1790
1823
|
createExternalSession,
|
|
1791
1824
|
query: externalQuery,
|
|
1792
1825
|
exec: externalExec,
|
|
1793
1826
|
};
|
|
1794
1827
|
}
|
|
1795
|
-
async function handleReplicationMessageBrowser(data, rawWrite, isClosed, closeConn, db, mutex, connection) {
|
|
1796
|
-
if (
|
|
1828
|
+
async function handleReplicationMessageBrowser(data, rawWrite, isClosed, closeConn, db, mutex, connection, instanceId, replicationRuntime, debugWire) {
|
|
1829
|
+
if (debugWire) {
|
|
1797
1830
|
console.debug(`[pg-proxy-repl] ENTRY type=0x${data[0].toString(16)} len=${data.length}`);
|
|
1798
1831
|
}
|
|
1799
1832
|
// for non-SimpleQuery messages (extended protocol), execute against PGlite directly.
|
|
1800
1833
|
if (data[0] !== 0x51) {
|
|
1801
|
-
if (
|
|
1834
|
+
if (debugWire) {
|
|
1802
1835
|
console.debug(`[pg-proxy-repl] ext protocol msg type=0x${data[0].toString(16)} len=${data.length}`);
|
|
1803
1836
|
}
|
|
1804
1837
|
await mutex.acquire();
|
|
1805
1838
|
try {
|
|
1806
1839
|
const result = await db.execProtocolRaw(data, { syncToFs: false });
|
|
1807
|
-
if (
|
|
1840
|
+
if (debugWire) {
|
|
1808
1841
|
console.debug(`[pg-proxy-repl] ext protocol result len=${result.length}`);
|
|
1809
1842
|
}
|
|
1810
1843
|
return result;
|
|
@@ -1820,11 +1853,6 @@ async function handleReplicationMessageBrowser(data, rawWrite, isClosed, closeCo
|
|
|
1820
1853
|
// check if this is a START_REPLICATION command
|
|
1821
1854
|
if (upper.startsWith('START_REPLICATION')) {
|
|
1822
1855
|
await connection.detach();
|
|
1823
|
-
// abort any previous replication handler to prevent zombies
|
|
1824
|
-
if (abortPreviousReplication) {
|
|
1825
|
-
log.proxy('aborting previous replication handler');
|
|
1826
|
-
abortPreviousReplication();
|
|
1827
|
-
}
|
|
1828
1856
|
let aborted = false;
|
|
1829
1857
|
const writer = {
|
|
1830
1858
|
write(chunk) {
|
|
@@ -1846,30 +1874,32 @@ async function handleReplicationMessageBrowser(data, rawWrite, isClosed, closeCo
|
|
|
1846
1874
|
},
|
|
1847
1875
|
};
|
|
1848
1876
|
const abort = () => {
|
|
1877
|
+
if (aborted)
|
|
1878
|
+
return;
|
|
1849
1879
|
aborted = true;
|
|
1850
1880
|
closeConn();
|
|
1851
1881
|
};
|
|
1852
|
-
|
|
1853
|
-
handleStartReplication(query, writer, db, mutex)
|
|
1882
|
+
replicationRuntime.replace(abort);
|
|
1883
|
+
replicationRuntime.track(handleStartReplication(query, writer, db, mutex, instanceId), abort, (err) => {
|
|
1854
1884
|
log.proxy(`replication stream ended: ${err}`);
|
|
1855
1885
|
});
|
|
1856
1886
|
return undefined;
|
|
1857
1887
|
}
|
|
1858
1888
|
// handle replication queries + fallthrough to pglite, all under mutex
|
|
1859
|
-
if (
|
|
1889
|
+
if (debugWire) {
|
|
1860
1890
|
console.debug(`[pg-proxy-repl] query: ${query.slice(0, 100)}`);
|
|
1861
1891
|
console.debug(`[pg-proxy-repl] acquiring mutex...`);
|
|
1862
1892
|
}
|
|
1863
1893
|
await mutex.acquire();
|
|
1864
|
-
if (
|
|
1894
|
+
if (debugWire)
|
|
1865
1895
|
console.debug(`[pg-proxy-repl] mutex acquired, testing db access...`);
|
|
1866
1896
|
try {
|
|
1867
1897
|
const testResult = await db.query('SELECT 1 as test');
|
|
1868
|
-
if (
|
|
1898
|
+
if (debugWire) {
|
|
1869
1899
|
console.debug(`[pg-proxy-repl] db.query works: ${JSON.stringify(testResult.rows)}`);
|
|
1870
1900
|
}
|
|
1871
|
-
const response = await handleReplicationQuery(query, db);
|
|
1872
|
-
if (
|
|
1901
|
+
const response = await handleReplicationQuery(query, db, instanceId);
|
|
1902
|
+
if (debugWire) {
|
|
1873
1903
|
console.debug(`[pg-proxy-repl] handleReplicationQuery result: ${response ? 'bytes(' + response.length + ')' : 'null'}`);
|
|
1874
1904
|
}
|
|
1875
1905
|
if (response)
|