@rocicorp/zero 1.11.0-canary.11 → 1.11.0-canary.13
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/analyze-query/src/analyze-cli.js +2 -2
- package/out/analyze-query/src/analyze-cli.js.map +1 -1
- package/out/shared/src/ring-buffer.d.ts +2 -0
- package/out/shared/src/ring-buffer.d.ts.map +1 -1
- package/out/shared/src/ring-buffer.js +4 -0
- package/out/shared/src/ring-buffer.js.map +1 -1
- package/out/zero/package.js +1 -1
- package/out/zero/package.js.map +1 -1
- package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/server/replicator.js +26 -13
- package/out/zero-cache/src/server/replicator.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js +169 -19
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +2 -2
- package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +45 -6
- 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 +118 -33
- 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.js +2 -2
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +10 -17
- 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.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.js +15 -4
- package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-message.d.ts +92 -0
- package/out/zero-cache/src/services/change-streamer/snapshot-message.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/snapshot-message.js +42 -0
- package/out/zero-cache/src/services/change-streamer/snapshot-message.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot.d.ts +7 -89
- package/out/zero-cache/src/services/change-streamer/snapshot.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot.js +26 -98
- package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscribe.d.ts +358 -0
- package/out/zero-cache/src/services/change-streamer/subscribe.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/subscribe.js +68 -0
- package/out/zero-cache/src/services/change-streamer/subscribe.js.map +1 -0
- package/out/zero-cache/src/services/litestream/commands.d.ts +2 -3
- package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.js +3 -2
- package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.js +12 -1
- package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.d.ts +2 -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 +13 -7
- package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
- package/out/zero-cache/src/services/replicator/replicator.d.ts +2 -1
- package/out/zero-cache/src/services/replicator/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/replicator.js +2 -2
- package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.d.ts +19 -2
- package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.js +8 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts +19 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.js +34 -11
- package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts +6 -2
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js +9 -5
- package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +44 -11
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +21 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js +14 -6
- package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +28 -4
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/streams.d.ts +33 -7
- package/out/zero-cache/src/types/streams.d.ts.map +1 -1
- package/out/zero-cache/src/types/streams.js +93 -50
- package/out/zero-cache/src/types/streams.js.map +1 -1
- package/out/zero-cache/src/types/subscription.d.ts +11 -0
- package/out/zero-cache/src/types/subscription.d.ts.map +1 -1
- package/out/zero-cache/src/types/subscription.js +56 -35
- package/out/zero-cache/src/types/subscription.js.map +1 -1
- package/out/zero-cache/src/types/ws.js +1 -31
- package/out/zero-cache/src/types/ws.js.map +1 -1
- package/out/zero-cache/src/workers/connection.js +3 -3
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.js +2 -2
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.js +5 -5
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-protocol/src/inspect-down.d.ts +81 -81
- package/out/zql/src/builder/builder.d.ts.map +1 -1
- package/out/zql/src/builder/builder.js +6 -4
- package/out/zql/src/builder/builder.js.map +1 -1
- package/out/zql/src/ivm/cap.d.ts +2 -1
- package/out/zql/src/ivm/cap.d.ts.map +1 -1
- package/out/zql/src/ivm/cap.js +3 -0
- package/out/zql/src/ivm/cap.js.map +1 -1
- package/out/zql/src/ivm/exists.d.ts +2 -0
- package/out/zql/src/ivm/exists.d.ts.map +1 -1
- package/out/zql/src/ivm/exists.js +3 -0
- package/out/zql/src/ivm/exists.js.map +1 -1
- package/out/zql/src/ivm/fan-in.d.ts +5 -1
- package/out/zql/src/ivm/fan-in.d.ts.map +1 -1
- package/out/zql/src/ivm/fan-in.js +7 -0
- package/out/zql/src/ivm/fan-in.js.map +1 -1
- package/out/zql/src/ivm/fan-out.d.ts +3 -0
- package/out/zql/src/ivm/fan-out.d.ts.map +1 -1
- package/out/zql/src/ivm/fan-out.js +4 -0
- package/out/zql/src/ivm/fan-out.js.map +1 -1
- package/out/zql/src/ivm/filter-operators.d.ts +2 -0
- package/out/zql/src/ivm/filter-operators.d.ts.map +1 -1
- package/out/zql/src/ivm/filter-operators.js +9 -0
- package/out/zql/src/ivm/filter-operators.js.map +1 -1
- package/out/zql/src/ivm/filter.d.ts +4 -1
- package/out/zql/src/ivm/filter.d.ts.map +1 -1
- package/out/zql/src/ivm/filter.js +3 -0
- package/out/zql/src/ivm/filter.js.map +1 -1
- package/out/zql/src/ivm/flipped-join.d.ts +4 -3
- package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
- package/out/zql/src/ivm/flipped-join.js +73 -22
- package/out/zql/src/ivm/flipped-join.js.map +1 -1
- package/out/zql/src/ivm/join-utils.d.ts +24 -7
- package/out/zql/src/ivm/join-utils.d.ts.map +1 -1
- package/out/zql/src/ivm/join-utils.js +87 -4
- package/out/zql/src/ivm/join-utils.js.map +1 -1
- package/out/zql/src/ivm/join.d.ts +4 -4
- package/out/zql/src/ivm/join.d.ts.map +1 -1
- package/out/zql/src/ivm/join.js +55 -64
- package/out/zql/src/ivm/join.js.map +1 -1
- package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
- package/out/zql/src/ivm/memory-source.js +33 -23
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/ivm/operator.d.ts +39 -10
- package/out/zql/src/ivm/operator.d.ts.map +1 -1
- package/out/zql/src/ivm/operator.js +9 -4
- package/out/zql/src/ivm/operator.js.map +1 -1
- package/out/zql/src/ivm/push-accumulated.d.ts +6 -1
- package/out/zql/src/ivm/push-accumulated.d.ts.map +1 -1
- package/out/zql/src/ivm/push-accumulated.js +47 -2
- package/out/zql/src/ivm/push-accumulated.js.map +1 -1
- package/out/zql/src/ivm/skip.d.ts +2 -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/take-gate.d.ts +24 -2
- package/out/zql/src/ivm/take-gate.d.ts.map +1 -1
- package/out/zql/src/ivm/take-gate.js +26 -1
- package/out/zql/src/ivm/take-gate.js.map +1 -1
- package/out/zql/src/ivm/take.d.ts +2 -1
- package/out/zql/src/ivm/take.d.ts.map +1 -1
- package/out/zql/src/ivm/take.js +59 -40
- package/out/zql/src/ivm/take.js.map +1 -1
- package/out/zql/src/ivm/union-fan-in.d.ts +3 -0
- package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
- package/out/zql/src/ivm/union-fan-in.js +56 -5
- package/out/zql/src/ivm/union-fan-in.js.map +1 -1
- package/out/zql/src/ivm/union-fan-out.d.ts +2 -1
- package/out/zql/src/ivm/union-fan-out.d.ts.map +1 -1
- package/out/zql/src/ivm/union-fan-out.js +5 -0
- package/out/zql/src/ivm/union-fan-out.js.map +1 -1
- package/out/zql/src/query/measure-push-operator.d.ts +2 -1
- package/out/zql/src/query/measure-push-operator.d.ts.map +1 -1
- package/out/zql/src/query/measure-push-operator.js +7 -0
- package/out/zql/src/query/measure-push-operator.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshot.js","names":[],"sources":["../../../../../../zero-cache/src/services/change-streamer/snapshot.ts"],"sourcesContent":["/**\n * The `snapshot` API serves the purpose of:\n * - informing subscribers (i.e. view-syncers) of the (litestream)\n * backup location from which to restore a replica snapshot\n * - checking whether a restored backup or existing replica is\n * compatible with the change-streamer\n * - preventing change-log cleanup while a snapshot restore is in\n * progress\n * - tracking the approximate time it takes from the beginning of\n * snapshot \"reservation\" to the subsequent subscription, which\n * serves as the minimum interval to wait before cleaning up\n * backed up changes.\n */\n\nimport type {LogContext} from '@rocicorp/logger';\nimport {
|
|
1
|
+
{"version":3,"file":"snapshot.js","names":[],"sources":["../../../../../../zero-cache/src/services/change-streamer/snapshot.ts"],"sourcesContent":["/**\n * The `snapshot` API serves the purpose of:\n * - informing subscribers (i.e. view-syncers) of the (litestream)\n * backup location from which to restore a replica snapshot\n * - checking whether a restored backup or existing replica is\n * compatible with the change-streamer\n * - preventing change-log cleanup while a snapshot restore is in\n * progress\n * - tracking the approximate time it takes from the beginning of\n * snapshot \"reservation\" to the subsequent subscription, which\n * serves as the minimum interval to wait before cleaning up\n * backed up changes.\n */\n\nimport type {LogContext} from '@rocicorp/logger';\nimport {AbortError} from '../../../../shared/src/abort-error.ts';\nimport {promiseOrAbort} from '../../../../shared/src/promise-race.ts';\nimport {sleep} from '../../../../shared/src/sleep.ts';\n\n// The schema definitions live in the pure schema module (with no dependency on\n// the HTTP client) so that subscribe.ts can import them without forming an\n// import cycle through change-streamer-http.ts. Imported here for local use and\n// re-exported for existing consumers.\nimport type {\n ReservationFollowup,\n SnapshotReserver,\n} from './change-streamer-http.ts';\nimport type {SnapshotStatus} from './subscribe.ts';\n\nexport type {SnapshotStatus};\n\nexport async function reserveAndGetSnapshotStatus(\n lc: LogContext,\n taskID: string,\n changeStreamer: SnapshotReserver,\n): Promise<{reserved: SnapshotStatus; followup: ReservationFollowup}> {\n const abort = new AbortController();\n const {signal} = abort;\n // Use our own AbortError as the abort reason (rather than the native\n // DOMException that AbortController.abort() defaults to) so that\n // promiseOrAbort()'s rejection, which propagates `signal.reason` verbatim,\n // is always an instance of the shared AbortError type.\n const onSignal = () => abort.abort(new AbortError('SIGTERM/SIGINT'));\n process.on('SIGINT', onSignal);\n process.on('SIGTERM', onSignal);\n\n try {\n for (let i = 0; ; i++) {\n let err: unknown;\n const reservation = changeStreamer.reserveSnapshot(taskID);\n try {\n return await promiseOrAbort(reservation, signal);\n } catch (e) {\n err = e;\n }\n if (signal.aborted) {\n // promiseOrAbort() does not cancel the loser of the race: the\n // reservation may still resolve later. If it does, cancel its\n // connection rather than leaking it, since this function has already\n // given up and nobody will call followup.subscribe().\n reservation.then(\n ({followup}) => followup.cancel(),\n () => {},\n );\n throw err;\n }\n // Retry in the view-syncer since it cannot proceed until it connects\n // to a (compatible) replication-manager. In particular, a\n // replication-manager that does not support the view-syncer's\n // change-streamer protocol will close the stream with an error; this\n // retry logic essentially delays the startup of a view-syncer until\n // a compatible replication-manager has been rolled out, allowing\n // replication-manager and view-syncer services to be updated in\n // parallel.\n lc.warn?.(\n `Unable to reserve snapshot (attempt ${i + 1}). Retrying in 5 seconds.`,\n String(err),\n );\n await sleep(5000, signal);\n }\n } finally {\n // This function is called repeatedly (e.g. by the replicator's restore\n // loop), so the signal handlers must not outlive the reservation.\n process.off('SIGINT', onSignal);\n process.off('SIGTERM', onSignal);\n }\n}\n"],"mappings":";;;;AA+BA,eAAsB,4BACpB,IACA,QACA,gBACoE;CACpE,MAAM,QAAQ,IAAI,gBAAgB;CAClC,MAAM,EAAC,WAAU;CAKjB,MAAM,iBAAiB,MAAM,MAAM,IAAI,WAAW,gBAAgB,CAAC;CACnE,QAAQ,GAAG,UAAU,QAAQ;CAC7B,QAAQ,GAAG,WAAW,QAAQ;CAE9B,IAAI;EACF,KAAK,IAAI,IAAI,IAAK,KAAK;GACrB,IAAI;GACJ,MAAM,cAAc,eAAe,gBAAgB,MAAM;GACzD,IAAI;IACF,OAAO,MAAM,eAAe,aAAa,MAAM;GACjD,SAAS,GAAG;IACV,MAAM;GACR;GACA,IAAI,OAAO,SAAS;IAKlB,YAAY,MACT,EAAC,eAAc,SAAS,OAAO,SAC1B,CAAC,CACT;IACA,MAAM;GACR;GASA,GAAG,OACD,uCAAuC,IAAI,EAAE,4BAC7C,OAAO,GAAG,CACZ;GACA,MAAM,MAAM,KAAM,MAAM;EAC1B;CACF,UAAU;EAGR,QAAQ,IAAI,UAAU,QAAQ;EAC9B,QAAQ,IAAI,WAAW,QAAQ;CACjC;AACF"}
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `subscribe` API (protocol v7+) merges the previous `/snapshot` and
|
|
3
|
+
* `/changes` connections into a single bidirectional WebSocket, so that the
|
|
4
|
+
* replication-manager that reserves change-log entries for a soon-to-be
|
|
5
|
+
* subscriber is the same replication-manager that serves the subscription.
|
|
6
|
+
*
|
|
7
|
+
* The connection is driven by an upstream (view-syncer → replication-manager)
|
|
8
|
+
* application message that selects the flow:
|
|
9
|
+
*
|
|
10
|
+
* - **Fresh startup:** the view-syncer sends `['reserve-snapshot', {taskID}]`.
|
|
11
|
+
* The change-streamer opens a snapshot reservation (pinning the change log)
|
|
12
|
+
* and replies downstream with a single `['reserved', …]` message telling the
|
|
13
|
+
* view-syncer where to restore the backup from and the watermark from which
|
|
14
|
+
* catchup is possible. Once the backup is restored, the view-syncer sends
|
|
15
|
+
* `['start-subscription', ctx]`, which transitions the same connection into
|
|
16
|
+
* the subscription phase (change stream downstream). The reservation is
|
|
17
|
+
* released only after the subscriber is registered, so there is never a gap
|
|
18
|
+
* in which the change-streamer does not know the client's watermark.
|
|
19
|
+
*
|
|
20
|
+
* - **Reconnect (replica already valid):** the view-syncer sends
|
|
21
|
+
* `['start-subscription', ctx]` immediately, with no reservation.
|
|
22
|
+
*
|
|
23
|
+
* The reservation status uses a distinct `'reserved'` tag (rather than the
|
|
24
|
+
* change stream's `'status'`) so the downstream union is unambiguous under
|
|
25
|
+
* passthrough parsing.
|
|
26
|
+
*/
|
|
27
|
+
import * as v from '../../../../shared/src/valita.ts';
|
|
28
|
+
declare const snapshotStatusSchema: v.ObjectType<Omit<{
|
|
29
|
+
tag: v.Type<"status">;
|
|
30
|
+
backupURL: v.Type<string>;
|
|
31
|
+
replicaVersion: v.Type<string>;
|
|
32
|
+
minWatermark: v.Type<string>;
|
|
33
|
+
replicaSize: v.Optional<number>;
|
|
34
|
+
}, "tag"> & {
|
|
35
|
+
tag: v.Type<"snapshot">;
|
|
36
|
+
}, undefined>;
|
|
37
|
+
export type SnapshotStatus = v.Infer<typeof snapshotStatusSchema>;
|
|
38
|
+
export declare const reserveSnapshotMessageSchema: v.TupleType<[v.Type<"reserve-snapshot">, v.ObjectType<{
|
|
39
|
+
taskID: v.Type<string>;
|
|
40
|
+
}, undefined>]>;
|
|
41
|
+
/**
|
|
42
|
+
* The subscription-initialization context, sent in the `start-subscription`
|
|
43
|
+
* message. Equivalent to the previous URL-encoded `SubscriberContext`, minus
|
|
44
|
+
* the fields that are now implicit on the merged connection:
|
|
45
|
+
* - `protocolVersion` comes from the request path.
|
|
46
|
+
* - `wsBatched` is always on (the transport batches internally).
|
|
47
|
+
*
|
|
48
|
+
* Parsed in `passthrough` mode so a forthcoming, orthogonal change can add
|
|
49
|
+
* arbitrary table/column/backfill state without a protocol bump.
|
|
50
|
+
*/
|
|
51
|
+
export declare const subscribeContextSchema: v.ObjectType<{
|
|
52
|
+
/** Task ID, links the subscription to a preceding snapshot reservation. */
|
|
53
|
+
taskID: v.Type<string>;
|
|
54
|
+
/** Subscriber id (debugging only). */
|
|
55
|
+
id: v.Type<string>;
|
|
56
|
+
/** 'serving' (user-facing) or 'backup' (in-RM replicator). */
|
|
57
|
+
mode: v.UnionType<[v.Type<"serving">, v.Type<"backup">]>;
|
|
58
|
+
/** The replica version the subscriber restored / is running. */
|
|
59
|
+
replicaVersion: v.Type<string>;
|
|
60
|
+
/** The watermark up to which the subscriber is up to date. */
|
|
61
|
+
watermark: v.Type<string>;
|
|
62
|
+
}, undefined>;
|
|
63
|
+
export type SubscribeContext = v.Infer<typeof subscribeContextSchema>;
|
|
64
|
+
export declare const startSubscriptionMessageSchema: v.TupleType<[v.Type<"start-subscription">, v.ObjectType<{
|
|
65
|
+
/** Task ID, links the subscription to a preceding snapshot reservation. */
|
|
66
|
+
taskID: v.Type<string>;
|
|
67
|
+
/** Subscriber id (debugging only). */
|
|
68
|
+
id: v.Type<string>;
|
|
69
|
+
/** 'serving' (user-facing) or 'backup' (in-RM replicator). */
|
|
70
|
+
mode: v.UnionType<[v.Type<"serving">, v.Type<"backup">]>;
|
|
71
|
+
/** The replica version the subscriber restored / is running. */
|
|
72
|
+
replicaVersion: v.Type<string>;
|
|
73
|
+
/** The watermark up to which the subscriber is up to date. */
|
|
74
|
+
watermark: v.Type<string>;
|
|
75
|
+
}, undefined>]>;
|
|
76
|
+
export declare const subscribeUpstreamSchema: v.UnionType<[v.TupleType<[v.Type<"reserve-snapshot">, v.ObjectType<{
|
|
77
|
+
taskID: v.Type<string>;
|
|
78
|
+
}, undefined>]>, v.TupleType<[v.Type<"start-subscription">, v.ObjectType<{
|
|
79
|
+
/** Task ID, links the subscription to a preceding snapshot reservation. */
|
|
80
|
+
taskID: v.Type<string>;
|
|
81
|
+
/** Subscriber id (debugging only). */
|
|
82
|
+
id: v.Type<string>;
|
|
83
|
+
/** 'serving' (user-facing) or 'backup' (in-RM replicator). */
|
|
84
|
+
mode: v.UnionType<[v.Type<"serving">, v.Type<"backup">]>;
|
|
85
|
+
/** The replica version the subscriber restored / is running. */
|
|
86
|
+
replicaVersion: v.Type<string>;
|
|
87
|
+
/** The watermark up to which the subscriber is up to date. */
|
|
88
|
+
watermark: v.Type<string>;
|
|
89
|
+
}, undefined>]>]>;
|
|
90
|
+
export type SubscribeUpstream = v.Infer<typeof subscribeUpstreamSchema>;
|
|
91
|
+
/**
|
|
92
|
+
* The reservation confirmation. Carries the same payload as the legacy
|
|
93
|
+
* `/snapshot` status (`backupURL`, `replicaVersion`, `minWatermark`,
|
|
94
|
+
* `replicaSize?`) but under a distinct `'reserved'` tag so it does not collide
|
|
95
|
+
* with the change stream's `['status', …]` message.
|
|
96
|
+
*/
|
|
97
|
+
export declare const reservedMessageSchema: v.TupleType<[v.Type<"reserved">, v.ObjectType<Omit<{
|
|
98
|
+
tag: v.Type<"status">;
|
|
99
|
+
backupURL: v.Type<string>;
|
|
100
|
+
replicaVersion: v.Type<string>;
|
|
101
|
+
minWatermark: v.Type<string>;
|
|
102
|
+
replicaSize: v.Optional<number>;
|
|
103
|
+
}, "tag"> & {
|
|
104
|
+
tag: v.Type<"snapshot">;
|
|
105
|
+
}, undefined>]>;
|
|
106
|
+
export type ReservedMessage = v.Infer<typeof reservedMessageSchema>;
|
|
107
|
+
export declare const subscribeDownstreamSchema: v.UnionType<[v.TupleType<[v.Type<"reserved">, v.ObjectType<Omit<{
|
|
108
|
+
tag: v.Type<"status">;
|
|
109
|
+
backupURL: v.Type<string>;
|
|
110
|
+
replicaVersion: v.Type<string>;
|
|
111
|
+
minWatermark: v.Type<string>;
|
|
112
|
+
replicaSize: v.Optional<number>;
|
|
113
|
+
}, "tag"> & {
|
|
114
|
+
tag: v.Type<"snapshot">;
|
|
115
|
+
}, undefined>]>, v.UnionType<[v.TupleType<[v.Type<"status">, v.ObjectType<{
|
|
116
|
+
tag: v.Type<"status">;
|
|
117
|
+
lagReport: v.Optional<{
|
|
118
|
+
lastTimings?: {
|
|
119
|
+
sendTimeMs: number;
|
|
120
|
+
commitTimeMs: number;
|
|
121
|
+
receiveTimeMs: number;
|
|
122
|
+
} | undefined;
|
|
123
|
+
nextSendTimeMs: number;
|
|
124
|
+
}>;
|
|
125
|
+
}, undefined>]>, v.UnionType<[v.TupleType<[v.Type<"begin">, v.ObjectType<{
|
|
126
|
+
tag: v.Type<"begin">;
|
|
127
|
+
json: v.Optional<"p" | "s">;
|
|
128
|
+
skipAck: v.Optional<boolean>;
|
|
129
|
+
}, undefined>, v.ObjectType<{
|
|
130
|
+
commitWatermark: v.Type<string>;
|
|
131
|
+
}, undefined>]>, v.TupleType<[v.Type<"data">, v.UnionType<[v.UnionType<[v.ObjectType<{
|
|
132
|
+
tag: v.Type<"insert">;
|
|
133
|
+
relation: v.Type<{
|
|
134
|
+
schema: string;
|
|
135
|
+
name: string;
|
|
136
|
+
keyColumns?: string[] | undefined;
|
|
137
|
+
replicaIdentity?: "default" | "full" | "index" | "nothing" | undefined;
|
|
138
|
+
rowKey: {
|
|
139
|
+
columns: string[];
|
|
140
|
+
type?: "default" | "full" | "index" | "nothing" | undefined;
|
|
141
|
+
};
|
|
142
|
+
}>;
|
|
143
|
+
new: v.Type<Record<string, import("../../../../shared/src/bigint-json.ts").JSONValue>>;
|
|
144
|
+
}, undefined>, v.ObjectType<{
|
|
145
|
+
tag: v.Type<"update">;
|
|
146
|
+
relation: v.Type<{
|
|
147
|
+
schema: string;
|
|
148
|
+
name: string;
|
|
149
|
+
keyColumns?: string[] | undefined;
|
|
150
|
+
replicaIdentity?: "default" | "full" | "index" | "nothing" | undefined;
|
|
151
|
+
rowKey: {
|
|
152
|
+
columns: string[];
|
|
153
|
+
type?: "default" | "full" | "index" | "nothing" | undefined;
|
|
154
|
+
};
|
|
155
|
+
}>;
|
|
156
|
+
key: v.Type<Record<string, import("../../../../shared/src/bigint-json.ts").JSONValue> | null>;
|
|
157
|
+
new: v.Type<Record<string, import("../../../../shared/src/bigint-json.ts").JSONValue>>;
|
|
158
|
+
}, undefined>, v.ObjectType<{
|
|
159
|
+
tag: v.Type<"delete">;
|
|
160
|
+
relation: v.Type<{
|
|
161
|
+
schema: string;
|
|
162
|
+
name: string;
|
|
163
|
+
keyColumns?: string[] | undefined;
|
|
164
|
+
replicaIdentity?: "default" | "full" | "index" | "nothing" | undefined;
|
|
165
|
+
rowKey: {
|
|
166
|
+
columns: string[];
|
|
167
|
+
type?: "default" | "full" | "index" | "nothing" | undefined;
|
|
168
|
+
};
|
|
169
|
+
}>;
|
|
170
|
+
key: v.Type<Record<string, import("../../../../shared/src/bigint-json.ts").JSONValue>>;
|
|
171
|
+
}, undefined>, v.ObjectType<{
|
|
172
|
+
tag: v.Type<"truncate">;
|
|
173
|
+
relations: v.ArrayType<v.Type<{
|
|
174
|
+
schema: string;
|
|
175
|
+
name: string;
|
|
176
|
+
keyColumns?: string[] | undefined;
|
|
177
|
+
replicaIdentity?: "default" | "full" | "index" | "nothing" | undefined;
|
|
178
|
+
rowKey: {
|
|
179
|
+
columns: string[];
|
|
180
|
+
type?: "default" | "full" | "index" | "nothing" | undefined;
|
|
181
|
+
};
|
|
182
|
+
}>>;
|
|
183
|
+
}, undefined>, v.ObjectType<{
|
|
184
|
+
tag: v.Type<"backfill">;
|
|
185
|
+
relation: v.ObjectType<{
|
|
186
|
+
schema: v.Type<string>;
|
|
187
|
+
name: v.Type<string>;
|
|
188
|
+
rowKey: v.ObjectType<{
|
|
189
|
+
columns: v.ArrayType<v.Type<string>>;
|
|
190
|
+
type: v.Optional<"default" | "full" | "index" | "nothing">;
|
|
191
|
+
}, undefined>;
|
|
192
|
+
}, undefined>;
|
|
193
|
+
columns: v.ArrayType<v.Type<string>>;
|
|
194
|
+
watermark: v.Type<string>;
|
|
195
|
+
rowValues: v.ArrayType<v.ArrayType<v.Type<import("../../../../shared/src/bigint-json.ts").JSONValue>>>;
|
|
196
|
+
status: v.Optional<{
|
|
197
|
+
rows: number;
|
|
198
|
+
totalRows: number;
|
|
199
|
+
totalBytes?: number | undefined;
|
|
200
|
+
}>;
|
|
201
|
+
}, undefined>]>, v.UnionType<[v.ObjectType<{
|
|
202
|
+
tag: v.Type<"create-table">;
|
|
203
|
+
spec: v.ObjectType<Omit<{
|
|
204
|
+
name: v.Type<string>;
|
|
205
|
+
columns: v.Type<Record<string, {
|
|
206
|
+
pos: number;
|
|
207
|
+
dataType: string;
|
|
208
|
+
pgTypeClass?: "b" | "c" | "d" | "e" | "m" | "p" | "r" | undefined;
|
|
209
|
+
elemPgTypeClass?: "b" | "c" | "d" | "e" | "m" | "p" | "r" | null | undefined;
|
|
210
|
+
characterMaximumLength?: number | null | undefined;
|
|
211
|
+
notNull?: boolean | null | undefined;
|
|
212
|
+
dflt?: string | null | undefined;
|
|
213
|
+
}>>;
|
|
214
|
+
primaryKey: v.Optional<string[]>;
|
|
215
|
+
}, "schema"> & {
|
|
216
|
+
schema: v.Type<string>;
|
|
217
|
+
}, undefined>;
|
|
218
|
+
metadata: v.Optional<{
|
|
219
|
+
[x: string]: import("../../../../shared/src/bigint-json.ts").JSONValue;
|
|
220
|
+
rowKey: Record<string, import("../../../../shared/src/bigint-json.ts").JSONValue>;
|
|
221
|
+
}>;
|
|
222
|
+
backfill: v.Optional<Record<string, Record<string, import("../change-source/protocol/current.ts").JSONValue | undefined>>>;
|
|
223
|
+
}, undefined>, v.ObjectType<{
|
|
224
|
+
tag: v.Type<"rename-table">;
|
|
225
|
+
old: v.ObjectType<{
|
|
226
|
+
schema: v.Type<string>;
|
|
227
|
+
name: v.Type<string>;
|
|
228
|
+
}, undefined>;
|
|
229
|
+
new: v.ObjectType<{
|
|
230
|
+
schema: v.Type<string>;
|
|
231
|
+
name: v.Type<string>;
|
|
232
|
+
}, undefined>;
|
|
233
|
+
}, undefined>, v.ObjectType<{
|
|
234
|
+
tag: v.Type<"update-table-metadata">;
|
|
235
|
+
table: v.ObjectType<{
|
|
236
|
+
schema: v.Type<string>;
|
|
237
|
+
name: v.Type<string>;
|
|
238
|
+
}, undefined>;
|
|
239
|
+
old: v.ObjectType<{
|
|
240
|
+
rowKey: v.Type<Record<string, import("../../../../shared/src/bigint-json.ts").JSONValue>>;
|
|
241
|
+
}, v.Type<import("../../../../shared/src/bigint-json.ts").JSONValue>>;
|
|
242
|
+
new: v.ObjectType<{
|
|
243
|
+
rowKey: v.Type<Record<string, import("../../../../shared/src/bigint-json.ts").JSONValue>>;
|
|
244
|
+
}, v.Type<import("../../../../shared/src/bigint-json.ts").JSONValue>>;
|
|
245
|
+
}, undefined>, v.ObjectType<{
|
|
246
|
+
tag: v.Type<"add-column">;
|
|
247
|
+
table: v.ObjectType<{
|
|
248
|
+
schema: v.Type<string>;
|
|
249
|
+
name: v.Type<string>;
|
|
250
|
+
}, undefined>;
|
|
251
|
+
column: v.ObjectType<{
|
|
252
|
+
name: v.Type<string>;
|
|
253
|
+
spec: v.ObjectType<{
|
|
254
|
+
pos: v.Type<number>;
|
|
255
|
+
dataType: v.Type<string>;
|
|
256
|
+
pgTypeClass: v.Optional<"b" | "c" | "d" | "e" | "m" | "p" | "r">;
|
|
257
|
+
elemPgTypeClass: v.Optional<"b" | "c" | "d" | "e" | "m" | "p" | "r" | null>;
|
|
258
|
+
characterMaximumLength: v.Optional<number | null>;
|
|
259
|
+
notNull: v.Optional<boolean | null>;
|
|
260
|
+
dflt: v.Optional<string | null>;
|
|
261
|
+
}, undefined>;
|
|
262
|
+
}, undefined>;
|
|
263
|
+
tableMetadata: v.Optional<{
|
|
264
|
+
[x: string]: import("../../../../shared/src/bigint-json.ts").JSONValue;
|
|
265
|
+
rowKey: Record<string, import("../../../../shared/src/bigint-json.ts").JSONValue>;
|
|
266
|
+
}>;
|
|
267
|
+
backfill: v.Optional<Record<string, import("../change-source/protocol/current.ts").JSONValue | undefined>>;
|
|
268
|
+
}, undefined>, v.ObjectType<{
|
|
269
|
+
tag: v.Type<"update-column">;
|
|
270
|
+
table: v.ObjectType<{
|
|
271
|
+
schema: v.Type<string>;
|
|
272
|
+
name: v.Type<string>;
|
|
273
|
+
}, undefined>;
|
|
274
|
+
old: v.ObjectType<{
|
|
275
|
+
name: v.Type<string>;
|
|
276
|
+
spec: v.ObjectType<{
|
|
277
|
+
pos: v.Type<number>;
|
|
278
|
+
dataType: v.Type<string>;
|
|
279
|
+
pgTypeClass: v.Optional<"b" | "c" | "d" | "e" | "m" | "p" | "r">;
|
|
280
|
+
elemPgTypeClass: v.Optional<"b" | "c" | "d" | "e" | "m" | "p" | "r" | null>;
|
|
281
|
+
characterMaximumLength: v.Optional<number | null>;
|
|
282
|
+
notNull: v.Optional<boolean | null>;
|
|
283
|
+
dflt: v.Optional<string | null>;
|
|
284
|
+
}, undefined>;
|
|
285
|
+
}, undefined>;
|
|
286
|
+
new: v.ObjectType<{
|
|
287
|
+
name: v.Type<string>;
|
|
288
|
+
spec: v.ObjectType<{
|
|
289
|
+
pos: v.Type<number>;
|
|
290
|
+
dataType: v.Type<string>;
|
|
291
|
+
pgTypeClass: v.Optional<"b" | "c" | "d" | "e" | "m" | "p" | "r">;
|
|
292
|
+
elemPgTypeClass: v.Optional<"b" | "c" | "d" | "e" | "m" | "p" | "r" | null>;
|
|
293
|
+
characterMaximumLength: v.Optional<number | null>;
|
|
294
|
+
notNull: v.Optional<boolean | null>;
|
|
295
|
+
dflt: v.Optional<string | null>;
|
|
296
|
+
}, undefined>;
|
|
297
|
+
}, undefined>;
|
|
298
|
+
}, undefined>, v.ObjectType<{
|
|
299
|
+
tag: v.Type<"drop-column">;
|
|
300
|
+
table: v.ObjectType<{
|
|
301
|
+
schema: v.Type<string>;
|
|
302
|
+
name: v.Type<string>;
|
|
303
|
+
}, undefined>;
|
|
304
|
+
column: v.Type<string>;
|
|
305
|
+
}, undefined>, v.ObjectType<{
|
|
306
|
+
tag: v.Type<"drop-table">;
|
|
307
|
+
id: v.ObjectType<{
|
|
308
|
+
schema: v.Type<string>;
|
|
309
|
+
name: v.Type<string>;
|
|
310
|
+
}, undefined>;
|
|
311
|
+
}, undefined>, v.ObjectType<{
|
|
312
|
+
tag: v.Type<"create-index">;
|
|
313
|
+
spec: v.ObjectType<Omit<{
|
|
314
|
+
name: v.Type<string>;
|
|
315
|
+
tableName: v.Type<string>;
|
|
316
|
+
unique: v.Type<boolean>;
|
|
317
|
+
columns: v.Type<Record<string, "ASC" | "DESC">>;
|
|
318
|
+
predicate: v.Optional<import("../../db/specs.ts").IndexPredicate>;
|
|
319
|
+
}, "schema"> & {
|
|
320
|
+
schema: v.Type<string>;
|
|
321
|
+
}, undefined>;
|
|
322
|
+
}, undefined>, v.ObjectType<{
|
|
323
|
+
tag: v.Type<"drop-index">;
|
|
324
|
+
id: v.ObjectType<{
|
|
325
|
+
schema: v.Type<string>;
|
|
326
|
+
name: v.Type<string>;
|
|
327
|
+
}, undefined>;
|
|
328
|
+
}, undefined>, v.ObjectType<{
|
|
329
|
+
tag: v.Type<"backfill-completed">;
|
|
330
|
+
relation: v.ObjectType<{
|
|
331
|
+
schema: v.Type<string>;
|
|
332
|
+
name: v.Type<string>;
|
|
333
|
+
rowKey: v.ObjectType<{
|
|
334
|
+
columns: v.ArrayType<v.Type<string>>;
|
|
335
|
+
type: v.Optional<"default" | "full" | "index" | "nothing">;
|
|
336
|
+
}, undefined>;
|
|
337
|
+
}, undefined>;
|
|
338
|
+
columns: v.ArrayType<v.Type<string>>;
|
|
339
|
+
watermark: v.Type<string>;
|
|
340
|
+
status: v.Optional<{
|
|
341
|
+
rows: number;
|
|
342
|
+
totalRows: number;
|
|
343
|
+
totalBytes?: number | undefined;
|
|
344
|
+
}>;
|
|
345
|
+
}, undefined>]>]>]>, v.TupleType<[v.Type<"commit">, v.ObjectType<{
|
|
346
|
+
tag: v.Type<"commit">;
|
|
347
|
+
commitTimeMs: v.Optional<number>;
|
|
348
|
+
}, undefined>, v.ObjectType<{
|
|
349
|
+
watermark: v.Type<string>;
|
|
350
|
+
}, undefined>]>, v.TupleType<[v.Type<"rollback">, v.ObjectType<{
|
|
351
|
+
tag: v.Type<"rollback">;
|
|
352
|
+
}, undefined>]>]>, v.TupleType<[v.Type<"error">, v.ObjectType<{
|
|
353
|
+
type: v.Type<number>;
|
|
354
|
+
message: v.Optional<string>;
|
|
355
|
+
}, undefined>]>]>]>;
|
|
356
|
+
export type SubscribeDownstream = v.Infer<typeof subscribeDownstreamSchema>;
|
|
357
|
+
export {};
|
|
358
|
+
//# sourceMappingURL=subscribe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscribe.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/subscribe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,CAAC,MAAM,kCAAkC,CAAC;AAItD,QAAA,MAAM,oBAAoB;;;;;;;;aAExB,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAMlE,eAAO,MAAM,4BAA4B;;eAGvC,CAAC;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB;IACjC,2EAA2E;;IAE3E,sCAAsC;;IAEtC,8DAA8D;;IAE9D,gEAAgE;;IAEhE,8DAA8D;;aAE9D,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,eAAO,MAAM,8BAA8B;IAdzC,2EAA2E;;IAE3E,sCAAsC;;IAEtC,8DAA8D;;IAE9D,gEAAgE;;IAEhE,8DAA8D;;eAS9D,CAAC;AAEH,eAAO,MAAM,uBAAuB;;;IAnBlC,2EAA2E;;IAE3E,sCAAsC;;IAEtC,8DAA8D;;IAE9D,gEAAgE;;IAEhE,8DAA8D;;iBAc/D,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAMxE;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;eAGhC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAGrC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { valita_exports } from "../../../../shared/src/valita.js";
|
|
2
|
+
import { downstreamSchema } from "./change-streamer.js";
|
|
3
|
+
import { statusSchema } from "./snapshot-message.js";
|
|
4
|
+
//#region ../zero-cache/src/services/change-streamer/subscribe.ts
|
|
5
|
+
/**
|
|
6
|
+
* The `subscribe` API (protocol v7+) merges the previous `/snapshot` and
|
|
7
|
+
* `/changes` connections into a single bidirectional WebSocket, so that the
|
|
8
|
+
* replication-manager that reserves change-log entries for a soon-to-be
|
|
9
|
+
* subscriber is the same replication-manager that serves the subscription.
|
|
10
|
+
*
|
|
11
|
+
* The connection is driven by an upstream (view-syncer → replication-manager)
|
|
12
|
+
* application message that selects the flow:
|
|
13
|
+
*
|
|
14
|
+
* - **Fresh startup:** the view-syncer sends `['reserve-snapshot', {taskID}]`.
|
|
15
|
+
* The change-streamer opens a snapshot reservation (pinning the change log)
|
|
16
|
+
* and replies downstream with a single `['reserved', …]` message telling the
|
|
17
|
+
* view-syncer where to restore the backup from and the watermark from which
|
|
18
|
+
* catchup is possible. Once the backup is restored, the view-syncer sends
|
|
19
|
+
* `['start-subscription', ctx]`, which transitions the same connection into
|
|
20
|
+
* the subscription phase (change stream downstream). The reservation is
|
|
21
|
+
* released only after the subscriber is registered, so there is never a gap
|
|
22
|
+
* in which the change-streamer does not know the client's watermark.
|
|
23
|
+
*
|
|
24
|
+
* - **Reconnect (replica already valid):** the view-syncer sends
|
|
25
|
+
* `['start-subscription', ctx]` immediately, with no reservation.
|
|
26
|
+
*
|
|
27
|
+
* The reservation status uses a distinct `'reserved'` tag (rather than the
|
|
28
|
+
* change stream's `'status'`) so the downstream union is unambiguous under
|
|
29
|
+
* passthrough parsing.
|
|
30
|
+
*/
|
|
31
|
+
var snapshotStatusSchema = statusSchema.extend({ tag: valita_exports.literal("snapshot") });
|
|
32
|
+
var reserveSnapshotMessageSchema = valita_exports.tuple([valita_exports.literal("reserve-snapshot"), valita_exports.object({ taskID: valita_exports.string() })]);
|
|
33
|
+
/**
|
|
34
|
+
* The subscription-initialization context, sent in the `start-subscription`
|
|
35
|
+
* message. Equivalent to the previous URL-encoded `SubscriberContext`, minus
|
|
36
|
+
* the fields that are now implicit on the merged connection:
|
|
37
|
+
* - `protocolVersion` comes from the request path.
|
|
38
|
+
* - `wsBatched` is always on (the transport batches internally).
|
|
39
|
+
*
|
|
40
|
+
* Parsed in `passthrough` mode so a forthcoming, orthogonal change can add
|
|
41
|
+
* arbitrary table/column/backfill state without a protocol bump.
|
|
42
|
+
*/
|
|
43
|
+
var subscribeContextSchema = valita_exports.object({
|
|
44
|
+
/** Task ID, links the subscription to a preceding snapshot reservation. */
|
|
45
|
+
taskID: valita_exports.string(),
|
|
46
|
+
/** Subscriber id (debugging only). */
|
|
47
|
+
id: valita_exports.string(),
|
|
48
|
+
/** 'serving' (user-facing) or 'backup' (in-RM replicator). */
|
|
49
|
+
mode: valita_exports.union(valita_exports.literal("serving"), valita_exports.literal("backup")),
|
|
50
|
+
/** The replica version the subscriber restored / is running. */
|
|
51
|
+
replicaVersion: valita_exports.string(),
|
|
52
|
+
/** The watermark up to which the subscriber is up to date. */
|
|
53
|
+
watermark: valita_exports.string()
|
|
54
|
+
});
|
|
55
|
+
var startSubscriptionMessageSchema = valita_exports.tuple([valita_exports.literal("start-subscription"), subscribeContextSchema]);
|
|
56
|
+
var subscribeUpstreamSchema = valita_exports.union(reserveSnapshotMessageSchema, startSubscriptionMessageSchema);
|
|
57
|
+
/**
|
|
58
|
+
* The reservation confirmation. Carries the same payload as the legacy
|
|
59
|
+
* `/snapshot` status (`backupURL`, `replicaVersion`, `minWatermark`,
|
|
60
|
+
* `replicaSize?`) but under a distinct `'reserved'` tag so it does not collide
|
|
61
|
+
* with the change stream's `['status', …]` message.
|
|
62
|
+
*/
|
|
63
|
+
var reservedMessageSchema = valita_exports.tuple([valita_exports.literal("reserved"), snapshotStatusSchema]);
|
|
64
|
+
var subscribeDownstreamSchema = valita_exports.union(reservedMessageSchema, downstreamSchema);
|
|
65
|
+
//#endregion
|
|
66
|
+
export { subscribeDownstreamSchema, subscribeUpstreamSchema };
|
|
67
|
+
|
|
68
|
+
//# sourceMappingURL=subscribe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscribe.js","names":[],"sources":["../../../../../../zero-cache/src/services/change-streamer/subscribe.ts"],"sourcesContent":["/**\n * The `subscribe` API (protocol v7+) merges the previous `/snapshot` and\n * `/changes` connections into a single bidirectional WebSocket, so that the\n * replication-manager that reserves change-log entries for a soon-to-be\n * subscriber is the same replication-manager that serves the subscription.\n *\n * The connection is driven by an upstream (view-syncer → replication-manager)\n * application message that selects the flow:\n *\n * - **Fresh startup:** the view-syncer sends `['reserve-snapshot', {taskID}]`.\n * The change-streamer opens a snapshot reservation (pinning the change log)\n * and replies downstream with a single `['reserved', …]` message telling the\n * view-syncer where to restore the backup from and the watermark from which\n * catchup is possible. Once the backup is restored, the view-syncer sends\n * `['start-subscription', ctx]`, which transitions the same connection into\n * the subscription phase (change stream downstream). The reservation is\n * released only after the subscriber is registered, so there is never a gap\n * in which the change-streamer does not know the client's watermark.\n *\n * - **Reconnect (replica already valid):** the view-syncer sends\n * `['start-subscription', ctx]` immediately, with no reservation.\n *\n * The reservation status uses a distinct `'reserved'` tag (rather than the\n * change stream's `'status'`) so the downstream union is unambiguous under\n * passthrough parsing.\n */\n\nimport * as v from '../../../../shared/src/valita.ts';\nimport {downstreamSchema} from './change-streamer.ts';\nimport {statusSchema} from './snapshot-message.ts';\n\nconst snapshotStatusSchema = statusSchema.extend({\n tag: v.literal('snapshot'), // Replace tag: 'status' with tag: 'snapshot'\n});\n\nexport type SnapshotStatus = v.Infer<typeof snapshotStatusSchema>;\n\n// ---------------------------------------------------------------------------\n// Upstream (view-syncer → replication-manager) — application control messages.\n// ---------------------------------------------------------------------------\n\nexport const reserveSnapshotMessageSchema = v.tuple([\n v.literal('reserve-snapshot'),\n v.object({taskID: v.string()}),\n]);\n\n/**\n * The subscription-initialization context, sent in the `start-subscription`\n * message. Equivalent to the previous URL-encoded `SubscriberContext`, minus\n * the fields that are now implicit on the merged connection:\n * - `protocolVersion` comes from the request path.\n * - `wsBatched` is always on (the transport batches internally).\n *\n * Parsed in `passthrough` mode so a forthcoming, orthogonal change can add\n * arbitrary table/column/backfill state without a protocol bump.\n */\nexport const subscribeContextSchema = v.object({\n /** Task ID, links the subscription to a preceding snapshot reservation. */\n taskID: v.string(),\n /** Subscriber id (debugging only). */\n id: v.string(),\n /** 'serving' (user-facing) or 'backup' (in-RM replicator). */\n mode: v.union(v.literal('serving'), v.literal('backup')),\n /** The replica version the subscriber restored / is running. */\n replicaVersion: v.string(),\n /** The watermark up to which the subscriber is up to date. */\n watermark: v.string(),\n});\n\nexport type SubscribeContext = v.Infer<typeof subscribeContextSchema>;\n\nexport const startSubscriptionMessageSchema = v.tuple([\n v.literal('start-subscription'),\n subscribeContextSchema,\n]);\n\nexport const subscribeUpstreamSchema = v.union(\n reserveSnapshotMessageSchema,\n startSubscriptionMessageSchema,\n);\n\nexport type SubscribeUpstream = v.Infer<typeof subscribeUpstreamSchema>;\n\n// ---------------------------------------------------------------------------\n// Downstream (replication-manager → view-syncer).\n// ---------------------------------------------------------------------------\n\n/**\n * The reservation confirmation. Carries the same payload as the legacy\n * `/snapshot` status (`backupURL`, `replicaVersion`, `minWatermark`,\n * `replicaSize?`) but under a distinct `'reserved'` tag so it does not collide\n * with the change stream's `['status', …]` message.\n */\nexport const reservedMessageSchema = v.tuple([\n v.literal('reserved'),\n snapshotStatusSchema,\n]);\n\nexport type ReservedMessage = v.Infer<typeof reservedMessageSchema>;\n\nexport const subscribeDownstreamSchema = v.union(\n reservedMessageSchema,\n downstreamSchema,\n);\n\nexport type SubscribeDownstream = v.Infer<typeof subscribeDownstreamSchema>;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,IAAM,uBAAuB,aAAa,OAAO,EAC/C,KAAK,eAAE,QAAQ,UAAU,EAC3B,CAAC;AAQD,IAAa,+BAA+B,eAAE,MAAM,CAClD,eAAE,QAAQ,kBAAkB,GAC5B,eAAE,OAAO,EAAC,QAAQ,eAAE,OAAO,EAAC,CAAC,CAC/B,CAAC;;;;;;;;;;;AAYD,IAAa,yBAAyB,eAAE,OAAO;;CAE7C,QAAQ,eAAE,OAAO;;CAEjB,IAAI,eAAE,OAAO;;CAEb,MAAM,eAAE,MAAM,eAAE,QAAQ,SAAS,GAAG,eAAE,QAAQ,QAAQ,CAAC;;CAEvD,gBAAgB,eAAE,OAAO;;CAEzB,WAAW,eAAE,OAAO;AACtB,CAAC;AAID,IAAa,iCAAiC,eAAE,MAAM,CACpD,eAAE,QAAQ,oBAAoB,GAC9B,sBACF,CAAC;AAED,IAAa,0BAA0B,eAAE,MACvC,8BACA,8BACF;;;;;;;AAcA,IAAa,wBAAwB,eAAE,MAAM,CAC3C,eAAE,QAAQ,UAAU,GACpB,oBACF,CAAC;AAID,IAAa,4BAA4B,eAAE,MACzC,uBACA,gBACF"}
|
|
@@ -7,7 +7,7 @@ export type ReplicaConstraints = {
|
|
|
7
7
|
minWatermark: string;
|
|
8
8
|
};
|
|
9
9
|
export type RestoreResult = 'success' | 'no_backup' | 'invalid_replica' | 'error';
|
|
10
|
-
type RestoreAttempt = {
|
|
10
|
+
export type RestoreAttempt = {
|
|
11
11
|
restored: boolean;
|
|
12
12
|
backupURL: string | undefined;
|
|
13
13
|
result: RestoreResult;
|
|
@@ -16,7 +16,7 @@ export declare class BackupNotFoundException extends Error {
|
|
|
16
16
|
static readonly name = "BackupNotFoundException";
|
|
17
17
|
constructor(backupURL: string | undefined);
|
|
18
18
|
}
|
|
19
|
-
export declare function tryRestore(lc: LogContext, config: LitestreamConfig, replicaFile: string, replicaConstraints: ReplicaConstraints | undefined, role: LitestreamRole): Promise<RestoreAttempt>;
|
|
19
|
+
export declare function tryRestore(lc: LogContext, config: LitestreamConfig, replicaFile: string, replicaConstraints: ReplicaConstraints | undefined, role: LitestreamRole, signal?: AbortSignal): Promise<RestoreAttempt>;
|
|
20
20
|
export declare function startReplicaBackupProcess(lc: LogContext, config: LitestreamConfig, replicaFile: string): ChildProcess;
|
|
21
21
|
/**
|
|
22
22
|
* Returns the time of the most recent object (snapshot or WAL segment)
|
|
@@ -41,5 +41,4 @@ export declare function getLastBackupTime(lc: LogContext, config: LitestreamConf
|
|
|
41
41
|
* Exported for testing.
|
|
42
42
|
*/
|
|
43
43
|
export declare function parseBackupCreatedTimes(lc: LogContext, command: 'snapshots' | 'wal', output: string): Date[];
|
|
44
|
-
export {};
|
|
45
44
|
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/litestream/commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAC;AAIrD,OAAO,KAAK,EAAC,UAAU,EAAW,MAAM,kBAAkB,CAAC;AAI3D,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAUhE,OAAO,EAWL,KAAK,cAAc,EACpB,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,kBAAkB,GAAG;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB,SAAS,GACT,WAAW,GACX,iBAAiB,GACjB,OAAO,CAAC;AAEZ,
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/litestream/commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAC;AAIrD,OAAO,KAAK,EAAC,UAAU,EAAW,MAAM,kBAAkB,CAAC;AAI3D,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAUhE,OAAO,EAWL,KAAK,cAAc,EACpB,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,kBAAkB,GAAG;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB,SAAS,GACT,WAAW,GACX,iBAAiB,GACjB,OAAO,CAAC;AAEZ,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,MAAM,EAAE,aAAa,CAAC;CACvB,CAAC;AAKF,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,MAAM,CAAC,QAAQ,CAAC,IAAI,6BAA6B;IAEjD,YAAY,SAAS,EAAE,MAAM,GAAG,SAAS,EAExC;CACF;AAyFD,wBAAsB,UAAU,CAC9B,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,MAAM,EACnB,kBAAkB,EAAE,kBAAkB,GAAG,SAAS,EAClD,IAAI,EAAE,cAAc,EACpB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,CAAC,CAwIzB;AAoGD,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,MAAM,GAClB,YAAY,CAuCd;AAQD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAgBf;AA8DD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,EAAE,EAAE,UAAU,EACd,OAAO,EAAE,WAAW,GAAG,KAAK,EAC5B,MAAM,EAAE,MAAM,GACb,IAAI,EAAE,CAoBR"}
|
|
@@ -47,7 +47,7 @@ function getLitestream(mode, config, replicaFile, logLevelOverride) {
|
|
|
47
47
|
}
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
|
-
async function tryRestore(lc, config, replicaFile, replicaConstraints, role) {
|
|
50
|
+
async function tryRestore(lc, config, replicaFile, replicaConstraints, role, signal) {
|
|
51
51
|
const { backupURL } = config;
|
|
52
52
|
const attrs = litestreamRestoreMetricAttrs(config, role, backupURL);
|
|
53
53
|
let result = "error";
|
|
@@ -76,7 +76,8 @@ async function tryRestore(lc, config, replicaFile, replicaConstraints, role) {
|
|
|
76
76
|
"pipe",
|
|
77
77
|
"pipe"
|
|
78
78
|
],
|
|
79
|
-
windowsHide: true
|
|
79
|
+
windowsHide: true,
|
|
80
|
+
signal
|
|
80
81
|
});
|
|
81
82
|
let stdout = "";
|
|
82
83
|
let stderr = "";
|