@rocicorp/zero 1.0.0 → 1.1.0
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/out/_virtual/{_@oxc-project_runtime@0.115.0 → _@oxc-project_runtime@0.122.0}/helpers/usingCtx.js +1 -1
- package/out/analyze-query/src/bin-analyze.js +19 -7
- package/out/analyze-query/src/bin-analyze.js.map +1 -1
- package/out/replicache/src/mutation-recovery.js +0 -3
- package/out/zero/package.js +7 -6
- package/out/zero/package.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +6 -0
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +12 -0
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/server/anonymous-otel-start.d.ts.map +1 -1
- package/out/zero-cache/src/server/anonymous-otel-start.js +1 -14
- package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
- package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +2 -2
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/services/analyze.js +2 -2
- package/out/zero-cache/src/services/change-source/change-source.d.ts +7 -0
- package/out/zero-cache/src/services/change-source/change-source.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js +1 -1
- package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +3 -0
- package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.d.ts +9 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.js +172 -46
- package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/lsn.js +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +8 -0
- package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/status.d.ts +26 -1
- package/out/zero-cache/src/services/change-source/protocol/current/status.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/status.js +7 -2
- package/out/zero-cache/src/services/change-source/protocol/current/status.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts +8 -0
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +10 -2
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +25 -0
- package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.js +8 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/forwarder.d.ts +2 -0
- package/out/zero-cache/src/services/change-streamer/forwarder.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/forwarder.js +3 -0
- package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +3 -2
- package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.js +17 -8
- package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
- package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
- package/out/zero-cache/src/services/life-cycle.js +6 -2
- package/out/zero-cache/src/services/life-cycle.js.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.d.ts +2 -2
- package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.js +19 -4
- package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
- package/out/zero-cache/src/services/replicator/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/replicator.js +2 -2
- package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
- package/out/zero-cache/src/services/replicator/reporter/recorder.d.ts +12 -0
- package/out/zero-cache/src/services/replicator/reporter/recorder.d.ts.map +1 -0
- package/out/zero-cache/src/services/replicator/reporter/recorder.js +58 -0
- package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -0
- package/out/zero-cache/src/services/replicator/reporter/report-schema.d.ts +35 -0
- package/out/zero-cache/src/services/replicator/reporter/report-schema.d.ts.map +1 -0
- package/out/zero-cache/src/services/replicator/reporter/report-schema.js +20 -0
- package/out/zero-cache/src/services/replicator/reporter/report-schema.js.map +1 -0
- package/out/zero-cache/src/services/run-ast.js +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js +1 -1
- package/out/zero-cache/src/types/pg.d.ts.map +1 -1
- package/out/zero-cache/src/types/pg.js +2 -0
- package/out/zero-cache/src/types/pg.js.map +1 -1
- package/out/zero-cache/src/workers/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/workers/replicator.js +1 -0
- package/out/zero-cache/src/workers/replicator.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zql/src/builder/builder.d.ts.map +1 -1
- package/out/zql/src/builder/builder.js +15 -5
- package/out/zql/src/builder/builder.js.map +1 -1
- package/out/zql/src/ivm/cap.d.ts +32 -0
- package/out/zql/src/ivm/cap.d.ts.map +1 -0
- package/out/zql/src/ivm/cap.js +226 -0
- package/out/zql/src/ivm/cap.js.map +1 -0
- package/out/zql/src/ivm/join-utils.d.ts +2 -0
- package/out/zql/src/ivm/join-utils.d.ts.map +1 -1
- package/out/zql/src/ivm/join-utils.js +35 -1
- package/out/zql/src/ivm/join-utils.js.map +1 -1
- package/out/zql/src/ivm/join.d.ts.map +1 -1
- package/out/zql/src/ivm/join.js +6 -2
- package/out/zql/src/ivm/join.js.map +1 -1
- package/out/zql/src/ivm/memory-source.d.ts +15 -2
- package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
- package/out/zql/src/ivm/memory-source.js +69 -8
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/ivm/schema.d.ts +1 -1
- package/out/zql/src/ivm/schema.d.ts.map +1 -1
- package/out/zql/src/ivm/skip.d.ts.map +1 -1
- package/out/zql/src/ivm/skip.js +3 -0
- package/out/zql/src/ivm/skip.js.map +1 -1
- package/out/zql/src/ivm/source.d.ts +1 -1
- package/out/zql/src/ivm/source.d.ts.map +1 -1
- package/out/zql/src/ivm/take.d.ts +4 -1
- package/out/zql/src/ivm/take.d.ts.map +1 -1
- package/out/zql/src/ivm/take.js +4 -2
- package/out/zql/src/ivm/take.js.map +1 -1
- package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
- package/out/zql/src/ivm/union-fan-in.js +1 -0
- package/out/zql/src/ivm/union-fan-in.js.map +1 -1
- package/out/zqlite/src/query-builder.d.ts +1 -1
- package/out/zqlite/src/query-builder.d.ts.map +1 -1
- package/out/zqlite/src/query-builder.js +7 -2
- package/out/zqlite/src/query-builder.js.map +1 -1
- package/out/zqlite/src/table-source.d.ts +1 -1
- package/out/zqlite/src/table-source.d.ts.map +1 -1
- package/out/zqlite/src/table-source.js +15 -10
- package/out/zqlite/src/table-source.js.map +1 -1
- package/package.json +7 -6
- package/out/analyze-query/src/run-ast.d.ts +0 -22
- package/out/analyze-query/src/run-ast.d.ts.map +0 -1
- package/out/analyze-query/src/run-ast.js +0 -75
- package/out/analyze-query/src/run-ast.js.map +0 -1
- package/out/replicache/src/mutation-recovery.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { assert } from "../../../../../shared/src/asserts.js";
|
|
1
2
|
import { deepEqual } from "../../../../../shared/src/json.js";
|
|
2
|
-
import { promiseVoid } from "../../../../../shared/src/resolved-promises.js";
|
|
3
3
|
import { AbortError } from "../../../../../shared/src/abort-error.js";
|
|
4
4
|
import { sleep } from "../../../../../shared/src/sleep.js";
|
|
5
|
-
import { parse } from "../../../../../shared/src/valita.js";
|
|
5
|
+
import { parse, valita_exports } from "../../../../../shared/src/valita.js";
|
|
6
6
|
import { must } from "../../../../../shared/src/must.js";
|
|
7
7
|
import { mapValues } from "../../../../../shared/src/objects.js";
|
|
8
8
|
import { equals, intersection, symmetricDifferences } from "../../../../../shared/src/set-utils.js";
|
|
@@ -12,7 +12,7 @@ import { upstreamSchema } from "../../../types/shards.js";
|
|
|
12
12
|
import { StatementRunner } from "../../../db/statements.js";
|
|
13
13
|
import { pgClient } from "../../../types/pg.js";
|
|
14
14
|
import { majorVersionFromString, majorVersionToString } from "../../../types/state-version.js";
|
|
15
|
-
import { fromBigInt, toStateVersionString } from "./lsn.js";
|
|
15
|
+
import { fromBigInt, toBigInt, toStateVersionString } from "./lsn.js";
|
|
16
16
|
import { UnsupportedColumnDefaultError, mapPostgresToLiteColumn } from "../../../db/pg-to-lite.js";
|
|
17
17
|
import { getSubscriptionStateAndContext } from "../../replicator/schema/replication-state.js";
|
|
18
18
|
import { runTx } from "../../../db/run-transaction.js";
|
|
@@ -29,6 +29,7 @@ import { initialSync } from "./initial-sync.js";
|
|
|
29
29
|
import { streamBackfill } from "./backfill-stream.js";
|
|
30
30
|
import { subscribe } from "./logical-replication/stream.js";
|
|
31
31
|
import postgres from "postgres";
|
|
32
|
+
import { nanoid } from "nanoid";
|
|
32
33
|
import { PG_ADMIN_SHUTDOWN, PG_OBJECT_IN_USE } from "@drdgvhbh/postgres-error-codes";
|
|
33
34
|
//#region ../zero-cache/src/services/change-source/pg/change-source.ts
|
|
34
35
|
/**
|
|
@@ -36,7 +37,7 @@ import { PG_ADMIN_SHUTDOWN, PG_OBJECT_IN_USE } from "@drdgvhbh/postgres-error-co
|
|
|
36
37
|
* replica, before streaming changes from the corresponding logical replication
|
|
37
38
|
* stream.
|
|
38
39
|
*/
|
|
39
|
-
async function initializePostgresChangeSource(lc, upstreamURI, shard, replicaDbFile, syncOptions, context) {
|
|
40
|
+
async function initializePostgresChangeSource(lc, upstreamURI, shard, replicaDbFile, syncOptions, context, lagReportIntervalMs) {
|
|
40
41
|
await initReplica(lc, `replica-${shard.appID}-${shard.shardNum}`, replicaDbFile, (log, tx) => initialSync(log, shard, tx, upstreamURI, syncOptions, context));
|
|
41
42
|
const replica = new Database(lc, replicaDbFile);
|
|
42
43
|
const subscriptionState = getSubscriptionStateAndContext(new StatementRunner(replica));
|
|
@@ -45,7 +46,7 @@ async function initializePostgresChangeSource(lc, upstreamURI, shard, replicaDbF
|
|
|
45
46
|
try {
|
|
46
47
|
return {
|
|
47
48
|
subscriptionState,
|
|
48
|
-
changeSource: new PostgresChangeSource(lc, upstreamURI, shard, await checkAndUpdateUpstream(lc, db, shard, subscriptionState), context)
|
|
49
|
+
changeSource: new PostgresChangeSource(lc, upstreamURI, shard, await checkAndUpdateUpstream(lc, db, shard, subscriptionState), context, lagReportIntervalMs ?? null)
|
|
49
50
|
};
|
|
50
51
|
} finally {
|
|
51
52
|
await db.end();
|
|
@@ -83,50 +84,69 @@ var MAX_LOW_PRIORITY_DELAY_MS = 1e3;
|
|
|
83
84
|
*/
|
|
84
85
|
var PostgresChangeSource = class {
|
|
85
86
|
#lc;
|
|
87
|
+
#db;
|
|
86
88
|
#upstreamUri;
|
|
87
89
|
#shard;
|
|
88
90
|
#replica;
|
|
89
91
|
#context;
|
|
90
|
-
|
|
92
|
+
#lagReporter;
|
|
93
|
+
constructor(lc, upstreamUri, shard, replica, context, lagReportIntervalMs) {
|
|
91
94
|
this.#lc = lc.withContext("component", "change-source");
|
|
95
|
+
this.#db = pgClient(lc, upstreamUri, {
|
|
96
|
+
["idle_timeout"]: 60,
|
|
97
|
+
connection: { ["application_name"]: "zero-replication-monitor" }
|
|
98
|
+
});
|
|
92
99
|
this.#upstreamUri = upstreamUri;
|
|
93
100
|
this.#shard = shard;
|
|
94
101
|
this.#replica = replica;
|
|
95
102
|
this.#context = context;
|
|
103
|
+
this.#lagReporter = lagReportIntervalMs ? new LagReporter(lc.withContext("component", "lag-reporter"), shard, this.#db, lagReportIntervalMs) : null;
|
|
104
|
+
}
|
|
105
|
+
startLagReporter() {
|
|
106
|
+
return this.#lagReporter ? this.#lagReporter.initiateLagReport(true) : null;
|
|
96
107
|
}
|
|
97
108
|
async startStream(clientWatermark, backfillRequests = []) {
|
|
98
|
-
const db = pgClient(this.#lc, this.#upstreamUri);
|
|
99
109
|
const { slot } = this.#replica;
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
this.#lc.info?.(`starting replication stream@${slot}`);
|
|
105
|
-
return await this.#startStream(db, slot, clientWatermark, config, backfillRequests);
|
|
106
|
-
} finally {
|
|
107
|
-
cleanup.then(() => db.end());
|
|
108
|
-
}
|
|
110
|
+
await this.#stopExistingReplicationSlotSubscribers(slot);
|
|
111
|
+
const config = await getInternalShardConfig(this.#db, this.#shard);
|
|
112
|
+
this.#lc.info?.(`starting replication stream@${slot}`);
|
|
113
|
+
return this.#startStream(slot, clientWatermark, config, backfillRequests);
|
|
109
114
|
}
|
|
110
|
-
async #startStream(
|
|
115
|
+
async #startStream(slot, clientWatermark, shardConfig, backfillRequests) {
|
|
111
116
|
const clientStart = majorVersionFromString(clientWatermark) + 1n;
|
|
112
|
-
const { messages, acks } = await subscribe(this.#lc, db, slot, [...shardConfig.publications], clientStart);
|
|
117
|
+
const { messages, acks } = await subscribe(this.#lc, this.#db, slot, [...shardConfig.publications], clientStart);
|
|
113
118
|
const acker = new Acker(acks);
|
|
114
119
|
const changes = new ChangeStreamMultiplexer(this.#lc, clientWatermark);
|
|
115
120
|
const backfillManager = new BackfillManager(this.#lc, changes, (req) => streamBackfill(this.#lc, this.#upstreamUri, this.#replica, req));
|
|
116
121
|
changes.addProducers(messages, backfillManager).addListeners(backfillManager, acker);
|
|
117
122
|
backfillManager.run(clientWatermark, backfillRequests);
|
|
118
|
-
const changeMaker = new ChangeMaker(this.#lc, this.#shard, shardConfig, this.#
|
|
123
|
+
const changeMaker = new ChangeMaker(this.#lc, this.#shard, shardConfig, this.#db, this.#replica.initialSchema);
|
|
124
|
+
/**
|
|
125
|
+
* Determines if the incoming message is transactional, otherwise handling
|
|
126
|
+
* non-transactional messages with a downstream status message.
|
|
127
|
+
*/
|
|
128
|
+
const isTransactionalMessage = (lsn, msg) => {
|
|
129
|
+
if (msg.tag === "message" && msg.prefix === this.#lagReporter?.messagePrefix) {
|
|
130
|
+
changes.pushStatus(this.#lagReporter.processLagReport(msg));
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
this.#lagReporter?.checkCurrentLSN(lsn);
|
|
134
|
+
if (msg.tag === "keepalive") {
|
|
135
|
+
changes.pushStatus([
|
|
136
|
+
"status",
|
|
137
|
+
{ ack: msg.shouldRespond },
|
|
138
|
+
{ watermark: majorVersionToString(lsn) }
|
|
139
|
+
]);
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
return true;
|
|
143
|
+
};
|
|
119
144
|
(async () => {
|
|
120
145
|
try {
|
|
121
146
|
let reservation = null;
|
|
122
147
|
let inTransaction = false;
|
|
123
148
|
for await (const [lsn, msg] of messages) {
|
|
124
|
-
if (msg
|
|
125
|
-
changes.pushStatus([
|
|
126
|
-
"status",
|
|
127
|
-
{ ack: msg.shouldRespond },
|
|
128
|
-
{ watermark: majorVersionToString(lsn) }
|
|
129
|
-
]);
|
|
149
|
+
if (!isTransactionalMessage(lsn, msg)) {
|
|
130
150
|
if (!inTransaction && reservation?.lastWatermark) {
|
|
131
151
|
changes.release(reservation.lastWatermark);
|
|
132
152
|
reservation = null;
|
|
@@ -170,14 +190,11 @@ var PostgresChangeSource = class {
|
|
|
170
190
|
};
|
|
171
191
|
}
|
|
172
192
|
async #logCurrentReplicaInfo() {
|
|
173
|
-
const db = pgClient(this.#lc, this.#upstreamUri);
|
|
174
193
|
try {
|
|
175
|
-
const replica = await getReplicaAtVersion(this.#lc, db, this.#shard, this.#replica.version);
|
|
194
|
+
const replica = await getReplicaAtVersion(this.#lc, this.#db, this.#shard, this.#replica.version);
|
|
176
195
|
if (replica) this.#lc.info?.(`Shutdown signal from replica@${this.#replica.version}: ${stringify(replica.subscriberContext)}`);
|
|
177
196
|
} catch (e) {
|
|
178
197
|
this.#lc.warn?.(`error logging replica info`, e);
|
|
179
|
-
} finally {
|
|
180
|
-
await db.end();
|
|
181
198
|
}
|
|
182
199
|
}
|
|
183
200
|
/**
|
|
@@ -189,10 +206,10 @@ var PostgresChangeSource = class {
|
|
|
189
206
|
* the timestamp suffix) are preserved, as those are newly syncing replicas
|
|
190
207
|
* that will soon take over the slot.
|
|
191
208
|
*/
|
|
192
|
-
async #stopExistingReplicationSlotSubscribers(
|
|
209
|
+
async #stopExistingReplicationSlotSubscribers(slotToKeep) {
|
|
193
210
|
const slotExpression = replicationSlotExpression(this.#shard);
|
|
194
211
|
const legacySlotName = legacyReplicationSlot(this.#shard);
|
|
195
|
-
const result = await runTx(db, async (sql) => {
|
|
212
|
+
const result = await runTx(this.#db, async (sql) => {
|
|
196
213
|
const result = await sql`
|
|
197
214
|
SELECT slot_name as slot, pg_terminate_backend(active_pid) as terminated, active_pid as pid
|
|
198
215
|
FROM pg_replication_slots
|
|
@@ -230,10 +247,11 @@ var PostgresChangeSource = class {
|
|
|
230
247
|
const pids = result.filter(({ pid }) => pid !== null).map(({ pid }) => pid);
|
|
231
248
|
if (pids.length) this.#lc.info?.(`signaled subscriber ${pids} to shut down`);
|
|
232
249
|
const otherSlots = result.filter(({ slot }) => slot !== slotToKeep).map(({ slot }) => slot);
|
|
233
|
-
|
|
250
|
+
if (otherSlots.length) this.#dropReplicationSlots(otherSlots).catch((e) => this.#lc.warn?.(`error dropping replication slots`, e));
|
|
234
251
|
}
|
|
235
|
-
async #dropReplicationSlots(
|
|
252
|
+
async #dropReplicationSlots(slots) {
|
|
236
253
|
this.#lc.info?.(`dropping other replication slot(s) ${slots}`);
|
|
254
|
+
const sql = this.#db;
|
|
237
255
|
for (let i = 0; i < 5; i++) try {
|
|
238
256
|
await sql`
|
|
239
257
|
SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots
|
|
@@ -282,24 +300,132 @@ var Acker = class {
|
|
|
282
300
|
this.#acks.push(lsn);
|
|
283
301
|
}
|
|
284
302
|
};
|
|
303
|
+
var lagReportSchema = valita_exports.object({
|
|
304
|
+
id: valita_exports.string(),
|
|
305
|
+
sendTimeMs: valita_exports.number(),
|
|
306
|
+
commitTimeMs: valita_exports.number()
|
|
307
|
+
});
|
|
308
|
+
var LagReporter = class LagReporter {
|
|
309
|
+
static MESSAGE_SUFFIX = "/lag-report/v1";
|
|
310
|
+
#lc;
|
|
311
|
+
messagePrefix;
|
|
312
|
+
#db;
|
|
313
|
+
#lagIntervalMs;
|
|
314
|
+
#pgVersion;
|
|
315
|
+
#expectingLagReport = null;
|
|
316
|
+
#timer;
|
|
317
|
+
constructor(lc, shard, db, lagIntervalMs) {
|
|
318
|
+
this.#lc = lc;
|
|
319
|
+
this.messagePrefix = `${shard.appID}/${shard.shardNum}${LagReporter.MESSAGE_SUFFIX}`;
|
|
320
|
+
this.#db = db;
|
|
321
|
+
this.#lagIntervalMs = lagIntervalMs;
|
|
322
|
+
}
|
|
323
|
+
async #getPgVersion() {
|
|
324
|
+
if (this.#pgVersion === void 0) {
|
|
325
|
+
const [{ pgVersion }] = await this.#db`
|
|
326
|
+
SELECT current_setting('server_version_num')::int as "pgVersion"`;
|
|
327
|
+
this.#pgVersion = pgVersion;
|
|
328
|
+
}
|
|
329
|
+
return this.#pgVersion;
|
|
330
|
+
}
|
|
331
|
+
async initiateLagReport(log = false) {
|
|
332
|
+
const pgVersion = this.#pgVersion ?? await this.#getPgVersion();
|
|
333
|
+
const now = Date.now();
|
|
334
|
+
const id = nanoid();
|
|
335
|
+
const lagReport = {
|
|
336
|
+
id,
|
|
337
|
+
lsn: 0n
|
|
338
|
+
};
|
|
339
|
+
this.#expectingLagReport = lagReport;
|
|
340
|
+
let lsn;
|
|
341
|
+
if (pgVersion >= 17e4) [{lsn}] = await this.#db`
|
|
342
|
+
SELECT pg_logical_emit_message(
|
|
343
|
+
false,
|
|
344
|
+
${this.messagePrefix},
|
|
345
|
+
json_build_object(
|
|
346
|
+
'id', ${id}::text,
|
|
347
|
+
'sendTimeMs', ${now}::int8,
|
|
348
|
+
'commitTimeMs', extract(epoch from now()) * 1000
|
|
349
|
+
)::text,
|
|
350
|
+
true
|
|
351
|
+
) as lsn;
|
|
352
|
+
`;
|
|
353
|
+
else [{lsn}] = await this.#db`
|
|
354
|
+
SELECT pg_logical_emit_message(
|
|
355
|
+
false,
|
|
356
|
+
${this.messagePrefix},
|
|
357
|
+
json_build_object(
|
|
358
|
+
'id', ${id}::text,
|
|
359
|
+
'sendTimeMs', ${now}::int8,
|
|
360
|
+
'commitTimeMs', extract(epoch from now()) * 1000
|
|
361
|
+
)::text
|
|
362
|
+
) as lsn;
|
|
363
|
+
`;
|
|
364
|
+
lagReport.lsn = toBigInt(lsn);
|
|
365
|
+
if (log) this.#lc.info?.(`initiated lag report at lsn ${lsn}`, {
|
|
366
|
+
id,
|
|
367
|
+
lsn
|
|
368
|
+
});
|
|
369
|
+
return { nextSendTimeMs: now };
|
|
370
|
+
}
|
|
371
|
+
checkCurrentLSN(lsn) {
|
|
372
|
+
if (this.#expectingLagReport?.lsn && lsn > this.#expectingLagReport.lsn) {
|
|
373
|
+
this.#lc.warn?.(`LSN ${fromBigInt(lsn)} is passed expected lag report ${fromBigInt(this.#expectingLagReport.lsn)}. Initiating new report.`);
|
|
374
|
+
this.#scheduleNextReport(0);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
#scheduleNextReport(delayMs) {
|
|
378
|
+
this.#expectingLagReport = null;
|
|
379
|
+
clearTimeout(this.#timer);
|
|
380
|
+
this.#timer = setTimeout(async () => {
|
|
381
|
+
try {
|
|
382
|
+
await this.initiateLagReport();
|
|
383
|
+
} catch (e) {
|
|
384
|
+
this.#lc.warn?.(`error initiating lag report`, e);
|
|
385
|
+
this.#scheduleNextReport(this.#lagIntervalMs);
|
|
386
|
+
}
|
|
387
|
+
}, delayMs);
|
|
388
|
+
}
|
|
389
|
+
processLagReport(msg) {
|
|
390
|
+
assert(msg.prefix === this.messagePrefix, `unexpected message prefix: ${msg.prefix}`);
|
|
391
|
+
const report = parseLogicalMessageContent(msg, lagReportSchema);
|
|
392
|
+
const now = Date.now();
|
|
393
|
+
const nextSendTimeMs = Math.max(now, report.sendTimeMs + this.#lagIntervalMs);
|
|
394
|
+
if (report.id === this.#expectingLagReport?.id) this.#scheduleNextReport(nextSendTimeMs - now);
|
|
395
|
+
else this.#lc.debug?.(`received extraneous lag report`, { report });
|
|
396
|
+
const { sendTimeMs, commitTimeMs } = report;
|
|
397
|
+
return [
|
|
398
|
+
"status",
|
|
399
|
+
{
|
|
400
|
+
ack: false,
|
|
401
|
+
lagReport: {
|
|
402
|
+
lastTimings: {
|
|
403
|
+
sendTimeMs,
|
|
404
|
+
commitTimeMs,
|
|
405
|
+
receiveTimeMs: now
|
|
406
|
+
},
|
|
407
|
+
nextSendTimeMs
|
|
408
|
+
}
|
|
409
|
+
},
|
|
410
|
+
{ watermark: toStateVersionString(msg.messageLsn ?? "0/0") }
|
|
411
|
+
];
|
|
412
|
+
}
|
|
413
|
+
};
|
|
285
414
|
var SET_REPLICA_IDENTITY_DELAY_MS = 50;
|
|
286
415
|
var ChangeMaker = class {
|
|
287
416
|
#lc;
|
|
288
417
|
#shardPrefix;
|
|
289
418
|
#shardConfig;
|
|
290
419
|
#initialSchema;
|
|
291
|
-
#
|
|
420
|
+
#db;
|
|
292
421
|
#replicaIdentityTimer;
|
|
293
422
|
#error;
|
|
294
|
-
constructor(lc, { appID, shardNum }, shardConfig,
|
|
423
|
+
constructor(lc, { appID, shardNum }, shardConfig, db, initialSchema) {
|
|
295
424
|
this.#lc = lc;
|
|
296
425
|
this.#shardPrefix = `${appID}/${shardNum}`;
|
|
297
426
|
this.#shardConfig = shardConfig;
|
|
298
427
|
this.#initialSchema = initialSchema;
|
|
299
|
-
this.#
|
|
300
|
-
["idle_timeout"]: 10,
|
|
301
|
-
connection: { ["application_name"]: "zero-schema-change-detector" }
|
|
302
|
-
});
|
|
428
|
+
this.#db = db;
|
|
303
429
|
}
|
|
304
430
|
async makeChanges(lsn, msg) {
|
|
305
431
|
if (this.#error) {
|
|
@@ -398,7 +524,7 @@ var ChangeMaker = class {
|
|
|
398
524
|
#preSchema;
|
|
399
525
|
#lastSnapshotInTx;
|
|
400
526
|
#handleDdlMessage(msg) {
|
|
401
|
-
const event =
|
|
527
|
+
const event = parseLogicalMessageContent(msg, replicationEventSchema);
|
|
402
528
|
clearTimeout(this.#replicaIdentityTimer);
|
|
403
529
|
let previousSchema;
|
|
404
530
|
const { type } = event;
|
|
@@ -427,7 +553,7 @@ var ChangeMaker = class {
|
|
|
427
553
|
const replicaIdentities = replicaIdentitiesForTablesWithoutPrimaryKeys(event.schema);
|
|
428
554
|
if (replicaIdentities) this.#replicaIdentityTimer = setTimeout(async () => {
|
|
429
555
|
try {
|
|
430
|
-
await replicaIdentities.apply(this.#lc, this.#
|
|
556
|
+
await replicaIdentities.apply(this.#lc, this.#db);
|
|
431
557
|
} catch (err) {
|
|
432
558
|
this.#lc.warn?.(`error setting replica identities`, err);
|
|
433
559
|
}
|
|
@@ -602,10 +728,6 @@ var ChangeMaker = class {
|
|
|
602
728
|
}
|
|
603
729
|
return changes;
|
|
604
730
|
}
|
|
605
|
-
#parseReplicationEvent(content) {
|
|
606
|
-
const str = content instanceof Buffer ? content.toString("utf-8") : new TextDecoder().decode(content);
|
|
607
|
-
return parse(JSON.parse(str), replicationEventSchema, "passthrough");
|
|
608
|
-
}
|
|
609
731
|
/**
|
|
610
732
|
* If `ddlDetection === true`, relation messages are irrelevant,
|
|
611
733
|
* as schema changes are detected by event triggers that
|
|
@@ -627,7 +749,7 @@ var ChangeMaker = class {
|
|
|
627
749
|
async #handleRelation(rel) {
|
|
628
750
|
const { publications, ddlDetection } = this.#shardConfig;
|
|
629
751
|
if (ddlDetection) return [];
|
|
630
|
-
const currentSchema = await getPublicationInfo(this.#
|
|
752
|
+
const currentSchema = await getPublicationInfo(this.#db, publications);
|
|
631
753
|
const difference = getSchemaDifference(this.#initialSchema, currentSchema);
|
|
632
754
|
if (difference !== null) throw new MissingEventTriggerSupport(difference);
|
|
633
755
|
const orel = this.#initialSchema.tables.find((t) => t.oid === rel.relationOid);
|
|
@@ -752,6 +874,10 @@ var ShutdownSignal = class extends AbortError {
|
|
|
752
874
|
super("shutdown signal received (e.g. another zero-cache taking over the replication stream)", { cause });
|
|
753
875
|
}
|
|
754
876
|
};
|
|
877
|
+
function parseLogicalMessageContent({ content }, schema) {
|
|
878
|
+
const str = content instanceof Buffer ? content.toString("utf-8") : new TextDecoder().decode(content);
|
|
879
|
+
return parse(JSON.parse(str), schema, "passthrough");
|
|
880
|
+
}
|
|
755
881
|
//#endregion
|
|
756
882
|
export { initializePostgresChangeSource };
|
|
757
883
|
|