@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,6 +1,7 @@
|
|
|
1
1
|
import type { LogContext } from '@rocicorp/logger';
|
|
2
2
|
import type { ReadonlyJSONObject } from '../../../../shared/src/json.ts';
|
|
3
3
|
import type { Source } from '../../types/streams.ts';
|
|
4
|
+
import type { ReservationFollowup } from '../change-streamer/change-streamer-http.ts';
|
|
4
5
|
import type { ChangeStreamer } from '../change-streamer/change-streamer.ts';
|
|
5
6
|
import type { Service } from '../service.ts';
|
|
6
7
|
import { type ReplicationStatusPublisher } from './replication-status.ts';
|
|
@@ -71,7 +72,7 @@ export type ReplicatorMode = 'backup' | 'serving';
|
|
|
71
72
|
export declare class ReplicatorService implements Replicator, Service {
|
|
72
73
|
#private;
|
|
73
74
|
readonly id: string;
|
|
74
|
-
constructor(lc: LogContext, taskID: string, id: string, mode: ReplicatorMode, changeStreamer: ChangeStreamer, worker: WriteWorkerClient, statusPublisher: ReplicationStatusPublisher | null);
|
|
75
|
+
constructor(lc: LogContext, taskID: string, id: string, mode: ReplicatorMode, changeStreamer: ChangeStreamer, worker: WriteWorkerClient, statusPublisher: ReplicationStatusPublisher | null, initialConnection?: ReservationFollowup);
|
|
75
76
|
status(): Promise<{
|
|
76
77
|
status: string;
|
|
77
78
|
}>;
|
|
@@ -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,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,EAEL,KAAK,0BAA0B,EAChC,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,0BAA0B,CAAC;AAEhE,oDAAoD;AACpD,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC;;OAEG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAEjD;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAInD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C,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;IAOpB,YACE,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,
|
|
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,mBAAmB,EAAC,MAAM,4CAA4C,CAAC;AACpF,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,uCAAuC,CAAC;AAC1E,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,eAAe,CAAC;AAE3C,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,0BAA0B,CAAC;AAEhE,oDAAoD;AACpD,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC;;OAEG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAEjD;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAInD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C,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;IAOpB,YACE,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,EAKlD,iBAAiB,CAAC,EAAE,mBAAmB,EAmBxC;IAED,MAAM;;OAEL;IAED,GAAG,kBAYF;IAED,SAAS,IAAI,MAAM,CAAC,YAAY,CAAC,CAEhC;IAEK,IAAI,kBAOT;CACF"}
|
|
@@ -8,12 +8,12 @@ var ReplicatorService = class {
|
|
|
8
8
|
#shouldPublishErrors;
|
|
9
9
|
#worker;
|
|
10
10
|
#runPromise;
|
|
11
|
-
constructor(lc, taskID, id, mode, changeStreamer, worker, statusPublisher) {
|
|
11
|
+
constructor(lc, taskID, id, mode, changeStreamer, worker, statusPublisher, initialConnection) {
|
|
12
12
|
this.id = id;
|
|
13
13
|
this.#lc = lc.withContext("component", "replicator").withContext("serviceID", this.id);
|
|
14
14
|
this.#worker = worker;
|
|
15
15
|
this.#shouldPublishErrors = statusPublisher !== null;
|
|
16
|
-
this.#incrementalSyncer = new IncrementalSyncer(this.#lc, taskID, `${taskID}/${id}`, changeStreamer, worker, mode, statusPublisher);
|
|
16
|
+
this.#incrementalSyncer = new IncrementalSyncer(this.#lc, taskID, `${taskID}/${id}`, changeStreamer, worker, mode, statusPublisher, initialConnection);
|
|
17
17
|
}
|
|
18
18
|
status() {
|
|
19
19
|
return Promise.resolve({ status: "ok" });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replicator.js","names":["#lc","#incrementalSyncer","#shouldPublishErrors","#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 {\n publishReplicationError,\n type ReplicationStatusPublisher,\n} 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 * The replica watermark that became ready. Omitted for the initial\n * \"current state is ready\" notification, which should not count as newly\n * unserved work for a freshly-started ViewSyncer.\n */\n readonly watermark?: string | undefined;\n /**\n * Millisecond epoch when `watermark` became ready to read from the replica.\n */\n readonly replicaReadyTimeMs?: number | undefined;\n\n /**\n * Millisecond epoch at which the transaction behind `watermark` committed\n * *upstream*, as reported by the ChangeSource. Unlike `replicaReadyTimeMs`,\n * which is stamped locally when the change lands in the replica, this is the\n * origin of the whole pipeline, so `now - upstreamCommitTimeMs` at the point\n * the change is poked to clients is the end-to-end serving lag.\n *\n * Note that this crosses a clock domain: it comes from the upstream\n * database, whereas it is subtracted from the local clock in the ViewSyncer.\n * The existing `zero.replication.total_lag` metric spans the same boundary.\n *\n * Undefined when the ChangeSource does not report a commit time, and for the\n * initial \"current state is ready\" notification.\n */\n readonly upstreamCommitTimeMs?: number | undefined;\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 | undefined;\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 #shouldPublishErrors: boolean;\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 this.#shouldPublishErrors = statusPublisher !== null;\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().catch(async error => {\n if (this.#shouldPublishErrors) {\n await publishReplicationError(\n this.#lc,\n 'Replicating',\n 'Replication stopped because the replica writer failed',\n );\n }\n throw error;\n });\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.processMessages() 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":";;;
|
|
1
|
+
{"version":3,"file":"replicator.js","names":["#lc","#incrementalSyncer","#shouldPublishErrors","#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 {ReservationFollowup} from '../change-streamer/change-streamer-http.ts';\nimport type {ChangeStreamer} from '../change-streamer/change-streamer.ts';\nimport type {Service} from '../service.ts';\nimport {IncrementalSyncer} from './incremental-sync.ts';\nimport {\n publishReplicationError,\n type ReplicationStatusPublisher,\n} 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 * The replica watermark that became ready. Omitted for the initial\n * \"current state is ready\" notification, which should not count as newly\n * unserved work for a freshly-started ViewSyncer.\n */\n readonly watermark?: string | undefined;\n /**\n * Millisecond epoch when `watermark` became ready to read from the replica.\n */\n readonly replicaReadyTimeMs?: number | undefined;\n\n /**\n * Millisecond epoch at which the transaction behind `watermark` committed\n * *upstream*, as reported by the ChangeSource. Unlike `replicaReadyTimeMs`,\n * which is stamped locally when the change lands in the replica, this is the\n * origin of the whole pipeline, so `now - upstreamCommitTimeMs` at the point\n * the change is poked to clients is the end-to-end serving lag.\n *\n * Note that this crosses a clock domain: it comes from the upstream\n * database, whereas it is subtracted from the local clock in the ViewSyncer.\n * The existing `zero.replication.total_lag` metric spans the same boundary.\n *\n * Undefined when the ChangeSource does not report a commit time, and for the\n * initial \"current state is ready\" notification.\n */\n readonly upstreamCommitTimeMs?: number | undefined;\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 | undefined;\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 #shouldPublishErrors: boolean;\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 // The still-open connection from the snapshot reservation that produced\n // the replica this process just restored (serving mode only). Used for\n // IncrementalSyncer's first subscribe() so the replication-manager that\n // reserved the change log is the one that serves the subscription.\n initialConnection?: ReservationFollowup,\n ) {\n this.id = id;\n this.#lc = lc\n .withContext('component', 'replicator')\n .withContext('serviceID', this.id);\n this.#worker = worker;\n this.#shouldPublishErrors = statusPublisher !== null;\n\n this.#incrementalSyncer = new IncrementalSyncer(\n this.#lc,\n taskID,\n `${taskID}/${id}`,\n changeStreamer,\n worker,\n mode,\n statusPublisher,\n initialConnection,\n );\n }\n\n status() {\n return Promise.resolve({status: 'ok'});\n }\n\n run() {\n this.#runPromise = this.#incrementalSyncer.run().catch(async error => {\n if (this.#shouldPublishErrors) {\n await publishReplicationError(\n this.#lc,\n 'Replicating',\n 'Replication stopped because the replica writer failed',\n );\n }\n throw error;\n });\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.processMessages() 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":";;;AAoFA,IAAa,oBAAb,MAA8D;CAC5D;CACA;CACA;CACA;CACA;CACA;CAEA,YACE,IACA,QACA,IACA,MACA,gBACA,QACA,iBAKA,mBACA;EACA,KAAK,KAAK;EACV,KAAKA,MAAM,GACR,YAAY,aAAa,YAAY,EACrC,YAAY,aAAa,KAAK,EAAE;EACnC,KAAKG,UAAU;EACf,KAAKD,uBAAuB,oBAAoB;EAEhD,KAAKD,qBAAqB,IAAI,kBAC5B,KAAKD,KACL,QACA,GAAG,OAAO,GAAG,MACb,gBACA,QACA,MACA,iBACA,iBACF;CACF;CAEA,SAAS;EACP,OAAO,QAAQ,QAAQ,EAAC,QAAQ,KAAI,CAAC;CACvC;CAEA,MAAM;EACJ,KAAKI,cAAc,KAAKH,mBAAmB,IAAI,EAAE,MAAM,OAAM,UAAS;GACpE,IAAI,KAAKC,sBACP,MAAM,wBACJ,KAAKF,KACL,eACA,uDACF;GAEF,MAAM;EACR,CAAC;EACD,OAAO,KAAKI;CACd;CAEA,YAAkC;EAChC,OAAO,KAAKH,mBAAmB,UAAU;CAC3C;CAEA,MAAM,OAAO;EACX,KAAKA,mBAAmB,KAAK,KAAKD,GAAG;EAIrC,MAAM,KAAKI;EACX,MAAM,KAAKD,QAAQ,KAAK;CAC1B;AACF"}
|
|
@@ -31,8 +31,25 @@ export interface PokeHandler {
|
|
|
31
31
|
cancel(): Promise<void>;
|
|
32
32
|
end(finalVersion: CVRVersion): Promise<void>;
|
|
33
33
|
}
|
|
34
|
+
/** A {@link PokeHandler} for a single client. */
|
|
35
|
+
export interface ClientPokeHandler extends PokeHandler {
|
|
36
|
+
/**
|
|
37
|
+
* Whether a patch has been sent to the client. Once one has, the poke can
|
|
38
|
+
* only be ended with a `finalVersion` that is ahead of the client's base
|
|
39
|
+
* version.
|
|
40
|
+
*/
|
|
41
|
+
readonly patchesSent: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A {@link PokeHandler} for multiple clients, as returned by
|
|
45
|
+
* {@link startPoke}.
|
|
46
|
+
*/
|
|
47
|
+
export interface MultiPokeHandler extends PokeHandler {
|
|
48
|
+
/** Whether a patch has been sent to any of the clients. */
|
|
49
|
+
readonly patchesSent: boolean;
|
|
50
|
+
}
|
|
34
51
|
/** Wraps PokeHandlers for multiple clients in a single PokeHandler. */
|
|
35
|
-
export declare function startPoke(lc: LogContext, clients: ClientHandler[], tentativeVersion: CVRVersion):
|
|
52
|
+
export declare function startPoke(lc: LogContext, clients: ClientHandler[], tentativeVersion: CVRVersion): MultiPokeHandler;
|
|
36
53
|
export declare const POKE_PART_FLUSH_THRESHOLD_CHARS: number;
|
|
37
54
|
/**
|
|
38
55
|
* Handles a single `ViewSyncer` connection.
|
|
@@ -45,7 +62,7 @@ export declare class ClientHandler {
|
|
|
45
62
|
version(): NullableCVRVersion;
|
|
46
63
|
fail(e: unknown): void;
|
|
47
64
|
close(reason: string): void;
|
|
48
|
-
startPoke(tentativeVersion: CVRVersion):
|
|
65
|
+
startPoke(tentativeVersion: CVRVersion): ClientPokeHandler;
|
|
49
66
|
sendDeleteClients(lc: LogContext, deletedClientIDs: string[], deletedClientGroupIDs: string[]): Promise<void>;
|
|
50
67
|
sendQueryTransformApplicationErrors(errors: ErroredQuery[]): void;
|
|
51
68
|
sendQueryTransformFailedError(error: TransformFailedBody): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-handler.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/client-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,uCAAuC,CAAC;AACtE,OAAO,EAEL,KAAK,UAAU,IAAI,cAAc,EAClC,MAAM,gCAAgC,CAAC;AAIxC,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,iDAAiD,CAAC;AAIlF,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,wCAAwC,CAAC;AAChD,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,+CAA+C,CAAC;AAanF,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,2BAA2B,CAAC;AAKpE,OAAO,EAAiB,KAAK,OAAO,EAAC,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAKL,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,KAAK,EACX,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,KAAK,CAAC;IACZ,EAAE,EAAE,KAAK,CAAC;IACV,EAAE,EAAE,KAAK,CAAC;IACV,QAAQ,EAAE,UAAU,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,KAAK,CAAC;IACZ,EAAE,EAAE,KAAK,CAAC;IACV,EAAE,EAAE,KAAK,CAAC;CACX,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,cAAc,CAAC;AACpD,MAAM,MAAM,WAAW,GAAG,aAAa,GAAG,aAAa,CAAC;AAExD,MAAM,MAAM,KAAK,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE3C,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,UAAU,CAAC;CACvB,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,GAAG,CAAC,YAAY,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9C;
|
|
1
|
+
{"version":3,"file":"client-handler.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/client-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,6 +12,7 @@ import { cmpVersions, cookieToVersion, versionToCookie, versionToNullableCookie
|
|
|
12
12
|
import { getLogLevel, wrapWithProtocolError } from "../../types/error-with-level.js";
|
|
13
13
|
//#region ../zero-cache/src/services/view-syncer/client-handler.ts
|
|
14
14
|
var NOOP = {
|
|
15
|
+
patchesSent: false,
|
|
15
16
|
addPatch: () => promiseVoid,
|
|
16
17
|
cancel: () => promiseVoid,
|
|
17
18
|
end: () => promiseVoid
|
|
@@ -23,6 +24,9 @@ function startPoke(lc, clients, tentativeVersion) {
|
|
|
23
24
|
for (const result of await Promise.allSettled(results)) if (result.status === "rejected") lc.error?.(`unhandled error poking client (${op})`, result.reason);
|
|
24
25
|
};
|
|
25
26
|
return {
|
|
27
|
+
get patchesSent() {
|
|
28
|
+
return pokers.some((poker) => poker.patchesSent);
|
|
29
|
+
},
|
|
26
30
|
addPatch: (patch) => settle("addPatch", pokers.map((poker) => poker.addPatch(patch))),
|
|
27
31
|
cancel: () => settle("cancel", pokers.map((poker) => poker.cancel())),
|
|
28
32
|
end: (finalVersion) => settle("end", pokers.map((poker) => poker.end(finalVersion)))
|
|
@@ -164,6 +168,9 @@ var ClientHandler = class {
|
|
|
164
168
|
if (serializedRowsLength >= 1048576 || partCount >= PART_COUNT_FLUSH_THRESHOLD) await flushBody();
|
|
165
169
|
};
|
|
166
170
|
return {
|
|
171
|
+
get patchesSent() {
|
|
172
|
+
return pokeStarted;
|
|
173
|
+
},
|
|
167
174
|
addPatch: async (patchToVersion) => {
|
|
168
175
|
try {
|
|
169
176
|
await addPatch(patchToVersion);
|
|
@@ -189,7 +196,7 @@ var ClientHandler = class {
|
|
|
189
196
|
if (!pokeStarted) {
|
|
190
197
|
if (cmpVersions(this.#baseVersion, finalVersion) === 0 && !forceInitialPoke) return;
|
|
191
198
|
await this.#push(["pokeStart", pokeStart]);
|
|
192
|
-
} else if (cmpVersions(this.#baseVersion, finalVersion) >= 0) throw new Error(`Patches were sent but finalVersion ${
|
|
199
|
+
} else if (cmpVersions(this.#baseVersion, finalVersion) >= 0) throw new Error(`Patches were sent but finalVersion ${cookie} is not greater than baseVersion ${versionToNullableCookie(this.#baseVersion)}`);
|
|
193
200
|
await flushBody();
|
|
194
201
|
await this.#push(["pokeEnd", {
|
|
195
202
|
pokeID,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-handler.js","names":["#clientGroupID","#zeroClientsTable","#zeroMutationsTable","#lc","#downstream","#pokeTime","#pokeTransactions","#pokedRows","#baseVersion","#push","#everPoked","#updateLMIDs"],"sources":["../../../../../../zero-cache/src/services/view-syncer/client-handler.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert, unreachable} from '../../../../shared/src/asserts.ts';\nimport type {JSONObject} from '../../../../shared/src/bigint-json.ts';\nimport {\n assertJSONValue,\n type JSONObject as SafeJSONObject,\n} from '../../../../shared/src/json.ts';\nimport {promiseVoid} from '../../../../shared/src/resolved-promises.ts';\nimport * as v from '../../../../shared/src/valita.ts';\nimport type {Writable} from '../../../../shared/src/writable.ts';\nimport type {ErroredQuery} from '../../../../zero-protocol/src/custom-queries.ts';\nimport {rowSchema} from '../../../../zero-protocol/src/data.ts';\nimport type {DeleteClientsBody} from '../../../../zero-protocol/src/delete-clients.ts';\nimport type {Downstream} from '../../../../zero-protocol/src/down.ts';\nimport {\n ProtocolError,\n type TransformFailedBody,\n} from '../../../../zero-protocol/src/error.ts';\nimport type {InspectDownBody} from '../../../../zero-protocol/src/inspect-down.ts';\nimport {mutationResultSchema} from '../../../../zero-protocol/src/mutation.ts';\nimport type {\n PokePartBody,\n PokePartMessage,\n PokeStartBody,\n} from '../../../../zero-protocol/src/poke.ts';\nimport {primaryKeyValueRecordSchema} from '../../../../zero-protocol/src/primary-key.ts';\nimport type {RowPatchOp} from '../../../../zero-protocol/src/row-patch.ts';\nimport {\n getOrCreateCounter,\n getOrCreateLatencyHistogram,\n} from '../../observability/metrics.ts';\nimport type {ViewSyncerDownstream} from '../../types/downstream.ts';\nimport {\n getLogLevel,\n wrapWithProtocolError,\n} from '../../types/error-with-level.ts';\nimport {upstreamSchema, type ShardID} from '../../types/shards.ts';\nimport type {Subscription} from '../../types/subscription.ts';\nimport {\n cmpVersions,\n cookieToVersion,\n versionToCookie,\n versionToNullableCookie,\n type CVRVersion,\n type DelQueryPatch,\n type NullableCVRVersion,\n type PutQueryPatch,\n type RowID,\n} from './schema/types.ts';\n\nexport type PutRowPatch = {\n type: 'row';\n op: 'put';\n id: RowID;\n contents: JSONObject;\n};\n\nexport type DeleteRowPatch = {\n type: 'row';\n op: 'del';\n id: RowID;\n};\n\nexport type RowPatch = PutRowPatch | DeleteRowPatch;\nexport type ConfigPatch = DelQueryPatch | PutQueryPatch;\n\nexport type Patch = ConfigPatch | RowPatch;\n\nexport type PatchToVersion = {\n patch: Patch;\n toVersion: CVRVersion;\n};\n\nexport interface PokeHandler {\n addPatch(patch: PatchToVersion): Promise<void>;\n cancel(): Promise<void>;\n end(finalVersion: CVRVersion): Promise<void>;\n}\n\nconst NOOP: PokeHandler = {\n addPatch: () => promiseVoid,\n cancel: () => promiseVoid,\n end: () => promiseVoid,\n};\n\n/** Wraps PokeHandlers for multiple clients in a single PokeHandler. */\nexport function startPoke(\n lc: LogContext,\n clients: ClientHandler[],\n tentativeVersion: CVRVersion,\n): PokeHandler {\n const pokers = clients.map(c => c.startPoke(tentativeVersion));\n\n // Promise.allSettled() ensures that a failed (e.g. disconnected) client\n // does not prevent other clients from receiving the pokes. However, the\n // rate (per client group) will be limited by the slowest connection.\n //\n // The individual pokers report their own failures (by failing the\n // connection), so a rejection here is not expected. Log it rather than\n // dropping it on the floor.\n const settle = async (op: string, results: Promise<void>[]) => {\n for (const result of await Promise.allSettled(results)) {\n if (result.status === 'rejected') {\n lc.error?.(`unhandled error poking client (${op})`, result.reason);\n }\n }\n };\n\n return {\n addPatch: patch =>\n settle(\n 'addPatch',\n pokers.map(poker => poker.addPatch(patch)),\n ),\n cancel: () =>\n settle(\n 'cancel',\n pokers.map(poker => poker.cancel()),\n ),\n end: finalVersion =>\n settle(\n 'end',\n pokers.map(poker => poker.end(finalVersion)),\n ),\n };\n}\n\n// Soft limit on the serialized row characters accumulated in a poke part.\n// This bounds view-syncer memory and keeps legacy JSON messages reasonably\n// sized. PokeChunkEncoder separately enforces the binary frame limit in bytes.\nexport const POKE_PART_FLUSH_THRESHOLD_CHARS = 1024 * 1024;\nconst PART_COUNT_FLUSH_THRESHOLD = 100;\n\n/**\n * Handles a single `ViewSyncer` connection.\n */\nexport class ClientHandler {\n readonly #clientGroupID: string;\n readonly clientID: string;\n readonly wsID: string;\n readonly #zeroClientsTable: string;\n readonly #zeroMutationsTable: string;\n readonly #lc: LogContext;\n readonly #downstream: Subscription<ViewSyncerDownstream>;\n #baseVersion: NullableCVRVersion;\n // We will send a poke on connect even if the client is already caught up, so that it can learn its\n // got-queries state has been reconciled with the server. After that, we will only send a poke if\n // the client is behind.\n #everPoked = false;\n\n readonly #pokeTime = getOrCreateLatencyHistogram(\n 'sync',\n 'poke.time',\n 'Time elapsed for each poke transaction. Canceled / noop pokes are excluded.',\n );\n\n readonly #pokeTransactions = getOrCreateCounter(\n 'sync',\n 'poke.transactions',\n 'Count of poke transactions.',\n );\n\n readonly #pokedRows = getOrCreateCounter(\n 'sync',\n 'poke.rows',\n 'Count of poked rows.',\n );\n\n constructor(\n lc: LogContext,\n clientGroupID: string,\n clientID: string,\n wsID: string,\n shard: ShardID,\n baseCookie: string | null,\n downstream: Subscription<ViewSyncerDownstream>,\n ) {\n lc.debug?.('new client handler');\n this.#clientGroupID = clientGroupID;\n this.clientID = clientID;\n this.wsID = wsID;\n this.#zeroClientsTable = `${upstreamSchema(shard)}.clients`;\n this.#zeroMutationsTable = `${upstreamSchema(shard)}.mutations`;\n this.#lc = lc;\n this.#downstream = downstream;\n this.#baseVersion = cookieToVersion(baseCookie);\n }\n\n version(): NullableCVRVersion {\n return this.#baseVersion;\n }\n\n async #push(msg: Downstream, serialized?: string | undefined): Promise<void> {\n const {result} = this.#downstream.push({message: msg, serialized});\n await result;\n }\n\n fail(e: unknown) {\n this.#lc[getLogLevel(e)]?.(\n `view-syncer closing connection with error: ${String(e)}`,\n e,\n );\n this.#downstream.fail(wrapWithProtocolError(e));\n }\n\n close(reason: string) {\n this.#lc.debug?.(`view-syncer closing connection: ${reason}`);\n this.#downstream.cancel();\n }\n\n startPoke(tentativeVersion: CVRVersion): PokeHandler {\n const pokeID = versionToCookie(tentativeVersion);\n const lc = this.#lc.withContext('pokeID', pokeID);\n\n // Force an (empty) initial poke even when already caught up; see #everPoked.\n const forceInitialPoke = !this.#everPoked;\n const cmp = cmpVersions(this.#baseVersion, tentativeVersion);\n if (cmp > 0 || (cmp === 0 && !forceInitialPoke)) {\n lc.info?.(`already caught up, not sending poke.`);\n return NOOP;\n }\n\n const baseCookie = versionToNullableCookie(this.#baseVersion);\n const cookie = versionToCookie(tentativeVersion);\n lc.debug?.(`starting poke from ${baseCookie} to ${cookie}`);\n\n const start = performance.now();\n\n const pokeStart: PokeStartBody = {pokeID, baseCookie};\n\n let pokeStarted = false;\n let body: PokePartBody | undefined;\n let partCount = 0;\n let serializedRowsLength = 0;\n\n const ensureBody = async () => {\n if (!pokeStarted) {\n await this.#push(['pokeStart', pokeStart]);\n pokeStarted = true;\n }\n return (body ??= {pokeID});\n };\n\n const flushBody = async () => {\n if (body) {\n const message = ['pokePart', body] satisfies PokePartMessage;\n await this.#push(message, JSON.stringify(message));\n body = undefined;\n partCount = 0;\n serializedRowsLength = 0;\n }\n };\n\n const addPatch = async (patchToVersion: PatchToVersion) => {\n const {patch, toVersion} = patchToVersion;\n if (cmpVersions(toVersion, this.#baseVersion) <= 0) {\n return;\n }\n const target = await ensureBody();\n\n const {type, op} = patch;\n switch (type) {\n case 'query': {\n const patches = patch.clientID\n ? ((target.desiredQueriesPatches ??= {})[patch.clientID] ??= [])\n : (target.gotQueriesPatch ??= []);\n patches.push({op, hash: patch.id});\n break;\n }\n case 'row':\n if (patch.id.table === this.#zeroClientsTable) {\n this.#updateLMIDs((target.lastMutationIDChanges ??= {}), patch);\n } else if (patch.id.table === this.#zeroMutationsTable) {\n const patches = (target.mutationsPatch ??= []);\n if (op === 'put') {\n const row = v.parse(\n ensureSafeJSON(patch.contents),\n mutationRowSchema,\n 'passthrough',\n );\n patches.push({\n op: 'put',\n mutation: {\n id: {\n clientID: row.clientID,\n id: row.mutationID,\n },\n result: row.result,\n },\n });\n } else {\n const {clientID, mutationID} = patch.id.rowKey;\n assert(\n typeof clientID === 'string',\n 'client id must be a string',\n );\n const id = Number(mutationID);\n assert(\n !Number.isNaN(id) && Number.isFinite(id) && id >= 0,\n 'mutation id must be a finite number',\n );\n patches.push({\n op: 'del',\n id: {\n clientID,\n id,\n },\n });\n }\n } else {\n const rowPatch = makeRowPatch(patch);\n (target.rowsPatch ??= []).push(rowPatch);\n serializedRowsLength += JSON.stringify(rowPatch).length;\n }\n break;\n default:\n unreachable(patch);\n }\n\n partCount++;\n if (\n serializedRowsLength >= POKE_PART_FLUSH_THRESHOLD_CHARS ||\n partCount >= PART_COUNT_FLUSH_THRESHOLD\n ) {\n await flushBody();\n }\n };\n\n // A poke that was started must always be ended. If it isn't, the\n // connection is left mid-poke and the *next* pokeStart fails the\n // DownstreamSender's in-progress check, surfacing the error far from\n // its cause. Fail the connection instead; the client reconnects and\n // resyncs from its baseVersion.\n return {\n addPatch: async (patchToVersion: PatchToVersion) => {\n try {\n await addPatch(patchToVersion);\n if (patchToVersion.patch.type === 'row') {\n this.#pokedRows.add(1);\n }\n } catch (e) {\n this.fail(e);\n }\n },\n\n cancel: async () => {\n try {\n if (pokeStarted) {\n await this.#push(['pokeEnd', {pokeID, cookie: '', cancel: true}]);\n }\n } catch (e) {\n this.fail(e);\n }\n },\n\n end: async (finalVersion: CVRVersion) => {\n try {\n const cookie = versionToCookie(finalVersion);\n if (!pokeStarted) {\n if (\n cmpVersions(this.#baseVersion, finalVersion) === 0 &&\n !forceInitialPoke\n ) {\n return; // Nothing changed and nothing was sent.\n }\n await this.#push(['pokeStart', pokeStart]);\n } else if (cmpVersions(this.#baseVersion, finalVersion) >= 0) {\n // Sanity check: If the poke was started, the finalVersion\n // must be > #baseVersion.\n throw new Error(\n `Patches were sent but finalVersion ${finalVersion} is ` +\n `not greater than baseVersion ${this.#baseVersion}`,\n );\n }\n await flushBody();\n await this.#push(['pokeEnd', {pokeID, cookie}]);\n this.#baseVersion = finalVersion;\n this.#everPoked = true;\n\n const elapsed = performance.now() - start;\n this.#pokeTransactions.add(1);\n this.#pokeTime.recordMs(elapsed);\n } catch (e) {\n this.fail(e);\n }\n },\n };\n }\n\n async sendDeleteClients(\n lc: LogContext,\n deletedClientIDs: string[],\n deletedClientGroupIDs: string[],\n ) {\n const deleteClientsBody: Writable<DeleteClientsBody> = {};\n if (deletedClientIDs.length > 0) {\n deleteClientsBody.clientIDs = deletedClientIDs;\n }\n if (deletedClientGroupIDs.length > 0) {\n deleteClientsBody.clientGroupIDs = deletedClientGroupIDs;\n }\n lc.debug?.('sending deleteClients', deleteClientsBody);\n await this.#push(['deleteClients', deleteClientsBody]);\n }\n\n sendQueryTransformApplicationErrors(errors: ErroredQuery[]) {\n void this.#push(['transformError', errors]);\n }\n\n sendQueryTransformFailedError(error: TransformFailedBody) {\n this.fail(new ProtocolError(error));\n }\n\n sendInspectResponse(lc: LogContext, response: InspectDownBody): void {\n lc.debug?.('sending inspect response', response);\n this.#downstream.push({\n message: ['inspect', response],\n serialized: undefined,\n });\n }\n\n #updateLMIDs(lmids: Record<string, number>, patch: RowPatch) {\n if (patch.op === 'put') {\n const row = ensureSafeJSON(patch.contents);\n const {clientGroupID, clientID, lastMutationID} = v.parse(\n row,\n lmidRowSchema,\n 'passthrough',\n );\n if (clientGroupID !== this.#clientGroupID) {\n this.#lc.error?.(\n `Received clients row for wrong clientGroupID. Ignoring.`,\n clientGroupID,\n );\n } else {\n lmids[clientID] = lastMutationID;\n }\n } else {\n // The 'constrain' and 'del' ops for clients can be ignored.\n patch.op satisfies 'constrain' | 'del';\n }\n }\n}\n\n// Note: The {APP_ID}_{SHARD_ID}.clients table is set up in replicator/initial-sync.ts.\nconst lmidRowSchema = v.object({\n clientGroupID: v.string(),\n clientID: v.string(),\n lastMutationID: v.number(), // Actually returned as a bigint, but converted by ensureSafeJSON().\n});\n\nconst mutationRowSchema = v.object({\n clientGroupID: v.string(),\n clientID: v.string(),\n mutationID: v.number(),\n result: mutationResultSchema,\n});\n\nfunction makeRowPatch(patch: RowPatch): RowPatchOp {\n const {\n op,\n id: {table: tableName, rowKey: id},\n } = patch;\n\n switch (op) {\n case 'put':\n return {\n op: 'put',\n tableName,\n value: v.parse(ensureSafeJSON(patch.contents), rowSchema),\n };\n\n case 'del':\n return {\n op,\n tableName,\n id: v.parse(id, primaryKeyValueRecordSchema),\n };\n\n default:\n unreachable(op);\n }\n}\n\n/**\n * Column values of type INT8 are returned as the `bigint` from the\n * Postgres library. These are converted to `number` if they are within\n * the safe Number range, allowing the protocol to support numbers larger\n * than 32-bits. Values outside of the safe number range (e.g. > 2^53) will\n * result in an Error.\n */\nexport function ensureSafeJSON(row: JSONObject): SafeJSONObject {\n const modified = Object.entries(row)\n .filter(([k, v]) => {\n if (typeof v === 'bigint') {\n if (v >= Number.MIN_SAFE_INTEGER && v <= Number.MAX_SAFE_INTEGER) {\n return true; // send this entry onto the next map() step.\n }\n throw new Error(`Value of \"${k}\" exceeds safe Number range (${v})`);\n } else if (typeof v === 'object') {\n assertJSONValue(v);\n }\n return false;\n })\n .map(([k, v]) => [k, Number(v)]);\n\n return modified.length\n ? {...row, ...Object.fromEntries(modified)}\n : (row as SafeJSONObject);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client-handler.js","names":["#clientGroupID","#zeroClientsTable","#zeroMutationsTable","#lc","#downstream","#pokeTime","#pokeTransactions","#pokedRows","#baseVersion","#push","#everPoked","#updateLMIDs"],"sources":["../../../../../../zero-cache/src/services/view-syncer/client-handler.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert, unreachable} from '../../../../shared/src/asserts.ts';\nimport type {JSONObject} from '../../../../shared/src/bigint-json.ts';\nimport {\n assertJSONValue,\n type JSONObject as SafeJSONObject,\n} from '../../../../shared/src/json.ts';\nimport {promiseVoid} from '../../../../shared/src/resolved-promises.ts';\nimport * as v from '../../../../shared/src/valita.ts';\nimport type {Writable} from '../../../../shared/src/writable.ts';\nimport type {ErroredQuery} from '../../../../zero-protocol/src/custom-queries.ts';\nimport {rowSchema} from '../../../../zero-protocol/src/data.ts';\nimport type {DeleteClientsBody} from '../../../../zero-protocol/src/delete-clients.ts';\nimport type {Downstream} from '../../../../zero-protocol/src/down.ts';\nimport {\n ProtocolError,\n type TransformFailedBody,\n} from '../../../../zero-protocol/src/error.ts';\nimport type {InspectDownBody} from '../../../../zero-protocol/src/inspect-down.ts';\nimport {mutationResultSchema} from '../../../../zero-protocol/src/mutation.ts';\nimport type {\n PokePartBody,\n PokePartMessage,\n PokeStartBody,\n} from '../../../../zero-protocol/src/poke.ts';\nimport {primaryKeyValueRecordSchema} from '../../../../zero-protocol/src/primary-key.ts';\nimport type {RowPatchOp} from '../../../../zero-protocol/src/row-patch.ts';\nimport {\n getOrCreateCounter,\n getOrCreateLatencyHistogram,\n} from '../../observability/metrics.ts';\nimport type {ViewSyncerDownstream} from '../../types/downstream.ts';\nimport {\n getLogLevel,\n wrapWithProtocolError,\n} from '../../types/error-with-level.ts';\nimport {upstreamSchema, type ShardID} from '../../types/shards.ts';\nimport type {Subscription} from '../../types/subscription.ts';\nimport {\n cmpVersions,\n cookieToVersion,\n versionToCookie,\n versionToNullableCookie,\n type CVRVersion,\n type DelQueryPatch,\n type NullableCVRVersion,\n type PutQueryPatch,\n type RowID,\n} from './schema/types.ts';\n\nexport type PutRowPatch = {\n type: 'row';\n op: 'put';\n id: RowID;\n contents: JSONObject;\n};\n\nexport type DeleteRowPatch = {\n type: 'row';\n op: 'del';\n id: RowID;\n};\n\nexport type RowPatch = PutRowPatch | DeleteRowPatch;\nexport type ConfigPatch = DelQueryPatch | PutQueryPatch;\n\nexport type Patch = ConfigPatch | RowPatch;\n\nexport type PatchToVersion = {\n patch: Patch;\n toVersion: CVRVersion;\n};\n\nexport interface PokeHandler {\n addPatch(patch: PatchToVersion): Promise<void>;\n cancel(): Promise<void>;\n end(finalVersion: CVRVersion): Promise<void>;\n}\n\n/** A {@link PokeHandler} for a single client. */\nexport interface ClientPokeHandler extends PokeHandler {\n /**\n * Whether a patch has been sent to the client. Once one has, the poke can\n * only be ended with a `finalVersion` that is ahead of the client's base\n * version.\n */\n readonly patchesSent: boolean;\n}\n\n/**\n * A {@link PokeHandler} for multiple clients, as returned by\n * {@link startPoke}.\n */\nexport interface MultiPokeHandler extends PokeHandler {\n /** Whether a patch has been sent to any of the clients. */\n readonly patchesSent: boolean;\n}\n\nconst NOOP: ClientPokeHandler = {\n patchesSent: false,\n addPatch: () => promiseVoid,\n cancel: () => promiseVoid,\n end: () => promiseVoid,\n};\n\n/** Wraps PokeHandlers for multiple clients in a single PokeHandler. */\nexport function startPoke(\n lc: LogContext,\n clients: ClientHandler[],\n tentativeVersion: CVRVersion,\n): MultiPokeHandler {\n const pokers = clients.map(c => c.startPoke(tentativeVersion));\n\n // Promise.allSettled() ensures that a failed (e.g. disconnected) client\n // does not prevent other clients from receiving the pokes. However, the\n // rate (per client group) will be limited by the slowest connection.\n //\n // The individual pokers report their own failures (by failing the\n // connection), so a rejection here is not expected. Log it rather than\n // dropping it on the floor.\n const settle = async (op: string, results: Promise<void>[]) => {\n for (const result of await Promise.allSettled(results)) {\n if (result.status === 'rejected') {\n lc.error?.(`unhandled error poking client (${op})`, result.reason);\n }\n }\n };\n\n return {\n get patchesSent() {\n return pokers.some(poker => poker.patchesSent);\n },\n addPatch: patch =>\n settle(\n 'addPatch',\n pokers.map(poker => poker.addPatch(patch)),\n ),\n cancel: () =>\n settle(\n 'cancel',\n pokers.map(poker => poker.cancel()),\n ),\n end: finalVersion =>\n settle(\n 'end',\n pokers.map(poker => poker.end(finalVersion)),\n ),\n };\n}\n\n// Soft limit on the serialized row characters accumulated in a poke part.\n// This bounds view-syncer memory and keeps legacy JSON messages reasonably\n// sized. PokeChunkEncoder separately enforces the binary frame limit in bytes.\nexport const POKE_PART_FLUSH_THRESHOLD_CHARS = 1024 * 1024;\nconst PART_COUNT_FLUSH_THRESHOLD = 100;\n\n/**\n * Handles a single `ViewSyncer` connection.\n */\nexport class ClientHandler {\n readonly #clientGroupID: string;\n readonly clientID: string;\n readonly wsID: string;\n readonly #zeroClientsTable: string;\n readonly #zeroMutationsTable: string;\n readonly #lc: LogContext;\n readonly #downstream: Subscription<ViewSyncerDownstream>;\n #baseVersion: NullableCVRVersion;\n // We will send a poke on connect even if the client is already caught up, so that it can learn its\n // got-queries state has been reconciled with the server. After that, we will only send a poke if\n // the client is behind.\n #everPoked = false;\n\n readonly #pokeTime = getOrCreateLatencyHistogram(\n 'sync',\n 'poke.time',\n 'Time elapsed for each poke transaction. Canceled / noop pokes are excluded.',\n );\n\n readonly #pokeTransactions = getOrCreateCounter(\n 'sync',\n 'poke.transactions',\n 'Count of poke transactions.',\n );\n\n readonly #pokedRows = getOrCreateCounter(\n 'sync',\n 'poke.rows',\n 'Count of poked rows.',\n );\n\n constructor(\n lc: LogContext,\n clientGroupID: string,\n clientID: string,\n wsID: string,\n shard: ShardID,\n baseCookie: string | null,\n downstream: Subscription<ViewSyncerDownstream>,\n ) {\n lc.debug?.('new client handler');\n this.#clientGroupID = clientGroupID;\n this.clientID = clientID;\n this.wsID = wsID;\n this.#zeroClientsTable = `${upstreamSchema(shard)}.clients`;\n this.#zeroMutationsTable = `${upstreamSchema(shard)}.mutations`;\n this.#lc = lc;\n this.#downstream = downstream;\n this.#baseVersion = cookieToVersion(baseCookie);\n }\n\n version(): NullableCVRVersion {\n return this.#baseVersion;\n }\n\n async #push(msg: Downstream, serialized?: string | undefined): Promise<void> {\n const {result} = this.#downstream.push({message: msg, serialized});\n await result;\n }\n\n fail(e: unknown) {\n this.#lc[getLogLevel(e)]?.(\n `view-syncer closing connection with error: ${String(e)}`,\n e,\n );\n this.#downstream.fail(wrapWithProtocolError(e));\n }\n\n close(reason: string) {\n this.#lc.debug?.(`view-syncer closing connection: ${reason}`);\n this.#downstream.cancel();\n }\n\n startPoke(tentativeVersion: CVRVersion): ClientPokeHandler {\n const pokeID = versionToCookie(tentativeVersion);\n const lc = this.#lc.withContext('pokeID', pokeID);\n\n // Force an (empty) initial poke even when already caught up; see #everPoked.\n const forceInitialPoke = !this.#everPoked;\n const cmp = cmpVersions(this.#baseVersion, tentativeVersion);\n if (cmp > 0 || (cmp === 0 && !forceInitialPoke)) {\n lc.info?.(`already caught up, not sending poke.`);\n return NOOP;\n }\n\n const baseCookie = versionToNullableCookie(this.#baseVersion);\n const cookie = versionToCookie(tentativeVersion);\n lc.debug?.(`starting poke from ${baseCookie} to ${cookie}`);\n\n const start = performance.now();\n\n const pokeStart: PokeStartBody = {pokeID, baseCookie};\n\n let pokeStarted = false;\n let body: PokePartBody | undefined;\n let partCount = 0;\n let serializedRowsLength = 0;\n\n const ensureBody = async () => {\n if (!pokeStarted) {\n await this.#push(['pokeStart', pokeStart]);\n pokeStarted = true;\n }\n return (body ??= {pokeID});\n };\n\n const flushBody = async () => {\n if (body) {\n const message = ['pokePart', body] satisfies PokePartMessage;\n await this.#push(message, JSON.stringify(message));\n body = undefined;\n partCount = 0;\n serializedRowsLength = 0;\n }\n };\n\n const addPatch = async (patchToVersion: PatchToVersion) => {\n const {patch, toVersion} = patchToVersion;\n if (cmpVersions(toVersion, this.#baseVersion) <= 0) {\n return;\n }\n const target = await ensureBody();\n\n const {type, op} = patch;\n switch (type) {\n case 'query': {\n const patches = patch.clientID\n ? ((target.desiredQueriesPatches ??= {})[patch.clientID] ??= [])\n : (target.gotQueriesPatch ??= []);\n patches.push({op, hash: patch.id});\n break;\n }\n case 'row':\n if (patch.id.table === this.#zeroClientsTable) {\n this.#updateLMIDs((target.lastMutationIDChanges ??= {}), patch);\n } else if (patch.id.table === this.#zeroMutationsTable) {\n const patches = (target.mutationsPatch ??= []);\n if (op === 'put') {\n const row = v.parse(\n ensureSafeJSON(patch.contents),\n mutationRowSchema,\n 'passthrough',\n );\n patches.push({\n op: 'put',\n mutation: {\n id: {\n clientID: row.clientID,\n id: row.mutationID,\n },\n result: row.result,\n },\n });\n } else {\n const {clientID, mutationID} = patch.id.rowKey;\n assert(\n typeof clientID === 'string',\n 'client id must be a string',\n );\n const id = Number(mutationID);\n assert(\n !Number.isNaN(id) && Number.isFinite(id) && id >= 0,\n 'mutation id must be a finite number',\n );\n patches.push({\n op: 'del',\n id: {\n clientID,\n id,\n },\n });\n }\n } else {\n const rowPatch = makeRowPatch(patch);\n (target.rowsPatch ??= []).push(rowPatch);\n serializedRowsLength += JSON.stringify(rowPatch).length;\n }\n break;\n default:\n unreachable(patch);\n }\n\n partCount++;\n if (\n serializedRowsLength >= POKE_PART_FLUSH_THRESHOLD_CHARS ||\n partCount >= PART_COUNT_FLUSH_THRESHOLD\n ) {\n await flushBody();\n }\n };\n\n // A poke that was started must always be ended. If it isn't, the\n // connection is left mid-poke and the *next* pokeStart fails the\n // DownstreamSender's in-progress check, surfacing the error far from\n // its cause. Fail the connection instead; the client reconnects and\n // resyncs from its baseVersion.\n return {\n get patchesSent() {\n return pokeStarted;\n },\n addPatch: async (patchToVersion: PatchToVersion) => {\n try {\n await addPatch(patchToVersion);\n if (patchToVersion.patch.type === 'row') {\n this.#pokedRows.add(1);\n }\n } catch (e) {\n this.fail(e);\n }\n },\n\n cancel: async () => {\n try {\n if (pokeStarted) {\n await this.#push(['pokeEnd', {pokeID, cookie: '', cancel: true}]);\n }\n } catch (e) {\n this.fail(e);\n }\n },\n\n end: async (finalVersion: CVRVersion) => {\n try {\n const cookie = versionToCookie(finalVersion);\n if (!pokeStarted) {\n if (\n cmpVersions(this.#baseVersion, finalVersion) === 0 &&\n !forceInitialPoke\n ) {\n return; // Nothing changed and nothing was sent.\n }\n await this.#push(['pokeStart', pokeStart]);\n } else if (cmpVersions(this.#baseVersion, finalVersion) >= 0) {\n // Sanity check: If the poke was started, the finalVersion\n // must be > #baseVersion.\n throw new Error(\n `Patches were sent but finalVersion ${cookie} is not ` +\n `greater than baseVersion ` +\n `${versionToNullableCookie(this.#baseVersion)}`,\n );\n }\n await flushBody();\n await this.#push(['pokeEnd', {pokeID, cookie}]);\n this.#baseVersion = finalVersion;\n this.#everPoked = true;\n\n const elapsed = performance.now() - start;\n this.#pokeTransactions.add(1);\n this.#pokeTime.recordMs(elapsed);\n } catch (e) {\n this.fail(e);\n }\n },\n };\n }\n\n async sendDeleteClients(\n lc: LogContext,\n deletedClientIDs: string[],\n deletedClientGroupIDs: string[],\n ) {\n const deleteClientsBody: Writable<DeleteClientsBody> = {};\n if (deletedClientIDs.length > 0) {\n deleteClientsBody.clientIDs = deletedClientIDs;\n }\n if (deletedClientGroupIDs.length > 0) {\n deleteClientsBody.clientGroupIDs = deletedClientGroupIDs;\n }\n lc.debug?.('sending deleteClients', deleteClientsBody);\n await this.#push(['deleteClients', deleteClientsBody]);\n }\n\n sendQueryTransformApplicationErrors(errors: ErroredQuery[]) {\n void this.#push(['transformError', errors]);\n }\n\n sendQueryTransformFailedError(error: TransformFailedBody) {\n this.fail(new ProtocolError(error));\n }\n\n sendInspectResponse(lc: LogContext, response: InspectDownBody): void {\n lc.debug?.('sending inspect response', response);\n this.#downstream.push({\n message: ['inspect', response],\n serialized: undefined,\n });\n }\n\n #updateLMIDs(lmids: Record<string, number>, patch: RowPatch) {\n if (patch.op === 'put') {\n const row = ensureSafeJSON(patch.contents);\n const {clientGroupID, clientID, lastMutationID} = v.parse(\n row,\n lmidRowSchema,\n 'passthrough',\n );\n if (clientGroupID !== this.#clientGroupID) {\n this.#lc.error?.(\n `Received clients row for wrong clientGroupID. Ignoring.`,\n clientGroupID,\n );\n } else {\n lmids[clientID] = lastMutationID;\n }\n } else {\n // The 'constrain' and 'del' ops for clients can be ignored.\n patch.op satisfies 'constrain' | 'del';\n }\n }\n}\n\n// Note: The {APP_ID}_{SHARD_ID}.clients table is set up in replicator/initial-sync.ts.\nconst lmidRowSchema = v.object({\n clientGroupID: v.string(),\n clientID: v.string(),\n lastMutationID: v.number(), // Actually returned as a bigint, but converted by ensureSafeJSON().\n});\n\nconst mutationRowSchema = v.object({\n clientGroupID: v.string(),\n clientID: v.string(),\n mutationID: v.number(),\n result: mutationResultSchema,\n});\n\nfunction makeRowPatch(patch: RowPatch): RowPatchOp {\n const {\n op,\n id: {table: tableName, rowKey: id},\n } = patch;\n\n switch (op) {\n case 'put':\n return {\n op: 'put',\n tableName,\n value: v.parse(ensureSafeJSON(patch.contents), rowSchema),\n };\n\n case 'del':\n return {\n op,\n tableName,\n id: v.parse(id, primaryKeyValueRecordSchema),\n };\n\n default:\n unreachable(op);\n }\n}\n\n/**\n * Column values of type INT8 are returned as the `bigint` from the\n * Postgres library. These are converted to `number` if they are within\n * the safe Number range, allowing the protocol to support numbers larger\n * than 32-bits. Values outside of the safe number range (e.g. > 2^53) will\n * result in an Error.\n */\nexport function ensureSafeJSON(row: JSONObject): SafeJSONObject {\n const modified = Object.entries(row)\n .filter(([k, v]) => {\n if (typeof v === 'bigint') {\n if (v >= Number.MIN_SAFE_INTEGER && v <= Number.MAX_SAFE_INTEGER) {\n return true; // send this entry onto the next map() step.\n }\n throw new Error(`Value of \"${k}\" exceeds safe Number range (${v})`);\n } else if (typeof v === 'object') {\n assertJSONValue(v);\n }\n return false;\n })\n .map(([k, v]) => [k, Number(v)]);\n\n return modified.length\n ? {...row, ...Object.fromEntries(modified)}\n : (row as SafeJSONObject);\n}\n"],"mappings":"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"}
|
|
@@ -67,7 +67,25 @@ export declare class CVRStore {
|
|
|
67
67
|
catchupRowPatches(lc: LogContext, afterVersion: NullableCVRVersion, upToCVR: CVRSnapshot, current: CVRVersion, excludeQueryHashes?: string[]): AsyncGenerator<RowsRow[], void, undefined>;
|
|
68
68
|
catchupConfigPatches(lc: LogContext, afterVersion: NullableCVRVersion, upToCVR: CVRSnapshot, current: CVRVersion): Promise<PatchToVersion[]>;
|
|
69
69
|
get rowCount(): number;
|
|
70
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Flushes pending writes, checking that `expectedCurrentVersion` is still
|
|
72
|
+
* the current version of the CVR and that this task still owns it.
|
|
73
|
+
*
|
|
74
|
+
* If there is nothing to write, the flush is a no-op and no check is done,
|
|
75
|
+
* unless `verifyNoop` is set.
|
|
76
|
+
*/
|
|
77
|
+
flush(lc: LogContext, expectedCurrentVersion: CVRVersion, cvr: CVRSnapshot, lastConnectTime: number, verifyNoop?: boolean): Promise<CVRFlushStats | null>;
|
|
78
|
+
/**
|
|
79
|
+
* Discards the writes queued since the last {@link flush}, i.e. those of
|
|
80
|
+
* an updater that is abandoned without being flushed (such as an
|
|
81
|
+
* advancement that aborts with a pipeline reset). Otherwise they would be
|
|
82
|
+
* written by the next flush, even though the poke that carried them to the
|
|
83
|
+
* clients was cancelled.
|
|
84
|
+
*
|
|
85
|
+
* The row cache is not affected: it only learns of writes when they are
|
|
86
|
+
* flushed.
|
|
87
|
+
*/
|
|
88
|
+
discardPending(): void;
|
|
71
89
|
hasPendingUpdates(): boolean;
|
|
72
90
|
/** Resolves when all pending updates are flushed. */
|
|
73
91
|
flushed(lc: LogContext): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cvr-store.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/cvr-store.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"cvr-store.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/cvr-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -691,7 +691,7 @@ var CVRStore = class {
|
|
|
691
691
|
if (owner !== this.#taskID && (grantedAt ?? 0) > lastConnectTime) throw new OwnershipError(owner, grantedAt, lastConnectTime);
|
|
692
692
|
if (version !== expected) throw new ConcurrentModificationException(expected, version);
|
|
693
693
|
}
|
|
694
|
-
async #flush(lc, expectedCurrentVersion, cvr, lastConnectTime) {
|
|
694
|
+
async #flush(lc, expectedCurrentVersion, cvr, lastConnectTime, verifyNoop) {
|
|
695
695
|
const stats = {
|
|
696
696
|
instances: 0,
|
|
697
697
|
queries: 0,
|
|
@@ -710,7 +710,10 @@ var CVRStore = class {
|
|
|
710
710
|
if (existing === void 0 && !row?.refCounts || deepEqual(row ?? void 0, existing)) this.#pendingRowRecordUpdates.delete(id);
|
|
711
711
|
}
|
|
712
712
|
}
|
|
713
|
-
if (this.#pendingRowRecordUpdates.size === 0 && this.#writes.size === 0 && this.#pendingInstanceWrite === void 0 && this.#pendingQueryUpdates.size === 0 && this.#pendingQueryPartialUpdates.size === 0 && this.#pendingDesireUpdates.size === 0)
|
|
713
|
+
if (this.#pendingRowRecordUpdates.size === 0 && this.#writes.size === 0 && this.#pendingInstanceWrite === void 0 && this.#pendingQueryUpdates.size === 0 && this.#pendingQueryPartialUpdates.size === 0 && this.#pendingDesireUpdates.size === 0) {
|
|
714
|
+
if (verifyNoop) await runTx(this.#db, (tx) => this.#checkVersionAndOwnership(lc, tx, expectedCurrentVersion, lastConnectTime), { mode: READ_COMMITTED });
|
|
715
|
+
return null;
|
|
716
|
+
}
|
|
714
717
|
this.putInstance(cvr);
|
|
715
718
|
const start = Date.now();
|
|
716
719
|
lc.debug?.("flush tx beginning");
|
|
@@ -768,11 +771,18 @@ var CVRStore = class {
|
|
|
768
771
|
get rowCount() {
|
|
769
772
|
return this.#rowCount;
|
|
770
773
|
}
|
|
771
|
-
|
|
774
|
+
/**
|
|
775
|
+
* Flushes pending writes, checking that `expectedCurrentVersion` is still
|
|
776
|
+
* the current version of the CVR and that this task still owns it.
|
|
777
|
+
*
|
|
778
|
+
* If there is nothing to write, the flush is a no-op and no check is done,
|
|
779
|
+
* unless `verifyNoop` is set.
|
|
780
|
+
*/
|
|
781
|
+
async flush(lc, expectedCurrentVersion, cvr, lastConnectTime, verifyNoop = false) {
|
|
772
782
|
const start = performance.now();
|
|
773
783
|
lc = lc.withContext("cvrFlushID", flushCounter++);
|
|
774
784
|
try {
|
|
775
|
-
const stats = await this.#flush(lc, expectedCurrentVersion, cvr, lastConnectTime);
|
|
785
|
+
const stats = await this.#flush(lc, expectedCurrentVersion, cvr, lastConnectTime, verifyNoop);
|
|
776
786
|
if (stats) {
|
|
777
787
|
const elapsed = performance.now() - start;
|
|
778
788
|
lc.info?.(`flushed cvr@${versionString(cvr.version)} ${JSON.stringify(stats)} in (${elapsed} ms)`);
|
|
@@ -792,15 +802,28 @@ var CVRStore = class {
|
|
|
792
802
|
this.#rowCache.clear();
|
|
793
803
|
throw e;
|
|
794
804
|
} finally {
|
|
795
|
-
this
|
|
796
|
-
this.#pendingInstanceWrite = void 0;
|
|
797
|
-
this.#pendingRowRecordUpdates.clear();
|
|
798
|
-
this.#forceUpdates.clear();
|
|
799
|
-
this.#pendingQueryUpdates.clear();
|
|
800
|
-
this.#pendingDesireUpdates.clear();
|
|
801
|
-
this.#pendingQueryPartialUpdates.clear();
|
|
805
|
+
this.discardPending();
|
|
802
806
|
}
|
|
803
807
|
}
|
|
808
|
+
/**
|
|
809
|
+
* Discards the writes queued since the last {@link flush}, i.e. those of
|
|
810
|
+
* an updater that is abandoned without being flushed (such as an
|
|
811
|
+
* advancement that aborts with a pipeline reset). Otherwise they would be
|
|
812
|
+
* written by the next flush, even though the poke that carried them to the
|
|
813
|
+
* clients was cancelled.
|
|
814
|
+
*
|
|
815
|
+
* The row cache is not affected: it only learns of writes when they are
|
|
816
|
+
* flushed.
|
|
817
|
+
*/
|
|
818
|
+
discardPending() {
|
|
819
|
+
this.#writes.clear();
|
|
820
|
+
this.#pendingInstanceWrite = void 0;
|
|
821
|
+
this.#pendingRowRecordUpdates.clear();
|
|
822
|
+
this.#forceUpdates.clear();
|
|
823
|
+
this.#pendingQueryUpdates.clear();
|
|
824
|
+
this.#pendingDesireUpdates.clear();
|
|
825
|
+
this.#pendingQueryPartialUpdates.clear();
|
|
826
|
+
}
|
|
804
827
|
hasPendingUpdates() {
|
|
805
828
|
return this.#rowCache.hasPendingUpdates();
|
|
806
829
|
}
|