@rocicorp/zero 1.0.1-canary.0 → 1.1.1
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/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 +1 -1
- package/out/zero-cache/src/config/zero-config.js +2 -2
- package/out/zero-cache/src/config/zero-config.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/server/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/server/replicator.js +3 -3
- package/out/zero-cache/src/server/replicator.js.map +1 -1
- package/out/zero-cache/src/services/analyze.js +1 -1
- package/out/zero-cache/src/services/change-source/change-source.d.ts +1 -1
- package/out/zero-cache/src/services/change-source/change-source.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +3 -3
- 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.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.js +55 -22
- package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/lsn.js +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +3 -3
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.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 +3 -3
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.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 +10 -14
- package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
- package/out/zero-cache/src/services/replicator/replication-status.d.ts +4 -2
- package/out/zero-cache/src/services/replicator/replication-status.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/replication-status.js +20 -7
- package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
- package/out/zero-cache/src/services/replicator/replicator.d.ts +2 -2
- 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/running-state.d.ts +3 -1
- package/out/zero-cache/src/services/running-state.d.ts.map +1 -1
- package/out/zero-cache/src/services/running-state.js +4 -0
- package/out/zero-cache/src/services/running-state.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js +1 -1
- package/out/zero-cache/src/types/pg.d.ts +1 -0
- package/out/zero-cache/src/types/pg.d.ts.map +1 -1
- package/out/zero-cache/src/types/pg.js +4 -1
- package/out/zero-cache/src/types/pg.js.map +1 -1
- package/out/zero-cache/src/workers/connection.js +2 -2
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/workers/replicator.js +18 -3
- 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/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 +3 -1
- 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/db.d.ts.map +1 -1
- package/out/zqlite/src/db.js +1 -1
- package/out/zqlite/src/db.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/replicache/src/mutation-recovery.js.map +0 -1
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { RunningState } from "../running-state.js";
|
|
2
2
|
import { getOrCreateCounter } from "../../observability/metrics.js";
|
|
3
|
-
import { ReplicationStatusPublisher } from "./replication-status.js";
|
|
4
3
|
import "../change-streamer/change-streamer.js";
|
|
5
4
|
import { Notifier } from "./notifier.js";
|
|
6
5
|
import { ReplicationReportRecorder } from "./reporter/recorder.js";
|
|
@@ -17,23 +16,21 @@ var IncrementalSyncer = class {
|
|
|
17
16
|
#taskID;
|
|
18
17
|
#id;
|
|
19
18
|
#changeStreamer;
|
|
20
|
-
#statusDb;
|
|
21
19
|
#worker;
|
|
22
20
|
#mode;
|
|
23
|
-
#
|
|
21
|
+
#statusPublisher;
|
|
24
22
|
#notifier;
|
|
25
23
|
#reporter;
|
|
26
24
|
#state = new RunningState("IncrementalSyncer");
|
|
27
25
|
#replicationEvents = getOrCreateCounter("replication", "events", "Number of replication events processed");
|
|
28
|
-
constructor(lc, taskID, id, changeStreamer,
|
|
26
|
+
constructor(lc, taskID, id, changeStreamer, worker, mode, statusPublisher) {
|
|
29
27
|
this.#lc = lc;
|
|
30
28
|
this.#taskID = taskID;
|
|
31
29
|
this.#id = id;
|
|
32
30
|
this.#changeStreamer = changeStreamer;
|
|
33
|
-
this.#statusDb = statusDb;
|
|
34
31
|
this.#worker = worker;
|
|
35
32
|
this.#mode = mode;
|
|
36
|
-
this.#
|
|
33
|
+
this.#statusPublisher = statusPublisher;
|
|
37
34
|
this.#notifier = new Notifier();
|
|
38
35
|
this.#reporter = new ReplicationReportRecorder(lc);
|
|
39
36
|
}
|
|
@@ -43,7 +40,6 @@ var IncrementalSyncer = class {
|
|
|
43
40
|
lc.info?.(`Starting IncrementalSyncer`);
|
|
44
41
|
const { watermark: initialWatermark } = await this.#worker.getSubscriptionState();
|
|
45
42
|
this.#notifier.notifySubscribers();
|
|
46
|
-
const statusPublisher = this.#publishReplicationStatus ? new ReplicationStatusPublisher(this.#statusDb) : void 0;
|
|
47
43
|
while (this.#state.shouldRun()) {
|
|
48
44
|
const { replicaVersion, watermark } = await this.#worker.getSubscriptionState();
|
|
49
45
|
let downstream;
|
|
@@ -61,7 +57,7 @@ var IncrementalSyncer = class {
|
|
|
61
57
|
});
|
|
62
58
|
this.#state.resetBackoff();
|
|
63
59
|
unregister = this.#state.cancelOnStop(downstream);
|
|
64
|
-
statusPublisher?.publish(lc, "Replicating", `Replicating from ${watermark}`);
|
|
60
|
+
this.#statusPublisher?.publish(lc, "Replicating", `Replicating from ${watermark}`);
|
|
65
61
|
let backfillStatus;
|
|
66
62
|
for await (const message of downstream) {
|
|
67
63
|
this.#replicationEvents.add(1);
|
|
@@ -87,7 +83,7 @@ var IncrementalSyncer = class {
|
|
|
87
83
|
const { status } = msg;
|
|
88
84
|
if (!backfillStatus) {
|
|
89
85
|
backfillStatus = status;
|
|
90
|
-
statusPublisher?.publish(lc, "Replicating", `Backfilling ${msg.relation.name} table`, 3e3, () => backfillStatus ? { downloadStatus: [{
|
|
86
|
+
this.#statusPublisher?.publish(lc, "Replicating", `Backfilling ${msg.relation.name} table`, 3e3, () => backfillStatus ? { downloadStatus: [{
|
|
91
87
|
...backfillStatus,
|
|
92
88
|
table: msg.relation.name,
|
|
93
89
|
columns: [...msg.relation.rowKey.columns, ...msg.columns]
|
|
@@ -96,7 +92,7 @@ var IncrementalSyncer = class {
|
|
|
96
92
|
backfillStatus = status;
|
|
97
93
|
}
|
|
98
94
|
const result = await this.#worker.processMessage(message);
|
|
99
|
-
this.#handleResult(lc, result
|
|
95
|
+
this.#handleResult(lc, result);
|
|
100
96
|
if (result?.completedBackfill) backfillStatus = void 0;
|
|
101
97
|
break;
|
|
102
98
|
}
|
|
@@ -109,18 +105,18 @@ var IncrementalSyncer = class {
|
|
|
109
105
|
} finally {
|
|
110
106
|
downstream?.cancel();
|
|
111
107
|
unregister();
|
|
112
|
-
statusPublisher?.stop();
|
|
108
|
+
this.#statusPublisher?.stop();
|
|
113
109
|
}
|
|
114
110
|
await this.#state.backoff(lc, err);
|
|
115
111
|
}
|
|
116
112
|
lc.info?.("IncrementalSyncer stopped");
|
|
117
113
|
}
|
|
118
|
-
#handleResult(lc, result
|
|
114
|
+
#handleResult(lc, result) {
|
|
119
115
|
if (!result) return;
|
|
120
116
|
if (result.completedBackfill) {
|
|
121
117
|
const status = result.completedBackfill;
|
|
122
|
-
statusPublisher?.publish(lc, "Replicating", `Backfilled ${status.table} table`, 0, () => ({ downloadStatus: [status] }));
|
|
123
|
-
} else if (result.schemaUpdated) statusPublisher?.publish(lc, "Replicating", "Schema updated");
|
|
118
|
+
this.#statusPublisher?.publish(lc, "Replicating", `Backfilled ${status.table} table`, 0, () => ({ downloadStatus: [status] }));
|
|
119
|
+
} else if (result.schemaUpdated) this.#statusPublisher?.publish(lc, "Replicating", "Schema updated");
|
|
124
120
|
if (result.watermark && result.changeLogUpdated) this.#notifier.notifySubscribers({ state: "version-ready" });
|
|
125
121
|
}
|
|
126
122
|
subscribe() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"incremental-sync.js","names":["#lc","#taskID","#id","#changeStreamer","#statusDb","#worker","#mode","#publishReplicationStatus","#notifier","#reporter","#state","#replicationEvents","#handleResult"],"sources":["../../../../../../zero-cache/src/services/replicator/incremental-sync.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {Database} from '../../../../zqlite/src/db.ts';\nimport {getOrCreateCounter} from '../../observability/metrics.ts';\nimport type {Source} from '../../types/streams.ts';\nimport type {DownloadStatus} from '../change-source/protocol/current.ts';\nimport type {ChangeStreamData} from '../change-source/protocol/current/downstream.ts';\nimport {\n PROTOCOL_VERSION,\n type ChangeStreamer,\n type Downstream,\n} from '../change-streamer/change-streamer.ts';\nimport {RunningState} from '../running-state.ts';\nimport type {CommitResult} from './change-processor.ts';\nimport {Notifier} from './notifier.ts';\nimport {ReplicationStatusPublisher} from './replication-status.ts';\nimport type {ReplicaState, ReplicatorMode} from './replicator.ts';\nimport {ReplicationReportRecorder} from './reporter/recorder.ts';\nimport type {ReplicationReport} from './reporter/report-schema.ts';\nimport type {WriteWorkerClient} from './write-worker-client.ts';\n\n/**\n * The {@link IncrementalSyncer} manages a logical replication stream from upstream,\n * handling application lifecycle events (start, stop) and retrying the\n * connection with exponential backoff. The actual handling of the logical\n * replication messages is done by the {@link ChangeProcessor}, which runs\n * in a worker thread via the {@link WriteWorkerClient}.\n */\nexport class IncrementalSyncer {\n readonly #lc: LogContext;\n readonly #taskID: string;\n readonly #id: string;\n readonly #changeStreamer: ChangeStreamer;\n readonly #statusDb: Database;\n readonly #worker: WriteWorkerClient;\n readonly #mode: ReplicatorMode;\n readonly #publishReplicationStatus: boolean;\n readonly #notifier: Notifier;\n readonly #reporter: ReplicationReportRecorder;\n\n readonly #state = new RunningState('IncrementalSyncer');\n\n readonly #replicationEvents = getOrCreateCounter(\n 'replication',\n 'events',\n 'Number of replication events processed',\n );\n\n constructor(\n lc: LogContext,\n taskID: string,\n id: string,\n changeStreamer: ChangeStreamer,\n statusDb: Database,\n worker: WriteWorkerClient,\n mode: ReplicatorMode,\n publishReplicationStatus: boolean,\n ) {\n this.#lc = lc;\n this.#taskID = taskID;\n this.#id = id;\n this.#changeStreamer = changeStreamer;\n this.#statusDb = statusDb;\n this.#worker = worker;\n this.#mode = mode;\n this.#publishReplicationStatus = publishReplicationStatus;\n this.#notifier = new Notifier();\n this.#reporter = new ReplicationReportRecorder(lc);\n }\n\n async run() {\n const lc = this.#lc;\n this.#worker.onError(err => this.#state.stop(lc, err));\n lc.info?.(`Starting IncrementalSyncer`);\n const {watermark: initialWatermark} =\n await this.#worker.getSubscriptionState();\n\n // Notify any waiting subscribers that the replica is ready to be read.\n void this.#notifier.notifySubscribers();\n\n // Only the backup replicator publishes replication status events.\n const statusPublisher = this.#publishReplicationStatus\n ? new ReplicationStatusPublisher(this.#statusDb)\n : undefined;\n\n while (this.#state.shouldRun()) {\n const {replicaVersion, watermark} =\n await this.#worker.getSubscriptionState();\n\n let downstream: Source<Downstream> | undefined;\n let unregister = () => {};\n let err: unknown | undefined;\n\n try {\n downstream = await this.#changeStreamer.subscribe({\n protocolVersion: PROTOCOL_VERSION,\n taskID: this.#taskID,\n id: this.#id,\n mode: this.#mode,\n watermark,\n replicaVersion,\n initial: watermark === initialWatermark,\n });\n this.#state.resetBackoff();\n unregister = this.#state.cancelOnStop(downstream);\n statusPublisher?.publish(\n lc,\n 'Replicating',\n `Replicating from ${watermark}`,\n );\n\n let backfillStatus: DownloadStatus | undefined;\n\n for await (const message of downstream) {\n this.#replicationEvents.add(1);\n switch (message[0]) {\n case 'status': {\n const {lagReport} = message[1];\n if (lagReport) {\n const report: ReplicationReport = {\n nextSendTimeMs: lagReport.nextSendTimeMs,\n };\n if (lagReport.lastTimings) {\n report.lastTimings = {\n ...lagReport.lastTimings,\n replicateTimeMs: Date.now(),\n };\n }\n this.#reporter.record(report);\n }\n break;\n }\n case 'error':\n // Unrecoverable error. Stop the service.\n this.stop(lc, message[1]);\n break;\n default: {\n const msg = message[1];\n if (msg.tag === 'backfill' && msg.status) {\n const {status} = msg;\n if (!backfillStatus) {\n // Start publishing the status every 3 seconds.\n backfillStatus = status;\n statusPublisher?.publish(\n lc,\n 'Replicating',\n `Backfilling ${msg.relation.name} table`,\n 3000,\n () =>\n backfillStatus\n ? {\n downloadStatus: [\n {\n ...backfillStatus,\n table: msg.relation.name,\n columns: [\n ...msg.relation.rowKey.columns,\n ...msg.columns,\n ],\n },\n ],\n }\n : {},\n );\n }\n backfillStatus = status; // Update the current status\n }\n\n const result = await this.#worker.processMessage(\n message as ChangeStreamData,\n );\n\n this.#handleResult(lc, result, statusPublisher);\n if (result?.completedBackfill) {\n backfillStatus = undefined;\n }\n break;\n }\n }\n }\n this.#worker.abort();\n } catch (e) {\n err = e;\n this.#worker.abort();\n } finally {\n downstream?.cancel();\n unregister();\n statusPublisher?.stop();\n }\n await this.#state.backoff(lc, err);\n }\n lc.info?.('IncrementalSyncer stopped');\n }\n\n #handleResult(\n lc: LogContext,\n result: CommitResult | null,\n statusPublisher: ReplicationStatusPublisher | undefined,\n ) {\n if (!result) {\n return;\n }\n if (result.completedBackfill) {\n // Publish the final status\n const status = result.completedBackfill;\n statusPublisher?.publish(\n lc,\n 'Replicating',\n `Backfilled ${status.table} table`,\n 0,\n () => ({downloadStatus: [status]}),\n );\n } else if (result.schemaUpdated) {\n statusPublisher?.publish(lc, 'Replicating', 'Schema updated');\n }\n if (result.watermark && result.changeLogUpdated) {\n void this.#notifier.notifySubscribers({state: 'version-ready'});\n }\n }\n\n subscribe(): Source<ReplicaState> {\n return this.#notifier.subscribe();\n }\n\n stop(lc: LogContext, err?: unknown) {\n this.#state.stop(lc, err);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"incremental-sync.js","names":["#lc","#taskID","#id","#changeStreamer","#worker","#mode","#statusPublisher","#notifier","#reporter","#state","#replicationEvents","#handleResult"],"sources":["../../../../../../zero-cache/src/services/replicator/incremental-sync.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {getOrCreateCounter} from '../../observability/metrics.ts';\nimport type {Source} from '../../types/streams.ts';\nimport type {DownloadStatus} from '../change-source/protocol/current.ts';\nimport type {ChangeStreamData} from '../change-source/protocol/current/downstream.ts';\nimport {\n PROTOCOL_VERSION,\n type ChangeStreamer,\n type Downstream,\n} from '../change-streamer/change-streamer.ts';\nimport {RunningState} from '../running-state.ts';\nimport type {CommitResult} from './change-processor.ts';\nimport {Notifier} from './notifier.ts';\nimport type {ReplicationStatusPublisher} from './replication-status.ts';\nimport type {ReplicaState, ReplicatorMode} from './replicator.ts';\nimport {ReplicationReportRecorder} from './reporter/recorder.ts';\nimport type {ReplicationReport} from './reporter/report-schema.ts';\nimport type {WriteWorkerClient} from './write-worker-client.ts';\n\n/**\n * The {@link IncrementalSyncer} manages a logical replication stream from upstream,\n * handling application lifecycle events (start, stop) and retrying the\n * connection with exponential backoff. The actual handling of the logical\n * replication messages is done by the {@link ChangeProcessor}, which runs\n * in a worker thread via the {@link WriteWorkerClient}.\n */\nexport class IncrementalSyncer {\n readonly #lc: LogContext;\n readonly #taskID: string;\n readonly #id: string;\n readonly #changeStreamer: ChangeStreamer;\n readonly #worker: WriteWorkerClient;\n readonly #mode: ReplicatorMode;\n readonly #statusPublisher: ReplicationStatusPublisher | null;\n readonly #notifier: Notifier;\n readonly #reporter: ReplicationReportRecorder;\n\n readonly #state = new RunningState('IncrementalSyncer');\n\n readonly #replicationEvents = getOrCreateCounter(\n 'replication',\n 'events',\n 'Number of replication events processed',\n );\n\n constructor(\n lc: LogContext,\n taskID: string,\n id: string,\n changeStreamer: ChangeStreamer,\n worker: WriteWorkerClient,\n mode: ReplicatorMode,\n statusPublisher: ReplicationStatusPublisher | null,\n ) {\n this.#lc = lc;\n this.#taskID = taskID;\n this.#id = id;\n this.#changeStreamer = changeStreamer;\n this.#worker = worker;\n this.#mode = mode;\n this.#statusPublisher = statusPublisher;\n this.#notifier = new Notifier();\n this.#reporter = new ReplicationReportRecorder(lc);\n }\n\n async run() {\n const lc = this.#lc;\n this.#worker.onError(err => this.#state.stop(lc, err));\n lc.info?.(`Starting IncrementalSyncer`);\n const {watermark: initialWatermark} =\n await this.#worker.getSubscriptionState();\n\n // Notify any waiting subscribers that the replica is ready to be read.\n void this.#notifier.notifySubscribers();\n\n while (this.#state.shouldRun()) {\n const {replicaVersion, watermark} =\n await this.#worker.getSubscriptionState();\n\n let downstream: Source<Downstream> | undefined;\n let unregister = () => {};\n let err: unknown | undefined;\n\n try {\n downstream = await this.#changeStreamer.subscribe({\n protocolVersion: PROTOCOL_VERSION,\n taskID: this.#taskID,\n id: this.#id,\n mode: this.#mode,\n watermark,\n replicaVersion,\n initial: watermark === initialWatermark,\n });\n this.#state.resetBackoff();\n unregister = this.#state.cancelOnStop(downstream);\n this.#statusPublisher?.publish(\n lc,\n 'Replicating',\n `Replicating from ${watermark}`,\n );\n\n let backfillStatus: DownloadStatus | undefined;\n\n for await (const message of downstream) {\n this.#replicationEvents.add(1);\n switch (message[0]) {\n case 'status': {\n const {lagReport} = message[1];\n if (lagReport) {\n const report: ReplicationReport = {\n nextSendTimeMs: lagReport.nextSendTimeMs,\n };\n if (lagReport.lastTimings) {\n report.lastTimings = {\n ...lagReport.lastTimings,\n replicateTimeMs: Date.now(),\n };\n }\n this.#reporter.record(report);\n }\n break;\n }\n case 'error':\n // Unrecoverable error. Stop the service.\n this.stop(lc, message[1]);\n break;\n default: {\n const msg = message[1];\n if (msg.tag === 'backfill' && msg.status) {\n const {status} = msg;\n if (!backfillStatus) {\n // Start publishing the status every 3 seconds.\n backfillStatus = status;\n this.#statusPublisher?.publish(\n lc,\n 'Replicating',\n `Backfilling ${msg.relation.name} table`,\n 3000,\n () =>\n backfillStatus\n ? {\n downloadStatus: [\n {\n ...backfillStatus,\n table: msg.relation.name,\n columns: [\n ...msg.relation.rowKey.columns,\n ...msg.columns,\n ],\n },\n ],\n }\n : {},\n );\n }\n backfillStatus = status; // Update the current status\n }\n\n const result = await this.#worker.processMessage(\n message as ChangeStreamData,\n );\n\n this.#handleResult(lc, result);\n if (result?.completedBackfill) {\n backfillStatus = undefined;\n }\n break;\n }\n }\n }\n this.#worker.abort();\n } catch (e) {\n err = e;\n this.#worker.abort();\n } finally {\n downstream?.cancel();\n unregister();\n this.#statusPublisher?.stop();\n }\n await this.#state.backoff(lc, err);\n }\n lc.info?.('IncrementalSyncer stopped');\n }\n\n #handleResult(lc: LogContext, result: CommitResult | null) {\n if (!result) {\n return;\n }\n if (result.completedBackfill) {\n // Publish the final status\n const status = result.completedBackfill;\n this.#statusPublisher?.publish(\n lc,\n 'Replicating',\n `Backfilled ${status.table} table`,\n 0,\n () => ({downloadStatus: [status]}),\n );\n } else if (result.schemaUpdated) {\n this.#statusPublisher?.publish(lc, 'Replicating', 'Schema updated');\n }\n if (result.watermark && result.changeLogUpdated) {\n void this.#notifier.notifySubscribers({state: 'version-ready'});\n }\n }\n\n subscribe(): Source<ReplicaState> {\n return this.#notifier.subscribe();\n }\n\n stop(lc: LogContext, err?: unknown) {\n this.#state.stop(lc, err);\n }\n}\n"],"mappings":"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"}
|
|
@@ -5,8 +5,10 @@ import { Database } from '../../../../zqlite/src/db.ts';
|
|
|
5
5
|
import { publishCriticalEvent } from '../../observability/events.ts';
|
|
6
6
|
export declare class ReplicationStatusPublisher {
|
|
7
7
|
#private;
|
|
8
|
-
static forTesting(
|
|
9
|
-
|
|
8
|
+
static forTesting(): ReplicationStatusPublisher;
|
|
9
|
+
static forRunningTransaction(tx: Database, publishFn?: typeof publishCriticalEvent): ReplicationStatusPublisher;
|
|
10
|
+
static forReplicaFile(file: string, publishFn?: typeof publishCriticalEvent): ReplicationStatusPublisher;
|
|
11
|
+
constructor(dbRunner: <T>(lc: LogContext, fn: (db: Database) => T) => T, publishFn?: typeof publishCriticalEvent);
|
|
10
12
|
publish(lc: LogContext, stage: ReplicationStage, description?: string, interval?: number, extraState?: () => Partial<ReplicationState>, now?: Date): this;
|
|
11
13
|
publishAndThrowError(lc: LogContext, stage: ReplicationStage, e: unknown): Promise<never>;
|
|
12
14
|
stop(): this;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replication-status.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/replicator/replication-status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,qCAAqC,CAAC;AACpE,OAAO,KAAK,EAGV,gBAAgB,EAChB,gBAAgB,EAChB,sBAAsB,EACtB,MAAM,EACP,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAC,QAAQ,EAAC,MAAM,8BAA8B,CAAC;AAGtD,OAAO,EAEL,oBAAoB,EACrB,MAAM,+BAA+B,CAAC;AAKvC,qBAAa,0BAA0B;;IAKrC,MAAM,CAAC,UAAU,CAAC,
|
|
1
|
+
{"version":3,"file":"replication-status.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/replicator/replication-status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,qCAAqC,CAAC;AACpE,OAAO,KAAK,EAGV,gBAAgB,EAChB,gBAAgB,EAChB,sBAAsB,EACtB,MAAM,EACP,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAC,QAAQ,EAAC,MAAM,8BAA8B,CAAC;AAGtD,OAAO,EAEL,oBAAoB,EACrB,MAAM,+BAA+B,CAAC;AAKvC,qBAAa,0BAA0B;;IAKrC,MAAM,CAAC,UAAU;IAIjB,MAAM,CAAC,qBAAqB,CAAC,EAAE,EAAE,QAAQ,EAAE,SAAS,8BAAuB;IAI3E,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,8BAAuB;gBAYlE,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,QAAQ,KAAK,CAAC,KAAK,CAAC,EAC3D,SAAS,8BAAuB;IAMlC,OAAO,CACL,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,gBAAgB,EACvB,WAAW,CAAC,EAAE,MAAM,EACpB,QAAQ,SAAI,EACZ,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,EAC5C,GAAG,OAAa,GACf,IAAI;IAuBD,oBAAoB,CACxB,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,gBAAgB,EACvB,CAAC,EAAE,OAAO,GACT,OAAO,CAAC,KAAK,CAAC;IASjB,IAAI,IAAI,IAAI;CAIb;AAED,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,gBAAgB,EACvB,WAAW,EAAE,MAAM,EACnB,YAAY,CAAC,EAAE,UAAU,EACzB,GAAG,OAAa,iBAYjB;AAED,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,gBAAgB,EACvB,CAAC,EAAE,OAAO,EACV,EAAE,CAAC,EAAE,QAAQ,EACb,GAAG,OAAa,0BAKjB;AAGD,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,QAAQ,GAAG,SAAS,EACxB,KAAK,EAAE,gBAAgB,EACvB,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,MAAM,EACpB,GAAG,OAAa,GACf,sBAAsB,CAmCxB"}
|
|
@@ -5,19 +5,32 @@ import { createSilentLogContext } from "../../../../shared/src/logging-test-util
|
|
|
5
5
|
//#region ../zero-cache/src/services/replicator/replication-status.ts
|
|
6
6
|
var byKeys = (a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0;
|
|
7
7
|
var ReplicationStatusPublisher = class ReplicationStatusPublisher {
|
|
8
|
-
#
|
|
8
|
+
#dbRunner;
|
|
9
9
|
#publish;
|
|
10
10
|
#timer;
|
|
11
|
-
static forTesting(
|
|
12
|
-
return
|
|
11
|
+
static forTesting() {
|
|
12
|
+
return ReplicationStatusPublisher.forReplicaFile(":memory:");
|
|
13
13
|
}
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
static forRunningTransaction(tx, publishFn = publishCriticalEvent) {
|
|
15
|
+
return new ReplicationStatusPublisher((_lc, fn) => fn(tx), publishFn);
|
|
16
|
+
}
|
|
17
|
+
static forReplicaFile(file, publishFn = publishCriticalEvent) {
|
|
18
|
+
return new ReplicationStatusPublisher((lc, fn) => {
|
|
19
|
+
const db = new Database(lc, file, { readonly: true });
|
|
20
|
+
try {
|
|
21
|
+
return fn(db);
|
|
22
|
+
} finally {
|
|
23
|
+
db.close();
|
|
24
|
+
}
|
|
25
|
+
}, publishFn);
|
|
26
|
+
}
|
|
27
|
+
constructor(dbRunner, publishFn = publishCriticalEvent) {
|
|
28
|
+
this.#dbRunner = dbRunner;
|
|
16
29
|
this.#publish = publishFn;
|
|
17
30
|
}
|
|
18
31
|
publish(lc, stage, description, interval = 0, extraState, now = /* @__PURE__ */ new Date()) {
|
|
19
32
|
this.stop();
|
|
20
|
-
const event = replicationStatusEvent(lc,
|
|
33
|
+
const event = this.#dbRunner(lc, (db) => replicationStatusEvent(lc, db, stage, "OK", description, now));
|
|
21
34
|
if (event.state) event.state = {
|
|
22
35
|
...event.state,
|
|
23
36
|
...extraState?.()
|
|
@@ -28,7 +41,7 @@ var ReplicationStatusPublisher = class ReplicationStatusPublisher {
|
|
|
28
41
|
}
|
|
29
42
|
async publishAndThrowError(lc, stage, e) {
|
|
30
43
|
this.stop();
|
|
31
|
-
const event = replicationStatusError(lc, stage, e,
|
|
44
|
+
const event = this.#dbRunner(lc, (db) => replicationStatusError(lc, stage, e, db));
|
|
32
45
|
await this.#publish(lc, event);
|
|
33
46
|
throw e;
|
|
34
47
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replication-status.js","names":["#
|
|
1
|
+
{"version":3,"file":"replication-status.js","names":["#dbRunner","#publish","#timer"],"sources":["../../../../../../zero-cache/src/services/replicator/replication-status.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {createSilentLogContext} from '../../../../shared/src/logging-test-utils.ts';\nimport type {JSONObject} from '../../../../zero-events/src/json.ts';\nimport type {\n ReplicatedIndex,\n ReplicatedTable,\n ReplicationStage,\n ReplicationState,\n ReplicationStatusEvent,\n Status,\n} from '../../../../zero-events/src/status.ts';\nimport {Database} from '../../../../zqlite/src/db.ts';\nimport {computeZqlSpecs, listIndexes} from '../../db/lite-tables.ts';\nimport type {LiteTableSpec} from '../../db/specs.ts';\nimport {\n makeErrorDetails,\n publishCriticalEvent,\n} from '../../observability/events.ts';\n\nconst byKeys = (a: [string, unknown], b: [string, unknown]) =>\n a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0;\n\nexport class ReplicationStatusPublisher {\n readonly #dbRunner: <T>(lc: LogContext, fn: (db: Database) => T) => T;\n readonly #publish: typeof publishCriticalEvent;\n #timer: NodeJS.Timeout | undefined;\n\n static forTesting() {\n return ReplicationStatusPublisher.forReplicaFile(':memory:');\n }\n\n static forRunningTransaction(tx: Database, publishFn = publishCriticalEvent) {\n return new ReplicationStatusPublisher((_lc, fn) => fn(tx), publishFn);\n }\n\n static forReplicaFile(file: string, publishFn = publishCriticalEvent) {\n return new ReplicationStatusPublisher((lc, fn) => {\n const db = new Database(lc, file, {readonly: true});\n try {\n return fn(db);\n } finally {\n db.close();\n }\n }, publishFn);\n }\n\n constructor(\n dbRunner: <T>(lc: LogContext, fn: (db: Database) => T) => T,\n publishFn = publishCriticalEvent,\n ) {\n this.#dbRunner = dbRunner;\n this.#publish = publishFn;\n }\n\n publish(\n lc: LogContext,\n stage: ReplicationStage,\n description?: string,\n interval = 0,\n extraState?: () => Partial<ReplicationState>,\n now = new Date(),\n ): this {\n this.stop();\n\n const event = this.#dbRunner(lc, db =>\n replicationStatusEvent(lc, db, stage, 'OK', description, now),\n );\n if (event.state) {\n event.state = {\n ...event.state,\n ...extraState?.(),\n };\n }\n void this.#publish(lc, event);\n\n if (interval) {\n this.#timer = setInterval(\n () => this.publish(lc, stage, description, interval, extraState),\n interval,\n );\n }\n return this;\n }\n\n async publishAndThrowError(\n lc: LogContext,\n stage: ReplicationStage,\n e: unknown,\n ): Promise<never> {\n this.stop();\n const event = this.#dbRunner(lc, db =>\n replicationStatusError(lc, stage, e, db),\n );\n await this.#publish(lc, event);\n throw e;\n }\n\n stop(): this {\n clearInterval(this.#timer);\n return this;\n }\n}\n\nexport async function publishReplicationError(\n lc: LogContext,\n stage: ReplicationStage,\n description: string,\n errorDetails?: JSONObject,\n now = new Date(),\n) {\n const event: ReplicationStatusEvent = {\n type: 'zero/events/status/replication/v1',\n component: 'replication',\n status: 'ERROR',\n stage,\n description,\n errorDetails,\n time: now.toISOString(),\n };\n await publishCriticalEvent(lc, event);\n}\n\nexport function replicationStatusError(\n lc: LogContext,\n stage: ReplicationStage,\n e: unknown,\n db?: Database,\n now = new Date(),\n) {\n const event = replicationStatusEvent(lc, db, stage, 'ERROR', String(e), now);\n event.errorDetails = makeErrorDetails(e);\n return event;\n}\n\n// Exported for testing.\nexport function replicationStatusEvent(\n lc: LogContext,\n db: Database | undefined,\n stage: ReplicationStage,\n status: Status,\n description?: string,\n now = new Date(),\n): ReplicationStatusEvent {\n const start = performance.now();\n try {\n return {\n type: 'zero/events/status/replication/v1',\n component: 'replication',\n status,\n stage,\n description,\n time: now.toISOString(),\n state: {\n tables: db ? getReplicatedTables(db) : [],\n indexes: db ? getReplicatedIndexes(db) : [],\n replicaSize: db ? getReplicaSize(db) : undefined,\n },\n };\n } catch (e) {\n lc.warn?.(`Unable to create full ReplicationStatusEvent`, e);\n return {\n type: 'zero/events/status/replication/v1',\n component: 'replication',\n status,\n stage,\n description,\n time: now.toISOString(),\n state: {\n tables: [],\n indexes: [],\n replicaSize: 0,\n },\n };\n } finally {\n const elapsed = (performance.now() - start).toFixed(3);\n lc.debug?.(`computed schema for replication event (${elapsed} ms)`);\n }\n}\n\nfunction getReplicatedTables(db: Database): ReplicatedTable[] {\n const fullTables = new Map<string, LiteTableSpec>();\n const clientSchema = computeZqlSpecs(\n createSilentLogContext(), // avoid logging warnings about indexes\n db,\n {includeBackfillingColumns: false},\n new Map(),\n fullTables,\n );\n\n return [...fullTables.entries()].sort(byKeys).map(([table, spec]) => ({\n table,\n columns: Object.entries(spec.columns)\n .sort(byKeys)\n .map(([column, spec]) => ({\n column,\n upstreamType: spec.dataType.split('|')[0],\n clientType: clientSchema.get(table)?.zqlSpec[column]?.type ?? null,\n })),\n }));\n}\n\nfunction getReplicatedIndexes(db: Database): ReplicatedIndex[] {\n return listIndexes(db).map(({tableName: table, columns, unique}) => ({\n table,\n unique,\n columns: Object.entries(columns)\n .sort(byKeys)\n .map(([column, dir]) => ({column, dir})),\n }));\n}\n\nfunction getReplicaSize(db: Database) {\n const [{page_count: pageCount}] = db.pragma<{page_count: number}>(\n 'page_count',\n );\n const [{page_size: pageSize}] = db.pragma<{page_size: number}>('page_size');\n return pageCount * pageSize;\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { LogContext } from '@rocicorp/logger';
|
|
2
2
|
import type { ReadonlyJSONObject } from '../../../../shared/src/json.ts';
|
|
3
|
-
import type { Database } from '../../../../zqlite/src/db.ts';
|
|
4
3
|
import type { Source } from '../../types/streams.ts';
|
|
5
4
|
import type { ChangeStreamer } from '../change-streamer/change-streamer.ts';
|
|
6
5
|
import type { Service } from '../service.ts';
|
|
6
|
+
import type { ReplicationStatusPublisher } from './replication-status.ts';
|
|
7
7
|
import type { WriteWorkerClient } from './write-worker-client.ts';
|
|
8
8
|
/** See {@link ReplicaStateNotifier.subscribe()}. */
|
|
9
9
|
export type ReplicaState = {
|
|
@@ -46,7 +46,7 @@ export type ReplicatorMode = 'backup' | 'serving';
|
|
|
46
46
|
export declare class ReplicatorService implements Replicator, Service {
|
|
47
47
|
#private;
|
|
48
48
|
readonly id: string;
|
|
49
|
-
constructor(lc: LogContext, taskID: string, id: string, mode: ReplicatorMode, changeStreamer: ChangeStreamer,
|
|
49
|
+
constructor(lc: LogContext, taskID: string, id: string, mode: ReplicatorMode, changeStreamer: ChangeStreamer, worker: WriteWorkerClient, statusPublisher: ReplicationStatusPublisher | null);
|
|
50
50
|
status(): Promise<{
|
|
51
51
|
status: string;
|
|
52
52
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replicator.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/replicator/replicator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAC,
|
|
1
|
+
{"version":3,"file":"replicator.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/replicator/replicator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,wBAAwB,CAAC;AACnD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,uCAAuC,CAAC;AAC1E,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,eAAe,CAAC;AAE3C,OAAO,KAAK,EAAC,0BAA0B,EAAC,MAAM,yBAAyB,CAAC;AACxE,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,0BAA0B,CAAC;AAEhE,oDAAoD;AACpD,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAIhC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,SAAS,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD;;;;OAIG;IACH,MAAM,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACvC;AAED,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,CAAC;AAElD,qBAAa,iBAAkB,YAAW,UAAU,EAAE,OAAO;;IAC3D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;gBAOlB,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,cAAc,EACpB,cAAc,EAAE,cAAc,EAC9B,MAAM,EAAE,iBAAiB,EACzB,eAAe,EAAE,0BAA0B,GAAG,IAAI;IAmBpD,MAAM;;;IAIN,GAAG;IAKH,SAAS,IAAI,MAAM,CAAC,YAAY,CAAC;IAI3B,IAAI;CAQX"}
|
|
@@ -6,11 +6,11 @@ var ReplicatorService = class {
|
|
|
6
6
|
#incrementalSyncer;
|
|
7
7
|
#worker;
|
|
8
8
|
#runPromise;
|
|
9
|
-
constructor(lc, taskID, id, mode, changeStreamer,
|
|
9
|
+
constructor(lc, taskID, id, mode, changeStreamer, worker, statusPublisher) {
|
|
10
10
|
this.id = id;
|
|
11
11
|
this.#lc = lc.withContext("component", "replicator").withContext("serviceID", this.id);
|
|
12
12
|
this.#worker = worker;
|
|
13
|
-
this.#incrementalSyncer = new IncrementalSyncer(this.#lc, taskID, `${taskID}/${id}`, changeStreamer,
|
|
13
|
+
this.#incrementalSyncer = new IncrementalSyncer(this.#lc, taskID, `${taskID}/${id}`, changeStreamer, worker, mode, statusPublisher);
|
|
14
14
|
}
|
|
15
15
|
status() {
|
|
16
16
|
return Promise.resolve({ status: "ok" });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replicator.js","names":["#lc","#incrementalSyncer","#worker","#runPromise"],"sources":["../../../../../../zero-cache/src/services/replicator/replicator.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {ReadonlyJSONObject} from '../../../../shared/src/json.ts';\nimport type {
|
|
1
|
+
{"version":3,"file":"replicator.js","names":["#lc","#incrementalSyncer","#worker","#runPromise"],"sources":["../../../../../../zero-cache/src/services/replicator/replicator.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {ReadonlyJSONObject} from '../../../../shared/src/json.ts';\nimport type {Source} from '../../types/streams.ts';\nimport type {ChangeStreamer} from '../change-streamer/change-streamer.ts';\nimport type {Service} from '../service.ts';\nimport {IncrementalSyncer} from './incremental-sync.ts';\nimport type {ReplicationStatusPublisher} from './replication-status.ts';\nimport type {WriteWorkerClient} from './write-worker-client.ts';\n\n/** See {@link ReplicaStateNotifier.subscribe()}. */\nexport type ReplicaState = {\n readonly state: 'version-ready';\n\n // Used in tests to verify behavior when additional information\n // is ferried in the future. Not set in production.\n readonly testSeqNum?: number;\n};\n\nexport interface ReplicaStateNotifier {\n /**\n * Creates a cancelable subscription of changes in the replica state.\n *\n * A `version-ready` message indicates that the replica is ready to be\n * read, and henceforth that a _new_ version is ready, i.e. whenever a\n * change is committed to the replica. The `version-ready` message itself\n * otherwise contains no other information; the subscriber queries the\n * replica for the current data.\n *\n * A `maintenance` state indicates that the replica should not be read from.\n * If a subscriber is holding any transaction locks, it should release them\n * until the next `version-ready` signal.\n *\n * Upon subscription, the current state of the replica is sent immediately\n * if known. If multiple notifications occur before the subscriber\n * can consume them, all but the last notification are discarded by the\n * Subscription object (i.e. not buffered). Thus, a subscriber only\n * ever consumes the current (i.e. known) state of the replica. This avoids\n * a buildup of \"work\" if a subscriber is too busy to consume all\n * notifications.\n */\n subscribe(): Source<ReplicaState>;\n}\n\nexport interface Replicator extends ReplicaStateNotifier {\n /**\n * Returns an opaque message for human-readable consumption. This is\n * purely for ensuring that the Replicator has started at least once to\n * bootstrap a new replica.\n */\n status(): Promise<ReadonlyJSONObject>;\n}\n\nexport type ReplicatorMode = 'backup' | 'serving';\n\nexport class ReplicatorService implements Replicator, Service {\n readonly id: string;\n readonly #lc: LogContext;\n readonly #incrementalSyncer: IncrementalSyncer;\n readonly #worker: WriteWorkerClient;\n #runPromise: Promise<void> | undefined;\n\n constructor(\n lc: LogContext,\n taskID: string,\n id: string,\n mode: ReplicatorMode,\n changeStreamer: ChangeStreamer,\n worker: WriteWorkerClient,\n statusPublisher: ReplicationStatusPublisher | null,\n ) {\n this.id = id;\n this.#lc = lc\n .withContext('component', 'replicator')\n .withContext('serviceID', this.id);\n this.#worker = worker;\n\n this.#incrementalSyncer = new IncrementalSyncer(\n this.#lc,\n taskID,\n `${taskID}/${id}`,\n changeStreamer,\n worker,\n mode,\n statusPublisher,\n );\n }\n\n status() {\n return Promise.resolve({status: 'ok'});\n }\n\n run() {\n this.#runPromise = this.#incrementalSyncer.run();\n return this.#runPromise;\n }\n\n subscribe(): Source<ReplicaState> {\n return this.#incrementalSyncer.subscribe();\n }\n\n async stop() {\n this.#incrementalSyncer.stop(this.#lc);\n // Wait for the syncer's run loop to finish so that any in-flight\n // worker.processMessage() call completes and clears #pending\n // before we send the 'stop' message to the worker.\n await this.#runPromise;\n await this.#worker.stop();\n }\n}\n"],"mappings":";;AAsDA,IAAa,oBAAb,MAA8D;CAC5D;CACA;CACA;CACA;CACA;CAEA,YACE,IACA,QACA,IACA,MACA,gBACA,QACA,iBACA;AACA,OAAK,KAAK;AACV,QAAA,KAAW,GACR,YAAY,aAAa,aAAa,CACtC,YAAY,aAAa,KAAK,GAAG;AACpC,QAAA,SAAe;AAEf,QAAA,oBAA0B,IAAI,kBAC5B,MAAA,IACA,QACA,GAAG,OAAO,GAAG,MACb,gBACA,QACA,MACA,gBACD;;CAGH,SAAS;AACP,SAAO,QAAQ,QAAQ,EAAC,QAAQ,MAAK,CAAC;;CAGxC,MAAM;AACJ,QAAA,aAAmB,MAAA,kBAAwB,KAAK;AAChD,SAAO,MAAA;;CAGT,YAAkC;AAChC,SAAO,MAAA,kBAAwB,WAAW;;CAG5C,MAAM,OAAO;AACX,QAAA,kBAAwB,KAAK,MAAA,GAAS;AAItC,QAAM,MAAA;AACN,QAAM,MAAA,OAAa,MAAM"}
|
|
@@ -59,8 +59,10 @@ export declare class RunningState {
|
|
|
59
59
|
* When using {@link backoff()}, this method should be called when the
|
|
60
60
|
* implementation receives a healthy signal (e.g. a successful
|
|
61
61
|
* response). This resets the delay used in {@link backoff()}.
|
|
62
|
+
*
|
|
63
|
+
* @returns The previous backoff delay
|
|
62
64
|
*/
|
|
63
|
-
resetBackoff():
|
|
65
|
+
resetBackoff(): number;
|
|
64
66
|
}
|
|
65
67
|
/**
|
|
66
68
|
* Superclass for Errors that should bypass exponential backoff
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"running-state.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/services/running-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAGjD,OAAO,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAC;AAG5D,eAAO,MAAM,0BAA0B,QAAQ,CAAC;AAEhD,MAAM,MAAM,WAAW,GAAG;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,WAAW,UAAU;IACzB,MAAM,IAAI,IAAI,CAAC;CAChB;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC;AAEtC;;GAEG;AACH,qBAAa,YAAY;;gBAarB,WAAW,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,WAAW,EACzB,YAAY,oBAAa,EACzB,OAAO,wBAAiB;IA+B1B,IAAI,MAAM,IAAI,WAAW,CAExB;IAED,IAAI,UAAU,WAEb;IAED;;;;;OAKG;IACH,SAAS,IAAI,OAAO;IAIpB;;;OAGG;IACH,YAAY,CAAC,CAAC,EAAE,UAAU,GAAG,YAAY;IAMzC;;OAEG;IACH,UAAU,CAAC,KAAK,SAAS,OAAO,EAAE,EAChC,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,IAAI,EAC5B,SAAS,EAAE,MAAM,EACjB,GAAG,IAAI,EAAE,KAAK;IAWhB;;;OAGG;IACG,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItC;;;OAGG;IACH,IAAI,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,OAAO,GAAG,IAAI;IAQzC;;OAEG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB;;;;;;OAMG;IACG,OAAO,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB1D
|
|
1
|
+
{"version":3,"file":"running-state.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/services/running-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAGjD,OAAO,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAC;AAG5D,eAAO,MAAM,0BAA0B,QAAQ,CAAC;AAEhD,MAAM,MAAM,WAAW,GAAG;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,WAAW,UAAU;IACzB,MAAM,IAAI,IAAI,CAAC;CAChB;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC;AAEtC;;GAEG;AACH,qBAAa,YAAY;;gBAarB,WAAW,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,WAAW,EACzB,YAAY,oBAAa,EACzB,OAAO,wBAAiB;IA+B1B,IAAI,MAAM,IAAI,WAAW,CAExB;IAED,IAAI,UAAU,WAEb;IAED;;;;;OAKG;IACH,SAAS,IAAI,OAAO;IAIpB;;;OAGG;IACH,YAAY,CAAC,CAAC,EAAE,UAAU,GAAG,YAAY;IAMzC;;OAEG;IACH,UAAU,CAAC,KAAK,SAAS,OAAO,EAAE,EAChC,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,IAAI,EAC5B,SAAS,EAAE,MAAM,EACjB,GAAG,IAAI,EAAE,KAAK;IAWhB;;;OAGG;IACG,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItC;;;OAGG;IACH,IAAI,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,OAAO,GAAG,IAAI;IAQzC;;OAEG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB;;;;;;OAMG;IACG,OAAO,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB1D;;;;;;OAMG;IACH,YAAY;CAKb;AAED;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,wBAAwB;CACtC"}
|
|
@@ -114,9 +114,13 @@ var RunningState = class {
|
|
|
114
114
|
* When using {@link backoff()}, this method should be called when the
|
|
115
115
|
* implementation receives a healthy signal (e.g. a successful
|
|
116
116
|
* response). This resets the delay used in {@link backoff()}.
|
|
117
|
+
*
|
|
118
|
+
* @returns The previous backoff delay
|
|
117
119
|
*/
|
|
118
120
|
resetBackoff() {
|
|
121
|
+
const prevDelay = this.#retryDelay;
|
|
119
122
|
this.#retryDelay = this.#initialRetryDelay;
|
|
123
|
+
return prevDelay;
|
|
120
124
|
}
|
|
121
125
|
};
|
|
122
126
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"running-state.js","names":["#serviceName","#controller","#sleep","#setTimeout","#stopped","#initialRetryDelay","#maxRetryDelay","#pendingTimeouts","#retryDelay"],"sources":["../../../../../zero-cache/src/services/running-state.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {AbortError} from '../../../shared/src/abort-error.ts';\nimport {sleepWithAbort} from '../../../shared/src/sleep.ts';\n\nconst DEFAULT_INITIAL_RETRY_DELAY_MS = 25;\nexport const DEFAULT_MAX_RETRY_DELAY_MS = 10000;\n\nexport type RetryConfig = {\n initialRetryDelay?: number;\n maxRetryDelay?: number;\n};\n\nexport interface Cancelable {\n cancel(): void;\n}\n\nexport type UnregisterFn = () => void;\n\n/**\n * Facilitates lifecycle control with exponential backoff.\n */\nexport class RunningState {\n readonly #serviceName: string;\n readonly #controller: AbortController;\n readonly #sleep: typeof sleepWithAbort;\n readonly #setTimeout: typeof setTimeout;\n readonly #stopped: Promise<void>;\n\n readonly #initialRetryDelay: number;\n readonly #maxRetryDelay: number;\n readonly #pendingTimeouts = new Set<NodeJS.Timeout>();\n #retryDelay: number;\n\n constructor(\n serviceName: string,\n retryConfig?: RetryConfig,\n setTimeoutFn = setTimeout,\n sleeper = sleepWithAbort,\n ) {\n const {\n initialRetryDelay = DEFAULT_INITIAL_RETRY_DELAY_MS,\n maxRetryDelay = DEFAULT_MAX_RETRY_DELAY_MS,\n } = retryConfig ?? {};\n\n this.#serviceName = serviceName;\n this.#initialRetryDelay = initialRetryDelay;\n this.#maxRetryDelay = maxRetryDelay;\n this.#retryDelay = initialRetryDelay;\n\n this.#controller = new AbortController();\n this.#sleep = sleeper;\n this.#setTimeout = setTimeoutFn;\n\n const {promise, resolve} = resolver();\n this.#stopped = promise;\n this.#controller.signal.addEventListener(\n 'abort',\n () => {\n resolve();\n for (const timeout of this.#pendingTimeouts) {\n clearTimeout(timeout);\n }\n this.#pendingTimeouts.clear();\n },\n {once: true},\n );\n }\n\n get signal(): AbortSignal {\n return this.#controller.signal;\n }\n\n get retryDelay() {\n return this.#retryDelay;\n }\n\n /**\n * Returns `true` until {@link stop()} has been called.\n *\n * This is usually called as part of the service's main loop\n * conditional to determine if the next iteration should execute.\n */\n shouldRun(): boolean {\n return !this.#controller.signal.aborted;\n }\n\n /**\n * Registers a Cancelable object to be invoked when {@link stop()} is called.\n * Returns a method to unregister the object.\n */\n cancelOnStop(c: Cancelable): UnregisterFn {\n const onStop = () => c.cancel();\n this.#controller.signal.addEventListener('abort', onStop, {once: true});\n return () => this.#controller.signal.removeEventListener('abort', onStop);\n }\n\n /**\n * Sets a Timeout that is automatically cancelled if the service is cancelled.\n */\n setTimeout<TArgs extends unknown[]>(\n fn: (...args: TArgs) => void,\n timeoutMs: number,\n ...args: TArgs\n ) {\n const timeout = this.#setTimeout(() => {\n clearTimeout(timeout);\n this.#pendingTimeouts.delete(timeout);\n return fn(...args);\n }, timeoutMs);\n\n this.#pendingTimeouts.add(timeout);\n }\n\n /**\n * Returns a promise that resolves after `ms` milliseconds or when\n * the service is stopped.\n */\n async sleep(ms: number): Promise<void> {\n await Promise.race(this.#sleep(ms, this.#controller.signal));\n }\n\n /**\n * Called to stop the service. After this is called, {@link shouldRun()}\n * will return `false` and the {@link stopped()} Promise will be resolved.\n */\n stop(lc: LogContext, err?: unknown): void {\n if (this.shouldRun()) {\n const log = !err || err instanceof AbortError ? 'info' : 'error';\n lc[log]?.(`stopping ${this.#serviceName}`, err ?? '');\n this.#controller.abort();\n }\n }\n\n /**\n * Returns a Promise that resolves when {@link stop()} is called.\n */\n stopped(): Promise<void> {\n return this.#stopped;\n }\n\n /**\n * Call in response to an error or unexpected termination in the main\n * loop of the service. The returned Promise will resolve after an\n * exponential delay, or once {@link stop()} is called.\n *\n * If the supplied `err` is an `AbortError`, the service will shut down.\n */\n async backoff(lc: LogContext, err: unknown): Promise<void> {\n const delay = this.#retryDelay;\n this.#retryDelay = Math.min(delay * 2, this.#maxRetryDelay);\n\n if (err instanceof AbortError || err instanceof UnrecoverableError) {\n this.resetBackoff();\n this.stop(lc, err);\n } else if (this.shouldRun()) {\n // Use delay-based log level: higher delay means more retries\n const log: 'info' | 'warn' | 'error' =\n delay < 1000 ? 'info' : delay < 6500 ? 'warn' : 'error';\n\n lc[log]?.(`retrying ${this.#serviceName} in ${delay} ms`, err);\n await this.sleep(delay);\n }\n }\n\n /**\n * When using {@link backoff()}, this method should be called when the\n * implementation receives a healthy signal (e.g. a successful\n * response). This resets the delay used in {@link backoff()}.\n */\n resetBackoff() {\n this.#retryDelay = this.#initialRetryDelay;\n }\n}\n\n/**\n * Superclass for Errors that should bypass exponential backoff\n * and immediately shut down the server.\n */\nexport class UnrecoverableError extends Error {\n readonly name = 'UnrecoverableError';\n}\n"],"mappings":";;;;AAKA,IAAM,iCAAiC;AACvC,IAAa,6BAA6B;;;;AAgB1C,IAAa,eAAb,MAA0B;CACxB;CACA;CACA;CACA;CACA;CAEA;CACA;CACA,mCAA4B,IAAI,KAAqB;CACrD;CAEA,YACE,aACA,aACA,eAAe,YACf,UAAU,gBACV;EACA,MAAM,EACJ,oBAAoB,gCACpB,gBAAgB,+BACd,eAAe,EAAE;AAErB,QAAA,cAAoB;AACpB,QAAA,oBAA0B;AAC1B,QAAA,gBAAsB;AACtB,QAAA,aAAmB;AAEnB,QAAA,aAAmB,IAAI,iBAAiB;AACxC,QAAA,QAAc;AACd,QAAA,aAAmB;EAEnB,MAAM,EAAC,SAAS,YAAW,UAAU;AACrC,QAAA,UAAgB;AAChB,QAAA,WAAiB,OAAO,iBACtB,eACM;AACJ,YAAS;AACT,QAAK,MAAM,WAAW,MAAA,gBACpB,cAAa,QAAQ;AAEvB,SAAA,gBAAsB,OAAO;KAE/B,EAAC,MAAM,MAAK,CACb;;CAGH,IAAI,SAAsB;AACxB,SAAO,MAAA,WAAiB;;CAG1B,IAAI,aAAa;AACf,SAAO,MAAA;;;;;;;;CAST,YAAqB;AACnB,SAAO,CAAC,MAAA,WAAiB,OAAO;;;;;;CAOlC,aAAa,GAA6B;EACxC,MAAM,eAAe,EAAE,QAAQ;AAC/B,QAAA,WAAiB,OAAO,iBAAiB,SAAS,QAAQ,EAAC,MAAM,MAAK,CAAC;AACvE,eAAa,MAAA,WAAiB,OAAO,oBAAoB,SAAS,OAAO;;;;;CAM3E,WACE,IACA,WACA,GAAG,MACH;EACA,MAAM,UAAU,MAAA,iBAAuB;AACrC,gBAAa,QAAQ;AACrB,SAAA,gBAAsB,OAAO,QAAQ;AACrC,UAAO,GAAG,GAAG,KAAK;KACjB,UAAU;AAEb,QAAA,gBAAsB,IAAI,QAAQ;;;;;;CAOpC,MAAM,MAAM,IAA2B;AACrC,QAAM,QAAQ,KAAK,MAAA,MAAY,IAAI,MAAA,WAAiB,OAAO,CAAC;;;;;;CAO9D,KAAK,IAAgB,KAAqB;AACxC,MAAI,KAAK,WAAW,EAAE;AAEpB,MADY,CAAC,OAAO,eAAe,aAAa,SAAS,WAC/C,YAAY,MAAA,eAAqB,OAAO,GAAG;AACrD,SAAA,WAAiB,OAAO;;;;;;CAO5B,UAAyB;AACvB,SAAO,MAAA;;;;;;;;;CAUT,MAAM,QAAQ,IAAgB,KAA6B;EACzD,MAAM,QAAQ,MAAA;AACd,QAAA,aAAmB,KAAK,IAAI,QAAQ,GAAG,MAAA,cAAoB;AAE3D,MAAI,eAAe,cAAc,eAAe,oBAAoB;AAClE,QAAK,cAAc;AACnB,QAAK,KAAK,IAAI,IAAI;aACT,KAAK,WAAW,EAAE;AAK3B,MAFE,QAAQ,MAAO,SAAS,QAAQ,OAAO,SAAS,WAExC,YAAY,MAAA,YAAkB,MAAM,MAAM,MAAM,IAAI;AAC9D,SAAM,KAAK,MAAM,MAAM
|
|
1
|
+
{"version":3,"file":"running-state.js","names":["#serviceName","#controller","#sleep","#setTimeout","#stopped","#initialRetryDelay","#maxRetryDelay","#pendingTimeouts","#retryDelay"],"sources":["../../../../../zero-cache/src/services/running-state.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {AbortError} from '../../../shared/src/abort-error.ts';\nimport {sleepWithAbort} from '../../../shared/src/sleep.ts';\n\nconst DEFAULT_INITIAL_RETRY_DELAY_MS = 25;\nexport const DEFAULT_MAX_RETRY_DELAY_MS = 10000;\n\nexport type RetryConfig = {\n initialRetryDelay?: number;\n maxRetryDelay?: number;\n};\n\nexport interface Cancelable {\n cancel(): void;\n}\n\nexport type UnregisterFn = () => void;\n\n/**\n * Facilitates lifecycle control with exponential backoff.\n */\nexport class RunningState {\n readonly #serviceName: string;\n readonly #controller: AbortController;\n readonly #sleep: typeof sleepWithAbort;\n readonly #setTimeout: typeof setTimeout;\n readonly #stopped: Promise<void>;\n\n readonly #initialRetryDelay: number;\n readonly #maxRetryDelay: number;\n readonly #pendingTimeouts = new Set<NodeJS.Timeout>();\n #retryDelay: number;\n\n constructor(\n serviceName: string,\n retryConfig?: RetryConfig,\n setTimeoutFn = setTimeout,\n sleeper = sleepWithAbort,\n ) {\n const {\n initialRetryDelay = DEFAULT_INITIAL_RETRY_DELAY_MS,\n maxRetryDelay = DEFAULT_MAX_RETRY_DELAY_MS,\n } = retryConfig ?? {};\n\n this.#serviceName = serviceName;\n this.#initialRetryDelay = initialRetryDelay;\n this.#maxRetryDelay = maxRetryDelay;\n this.#retryDelay = initialRetryDelay;\n\n this.#controller = new AbortController();\n this.#sleep = sleeper;\n this.#setTimeout = setTimeoutFn;\n\n const {promise, resolve} = resolver();\n this.#stopped = promise;\n this.#controller.signal.addEventListener(\n 'abort',\n () => {\n resolve();\n for (const timeout of this.#pendingTimeouts) {\n clearTimeout(timeout);\n }\n this.#pendingTimeouts.clear();\n },\n {once: true},\n );\n }\n\n get signal(): AbortSignal {\n return this.#controller.signal;\n }\n\n get retryDelay() {\n return this.#retryDelay;\n }\n\n /**\n * Returns `true` until {@link stop()} has been called.\n *\n * This is usually called as part of the service's main loop\n * conditional to determine if the next iteration should execute.\n */\n shouldRun(): boolean {\n return !this.#controller.signal.aborted;\n }\n\n /**\n * Registers a Cancelable object to be invoked when {@link stop()} is called.\n * Returns a method to unregister the object.\n */\n cancelOnStop(c: Cancelable): UnregisterFn {\n const onStop = () => c.cancel();\n this.#controller.signal.addEventListener('abort', onStop, {once: true});\n return () => this.#controller.signal.removeEventListener('abort', onStop);\n }\n\n /**\n * Sets a Timeout that is automatically cancelled if the service is cancelled.\n */\n setTimeout<TArgs extends unknown[]>(\n fn: (...args: TArgs) => void,\n timeoutMs: number,\n ...args: TArgs\n ) {\n const timeout = this.#setTimeout(() => {\n clearTimeout(timeout);\n this.#pendingTimeouts.delete(timeout);\n return fn(...args);\n }, timeoutMs);\n\n this.#pendingTimeouts.add(timeout);\n }\n\n /**\n * Returns a promise that resolves after `ms` milliseconds or when\n * the service is stopped.\n */\n async sleep(ms: number): Promise<void> {\n await Promise.race(this.#sleep(ms, this.#controller.signal));\n }\n\n /**\n * Called to stop the service. After this is called, {@link shouldRun()}\n * will return `false` and the {@link stopped()} Promise will be resolved.\n */\n stop(lc: LogContext, err?: unknown): void {\n if (this.shouldRun()) {\n const log = !err || err instanceof AbortError ? 'info' : 'error';\n lc[log]?.(`stopping ${this.#serviceName}`, err ?? '');\n this.#controller.abort();\n }\n }\n\n /**\n * Returns a Promise that resolves when {@link stop()} is called.\n */\n stopped(): Promise<void> {\n return this.#stopped;\n }\n\n /**\n * Call in response to an error or unexpected termination in the main\n * loop of the service. The returned Promise will resolve after an\n * exponential delay, or once {@link stop()} is called.\n *\n * If the supplied `err` is an `AbortError`, the service will shut down.\n */\n async backoff(lc: LogContext, err: unknown): Promise<void> {\n const delay = this.#retryDelay;\n this.#retryDelay = Math.min(delay * 2, this.#maxRetryDelay);\n\n if (err instanceof AbortError || err instanceof UnrecoverableError) {\n this.resetBackoff();\n this.stop(lc, err);\n } else if (this.shouldRun()) {\n // Use delay-based log level: higher delay means more retries\n const log: 'info' | 'warn' | 'error' =\n delay < 1000 ? 'info' : delay < 6500 ? 'warn' : 'error';\n\n lc[log]?.(`retrying ${this.#serviceName} in ${delay} ms`, err);\n await this.sleep(delay);\n }\n }\n\n /**\n * When using {@link backoff()}, this method should be called when the\n * implementation receives a healthy signal (e.g. a successful\n * response). This resets the delay used in {@link backoff()}.\n *\n * @returns The previous backoff delay\n */\n resetBackoff() {\n const prevDelay = this.#retryDelay;\n this.#retryDelay = this.#initialRetryDelay;\n return prevDelay;\n }\n}\n\n/**\n * Superclass for Errors that should bypass exponential backoff\n * and immediately shut down the server.\n */\nexport class UnrecoverableError extends Error {\n readonly name = 'UnrecoverableError';\n}\n"],"mappings":";;;;AAKA,IAAM,iCAAiC;AACvC,IAAa,6BAA6B;;;;AAgB1C,IAAa,eAAb,MAA0B;CACxB;CACA;CACA;CACA;CACA;CAEA;CACA;CACA,mCAA4B,IAAI,KAAqB;CACrD;CAEA,YACE,aACA,aACA,eAAe,YACf,UAAU,gBACV;EACA,MAAM,EACJ,oBAAoB,gCACpB,gBAAgB,+BACd,eAAe,EAAE;AAErB,QAAA,cAAoB;AACpB,QAAA,oBAA0B;AAC1B,QAAA,gBAAsB;AACtB,QAAA,aAAmB;AAEnB,QAAA,aAAmB,IAAI,iBAAiB;AACxC,QAAA,QAAc;AACd,QAAA,aAAmB;EAEnB,MAAM,EAAC,SAAS,YAAW,UAAU;AACrC,QAAA,UAAgB;AAChB,QAAA,WAAiB,OAAO,iBACtB,eACM;AACJ,YAAS;AACT,QAAK,MAAM,WAAW,MAAA,gBACpB,cAAa,QAAQ;AAEvB,SAAA,gBAAsB,OAAO;KAE/B,EAAC,MAAM,MAAK,CACb;;CAGH,IAAI,SAAsB;AACxB,SAAO,MAAA,WAAiB;;CAG1B,IAAI,aAAa;AACf,SAAO,MAAA;;;;;;;;CAST,YAAqB;AACnB,SAAO,CAAC,MAAA,WAAiB,OAAO;;;;;;CAOlC,aAAa,GAA6B;EACxC,MAAM,eAAe,EAAE,QAAQ;AAC/B,QAAA,WAAiB,OAAO,iBAAiB,SAAS,QAAQ,EAAC,MAAM,MAAK,CAAC;AACvE,eAAa,MAAA,WAAiB,OAAO,oBAAoB,SAAS,OAAO;;;;;CAM3E,WACE,IACA,WACA,GAAG,MACH;EACA,MAAM,UAAU,MAAA,iBAAuB;AACrC,gBAAa,QAAQ;AACrB,SAAA,gBAAsB,OAAO,QAAQ;AACrC,UAAO,GAAG,GAAG,KAAK;KACjB,UAAU;AAEb,QAAA,gBAAsB,IAAI,QAAQ;;;;;;CAOpC,MAAM,MAAM,IAA2B;AACrC,QAAM,QAAQ,KAAK,MAAA,MAAY,IAAI,MAAA,WAAiB,OAAO,CAAC;;;;;;CAO9D,KAAK,IAAgB,KAAqB;AACxC,MAAI,KAAK,WAAW,EAAE;AAEpB,MADY,CAAC,OAAO,eAAe,aAAa,SAAS,WAC/C,YAAY,MAAA,eAAqB,OAAO,GAAG;AACrD,SAAA,WAAiB,OAAO;;;;;;CAO5B,UAAyB;AACvB,SAAO,MAAA;;;;;;;;;CAUT,MAAM,QAAQ,IAAgB,KAA6B;EACzD,MAAM,QAAQ,MAAA;AACd,QAAA,aAAmB,KAAK,IAAI,QAAQ,GAAG,MAAA,cAAoB;AAE3D,MAAI,eAAe,cAAc,eAAe,oBAAoB;AAClE,QAAK,cAAc;AACnB,QAAK,KAAK,IAAI,IAAI;aACT,KAAK,WAAW,EAAE;AAK3B,MAFE,QAAQ,MAAO,SAAS,QAAQ,OAAO,SAAS,WAExC,YAAY,MAAA,YAAkB,MAAM,MAAM,MAAM,IAAI;AAC9D,SAAM,KAAK,MAAM,MAAM;;;;;;;;;;CAW3B,eAAe;EACb,MAAM,YAAY,MAAA;AAClB,QAAA,aAAmB,MAAA;AACnB,SAAO;;;;;;;AAQX,IAAa,qBAAb,cAAwC,MAAM;CAC5C,OAAgB"}
|
|
@@ -4,7 +4,7 @@ import { Database } from "../../../../zqlite/src/db.js";
|
|
|
4
4
|
import { getServerVersion, isAdminPasswordValid } from "../../config/zero-config.js";
|
|
5
5
|
import { StatementRunner } from "../../db/statements.js";
|
|
6
6
|
import { loadPermissions } from "../../auth/load-permissions.js";
|
|
7
|
-
import { _usingCtx } from "../../../../_virtual/_@oxc-project_runtime@0.
|
|
7
|
+
import { _usingCtx } from "../../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/usingCtx.js";
|
|
8
8
|
import { analyzeQuery } from "../analyze.js";
|
|
9
9
|
//#region ../zero-cache/src/services/view-syncer/inspect-handler.ts
|
|
10
10
|
async function handleInspect(lc, body, cvr, client, inspectorDelegate, clientGroupID, cvrStore, config, headerOptions, userQueryURL, auth) {
|
|
@@ -97,5 +97,6 @@ export declare function pgClient(lc: LogContext, connectionURI: string, options?
|
|
|
97
97
|
*/
|
|
98
98
|
export declare function disableStatementTimeout(sql: PostgresTransaction): void;
|
|
99
99
|
export declare const typeNameByOID: Record<number, string>;
|
|
100
|
+
export declare function isPostgresError(e: unknown, ...codes: [string, ...string[]]): boolean;
|
|
100
101
|
export {};
|
|
101
102
|
//# sourceMappingURL=pg.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pg.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/types/pg.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,QAAQ,EAAE,EAAc,KAAK,YAAY,EAAC,MAAM,UAAU,CAAC;AAClE,OAAO,EAAa,KAAK,SAAS,EAAC,MAAM,oCAAoC,CAAC;AAc9E,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAqC7D;AAED,iBAAS,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAuBhD;AAcD,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAoBvE;AAED,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAkFrE;AAED,iBAAS,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG/C;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,UAAU,CAAC;AAEvD,MAAM,MAAM,WAAW,GAAG;IACxB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,uBAAoB,WAAgB;;;;;;;;;;;;;;;;;;2BAwBlD,OAAO;;;;;;2BAOP,OAAO;;;;;;2BAQP,MAAM,GAAG,IAAI;;;;;;2BAUb,MAAM;uBACV,MAAM,GAAG,MAAM;;;CAG9B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;CACjB,CAAC,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC,cAAc,CAAC;IACxD,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;CACjB,CAAC,CAAC;AAEH,wBAAgB,QAAQ,CACtB,EAAE,EAAE,UAAU,EACd,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC;IACzB,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IAC7B,IAAI,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;CAC/B,CAAC,EACF,IAAI,CAAC,EAAE,WAAW,GACjB,UAAU,CA4CZ;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,mBAAmB,QAE/D;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAOhD,CAAC"}
|
|
1
|
+
{"version":3,"file":"pg.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/types/pg.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,QAAQ,EAAE,EAAc,KAAK,YAAY,EAAC,MAAM,UAAU,CAAC;AAClE,OAAO,EAAa,KAAK,SAAS,EAAC,MAAM,oCAAoC,CAAC;AAc9E,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAqC7D;AAED,iBAAS,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAuBhD;AAcD,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAoBvE;AAED,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAkFrE;AAED,iBAAS,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG/C;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,UAAU,CAAC;AAEvD,MAAM,MAAM,WAAW,GAAG;IACxB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,uBAAoB,WAAgB;;;;;;;;;;;;;;;;;;2BAwBlD,OAAO;;;;;;2BAOP,OAAO;;;;;;2BAQP,MAAM,GAAG,IAAI;;;;;;2BAUb,MAAM;uBACV,MAAM,GAAG,MAAM;;;CAG9B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;CACjB,CAAC,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC,cAAc,CAAC;IACxD,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;CACjB,CAAC,CAAC;AAEH,wBAAgB,QAAQ,CACtB,EAAE,EAAE,UAAU,EACd,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC;IACzB,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IAC7B,IAAI,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;CAC/B,CAAC,EACF,IAAI,CAAC,EAAE,WAAW,GACjB,UAAU,CA4CZ;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,mBAAmB,QAE/D;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAOhD,CAAC;AAEF,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,WAE1E"}
|
|
@@ -161,7 +161,10 @@ function pgClient(lc, connectionURI, options, opts) {
|
|
|
161
161
|
});
|
|
162
162
|
}
|
|
163
163
|
Object.freeze(Object.fromEntries(Object.entries(OID).map(([name, oid]) => [oid, name.startsWith("_") ? `${name.substring(1)}[]` : name])));
|
|
164
|
+
function isPostgresError(e, ...codes) {
|
|
165
|
+
return e instanceof postgres.PostgresError && codes.includes(e.code);
|
|
166
|
+
}
|
|
164
167
|
//#endregion
|
|
165
|
-
export { pgClient };
|
|
168
|
+
export { isPostgresError, pgClient };
|
|
166
169
|
|
|
167
170
|
//# sourceMappingURL=pg.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pg.js","names":[],"sources":["../../../../../zero-cache/src/types/pg.ts"],"sourcesContent":["import {PreciseDate} from '@google-cloud/precise-date';\nimport {OID} from '@postgresql-typed/oids';\nimport type {LogContext} from '@rocicorp/logger';\nimport postgres, {type Notice, type PostgresType} from 'postgres';\nimport {BigIntJSON, type JSONValue} from '../../../shared/src/bigint-json.ts';\nimport {randInt} from '../../../shared/src/rand.ts';\nimport {\n DATE,\n JSON,\n JSONB,\n NUMERIC,\n TIME,\n TIMESTAMP,\n TIMESTAMPTZ,\n TIMETZ,\n} from './pg-types.ts';\n\n// exported for testing.\nexport function timestampToFpMillis(timestamp: string): number {\n // Convert from PG's time string, e.g. \"1999-01-08 12:05:06+00\" to \"Z\"\n // format expected by PreciseDate.\n timestamp = timestamp.replace(' ', 'T');\n const positiveOffset = timestamp.includes('+');\n const tzSplitIndex = positiveOffset\n ? timestamp.lastIndexOf('+')\n : timestamp.indexOf('-', timestamp.indexOf('T'));\n const timezoneOffset =\n tzSplitIndex === -1 ? undefined : timestamp.substring(tzSplitIndex);\n const tsWithoutTimezone =\n (tzSplitIndex === -1 ? timestamp : timestamp.substring(0, tzSplitIndex)) +\n 'Z';\n\n try {\n // PreciseDate does not return microsecond precision unless the provided\n // timestamp is in UTC time so we need to add the timezone offset back in.\n const fullTime = new PreciseDate(tsWithoutTimezone).getFullTime();\n const millis = Number(fullTime / 1_000_000n);\n const nanos = Number(fullTime % 1_000_000n);\n const ret = millis + nanos * 1e-6; // floating point milliseconds\n\n // add back in the timezone offset\n if (timezoneOffset) {\n const [hours, minutes] = timezoneOffset.split(':');\n const offset =\n Math.abs(Number(hours)) * 60 + (minutes ? Number(minutes) : 0);\n const offsetMillis = offset * 60 * 1_000;\n // If it is a positive offset, we subtract the offset from the UTC\n // because we passed in the \"local time\" as if it was UTC.\n // The opposite is true for negative offsets.\n return positiveOffset ? ret - offsetMillis : ret + offsetMillis;\n }\n return ret;\n } catch (e) {\n throw new Error(`Error parsing ${timestamp}`, {cause: e});\n }\n}\n\nfunction serializeTimestamp(val: unknown): string {\n switch (typeof val) {\n case 'string':\n return val; // Let Postgres parse it\n case 'number': {\n if (Number.isInteger(val)) {\n return new PreciseDate(val).toISOString();\n }\n // Convert floating point to bigint nanoseconds.\n const nanoseconds =\n 1_000_000n * BigInt(Math.trunc(val)) +\n BigInt(Math.trunc((val % 1) * 1e6));\n return new PreciseDate(nanoseconds).toISOString();\n }\n // Note: Don't support bigint inputs until we decide what the semantics are (e.g. micros vs nanos)\n // case 'bigint':\n // return new PreciseDate(val).toISOString();\n default:\n if (val instanceof Date) {\n return val.toISOString();\n }\n }\n throw new Error(`Unsupported type \"${typeof val}\" for timestamp: ${val}`);\n}\n\nconst MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;\n\nfunction serializeTime(x: unknown, type: 'time' | 'timetz'): string {\n switch (typeof x) {\n case 'string':\n return x; // Let Postgres parse it\n case 'number':\n return millisecondsToPostgresTime(x);\n }\n throw new Error(`Unsupported type \"${typeof x}\" for ${type}: ${x}`);\n}\n\nexport function millisecondsToPostgresTime(milliseconds: number): string {\n if (milliseconds < 0) {\n throw new Error('Milliseconds cannot be negative');\n }\n\n if (milliseconds >= MILLISECONDS_PER_DAY) {\n throw new Error(\n `Milliseconds cannot exceed 24 hours (${MILLISECONDS_PER_DAY}ms)`,\n );\n }\n\n milliseconds = Math.floor(milliseconds); // Ensure it's an integer\n\n const totalSeconds = Math.floor(milliseconds / 1000);\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n const ms = milliseconds % 1000;\n\n return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}.${ms.toString().padStart(3, '0')}+00`;\n}\n\nexport function postgresTimeToMilliseconds(timeString: string): number {\n // Validate basic format\n if (!timeString || typeof timeString !== 'string') {\n throw new Error('Invalid time string: must be a non-empty string');\n }\n\n // Regular expression to match HH:MM:SS, HH:MM:SS.mmm, or HH:MM:SS+00 / HH:MM:SS.mmm+00\n // Supports optional timezone offset\n const timeRegex =\n /^(\\d{1,2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,6}))?(?:([+-])(\\d{1,2})(?::(\\d{2}))?)?$/;\n const match = timeString.match(timeRegex);\n\n if (!match) {\n throw new Error(\n `Invalid time format: \"${timeString}\". Expected HH:MM:SS[.mmm][+|-HH[:MM]]`,\n );\n }\n\n // Extract components\n const hours = parseInt(match[1], 10);\n const minutes = parseInt(match[2], 10);\n const seconds = parseInt(match[3], 10);\n // Handle optional milliseconds, pad right with zeros if needed\n let milliseconds = 0;\n if (match[4]) {\n // Pad microseconds to 6 digits\n const msString = match[4].padEnd(6, '0');\n // slice milliseconds out of the microseconds\n // e.g. 123456 -> 123, 1234 -> 123,\n milliseconds = parseInt(msString.slice(0, 3), 10);\n }\n\n // Validate ranges\n if (hours < 0 || hours > 24) {\n throw new Error(\n `Invalid hours: ${hours}. Must be between 0 and 24 (24 means end of day)`,\n );\n }\n\n if (minutes < 0 || minutes >= 60) {\n throw new Error(`Invalid minutes: ${minutes}. Must be between 0 and 59`);\n }\n\n if (seconds < 0 || seconds >= 60) {\n throw new Error(`Invalid seconds: ${seconds}. Must be between 0 and 59`);\n }\n\n if (milliseconds < 0 || milliseconds >= 1000) {\n throw new Error(\n `Invalid milliseconds: ${milliseconds}. Must be between 0 and 999`,\n );\n }\n\n // Special case: PostgreSQL allows 24:00:00 to represent end of day\n if (hours === 24 && (minutes !== 0 || seconds !== 0 || milliseconds !== 0)) {\n throw new Error(\n 'Invalid time: when hours is 24, minutes, seconds, and milliseconds must be 0',\n );\n }\n\n // Calculate total milliseconds\n let totalMs =\n hours * 3600000 + minutes * 60000 + seconds * 1000 + milliseconds;\n\n // Timezone Offset\n if (match[5]) {\n const sign = match[5] === '+' ? 1 : -1;\n const tzHours = parseInt(match[6], 10);\n const tzMinutes = match[7] ? parseInt(match[7], 10) : 0;\n const offsetMs = sign * (tzHours * 3600000 + tzMinutes * 60000);\n totalMs -= offsetMs;\n }\n\n // Normalize to 0-24h only if outside valid range\n if (totalMs > MILLISECONDS_PER_DAY || totalMs < 0) {\n return (\n ((totalMs % MILLISECONDS_PER_DAY) + MILLISECONDS_PER_DAY) %\n MILLISECONDS_PER_DAY\n );\n }\n\n return totalMs;\n}\n\nfunction dateToUTCMidnight(date: string): number {\n const d = new Date(date);\n return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());\n}\n\n/**\n * The (javascript) types of objects that can be returned by our configured\n * Postgres clients. For initial-sync, these comes from the postgres.js client:\n *\n * https://github.com/porsager/postgres/blob/master/src/types.js\n *\n * and for the replication stream these come from the the node-postgres client:\n *\n * https://github.com/brianc/node-pg-types/blob/master/lib/textParsers.js\n */\nexport type PostgresValueType = JSONValue | Uint8Array;\n\nexport type TypeOptions = {\n /**\n * Sends strings directly as JSON values (i.e. without JSON stringification).\n * The application is responsible for ensuring that string inputs for JSON\n * columns are already stringified. Other data types (e.g. objects) will\n * still be stringified by the pg client.\n */\n sendStringAsJson?: boolean;\n};\n\n/**\n * Configures types for the Postgres.js client library (`postgres`).\n *\n * @param jsonAsString Keep JSON / JSONB values as strings instead of parsing.\n */\nexport const postgresTypeConfig = ({sendStringAsJson}: TypeOptions = {}) => ({\n // Type the type IDs as `number` so that Typescript doesn't complain about\n // referencing external types during type inference.\n types: {\n bigint: postgres.BigInt,\n json: {\n to: JSON,\n from: [JSON, JSONB],\n serialize: sendStringAsJson\n ? (x: unknown) => (typeof x === 'string' ? x : BigIntJSON.stringify(x))\n : BigIntJSON.stringify,\n parse: BigIntJSON.parse,\n },\n // Timestamps are converted to PreciseDate objects.\n timestamp: {\n to: TIMESTAMP,\n from: [TIMESTAMP, TIMESTAMPTZ],\n serialize: serializeTimestamp,\n parse: timestampToFpMillis,\n },\n // Times are converted as strings\n time: {\n to: TIME,\n from: [TIME, TIMETZ],\n serialize: (x: unknown) => serializeTime(x, 'time'),\n parse: postgresTimeToMilliseconds,\n },\n\n timetz: {\n to: TIMETZ,\n from: [TIME, TIMETZ],\n serialize: (x: unknown) => serializeTime(x, 'timetz'),\n parse: postgresTimeToMilliseconds,\n },\n\n // The DATE type is stored directly as the PG normalized date string.\n date: {\n to: DATE,\n from: [DATE],\n serialize: (x: string | Date) =>\n (x instanceof Date ? x : new Date(x)).toISOString(),\n parse: dateToUTCMidnight,\n },\n // Returns a `js` number which can lose precision for large numbers.\n // JS number is 53 bits so this should generally not occur.\n // An API will be provided for users to override this type.\n numeric: {\n to: NUMERIC,\n from: [NUMERIC],\n serialize: (x: number) => String(x), // pg expects a string\n parse: (x: string | number) => Number(x),\n },\n },\n});\n\nexport type PostgresDB = postgres.Sql<{\n bigint: bigint;\n json: JSONValue;\n}>;\n\nexport type PostgresTransaction = postgres.TransactionSql<{\n bigint: bigint;\n json: JSONValue;\n}>;\n\nexport function pgClient(\n lc: LogContext,\n connectionURI: string,\n options?: postgres.Options<{\n bigint: PostgresType<bigint>;\n json: PostgresType<JSONValue>;\n }>,\n opts?: TypeOptions,\n): PostgresDB {\n const onnotice = (n: Notice) => {\n // https://www.postgresql.org/docs/current/plpgsql-errors-and-messages.html#PLPGSQL-STATEMENTS-RAISE\n switch (n.severity) {\n case 'NOTICE':\n return; // silenced\n case 'DEBUG':\n lc.debug?.(n);\n return;\n case 'WARNING':\n lc.warn?.(n);\n return;\n case 'EXCEPTION':\n lc.error?.(n);\n return;\n case 'LOG':\n case 'INFO':\n default:\n lc.info?.(n);\n }\n };\n const url = new URL(connectionURI);\n const sslFlag =\n url.searchParams.get('ssl') ?? url.searchParams.get('sslmode') ?? 'prefer';\n\n let ssl: boolean | 'prefer' | {rejectUnauthorized: boolean};\n if (sslFlag === 'disable' || sslFlag === 'false') {\n ssl = false;\n } else if (sslFlag === 'no-verify') {\n ssl = {rejectUnauthorized: false};\n } else {\n ssl = sslFlag as 'prefer';\n }\n\n // Set connections to expire between 5 and 10 minutes to free up state on PG.\n const maxLifetimeSeconds = randInt(5 * 60, 10 * 60);\n\n return postgres(connectionURI, {\n ...postgresTypeConfig(opts),\n onnotice,\n ['max_lifetime']: maxLifetimeSeconds,\n ssl,\n ...options,\n });\n}\n\n/**\n * Disables any statement_timeout for the current transaction. By default,\n * Postgres does not impose a statement timeout, but some users and providers\n * set one at the database level (even though it is explicitly discouraged by\n * the Postgres documentation).\n *\n * Zero logic in particular often does not fit into the category of general\n * application logic; for potentially long-running operations like migrations\n * and background cleanup, the statement timeout should be disabled to prevent\n * these operations from timing out.\n */\nexport function disableStatementTimeout(sql: PostgresTransaction) {\n void sql`SET LOCAL statement_timeout = 0;`.execute();\n}\n\nexport const typeNameByOID: Record<number, string> = Object.freeze(\n Object.fromEntries(\n Object.entries(OID).map(([name, oid]) => [\n oid,\n name.startsWith('_') ? `${name.substring(1)}[]` : name,\n ]),\n ),\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pg.js","names":[],"sources":["../../../../../zero-cache/src/types/pg.ts"],"sourcesContent":["import {PreciseDate} from '@google-cloud/precise-date';\nimport {OID} from '@postgresql-typed/oids';\nimport type {LogContext} from '@rocicorp/logger';\nimport postgres, {type Notice, type PostgresType} from 'postgres';\nimport {BigIntJSON, type JSONValue} from '../../../shared/src/bigint-json.ts';\nimport {randInt} from '../../../shared/src/rand.ts';\nimport {\n DATE,\n JSON,\n JSONB,\n NUMERIC,\n TIME,\n TIMESTAMP,\n TIMESTAMPTZ,\n TIMETZ,\n} from './pg-types.ts';\n\n// exported for testing.\nexport function timestampToFpMillis(timestamp: string): number {\n // Convert from PG's time string, e.g. \"1999-01-08 12:05:06+00\" to \"Z\"\n // format expected by PreciseDate.\n timestamp = timestamp.replace(' ', 'T');\n const positiveOffset = timestamp.includes('+');\n const tzSplitIndex = positiveOffset\n ? timestamp.lastIndexOf('+')\n : timestamp.indexOf('-', timestamp.indexOf('T'));\n const timezoneOffset =\n tzSplitIndex === -1 ? undefined : timestamp.substring(tzSplitIndex);\n const tsWithoutTimezone =\n (tzSplitIndex === -1 ? timestamp : timestamp.substring(0, tzSplitIndex)) +\n 'Z';\n\n try {\n // PreciseDate does not return microsecond precision unless the provided\n // timestamp is in UTC time so we need to add the timezone offset back in.\n const fullTime = new PreciseDate(tsWithoutTimezone).getFullTime();\n const millis = Number(fullTime / 1_000_000n);\n const nanos = Number(fullTime % 1_000_000n);\n const ret = millis + nanos * 1e-6; // floating point milliseconds\n\n // add back in the timezone offset\n if (timezoneOffset) {\n const [hours, minutes] = timezoneOffset.split(':');\n const offset =\n Math.abs(Number(hours)) * 60 + (minutes ? Number(minutes) : 0);\n const offsetMillis = offset * 60 * 1_000;\n // If it is a positive offset, we subtract the offset from the UTC\n // because we passed in the \"local time\" as if it was UTC.\n // The opposite is true for negative offsets.\n return positiveOffset ? ret - offsetMillis : ret + offsetMillis;\n }\n return ret;\n } catch (e) {\n throw new Error(`Error parsing ${timestamp}`, {cause: e});\n }\n}\n\nfunction serializeTimestamp(val: unknown): string {\n switch (typeof val) {\n case 'string':\n return val; // Let Postgres parse it\n case 'number': {\n if (Number.isInteger(val)) {\n return new PreciseDate(val).toISOString();\n }\n // Convert floating point to bigint nanoseconds.\n const nanoseconds =\n 1_000_000n * BigInt(Math.trunc(val)) +\n BigInt(Math.trunc((val % 1) * 1e6));\n return new PreciseDate(nanoseconds).toISOString();\n }\n // Note: Don't support bigint inputs until we decide what the semantics are (e.g. micros vs nanos)\n // case 'bigint':\n // return new PreciseDate(val).toISOString();\n default:\n if (val instanceof Date) {\n return val.toISOString();\n }\n }\n throw new Error(`Unsupported type \"${typeof val}\" for timestamp: ${val}`);\n}\n\nconst MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;\n\nfunction serializeTime(x: unknown, type: 'time' | 'timetz'): string {\n switch (typeof x) {\n case 'string':\n return x; // Let Postgres parse it\n case 'number':\n return millisecondsToPostgresTime(x);\n }\n throw new Error(`Unsupported type \"${typeof x}\" for ${type}: ${x}`);\n}\n\nexport function millisecondsToPostgresTime(milliseconds: number): string {\n if (milliseconds < 0) {\n throw new Error('Milliseconds cannot be negative');\n }\n\n if (milliseconds >= MILLISECONDS_PER_DAY) {\n throw new Error(\n `Milliseconds cannot exceed 24 hours (${MILLISECONDS_PER_DAY}ms)`,\n );\n }\n\n milliseconds = Math.floor(milliseconds); // Ensure it's an integer\n\n const totalSeconds = Math.floor(milliseconds / 1000);\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n const ms = milliseconds % 1000;\n\n return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}.${ms.toString().padStart(3, '0')}+00`;\n}\n\nexport function postgresTimeToMilliseconds(timeString: string): number {\n // Validate basic format\n if (!timeString || typeof timeString !== 'string') {\n throw new Error('Invalid time string: must be a non-empty string');\n }\n\n // Regular expression to match HH:MM:SS, HH:MM:SS.mmm, or HH:MM:SS+00 / HH:MM:SS.mmm+00\n // Supports optional timezone offset\n const timeRegex =\n /^(\\d{1,2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,6}))?(?:([+-])(\\d{1,2})(?::(\\d{2}))?)?$/;\n const match = timeString.match(timeRegex);\n\n if (!match) {\n throw new Error(\n `Invalid time format: \"${timeString}\". Expected HH:MM:SS[.mmm][+|-HH[:MM]]`,\n );\n }\n\n // Extract components\n const hours = parseInt(match[1], 10);\n const minutes = parseInt(match[2], 10);\n const seconds = parseInt(match[3], 10);\n // Handle optional milliseconds, pad right with zeros if needed\n let milliseconds = 0;\n if (match[4]) {\n // Pad microseconds to 6 digits\n const msString = match[4].padEnd(6, '0');\n // slice milliseconds out of the microseconds\n // e.g. 123456 -> 123, 1234 -> 123,\n milliseconds = parseInt(msString.slice(0, 3), 10);\n }\n\n // Validate ranges\n if (hours < 0 || hours > 24) {\n throw new Error(\n `Invalid hours: ${hours}. Must be between 0 and 24 (24 means end of day)`,\n );\n }\n\n if (minutes < 0 || minutes >= 60) {\n throw new Error(`Invalid minutes: ${minutes}. Must be between 0 and 59`);\n }\n\n if (seconds < 0 || seconds >= 60) {\n throw new Error(`Invalid seconds: ${seconds}. Must be between 0 and 59`);\n }\n\n if (milliseconds < 0 || milliseconds >= 1000) {\n throw new Error(\n `Invalid milliseconds: ${milliseconds}. Must be between 0 and 999`,\n );\n }\n\n // Special case: PostgreSQL allows 24:00:00 to represent end of day\n if (hours === 24 && (minutes !== 0 || seconds !== 0 || milliseconds !== 0)) {\n throw new Error(\n 'Invalid time: when hours is 24, minutes, seconds, and milliseconds must be 0',\n );\n }\n\n // Calculate total milliseconds\n let totalMs =\n hours * 3600000 + minutes * 60000 + seconds * 1000 + milliseconds;\n\n // Timezone Offset\n if (match[5]) {\n const sign = match[5] === '+' ? 1 : -1;\n const tzHours = parseInt(match[6], 10);\n const tzMinutes = match[7] ? parseInt(match[7], 10) : 0;\n const offsetMs = sign * (tzHours * 3600000 + tzMinutes * 60000);\n totalMs -= offsetMs;\n }\n\n // Normalize to 0-24h only if outside valid range\n if (totalMs > MILLISECONDS_PER_DAY || totalMs < 0) {\n return (\n ((totalMs % MILLISECONDS_PER_DAY) + MILLISECONDS_PER_DAY) %\n MILLISECONDS_PER_DAY\n );\n }\n\n return totalMs;\n}\n\nfunction dateToUTCMidnight(date: string): number {\n const d = new Date(date);\n return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());\n}\n\n/**\n * The (javascript) types of objects that can be returned by our configured\n * Postgres clients. For initial-sync, these comes from the postgres.js client:\n *\n * https://github.com/porsager/postgres/blob/master/src/types.js\n *\n * and for the replication stream these come from the the node-postgres client:\n *\n * https://github.com/brianc/node-pg-types/blob/master/lib/textParsers.js\n */\nexport type PostgresValueType = JSONValue | Uint8Array;\n\nexport type TypeOptions = {\n /**\n * Sends strings directly as JSON values (i.e. without JSON stringification).\n * The application is responsible for ensuring that string inputs for JSON\n * columns are already stringified. Other data types (e.g. objects) will\n * still be stringified by the pg client.\n */\n sendStringAsJson?: boolean;\n};\n\n/**\n * Configures types for the Postgres.js client library (`postgres`).\n *\n * @param jsonAsString Keep JSON / JSONB values as strings instead of parsing.\n */\nexport const postgresTypeConfig = ({sendStringAsJson}: TypeOptions = {}) => ({\n // Type the type IDs as `number` so that Typescript doesn't complain about\n // referencing external types during type inference.\n types: {\n bigint: postgres.BigInt,\n json: {\n to: JSON,\n from: [JSON, JSONB],\n serialize: sendStringAsJson\n ? (x: unknown) => (typeof x === 'string' ? x : BigIntJSON.stringify(x))\n : BigIntJSON.stringify,\n parse: BigIntJSON.parse,\n },\n // Timestamps are converted to PreciseDate objects.\n timestamp: {\n to: TIMESTAMP,\n from: [TIMESTAMP, TIMESTAMPTZ],\n serialize: serializeTimestamp,\n parse: timestampToFpMillis,\n },\n // Times are converted as strings\n time: {\n to: TIME,\n from: [TIME, TIMETZ],\n serialize: (x: unknown) => serializeTime(x, 'time'),\n parse: postgresTimeToMilliseconds,\n },\n\n timetz: {\n to: TIMETZ,\n from: [TIME, TIMETZ],\n serialize: (x: unknown) => serializeTime(x, 'timetz'),\n parse: postgresTimeToMilliseconds,\n },\n\n // The DATE type is stored directly as the PG normalized date string.\n date: {\n to: DATE,\n from: [DATE],\n serialize: (x: string | Date) =>\n (x instanceof Date ? x : new Date(x)).toISOString(),\n parse: dateToUTCMidnight,\n },\n // Returns a `js` number which can lose precision for large numbers.\n // JS number is 53 bits so this should generally not occur.\n // An API will be provided for users to override this type.\n numeric: {\n to: NUMERIC,\n from: [NUMERIC],\n serialize: (x: number) => String(x), // pg expects a string\n parse: (x: string | number) => Number(x),\n },\n },\n});\n\nexport type PostgresDB = postgres.Sql<{\n bigint: bigint;\n json: JSONValue;\n}>;\n\nexport type PostgresTransaction = postgres.TransactionSql<{\n bigint: bigint;\n json: JSONValue;\n}>;\n\nexport function pgClient(\n lc: LogContext,\n connectionURI: string,\n options?: postgres.Options<{\n bigint: PostgresType<bigint>;\n json: PostgresType<JSONValue>;\n }>,\n opts?: TypeOptions,\n): PostgresDB {\n const onnotice = (n: Notice) => {\n // https://www.postgresql.org/docs/current/plpgsql-errors-and-messages.html#PLPGSQL-STATEMENTS-RAISE\n switch (n.severity) {\n case 'NOTICE':\n return; // silenced\n case 'DEBUG':\n lc.debug?.(n);\n return;\n case 'WARNING':\n lc.warn?.(n);\n return;\n case 'EXCEPTION':\n lc.error?.(n);\n return;\n case 'LOG':\n case 'INFO':\n default:\n lc.info?.(n);\n }\n };\n const url = new URL(connectionURI);\n const sslFlag =\n url.searchParams.get('ssl') ?? url.searchParams.get('sslmode') ?? 'prefer';\n\n let ssl: boolean | 'prefer' | {rejectUnauthorized: boolean};\n if (sslFlag === 'disable' || sslFlag === 'false') {\n ssl = false;\n } else if (sslFlag === 'no-verify') {\n ssl = {rejectUnauthorized: false};\n } else {\n ssl = sslFlag as 'prefer';\n }\n\n // Set connections to expire between 5 and 10 minutes to free up state on PG.\n const maxLifetimeSeconds = randInt(5 * 60, 10 * 60);\n\n return postgres(connectionURI, {\n ...postgresTypeConfig(opts),\n onnotice,\n ['max_lifetime']: maxLifetimeSeconds,\n ssl,\n ...options,\n });\n}\n\n/**\n * Disables any statement_timeout for the current transaction. By default,\n * Postgres does not impose a statement timeout, but some users and providers\n * set one at the database level (even though it is explicitly discouraged by\n * the Postgres documentation).\n *\n * Zero logic in particular often does not fit into the category of general\n * application logic; for potentially long-running operations like migrations\n * and background cleanup, the statement timeout should be disabled to prevent\n * these operations from timing out.\n */\nexport function disableStatementTimeout(sql: PostgresTransaction) {\n void sql`SET LOCAL statement_timeout = 0;`.execute();\n}\n\nexport const typeNameByOID: Record<number, string> = Object.freeze(\n Object.fromEntries(\n Object.entries(OID).map(([name, oid]) => [\n oid,\n name.startsWith('_') ? `${name.substring(1)}[]` : name,\n ]),\n ),\n);\n\nexport function isPostgresError(e: unknown, ...codes: [string, ...string[]]) {\n return e instanceof postgres.PostgresError && codes.includes(e.code);\n}\n"],"mappings":"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"}
|
|
@@ -6,7 +6,7 @@ import { isProtocolError } from "../../../zero-protocol/src/error.js";
|
|
|
6
6
|
import "../../../zero-protocol/src/protocol-version.js";
|
|
7
7
|
import { ProtocolErrorWithLevel, getLogLevel, wrapWithProtocolError } from "../types/error-with-level.js";
|
|
8
8
|
import { upstreamSchema } from "../../../zero-protocol/src/up.js";
|
|
9
|
-
import WebSocket, { createWebSocketStream } from "ws";
|
|
9
|
+
import WebSocket$1, { createWebSocketStream } from "ws";
|
|
10
10
|
import { Readable, Writable, pipeline } from "node:stream";
|
|
11
11
|
//#region ../zero-cache/src/workers/connection.ts
|
|
12
12
|
var DOWNSTREAM_MSG_INTERVAL_MS = 6e3;
|
|
@@ -180,7 +180,7 @@ var Connection = class {
|
|
|
180
180
|
}
|
|
181
181
|
};
|
|
182
182
|
function send(lc, ws, data, callback) {
|
|
183
|
-
if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify(data), callback === "ignore-backpressure" ? void 0 : callback);
|
|
183
|
+
if (ws.readyState === WebSocket$1.OPEN) ws.send(JSON.stringify(data), callback === "ignore-backpressure" ? void 0 : callback);
|
|
184
184
|
else {
|
|
185
185
|
lc.debug?.(`Dropping outbound message on ws (state: ${ws.readyState})`, { dropped: data });
|
|
186
186
|
if (callback !== "ignore-backpressure") callback(new ProtocolErrorWithLevel({
|