@rocicorp/zero 0.20.2025050600 → 0.20.2025050800
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/{chunk-VA2YVVTK.js → chunk-GK3QDPY3.js} +2 -2
- package/out/shared/src/options.d.ts +1 -1
- package/out/shared/src/options.d.ts.map +1 -1
- package/out/shared/src/options.js +27 -15
- package/out/shared/src/options.js.map +1 -1
- package/out/solid.js +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +0 -8
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +3 -17
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/server/change-streamer.d.ts +1 -1
- package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +20 -10
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/main.d.ts.map +1 -1
- package/out/zero-cache/src/server/main.js +7 -13
- package/out/zero-cache/src/server/main.js.map +1 -1
- package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/server/replicator.js +1 -9
- package/out/zero-cache/src/server/replicator.js.map +1 -1
- package/out/zero-cache/src/server/worker-dispatcher.d.ts +1 -0
- package/out/zero-cache/src/server/worker-dispatcher.d.ts.map +1 -1
- package/out/zero-cache/src/server/worker-dispatcher.js +6 -2
- package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts +37 -2
- package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js +134 -69
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +5 -3
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +76 -26
- 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.d.ts +6 -1
- 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 +3 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot.d.ts +23 -0
- package/out/zero-cache/src/services/change-streamer/snapshot.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/snapshot.js +19 -0
- package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -0
- package/out/zero-cache/src/services/litestream/commands.d.ts +3 -5
- package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.js +70 -15
- package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.d.ts +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.js +5 -2
- 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 +1 -1
- package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +2 -2
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/streams.js +13 -4
- package/out/zero-cache/src/types/streams.js.map +1 -1
- package/out/zero-cache/src/types/websocket-handoff.d.ts +1 -1
- package/out/zero-cache/src/types/websocket-handoff.d.ts.map +1 -1
- package/out/zero-cache/src/types/websocket-handoff.js +1 -1
- package/out/zero-cache/src/types/websocket-handoff.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/workers/syncer.js +1 -1
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-pg/src/custom.d.ts.map +1 -1
- package/out/zero-pg/src/custom.js +2 -0
- package/out/zero-pg/src/custom.js.map +1 -1
- package/out/zero-pg/src/push-processor.d.ts.map +1 -1
- package/out/zero-pg/src/push-processor.js +2 -1
- package/out/zero-pg/src/push-processor.js.map +1 -1
- package/out/zero.js +1 -1
- package/package.json +1 -1
- /package/out/{chunk-VA2YVVTK.js.map → chunk-GK3QDPY3.js.map} +0 -0
|
@@ -2,7 +2,6 @@ import { LogContext } from '@rocicorp/logger';
|
|
|
2
2
|
import UrlPattern from 'url-pattern';
|
|
3
3
|
import { assert } from "../../../shared/src/asserts.js";
|
|
4
4
|
import { h32 } from "../../../shared/src/hash.js";
|
|
5
|
-
import { getSubscriberContext } from "../services/change-streamer/change-streamer-http.js";
|
|
6
5
|
import { RunningState } from "../services/running-state.js";
|
|
7
6
|
import { createWebSocketHandoffHandler, } from "../types/websocket-handoff.js";
|
|
8
7
|
import { getConnectParams } from "../workers/connect-params.js";
|
|
@@ -55,8 +54,13 @@ export class WorkerDispatcher {
|
|
|
55
54
|
assert(syncers.length === 0 && mutator === undefined, 'Dispatch to the change-streamer via the main port ' +
|
|
56
55
|
'is only allowed in multi-node mode');
|
|
57
56
|
assert(changeStreamer, 'Received a change-streamer request without a change-streamer worker');
|
|
57
|
+
const url = new URL(req.url ?? '', 'http://unused/');
|
|
58
|
+
const path = parsePath(url);
|
|
59
|
+
if (!path) {
|
|
60
|
+
throw new Error(`Invalid URL: ${req.url}`);
|
|
61
|
+
}
|
|
58
62
|
return {
|
|
59
|
-
payload:
|
|
63
|
+
payload: path.action,
|
|
60
64
|
receiver: changeStreamer,
|
|
61
65
|
};
|
|
62
66
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-dispatcher.js","sourceRoot":"","sources":["../../../../../zero-cache/src/server/worker-dispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAE5C,OAAO,UAAU,MAAM,aAAa,CAAC;AACrC,OAAO,EAAC,MAAM,EAAC,MAAM,gCAAgC,CAAC;AACtD,OAAO,EAAC,GAAG,EAAC,MAAM,6BAA6B,CAAC;AAChD,OAAO,EAAC,
|
|
1
|
+
{"version":3,"file":"worker-dispatcher.js","sourceRoot":"","sources":["../../../../../zero-cache/src/server/worker-dispatcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,48 @@
|
|
|
1
1
|
import type { LogContext } from '@rocicorp/logger';
|
|
2
|
+
import { Subscription } from '../../types/subscription.ts';
|
|
2
3
|
import type { Service } from '../service.ts';
|
|
3
4
|
import type { ChangeStreamerService } from './change-streamer.ts';
|
|
5
|
+
import type { SnapshotMessage } from './snapshot.ts';
|
|
4
6
|
export declare const CHECK_INTERVAL_MS: number;
|
|
7
|
+
/**
|
|
8
|
+
* The BackupMonitor polls the litestream "/metrics" endpoint to track the
|
|
9
|
+
* watermark (label) value of the `litestream_replica_progress` gauge and
|
|
10
|
+
* schedules cleanup of change log entries that can be purged as a result.
|
|
11
|
+
*
|
|
12
|
+
* See: https: *github.com/rocicorp/litestream/pull/3
|
|
13
|
+
*
|
|
14
|
+
* Note that change log entries cannot simply be purged as soon as they
|
|
15
|
+
* have been applied and backed up by litestream. Consider the case in which
|
|
16
|
+
* litestream backs up new wal segments every minute, but it takes 5 minutes
|
|
17
|
+
* to restore a replica: if a zero-cache starts restoring a replica at
|
|
18
|
+
* minute 0, and new watermarks are replicated at minutes 1, 2, 3, 4, and 5,
|
|
19
|
+
* purging changelog records as soon as those watermarks are replicated would
|
|
20
|
+
* result in the zero-cache not being able to catch up from minute 0 once it
|
|
21
|
+
* has finished restoring the replica.
|
|
22
|
+
*
|
|
23
|
+
* The `/snapshot` reservation protocol is used to prevent premature change
|
|
24
|
+
* log cleanup:
|
|
25
|
+
* - Clients restoring a snapshot initiate a `/snapshot` request and hold that
|
|
26
|
+
* request open while it restores its snapshot, prepares it, and
|
|
27
|
+
* starts its subscription to the change stream. During this time, no
|
|
28
|
+
* cleanups are scheduled.
|
|
29
|
+
* - When the subscription is started, the interval since the beginning of
|
|
30
|
+
* of the reservation is tracked to increase the background cleanup delay
|
|
31
|
+
* interval if needed. The reservation is ended (and request closed), and
|
|
32
|
+
* cleanup scheduling is resumed with the current delay interval.
|
|
33
|
+
*
|
|
34
|
+
* Note that the reservation request is the primary mechanism by which
|
|
35
|
+
* premature change log cleanup is prevented. The cleanup delay interval is
|
|
36
|
+
* a secondary safeguard.
|
|
37
|
+
*/
|
|
5
38
|
export declare class BackupMonitor implements Service {
|
|
6
39
|
#private;
|
|
7
40
|
readonly id = "backup-monitor";
|
|
8
|
-
constructor(lc: LogContext, metricsEndpoint: string, changeStreamer: ChangeStreamerService,
|
|
41
|
+
constructor(lc: LogContext, backupURL: string, metricsEndpoint: string, changeStreamer: ChangeStreamerService, initialCleanupDelayMs: number);
|
|
9
42
|
run(): Promise<void>;
|
|
10
|
-
|
|
43
|
+
startSnapshotReservation(taskID: string): Subscription<SnapshotMessage>;
|
|
44
|
+
endReservation(taskID: string, updateCleanupDelay?: boolean): void;
|
|
45
|
+
readonly checkWatermarksAndScheduleCleanup: () => Promise<void>;
|
|
11
46
|
stop(): Promise<void>;
|
|
12
47
|
}
|
|
13
48
|
//# sourceMappingURL=backup-monitor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backup-monitor.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/backup-monitor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"backup-monitor.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/backup-monitor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAGjD,OAAO,EAAC,YAAY,EAAC,MAAM,6BAA6B,CAAC;AAEzD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,sBAAsB,CAAC;AAChE,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,eAAe,CAAC;AAEnD,eAAO,MAAM,iBAAiB,QAAY,CAAC;AAQ3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,aAAc,YAAW,OAAO;;IAC3C,QAAQ,CAAC,EAAE,oBAAoB;gBAe7B,EAAE,EAAE,UAAU,EACd,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,qBAAqB,EACrC,qBAAqB,EAAE,MAAM;IAgB/B,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAYpB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAAC,eAAe,CAAC;IAgBvE,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,kBAAkB,UAAO;IAoBxD,QAAQ,CAAC,iCAAiC,sBAWxC;IAgEF,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAYtB"}
|
|
@@ -1,102 +1,167 @@
|
|
|
1
1
|
import parsePrometheusTextFormat from 'parse-prometheus-text-format';
|
|
2
2
|
import { promiseVoid } from "../../../../shared/src/resolved-promises.js";
|
|
3
|
+
import { Subscription } from "../../types/subscription.js";
|
|
3
4
|
import { RunningState } from "../running-state.js";
|
|
4
5
|
export const CHECK_INTERVAL_MS = 60 * 1000;
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
6
|
+
const MIN_CLEANUP_DELAY_MS = 30 * 1000;
|
|
7
|
+
/**
|
|
8
|
+
* The BackupMonitor polls the litestream "/metrics" endpoint to track the
|
|
9
|
+
* watermark (label) value of the `litestream_replica_progress` gauge and
|
|
10
|
+
* schedules cleanup of change log entries that can be purged as a result.
|
|
11
|
+
*
|
|
12
|
+
* See: https: *github.com/rocicorp/litestream/pull/3
|
|
13
|
+
*
|
|
14
|
+
* Note that change log entries cannot simply be purged as soon as they
|
|
15
|
+
* have been applied and backed up by litestream. Consider the case in which
|
|
16
|
+
* litestream backs up new wal segments every minute, but it takes 5 minutes
|
|
17
|
+
* to restore a replica: if a zero-cache starts restoring a replica at
|
|
18
|
+
* minute 0, and new watermarks are replicated at minutes 1, 2, 3, 4, and 5,
|
|
19
|
+
* purging changelog records as soon as those watermarks are replicated would
|
|
20
|
+
* result in the zero-cache not being able to catch up from minute 0 once it
|
|
21
|
+
* has finished restoring the replica.
|
|
22
|
+
*
|
|
23
|
+
* The `/snapshot` reservation protocol is used to prevent premature change
|
|
24
|
+
* log cleanup:
|
|
25
|
+
* - Clients restoring a snapshot initiate a `/snapshot` request and hold that
|
|
26
|
+
* request open while it restores its snapshot, prepares it, and
|
|
27
|
+
* starts its subscription to the change stream. During this time, no
|
|
28
|
+
* cleanups are scheduled.
|
|
29
|
+
* - When the subscription is started, the interval since the beginning of
|
|
30
|
+
* of the reservation is tracked to increase the background cleanup delay
|
|
31
|
+
* interval if needed. The reservation is ended (and request closed), and
|
|
32
|
+
* cleanup scheduling is resumed with the current delay interval.
|
|
33
|
+
*
|
|
34
|
+
* Note that the reservation request is the primary mechanism by which
|
|
35
|
+
* premature change log cleanup is prevented. The cleanup delay interval is
|
|
36
|
+
* a secondary safeguard.
|
|
37
|
+
*/
|
|
31
38
|
export class BackupMonitor {
|
|
32
39
|
id = 'backup-monitor';
|
|
33
40
|
#lc;
|
|
41
|
+
#backupURL;
|
|
34
42
|
#metricsEndpoint;
|
|
35
43
|
#changeStreamer;
|
|
36
44
|
#state = new RunningState(this.id);
|
|
45
|
+
#reservations = new Map();
|
|
46
|
+
#watermarks = new Map();
|
|
47
|
+
#lastWatermark = '';
|
|
37
48
|
#cleanupDelayMs;
|
|
38
|
-
#cleanupTimers = new Map();
|
|
39
49
|
#checkMetricsTimer;
|
|
40
|
-
constructor(lc, metricsEndpoint, changeStreamer,
|
|
50
|
+
constructor(lc, backupURL, metricsEndpoint, changeStreamer, initialCleanupDelayMs) {
|
|
41
51
|
this.#lc = lc.withContext('component', this.id);
|
|
52
|
+
this.#backupURL = backupURL;
|
|
42
53
|
this.#metricsEndpoint = metricsEndpoint;
|
|
43
54
|
this.#changeStreamer = changeStreamer;
|
|
44
|
-
this.#cleanupDelayMs =
|
|
55
|
+
this.#cleanupDelayMs = Math.max(initialCleanupDelayMs, MIN_CLEANUP_DELAY_MS);
|
|
56
|
+
this.#lc.info?.(`backup monitor started ${initialCleanupDelayMs} ms after snapshot restore`);
|
|
45
57
|
}
|
|
46
58
|
run() {
|
|
47
59
|
this.#lc.info?.(`monitoring backups at ${this.#metricsEndpoint} with ` +
|
|
48
60
|
`${this.#cleanupDelayMs} ms cleanup delay`);
|
|
49
|
-
this.#checkMetricsTimer = setInterval(this.
|
|
61
|
+
this.#checkMetricsTimer = setInterval(this.checkWatermarksAndScheduleCleanup, CHECK_INTERVAL_MS);
|
|
50
62
|
return this.#state.stopped();
|
|
51
63
|
}
|
|
64
|
+
startSnapshotReservation(taskID) {
|
|
65
|
+
this.#lc.info?.(`pausing change-log cleanup while ${taskID} snapshots`);
|
|
66
|
+
// In the case of retries, only track the last reservation.
|
|
67
|
+
this.#reservations.get(taskID)?.sub.cancel();
|
|
68
|
+
const sub = Subscription.create({
|
|
69
|
+
// If the reservation still exists when the connection closes
|
|
70
|
+
// (e.g. subscriber crashed), clean it up without updating the
|
|
71
|
+
// cleanup delay.
|
|
72
|
+
cleanup: () => this.endReservation(taskID, false),
|
|
73
|
+
});
|
|
74
|
+
this.#reservations.set(taskID, { start: new Date(), sub });
|
|
75
|
+
sub.push(['status', { tag: 'status', backupURL: this.#backupURL }]);
|
|
76
|
+
return sub;
|
|
77
|
+
}
|
|
78
|
+
endReservation(taskID, updateCleanupDelay = true) {
|
|
79
|
+
const res = this.#reservations.get(taskID);
|
|
80
|
+
if (res === undefined) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
this.#reservations.delete(taskID);
|
|
84
|
+
const { start, sub } = res;
|
|
85
|
+
sub.cancel(); // closes the connection if still open
|
|
86
|
+
if (updateCleanupDelay) {
|
|
87
|
+
const duration = Date.now() - start.getTime();
|
|
88
|
+
this.#lc.info?.(`snapshot initialized by ${taskID} in ${duration} ms`);
|
|
89
|
+
if (duration > this.#cleanupDelayMs) {
|
|
90
|
+
this.#cleanupDelayMs = duration;
|
|
91
|
+
this.#lc.info?.(`increased cleanup delay to ${duration} ms`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
52
95
|
// Exported for testing
|
|
53
|
-
|
|
96
|
+
checkWatermarksAndScheduleCleanup = async () => {
|
|
54
97
|
try {
|
|
55
|
-
|
|
56
|
-
if (!resp.ok) {
|
|
57
|
-
this.#lc.warn?.(`unable to fetch metrics at ${this.#metricsEndpoint}`, await resp.text());
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
const families = parsePrometheusTextFormat(await resp.text());
|
|
61
|
-
for (const family of families) {
|
|
62
|
-
if (family.type === 'GAUGE' &&
|
|
63
|
-
family.name === 'litestream_replica_progress') {
|
|
64
|
-
for (const metric of family.metrics) {
|
|
65
|
-
const watermark = metric.labels?.watermark;
|
|
66
|
-
if (watermark && !this.#cleanupTimers.has(watermark)) {
|
|
67
|
-
const time = new Date(parseFloat(metric.value) * 1000);
|
|
68
|
-
const now = Date.now();
|
|
69
|
-
const cleanupAt = Math.max(time.getTime() + this.#cleanupDelayMs, now);
|
|
70
|
-
this.#lc.info?.(`replicated watermark=${watermark} to ${metric.labels?.name}` +
|
|
71
|
-
` at ${time.toISOString()}. scheduling cleanup at ` +
|
|
72
|
-
new Date(cleanupAt).toISOString() +
|
|
73
|
-
` (in ${cleanupAt - now} ms)`);
|
|
74
|
-
this.#cleanupTimers.set(watermark, setTimeout(() => {
|
|
75
|
-
this.#changeStreamer.scheduleCleanup(watermark);
|
|
76
|
-
// Clean up all previous watermarks, leaving the latest one in the
|
|
77
|
-
// map to avoid redundant scheduling.
|
|
78
|
-
for (const [key, timer] of this.#cleanupTimers.entries()) {
|
|
79
|
-
if (key === watermark) {
|
|
80
|
-
break;
|
|
81
|
-
}
|
|
82
|
-
this.#cleanupTimers.delete(key);
|
|
83
|
-
clearTimeout(timer);
|
|
84
|
-
}
|
|
85
|
-
}, cleanupAt - now));
|
|
86
|
-
this.#lc.debug?.('timers', [...this.#cleanupTimers.entries()]);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
98
|
+
await this.#checkWatermarks();
|
|
91
99
|
}
|
|
92
100
|
catch (e) {
|
|
93
101
|
this.#lc.warn?.(`unable to fetch metrics at ${this.#metricsEndpoint}`, e);
|
|
94
102
|
}
|
|
103
|
+
try {
|
|
104
|
+
this.#scheduleCleanup();
|
|
105
|
+
}
|
|
106
|
+
catch (e) {
|
|
107
|
+
this.#lc.warn?.(`error scheduling cleanup`, e);
|
|
108
|
+
}
|
|
95
109
|
};
|
|
110
|
+
async #checkWatermarks() {
|
|
111
|
+
const resp = await fetch(this.#metricsEndpoint);
|
|
112
|
+
if (!resp.ok) {
|
|
113
|
+
this.#lc.warn?.(`unable to fetch metrics at ${this.#metricsEndpoint}`, await resp.text());
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const families = parsePrometheusTextFormat(await resp.text());
|
|
117
|
+
for (const family of families) {
|
|
118
|
+
if (family.type === 'GAUGE' &&
|
|
119
|
+
family.name === 'litestream_replica_progress') {
|
|
120
|
+
for (const metric of family.metrics) {
|
|
121
|
+
const watermark = metric.labels?.watermark;
|
|
122
|
+
if (watermark &&
|
|
123
|
+
watermark > this.#lastWatermark &&
|
|
124
|
+
!this.#watermarks.has(watermark)) {
|
|
125
|
+
const time = new Date(parseFloat(metric.value) * 1000);
|
|
126
|
+
this.#lc.info?.(`replicated watermark=${watermark} to ${metric.labels?.name}` +
|
|
127
|
+
` at ${time.toISOString()}.`);
|
|
128
|
+
this.#watermarks.set(watermark, time);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
#scheduleCleanup() {
|
|
135
|
+
if (this.#reservations.size > 0) {
|
|
136
|
+
this.#lc.info?.(`watermark cleanup paused for snapshot(s): ${[...this.#reservations.keys()]}`);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const latestCleanupTime = Date.now() - this.#cleanupDelayMs;
|
|
140
|
+
let maxWatermark = '';
|
|
141
|
+
for (const [watermark, backupTime] of this.#watermarks.entries()) {
|
|
142
|
+
if (backupTime.getTime() <= latestCleanupTime &&
|
|
143
|
+
watermark > maxWatermark) {
|
|
144
|
+
maxWatermark = watermark;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (maxWatermark.length) {
|
|
148
|
+
this.#changeStreamer.scheduleCleanup(maxWatermark);
|
|
149
|
+
for (const watermark of this.#watermarks.keys()) {
|
|
150
|
+
if (watermark <= maxWatermark) {
|
|
151
|
+
this.#watermarks.delete(watermark);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
this.#lastWatermark = maxWatermark;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
96
157
|
stop() {
|
|
97
158
|
clearInterval(this.#checkMetricsTimer);
|
|
98
|
-
for (const
|
|
99
|
-
|
|
159
|
+
for (const { sub } of this.#reservations.values()) {
|
|
160
|
+
// Close any pending reservations. This commonly happens when a new
|
|
161
|
+
// replication-manager makes a `/snapshot` reservation on the existing
|
|
162
|
+
// replication-manager, and then shuts it down when it takes over the
|
|
163
|
+
// replication slot.
|
|
164
|
+
sub.cancel();
|
|
100
165
|
}
|
|
101
166
|
this.#state.stop(this.#lc);
|
|
102
167
|
return promiseVoid;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backup-monitor.js","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/backup-monitor.ts"],"names":[],"mappings":"AACA,OAAO,yBAAyB,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAC,WAAW,EAAC,MAAM,6CAA6C,CAAC;AACxE,OAAO,EAAC,YAAY,EAAC,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"backup-monitor.js","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/backup-monitor.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import { LogContext } from '@rocicorp/logger';
|
|
2
2
|
import { IncomingMessage } from 'node:http';
|
|
3
|
-
import type { PostgresDB } from '../../types/pg.ts';
|
|
4
3
|
import { type Worker } from '../../types/processes.ts';
|
|
5
4
|
import type { ShardID } from '../../types/shards.ts';
|
|
6
5
|
import { type Source } from '../../types/streams.ts';
|
|
7
6
|
import { HttpService, type Options } from '../http-service.ts';
|
|
7
|
+
import type { BackupMonitor } from './backup-monitor.ts';
|
|
8
8
|
import { type ChangeStreamer, type Downstream, type SubscriberContext } from './change-streamer.ts';
|
|
9
|
+
import { type SnapshotMessage } from './snapshot.ts';
|
|
9
10
|
export declare class ChangeStreamerHttpServer extends HttpService {
|
|
10
11
|
#private;
|
|
11
12
|
readonly id = "change-streamer-http-server";
|
|
12
13
|
constructor(lc: LogContext, opts: Options, parent: Worker);
|
|
13
|
-
|
|
14
|
+
setDelegates(changeStreamer: ChangeStreamer, backupMonitor: BackupMonitor | null): void;
|
|
14
15
|
protected _respondToKeepalive(): boolean;
|
|
15
16
|
}
|
|
16
17
|
export declare class ChangeStreamerHttpClient implements ChangeStreamer {
|
|
17
18
|
#private;
|
|
18
|
-
constructor(lc: LogContext, shardID: ShardID, changeDB:
|
|
19
|
+
constructor(lc: LogContext, shardID: ShardID, changeDB: string);
|
|
20
|
+
reserveSnapshot(taskID: string): Promise<Source<SnapshotMessage>>;
|
|
19
21
|
subscribe(ctx: SubscriberContext): Promise<Source<Downstream>>;
|
|
20
22
|
}
|
|
21
23
|
type RequestHeaders = Pick<IncomingMessage, 'url' | 'headers'>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"change-streamer-http.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/change-streamer-http.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAC,eAAe,EAAC,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"change-streamer-http.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/change-streamer-http.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAC,eAAe,EAAC,MAAM,WAAW,CAAC;AAM1C,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,0BAA0B,CAAC;AACrD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAsB,KAAK,MAAM,EAAC,MAAM,wBAAwB,CAAC;AAIxE,OAAO,EAAC,WAAW,EAAE,KAAK,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAC7D,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,iBAAiB,EACvB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAwB,KAAK,eAAe,EAAC,MAAM,eAAe,CAAC;AAW1E,qBAAa,wBAAyB,SAAQ,WAAW;;IACvD,QAAQ,CAAC,EAAE,iCAAiC;gBAIhC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM;IAoBzD,YAAY,CACV,cAAc,EAAE,cAAc,EAC9B,aAAa,EAAE,aAAa,GAAG,IAAI;IASrC,SAAS,CAAC,mBAAmB,IAAI,OAAO;CAwEzC;AAED,qBAAa,wBAAyB,YAAW,cAAc;;gBAKjD,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM;IAyBxD,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IASjE,SAAS,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;CAQrE;AAED,KAAK,cAAc,GAAG,IAAI,CAAC,eAAe,EAAE,KAAK,GAAG,SAAS,CAAC,CAAC;AAE/D,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,iBAAiB,CAc3E"}
|
|
@@ -4,6 +4,7 @@ import { IncomingMessage } from 'node:http';
|
|
|
4
4
|
import WebSocket from 'ws';
|
|
5
5
|
import { assert } from "../../../../shared/src/asserts.js";
|
|
6
6
|
import { must } from "../../../../shared/src/must.js";
|
|
7
|
+
import { pgClient } from "../../types/pg.js";
|
|
7
8
|
import {} from "../../types/processes.js";
|
|
8
9
|
import { streamIn, streamOut } from "../../types/streams.js";
|
|
9
10
|
import { URLParams } from "../../types/url-params.js";
|
|
@@ -12,50 +13,81 @@ import { closeWithError, PROTOCOL_ERROR } from "../../types/ws.js";
|
|
|
12
13
|
import { HttpService } from "../http-service.js";
|
|
13
14
|
import { downstreamSchema, PROTOCOL_VERSION, } from "./change-streamer.js";
|
|
14
15
|
import { discoverChangeStreamerAddress } from "./schema/tables.js";
|
|
16
|
+
import { snapshotMessageSchema } from "./snapshot.js";
|
|
15
17
|
const MIN_SUPPORTED_PROTOCOL_VERSION = 1;
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const PATH_REGEX =
|
|
18
|
+
const SNAPSHOT_PATH_PATTERN = '/replication/:version/snapshot';
|
|
19
|
+
const CHANGES_PATH_PATTERN = '/replication/:version/changes';
|
|
20
|
+
const PATH_REGEX = /\/replication\/v(?<version>\d+)\/(changes|snapshot)$/;
|
|
21
|
+
const SNAPSHOT_PATH = `/replication/v${PROTOCOL_VERSION}/snapshot`;
|
|
19
22
|
const CHANGES_PATH = `/replication/v${PROTOCOL_VERSION}/changes`;
|
|
20
23
|
export class ChangeStreamerHttpServer extends HttpService {
|
|
21
24
|
id = 'change-streamer-http-server';
|
|
22
|
-
#
|
|
25
|
+
#changeStreamer = null;
|
|
26
|
+
#backupMonitor = null;
|
|
23
27
|
constructor(lc, opts, parent) {
|
|
24
28
|
super('change-streamer-http-server', lc, opts, async (fastify) => {
|
|
25
29
|
await fastify.register(websocket);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
fastify.get(TENANT_PATH_PATTERN, { websocket: true }, this.#subscribe);
|
|
30
|
-
installWebSocketReceiver(lc, fastify.websocketServer, this.#handleSubscription, parent);
|
|
30
|
+
fastify.get(CHANGES_PATH_PATTERN, { websocket: true }, this.#subscribe);
|
|
31
|
+
fastify.get(SNAPSHOT_PATH_PATTERN, { websocket: true }, this.#reserveSnapshot);
|
|
32
|
+
installWebSocketReceiver(lc, fastify.websocketServer, this.#receiveWebsocket, parent);
|
|
31
33
|
});
|
|
32
34
|
}
|
|
33
|
-
|
|
34
|
-
assert(this.#
|
|
35
|
-
this.#
|
|
35
|
+
setDelegates(changeStreamer, backupMonitor) {
|
|
36
|
+
assert(this.#changeStreamer === null, 'delegate already set');
|
|
37
|
+
this.#changeStreamer = changeStreamer;
|
|
38
|
+
this.#backupMonitor = backupMonitor;
|
|
36
39
|
}
|
|
37
40
|
// Only respond to LB health checks (on "/keepalive") if the delegate is
|
|
38
41
|
// initialized. Container health checks (on "/") are always acknowledged.
|
|
39
42
|
_respondToKeepalive() {
|
|
40
|
-
return this.#
|
|
43
|
+
return this.#changeStreamer !== null;
|
|
41
44
|
}
|
|
42
|
-
#
|
|
43
|
-
return must(this.#
|
|
45
|
+
#getChangeStreamer() {
|
|
46
|
+
return must(this.#changeStreamer, 'received request before change-streamer is ready');
|
|
44
47
|
}
|
|
45
|
-
#
|
|
46
|
-
|
|
48
|
+
#getBackupMonitor() {
|
|
49
|
+
return must(this.#backupMonitor, 'received request before change-streamer is ready');
|
|
50
|
+
}
|
|
51
|
+
// Called when receiving a web socket via the main dispatcher handoff.
|
|
52
|
+
#receiveWebsocket = (ws, action, msg) => {
|
|
53
|
+
switch (action) {
|
|
54
|
+
case 'snapshot':
|
|
55
|
+
return this.#reserveSnapshot(ws, msg);
|
|
56
|
+
case 'changes':
|
|
57
|
+
return this.#subscribe(ws, msg);
|
|
58
|
+
default:
|
|
59
|
+
closeWithError(this._lc, ws, `invalid action "${action}" received in handoff`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
#reserveSnapshot = (ws, req) => {
|
|
47
64
|
try {
|
|
48
|
-
|
|
65
|
+
const url = new URL(req.url ?? '', req.headers.origin ?? 'http://localhost');
|
|
66
|
+
const taskID = url.searchParams.get('taskID');
|
|
67
|
+
if (!taskID) {
|
|
68
|
+
throw new Error('Missing taskID in snapshot request');
|
|
69
|
+
}
|
|
70
|
+
const downstream = this.#getBackupMonitor().startSnapshotReservation(taskID);
|
|
71
|
+
void streamOut(this._lc, downstream, ws);
|
|
49
72
|
}
|
|
50
73
|
catch (err) {
|
|
51
74
|
closeWithError(this._lc, ws, err, PROTOCOL_ERROR);
|
|
52
|
-
return;
|
|
53
75
|
}
|
|
54
|
-
await this.#handleSubscription(ws, ctx);
|
|
55
76
|
};
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
-
|
|
77
|
+
#subscribe = async (ws, req) => {
|
|
78
|
+
try {
|
|
79
|
+
const ctx = getSubscriberContext(req);
|
|
80
|
+
const downstream = await this.#getChangeStreamer().subscribe(ctx);
|
|
81
|
+
if (ctx.initial && ctx.taskID && this.#backupMonitor) {
|
|
82
|
+
// Now that the change-streamer knows about the subscriber and watermark,
|
|
83
|
+
// end the reservation to safely resume scheduling cleanup.
|
|
84
|
+
this.#backupMonitor.endReservation(ctx.taskID);
|
|
85
|
+
}
|
|
86
|
+
void streamOut(this._lc, downstream, ws);
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
closeWithError(this._lc, ws, err, PROTOCOL_ERROR);
|
|
90
|
+
}
|
|
59
91
|
};
|
|
60
92
|
}
|
|
61
93
|
export class ChangeStreamerHttpClient {
|
|
@@ -65,15 +97,31 @@ export class ChangeStreamerHttpClient {
|
|
|
65
97
|
constructor(lc, shardID, changeDB) {
|
|
66
98
|
this.#lc = lc;
|
|
67
99
|
this.#shardID = shardID;
|
|
68
|
-
|
|
100
|
+
// Create a pg client with a single short-lived connection for the purpose
|
|
101
|
+
// of change-streamer discovery (i.e. ChangeDB as DNS).
|
|
102
|
+
this.#changeDB = pgClient(lc, changeDB, {
|
|
103
|
+
max: 1,
|
|
104
|
+
['idle_timeout']: 15,
|
|
105
|
+
connection: { ['application_name']: 'change-streamer-discovery' },
|
|
106
|
+
});
|
|
69
107
|
}
|
|
70
|
-
async
|
|
108
|
+
async #resolveChangeStreamer(path) {
|
|
71
109
|
const address = await discoverChangeStreamerAddress(this.#shardID, this.#changeDB);
|
|
72
110
|
if (!address) {
|
|
73
111
|
throw new Error(`no change-streamer is running`);
|
|
74
112
|
}
|
|
75
|
-
const uri = new URL(
|
|
113
|
+
const uri = new URL(path, `http://${address}/`);
|
|
76
114
|
this.#lc.info?.(`connecting to change-streamer@${uri}`);
|
|
115
|
+
return uri;
|
|
116
|
+
}
|
|
117
|
+
async reserveSnapshot(taskID) {
|
|
118
|
+
const uri = await this.#resolveChangeStreamer(SNAPSHOT_PATH);
|
|
119
|
+
const params = new URLSearchParams({ taskID });
|
|
120
|
+
const ws = new WebSocket(uri + `?${params.toString()}`);
|
|
121
|
+
return streamIn(this.#lc, ws, snapshotMessageSchema);
|
|
122
|
+
}
|
|
123
|
+
async subscribe(ctx) {
|
|
124
|
+
const uri = await this.#resolveChangeStreamer(CHANGES_PATH);
|
|
77
125
|
const params = getParams(ctx);
|
|
78
126
|
const ws = new WebSocket(uri + `?${params.toString()}`);
|
|
79
127
|
return streamIn(this.#lc, ws, downstreamSchema);
|
|
@@ -86,6 +134,7 @@ export function getSubscriberContext(req) {
|
|
|
86
134
|
return {
|
|
87
135
|
protocolVersion,
|
|
88
136
|
id: params.get('id', true),
|
|
137
|
+
taskID: params.get('taskID', false),
|
|
89
138
|
mode: params.get('mode', false) === 'backup' ? 'backup' : 'serving',
|
|
90
139
|
replicaVersion: params.get('replicaVersion', true),
|
|
91
140
|
watermark: params.get('watermark', true),
|
|
@@ -113,6 +162,7 @@ function getParams(ctx) {
|
|
|
113
162
|
assert(protocolVersion === PROTOCOL_VERSION, `replicator should be setting protocolVersion to ${PROTOCOL_VERSION}`);
|
|
114
163
|
return new URLSearchParams({
|
|
115
164
|
...stringParams,
|
|
165
|
+
taskID: ctx.taskID ? ctx.taskID : '',
|
|
116
166
|
initial: ctx.initial ? 'true' : 'false',
|
|
117
167
|
});
|
|
118
168
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"change-streamer-http.js","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/change-streamer-http.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAC,eAAe,EAAC,MAAM,WAAW,CAAC;AAC1C,OAAO,SAAS,MAAM,IAAI,CAAC;AAC3B,OAAO,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AACzD,OAAO,EAAC,IAAI,EAAC,MAAM,gCAAgC,CAAC;AAEpD,OAAO,EAAa,MAAM,0BAA0B,CAAC;AAErD,OAAO,EAAC,QAAQ,EAAE,SAAS,EAAc,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAC,SAAS,EAAC,MAAM,2BAA2B,CAAC;AACpD,OAAO,EAAC,wBAAwB,EAAC,MAAM,kCAAkC,CAAC;AAC1E,OAAO,EAAC,cAAc,EAAE,cAAc,EAAC,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAC,WAAW,EAAe,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"change-streamer-http.js","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/change-streamer-http.ts"],"names":[],"mappings":"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"}
|
|
@@ -45,12 +45,17 @@ export interface ChangeStreamer {
|
|
|
45
45
|
*/
|
|
46
46
|
subscribe(ctx: SubscriberContext): Promise<Source<Downstream>>;
|
|
47
47
|
}
|
|
48
|
-
export declare const PROTOCOL_VERSION =
|
|
48
|
+
export declare const PROTOCOL_VERSION = 3;
|
|
49
49
|
export type SubscriberContext = {
|
|
50
50
|
/**
|
|
51
51
|
* The supported change-streamer protocol version.
|
|
52
52
|
*/
|
|
53
53
|
protocolVersion: number;
|
|
54
|
+
/**
|
|
55
|
+
* Task ID. This is used to link the request with a preceding snapshot
|
|
56
|
+
* reservation.
|
|
57
|
+
*/
|
|
58
|
+
taskID: string | null;
|
|
54
59
|
/**
|
|
55
60
|
* Subscriber id. This is only used for debugging.
|
|
56
61
|
*/
|