@rocicorp/zero 1.11.0-canary.35 → 1.11.0-canary.37
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/zero/package.js +1 -1
- package/out/zero/package.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +28 -0
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +83 -0
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +2 -1
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/main.d.ts.map +1 -1
- package/out/zero-cache/src/server/main.js +5 -1
- package/out/zero-cache/src/server/main.js.map +1 -1
- package/out/zero-cache/src/server/replicator.d.ts +4 -0
- package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/server/replicator.js +18 -4
- package/out/zero-cache/src/server/replicator.js.map +1 -1
- package/out/zero-cache/src/server/runner/run-worker.d.ts.map +1 -1
- package/out/zero-cache/src/server/runner/run-worker.js +12 -6
- package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
- package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/server/syncer.js +17 -1
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +27 -7
- package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slots.js +124 -62
- package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +9 -3
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +19 -6
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +8 -0
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +131 -62
- 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 +124 -2
- 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 +33 -2
- package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/drain-tracker.d.ts +24 -0
- package/out/zero-cache/src/services/change-streamer/drain-tracker.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/drain-tracker.js +67 -0
- package/out/zero-cache/src/services/change-streamer/drain-tracker.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/restore-admission.d.ts +214 -0
- package/out/zero-cache/src/services/change-streamer/restore-admission.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/restore-admission.js +294 -0
- package/out/zero-cache/src/services/change-streamer/restore-admission.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts +13 -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 +7 -6
- 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 +2 -2
- package/out/zero-cache/src/services/change-streamer/snapshot.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot.js +2 -2
- package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscribe.d.ts +39 -0
- package/out/zero-cache/src/services/change-streamer/subscribe.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscribe.js +19 -2
- package/out/zero-cache/src/services/change-streamer/subscribe.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +22 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.js +41 -15
- package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
- package/out/zero-cache/src/services/http-service.d.ts +6 -0
- package/out/zero-cache/src/services/http-service.d.ts.map +1 -1
- package/out/zero-cache/src/services/http-service.js +14 -2
- package/out/zero-cache/src/services/http-service.js.map +1 -1
- package/out/zero-cache/src/services/life-cycle.d.ts +33 -0
- package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
- package/out/zero-cache/src/services/life-cycle.js +56 -10
- package/out/zero-cache/src/services/life-cycle.js.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.d.ts +8 -0
- package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.js +22 -1
- package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
- package/out/zero-cache/src/services/replicator/drain-handler.d.ts +74 -0
- package/out/zero-cache/src/services/replicator/drain-handler.d.ts.map +1 -0
- package/out/zero-cache/src/services/replicator/drain-handler.js +185 -0
- package/out/zero-cache/src/services/replicator/drain-handler.js.map +1 -0
- 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 +66 -13
- 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/batch-pacer.d.ts +79 -0
- package/out/zero-cache/src/services/view-syncer/batch-pacer.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/batch-pacer.js +249 -0
- package/out/zero-cache/src/services/view-syncer/batch-pacer.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts +27 -1
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js +107 -17
- 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 +85 -3
- 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 +301 -20
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/push-cost.d.ts +132 -0
- package/out/zero-cache/src/services/view-syncer/push-cost.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/push-cost.js +164 -0
- package/out/zero-cache/src/services/view-syncer/push-cost.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/push-cutoff.d.ts +55 -0
- package/out/zero-cache/src/services/view-syncer/push-cutoff.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/push-cutoff.js +56 -0
- package/out/zero-cache/src/services/view-syncer/push-cutoff.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/query-stats.d.ts +7 -0
- package/out/zero-cache/src/services/view-syncer/query-stats.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/query-stats.js +20 -1
- package/out/zero-cache/src/services/view-syncer/query-stats.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +136 -5
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js +246 -18
- 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 +8 -2
- 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 +381 -16
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/processes.d.ts +8 -0
- package/out/zero-cache/src/types/processes.d.ts.map +1 -1
- package/out/zero-cache/src/types/processes.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-react/src/use-query.js +1 -1
- package/out/zero-react/src/use-query.js.map +1 -1
- package/out/zero-server/src/queries/process-queries.d.ts.map +1 -1
- package/out/zero-server/src/queries/process-queries.js +1 -1
- package/out/zero-server/src/queries/process-queries.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { LogContext } from '@rocicorp/logger';
|
|
2
|
+
export type BatchPacerOptions = {
|
|
3
|
+
/**
|
|
4
|
+
* The largest share of the worker's time that it may be busy before its
|
|
5
|
+
* client groups are paced.
|
|
6
|
+
*/
|
|
7
|
+
readonly shareTarget: number;
|
|
8
|
+
/** The longest a client group waits between batches. */
|
|
9
|
+
readonly maxIntervalMs: number;
|
|
10
|
+
readonly now?: (() => number) | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* The share of the time since the previous call that the worker was busy.
|
|
13
|
+
* By default, the utilization of the event loop.
|
|
14
|
+
*/
|
|
15
|
+
readonly utilization?: (() => number) | undefined;
|
|
16
|
+
/** Where the worker says that it is pacing its client groups. */
|
|
17
|
+
readonly lc?: LogContext | undefined;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Paces the batches (advancements, including the queries they hydrate
|
|
21
|
+
* again) of the client groups of a sync worker, so that the worker is left
|
|
22
|
+
* with time for new hydrations and connections, and gives the hydrations of
|
|
23
|
+
* new queries priority over the batches.
|
|
24
|
+
*
|
|
25
|
+
* **Pacing.** After each batch, a client group waits {@link waitMs} before
|
|
26
|
+
* it starts the next. The wait is a multiple of the batch's processing
|
|
27
|
+
* time, so a client group that always has changes waiting is busy at most
|
|
28
|
+
* `1 / (1 + k)` of the time, where `k` is the {@link multiplier}. Because
|
|
29
|
+
* the wait is in proportion to a client group's own processing time, those
|
|
30
|
+
* that cause the load are slowed the most, and cheap ones barely at all.
|
|
31
|
+
*
|
|
32
|
+
* The multiplier is shared by the client groups of the worker. Once per
|
|
33
|
+
* second, it is scaled by how far the share of its time that the worker was
|
|
34
|
+
* busy, smoothed over a few seconds, is from the target, and by more while
|
|
35
|
+
* the worker has no time to spare. It never goes below 0, where no client
|
|
36
|
+
* group waits. The share is the utilization of the worker's event loop, i.e. all
|
|
37
|
+
* of what the worker does: what a batch costs is spread over the pipelines,
|
|
38
|
+
* the CVR, the pokes and garbage collection, and is not measurable batch by
|
|
39
|
+
* batch.
|
|
40
|
+
*
|
|
41
|
+
* **Priority of new hydrations.** The worker's time slices are handed out
|
|
42
|
+
* by the pacer (see {@link yieldSlice}), and while the hydration of new
|
|
43
|
+
* queries is waiting for one, most of them go to it rather than to batches.
|
|
44
|
+
*/
|
|
45
|
+
export declare class BatchPacer {
|
|
46
|
+
#private;
|
|
47
|
+
constructor({ shareTarget, maxIntervalMs, now, utilization, lc, }: BatchPacerOptions);
|
|
48
|
+
/**
|
|
49
|
+
* How long a client group should wait before its next batch, after one
|
|
50
|
+
* that took `processingMs` to process. The multiplier changes while a
|
|
51
|
+
* client group waits, so a long wait should be taken in steps, asking
|
|
52
|
+
* again after each.
|
|
53
|
+
*/
|
|
54
|
+
waitMs(processingMs: number): number;
|
|
55
|
+
/** `k`: a client group waits `k` times the processing time of a batch. */
|
|
56
|
+
get multiplier(): number;
|
|
57
|
+
/**
|
|
58
|
+
* The share of its time that the worker was busy, smoothed over the last
|
|
59
|
+
* few seconds, as of the last update of the multiplier.
|
|
60
|
+
*/
|
|
61
|
+
get share(): number;
|
|
62
|
+
/**
|
|
63
|
+
* Yields the worker to the other work that is waiting for a time slice,
|
|
64
|
+
* and resolves when it is the caller's turn again. One piece of work runs
|
|
65
|
+
* per turn of the event loop, as when the work of a worker's client groups
|
|
66
|
+
* simply takes turns, except that the hydration of new queries goes first:
|
|
67
|
+
* while both kinds of work are waiting, `HYDRATION_PRIORITY` slices go to
|
|
68
|
+
* hydrations for each one that goes to a batch.
|
|
69
|
+
*
|
|
70
|
+
* Batches are never made to wait for anything but a slice. If no
|
|
71
|
+
* hydration is waiting for one (because none is running, or because those
|
|
72
|
+
* that are running are waiting for I/O), batches get every slice. And
|
|
73
|
+
* however many hydrations there are, batches keep a share of the slices,
|
|
74
|
+
* so they keep moving, and so do the snapshots of the replica that they
|
|
75
|
+
* hold.
|
|
76
|
+
*/
|
|
77
|
+
yieldSlice(kind: 'hydration' | 'batch'): Promise<void>;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=batch-pacer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch-pacer.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/batch-pacer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAyDjD,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;OAGG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,wDAAwD;IACxD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,MAAM,CAAC,GAAG,SAAS,CAAC;IAC1C;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,MAAM,MAAM,CAAC,GAAG,SAAS,CAAC;IAClD,iEAAiE;IACjE,QAAQ,CAAC,EAAE,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CACtC,CAAC;AAYF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,UAAU;;IA8BrB,YAAY,EACV,WAAW,EACX,aAAa,EACb,GAAG,EACH,WAAW,EACX,EAAE,GACH,EAAE,iBAAiB,EAOnB;IAED;;;;;OAKG;IACH,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CASnC;IAED,0EAA0E;IAC1E,IAAI,UAAU,IAAI,MAAM,CAGvB;IAED;;;OAGG;IACH,IAAI,KAAK,IAAI,MAAM,CAGlB;IA+FD;;;;;;;;;;;;;;OAcG;IACH,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAQrD;CAuBF"}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { performance } from "node:perf_hooks";
|
|
2
|
+
//#region ../zero-cache/src/services/view-syncer/batch-pacer.ts
|
|
3
|
+
/** How often the multiplier is updated. */
|
|
4
|
+
var UPDATE_INTERVAL_MS = 1e3;
|
|
5
|
+
/**
|
|
6
|
+
* The weight of the latest second in the worker's smoothed busy share.
|
|
7
|
+
* Client groups that wait for seconds between batches make the share of
|
|
8
|
+
* any one second jump between idle and saturated, and a worker that keeps
|
|
9
|
+
* up has the odd busy second. Neither says what the load is.
|
|
10
|
+
*/
|
|
11
|
+
var SHARE_SMOOTHING = .3;
|
|
12
|
+
/**
|
|
13
|
+
* How much of the difference between the smoothed busy share and the target
|
|
14
|
+
* each update corrects for. The smoothed share only shows part of what the
|
|
15
|
+
* previous updates did, so correcting for all of the difference each time
|
|
16
|
+
* would overshoot and oscillate.
|
|
17
|
+
*/
|
|
18
|
+
var CORRECTION_EXPONENT = .3;
|
|
19
|
+
/**
|
|
20
|
+
* The share of its time above which a worker has no time to spare. A share
|
|
21
|
+
* target has to stay well clear of this to be held, which is why
|
|
22
|
+
* `syncUpdateShareTarget` is at most 0.9 (or 1, which no worker is over).
|
|
23
|
+
*/
|
|
24
|
+
var SATURATED_SHARE = .95;
|
|
25
|
+
/** The seconds that a worker has to be saturated for to be treated as such. */
|
|
26
|
+
var SATURATED_SECONDS = 2;
|
|
27
|
+
/**
|
|
28
|
+
* How much `1 + k` is scaled by each second that the worker is saturated.
|
|
29
|
+
* A saturated worker does not show how much more its client groups would
|
|
30
|
+
* use, which can be many times what it has, so the multiplier is raised by
|
|
31
|
+
* more than the difference from the target calls for until it does.
|
|
32
|
+
*/
|
|
33
|
+
var SATURATED_CORRECTION = 1.3;
|
|
34
|
+
/**
|
|
35
|
+
* How often a worker that is pacing its client groups says so in its log,
|
|
36
|
+
* and how long it has to have stopped for to say that it has.
|
|
37
|
+
*/
|
|
38
|
+
var PACING_LOG_INTERVAL_MS = 6e4;
|
|
39
|
+
/**
|
|
40
|
+
* The most updates that are made up for when nothing has asked the pacer
|
|
41
|
+
* for a wait in a while, which is enough for the multiplier to get to 0.
|
|
42
|
+
*/
|
|
43
|
+
var MAX_CATCH_UP_UPDATES = 60;
|
|
44
|
+
/**
|
|
45
|
+
* Bounds the multiplier, which otherwise has no limit while a worker stays
|
|
46
|
+
* over the target however long its client groups wait.
|
|
47
|
+
*/
|
|
48
|
+
var MAX_MULTIPLIER = 1e6;
|
|
49
|
+
function eventLoopUtilization() {
|
|
50
|
+
let last = performance.eventLoopUtilization();
|
|
51
|
+
return () => {
|
|
52
|
+
const current = performance.eventLoopUtilization();
|
|
53
|
+
const { utilization } = performance.eventLoopUtilization(current, last);
|
|
54
|
+
last = current;
|
|
55
|
+
return utilization;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Paces the batches (advancements, including the queries they hydrate
|
|
60
|
+
* again) of the client groups of a sync worker, so that the worker is left
|
|
61
|
+
* with time for new hydrations and connections, and gives the hydrations of
|
|
62
|
+
* new queries priority over the batches.
|
|
63
|
+
*
|
|
64
|
+
* **Pacing.** After each batch, a client group waits {@link waitMs} before
|
|
65
|
+
* it starts the next. The wait is a multiple of the batch's processing
|
|
66
|
+
* time, so a client group that always has changes waiting is busy at most
|
|
67
|
+
* `1 / (1 + k)` of the time, where `k` is the {@link multiplier}. Because
|
|
68
|
+
* the wait is in proportion to a client group's own processing time, those
|
|
69
|
+
* that cause the load are slowed the most, and cheap ones barely at all.
|
|
70
|
+
*
|
|
71
|
+
* The multiplier is shared by the client groups of the worker. Once per
|
|
72
|
+
* second, it is scaled by how far the share of its time that the worker was
|
|
73
|
+
* busy, smoothed over a few seconds, is from the target, and by more while
|
|
74
|
+
* the worker has no time to spare. It never goes below 0, where no client
|
|
75
|
+
* group waits. The share is the utilization of the worker's event loop, i.e. all
|
|
76
|
+
* of what the worker does: what a batch costs is spread over the pipelines,
|
|
77
|
+
* the CVR, the pokes and garbage collection, and is not measurable batch by
|
|
78
|
+
* batch.
|
|
79
|
+
*
|
|
80
|
+
* **Priority of new hydrations.** The worker's time slices are handed out
|
|
81
|
+
* by the pacer (see {@link yieldSlice}), and while the hydration of new
|
|
82
|
+
* queries is waiting for one, most of them go to it rather than to batches.
|
|
83
|
+
*/
|
|
84
|
+
var BatchPacer = class {
|
|
85
|
+
#shareTarget;
|
|
86
|
+
#maxIntervalMs;
|
|
87
|
+
#now;
|
|
88
|
+
#utilization;
|
|
89
|
+
#lc;
|
|
90
|
+
/** When the worker last logged that it is pacing, while it still is. */
|
|
91
|
+
#pacingLoggedAt;
|
|
92
|
+
/** Since when the worker has not been pacing, after it logged that it was. */
|
|
93
|
+
#notPacingSince;
|
|
94
|
+
#intervalStart;
|
|
95
|
+
#multiplier = 0;
|
|
96
|
+
/** The smoothed share of its time that the worker was busy. */
|
|
97
|
+
#share = 0;
|
|
98
|
+
#saturatedSeconds = 0;
|
|
99
|
+
/** The waits handed out since the last update, by whether at the cap. */
|
|
100
|
+
#waitsBelowMax = 0;
|
|
101
|
+
#waitsAtMax = 0;
|
|
102
|
+
/** The work that is waiting for a time slice, in the order it yielded. */
|
|
103
|
+
#waiting = {
|
|
104
|
+
hydration: [],
|
|
105
|
+
batch: []
|
|
106
|
+
};
|
|
107
|
+
#handingOutSlices = false;
|
|
108
|
+
/** The slices that have gone to hydrations since one went to a batch. */
|
|
109
|
+
#hydrationSlices = 0;
|
|
110
|
+
constructor({ shareTarget, maxIntervalMs, now, utilization, lc }) {
|
|
111
|
+
this.#shareTarget = shareTarget;
|
|
112
|
+
this.#maxIntervalMs = maxIntervalMs;
|
|
113
|
+
this.#now = now ?? (() => performance.now());
|
|
114
|
+
this.#utilization = utilization ?? eventLoopUtilization();
|
|
115
|
+
this.#lc = lc;
|
|
116
|
+
this.#intervalStart = this.#now();
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* How long a client group should wait before its next batch, after one
|
|
120
|
+
* that took `processingMs` to process. The multiplier changes while a
|
|
121
|
+
* client group waits, so a long wait should be taken in steps, asking
|
|
122
|
+
* again after each.
|
|
123
|
+
*/
|
|
124
|
+
waitMs(processingMs) {
|
|
125
|
+
this.#update();
|
|
126
|
+
const wait = this.#multiplier * processingMs;
|
|
127
|
+
if (wait < this.#maxIntervalMs) {
|
|
128
|
+
this.#waitsBelowMax++;
|
|
129
|
+
return wait;
|
|
130
|
+
}
|
|
131
|
+
this.#waitsAtMax++;
|
|
132
|
+
return this.#maxIntervalMs;
|
|
133
|
+
}
|
|
134
|
+
/** `k`: a client group waits `k` times the processing time of a batch. */
|
|
135
|
+
get multiplier() {
|
|
136
|
+
this.#update();
|
|
137
|
+
return this.#multiplier;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* The share of its time that the worker was busy, smoothed over the last
|
|
141
|
+
* few seconds, as of the last update of the multiplier.
|
|
142
|
+
*/
|
|
143
|
+
get share() {
|
|
144
|
+
this.#update();
|
|
145
|
+
return this.#share;
|
|
146
|
+
}
|
|
147
|
+
/** Updates the multiplier for each interval that has ended. */
|
|
148
|
+
#update() {
|
|
149
|
+
const now = this.#now();
|
|
150
|
+
const ended = Math.floor((now - this.#intervalStart) / UPDATE_INTERVAL_MS);
|
|
151
|
+
if (ended <= 0) return;
|
|
152
|
+
this.#intervalStart += ended * UPDATE_INTERVAL_MS;
|
|
153
|
+
const latest = this.#utilization();
|
|
154
|
+
const allAtMax = this.#waitsAtMax > 0 && this.#waitsBelowMax === 0;
|
|
155
|
+
this.#waitsAtMax = 0;
|
|
156
|
+
this.#waitsBelowMax = 0;
|
|
157
|
+
for (let i = Math.min(ended, MAX_CATCH_UP_UPDATES); i > 0; i--) {
|
|
158
|
+
this.#share += SHARE_SMOOTHING * (latest - this.#share);
|
|
159
|
+
this.#saturatedSeconds = latest >= SATURATED_SHARE && latest > this.#shareTarget ? this.#saturatedSeconds + 1 : 0;
|
|
160
|
+
let correction = this.#saturatedSeconds >= SATURATED_SECONDS ? SATURATED_CORRECTION : (this.#share / this.#shareTarget) ** CORRECTION_EXPONENT;
|
|
161
|
+
if (allAtMax && correction > 1) correction = 1;
|
|
162
|
+
this.#multiplier = Math.min(Math.max((1 + this.#multiplier) * correction - 1, 0), MAX_MULTIPLIER);
|
|
163
|
+
}
|
|
164
|
+
this.#logPacing(now, allAtMax);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Says in the worker's log when it starts to pace its client groups, once
|
|
168
|
+
* a minute while it does, and when it has stopped. The pacing of a client
|
|
169
|
+
* group is logged by its view syncer.
|
|
170
|
+
*/
|
|
171
|
+
#logPacing(now, allAtMax) {
|
|
172
|
+
const lc = this.#lc;
|
|
173
|
+
if (lc === void 0) return;
|
|
174
|
+
const state = {
|
|
175
|
+
busyShare: this.#share,
|
|
176
|
+
shareTarget: this.#shareTarget,
|
|
177
|
+
waitMultiplier: this.#multiplier,
|
|
178
|
+
maxBatchIntervalMs: this.#maxIntervalMs
|
|
179
|
+
};
|
|
180
|
+
if (this.#multiplier > 0) {
|
|
181
|
+
this.#notPacingSince = void 0;
|
|
182
|
+
if (this.#pacingLoggedAt === void 0) {
|
|
183
|
+
lc.info?.("Sync worker is over its share target: its client groups wait between batches", state);
|
|
184
|
+
this.#pacingLoggedAt = now;
|
|
185
|
+
} else if (now - this.#pacingLoggedAt >= PACING_LOG_INTERVAL_MS) {
|
|
186
|
+
if (allAtMax && this.#share > this.#shareTarget) lc.warn?.("Sync worker is over its share target although every client group waits the longest it may", state);
|
|
187
|
+
else lc.info?.("Sync worker is pacing its client groups", state);
|
|
188
|
+
this.#pacingLoggedAt = now;
|
|
189
|
+
}
|
|
190
|
+
} else if (this.#pacingLoggedAt !== void 0) {
|
|
191
|
+
this.#notPacingSince ??= now;
|
|
192
|
+
if (now - this.#notPacingSince >= PACING_LOG_INTERVAL_MS) {
|
|
193
|
+
lc.info?.("Sync worker is under its share target: its client groups no longer wait", state);
|
|
194
|
+
this.#pacingLoggedAt = void 0;
|
|
195
|
+
this.#notPacingSince = void 0;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Yields the worker to the other work that is waiting for a time slice,
|
|
201
|
+
* and resolves when it is the caller's turn again. One piece of work runs
|
|
202
|
+
* per turn of the event loop, as when the work of a worker's client groups
|
|
203
|
+
* simply takes turns, except that the hydration of new queries goes first:
|
|
204
|
+
* while both kinds of work are waiting, `HYDRATION_PRIORITY` slices go to
|
|
205
|
+
* hydrations for each one that goes to a batch.
|
|
206
|
+
*
|
|
207
|
+
* Batches are never made to wait for anything but a slice. If no
|
|
208
|
+
* hydration is waiting for one (because none is running, or because those
|
|
209
|
+
* that are running are waiting for I/O), batches get every slice. And
|
|
210
|
+
* however many hydrations there are, batches keep a share of the slices,
|
|
211
|
+
* so they keep moving, and so do the snapshots of the replica that they
|
|
212
|
+
* hold.
|
|
213
|
+
*/
|
|
214
|
+
yieldSlice(kind) {
|
|
215
|
+
return new Promise((resolve) => {
|
|
216
|
+
this.#waiting[kind].push(resolve);
|
|
217
|
+
if (!this.#handingOutSlices) {
|
|
218
|
+
this.#handingOutSlices = true;
|
|
219
|
+
setImmediate(this.#nextSlice);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
#nextSlice = () => {
|
|
224
|
+
const { hydration, batch } = this.#waiting;
|
|
225
|
+
let next;
|
|
226
|
+
if (hydration.length > 0 && (batch.length === 0 || this.#hydrationSlices < HYDRATION_PRIORITY)) {
|
|
227
|
+
next = hydration.shift();
|
|
228
|
+
this.#hydrationSlices++;
|
|
229
|
+
} else if (batch.length > 0) {
|
|
230
|
+
next = batch.shift();
|
|
231
|
+
this.#hydrationSlices = 0;
|
|
232
|
+
}
|
|
233
|
+
if (next === void 0) {
|
|
234
|
+
this.#handingOutSlices = false;
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
next();
|
|
238
|
+
setImmediate(this.#nextSlice);
|
|
239
|
+
};
|
|
240
|
+
};
|
|
241
|
+
/**
|
|
242
|
+
* How many time slices go to the hydration of new queries for each one that
|
|
243
|
+
* goes to a batch, while both are waiting for one.
|
|
244
|
+
*/
|
|
245
|
+
var HYDRATION_PRIORITY = 9;
|
|
246
|
+
//#endregion
|
|
247
|
+
export { BatchPacer };
|
|
248
|
+
|
|
249
|
+
//# sourceMappingURL=batch-pacer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch-pacer.js","names":["#shareTarget","#maxIntervalMs","#now","#utilization","#lc","#waiting","#intervalStart","#update","#multiplier","#waitsBelowMax","#waitsAtMax","#share","#saturatedSeconds","#logPacing","#notPacingSince","#pacingLoggedAt","#handingOutSlices","#nextSlice","#hydrationSlices"],"sources":["../../../../../../zero-cache/src/services/view-syncer/batch-pacer.ts"],"sourcesContent":["import {performance} from 'node:perf_hooks';\nimport type {LogContext} from '@rocicorp/logger';\n\n/** How often the multiplier is updated. */\nconst UPDATE_INTERVAL_MS = 1000;\n\n/**\n * The weight of the latest second in the worker's smoothed busy share.\n * Client groups that wait for seconds between batches make the share of\n * any one second jump between idle and saturated, and a worker that keeps\n * up has the odd busy second. Neither says what the load is.\n */\nconst SHARE_SMOOTHING = 0.3;\n\n/**\n * How much of the difference between the smoothed busy share and the target\n * each update corrects for. The smoothed share only shows part of what the\n * previous updates did, so correcting for all of the difference each time\n * would overshoot and oscillate.\n */\nconst CORRECTION_EXPONENT = 0.3;\n\n/**\n * The share of its time above which a worker has no time to spare. A share\n * target has to stay well clear of this to be held, which is why\n * `syncUpdateShareTarget` is at most 0.9 (or 1, which no worker is over).\n */\nconst SATURATED_SHARE = 0.95;\n\n/** The seconds that a worker has to be saturated for to be treated as such. */\nconst SATURATED_SECONDS = 2;\n\n/**\n * How much `1 + k` is scaled by each second that the worker is saturated.\n * A saturated worker does not show how much more its client groups would\n * use, which can be many times what it has, so the multiplier is raised by\n * more than the difference from the target calls for until it does.\n */\nconst SATURATED_CORRECTION = 1.3;\n\n/**\n * How often a worker that is pacing its client groups says so in its log,\n * and how long it has to have stopped for to say that it has.\n */\nconst PACING_LOG_INTERVAL_MS = 60_000;\n\n/**\n * The most updates that are made up for when nothing has asked the pacer\n * for a wait in a while, which is enough for the multiplier to get to 0.\n */\nconst MAX_CATCH_UP_UPDATES = 60;\n\n/**\n * Bounds the multiplier, which otherwise has no limit while a worker stays\n * over the target however long its client groups wait.\n */\nconst MAX_MULTIPLIER = 1_000_000;\n\nexport type BatchPacerOptions = {\n /**\n * The largest share of the worker's time that it may be busy before its\n * client groups are paced.\n */\n readonly shareTarget: number;\n /** The longest a client group waits between batches. */\n readonly maxIntervalMs: number;\n readonly now?: (() => number) | undefined;\n /**\n * The share of the time since the previous call that the worker was busy.\n * By default, the utilization of the event loop.\n */\n readonly utilization?: (() => number) | undefined;\n /** Where the worker says that it is pacing its client groups. */\n readonly lc?: LogContext | undefined;\n};\n\nfunction eventLoopUtilization(): () => number {\n let last = performance.eventLoopUtilization();\n return () => {\n const current = performance.eventLoopUtilization();\n const {utilization} = performance.eventLoopUtilization(current, last);\n last = current;\n return utilization;\n };\n}\n\n/**\n * Paces the batches (advancements, including the queries they hydrate\n * again) of the client groups of a sync worker, so that the worker is left\n * with time for new hydrations and connections, and gives the hydrations of\n * new queries priority over the batches.\n *\n * **Pacing.** After each batch, a client group waits {@link waitMs} before\n * it starts the next. The wait is a multiple of the batch's processing\n * time, so a client group that always has changes waiting is busy at most\n * `1 / (1 + k)` of the time, where `k` is the {@link multiplier}. Because\n * the wait is in proportion to a client group's own processing time, those\n * that cause the load are slowed the most, and cheap ones barely at all.\n *\n * The multiplier is shared by the client groups of the worker. Once per\n * second, it is scaled by how far the share of its time that the worker was\n * busy, smoothed over a few seconds, is from the target, and by more while\n * the worker has no time to spare. It never goes below 0, where no client\n * group waits. The share is the utilization of the worker's event loop, i.e. all\n * of what the worker does: what a batch costs is spread over the pipelines,\n * the CVR, the pokes and garbage collection, and is not measurable batch by\n * batch.\n *\n * **Priority of new hydrations.** The worker's time slices are handed out\n * by the pacer (see {@link yieldSlice}), and while the hydration of new\n * queries is waiting for one, most of them go to it rather than to batches.\n */\nexport class BatchPacer {\n readonly #shareTarget: number;\n readonly #maxIntervalMs: number;\n readonly #now: () => number;\n readonly #utilization: () => number;\n readonly #lc: LogContext | undefined;\n\n /** When the worker last logged that it is pacing, while it still is. */\n #pacingLoggedAt: number | undefined;\n /** Since when the worker has not been pacing, after it logged that it was. */\n #notPacingSince: number | undefined;\n\n #intervalStart: number;\n #multiplier = 0;\n /** The smoothed share of its time that the worker was busy. */\n #share = 0;\n #saturatedSeconds = 0;\n /** The waits handed out since the last update, by whether at the cap. */\n #waitsBelowMax = 0;\n #waitsAtMax = 0;\n\n /** The work that is waiting for a time slice, in the order it yielded. */\n readonly #waiting = {\n hydration: [] as (() => void)[],\n batch: [] as (() => void)[],\n };\n #handingOutSlices = false;\n /** The slices that have gone to hydrations since one went to a batch. */\n #hydrationSlices = 0;\n\n constructor({\n shareTarget,\n maxIntervalMs,\n now,\n utilization,\n lc,\n }: BatchPacerOptions) {\n this.#shareTarget = shareTarget;\n this.#maxIntervalMs = maxIntervalMs;\n this.#now = now ?? (() => performance.now());\n this.#utilization = utilization ?? eventLoopUtilization();\n this.#lc = lc;\n this.#intervalStart = this.#now();\n }\n\n /**\n * How long a client group should wait before its next batch, after one\n * that took `processingMs` to process. The multiplier changes while a\n * client group waits, so a long wait should be taken in steps, asking\n * again after each.\n */\n waitMs(processingMs: number): number {\n this.#update();\n const wait = this.#multiplier * processingMs;\n if (wait < this.#maxIntervalMs) {\n this.#waitsBelowMax++;\n return wait;\n }\n this.#waitsAtMax++;\n return this.#maxIntervalMs;\n }\n\n /** `k`: a client group waits `k` times the processing time of a batch. */\n get multiplier(): number {\n this.#update();\n return this.#multiplier;\n }\n\n /**\n * The share of its time that the worker was busy, smoothed over the last\n * few seconds, as of the last update of the multiplier.\n */\n get share(): number {\n this.#update();\n return this.#share;\n }\n\n /** Updates the multiplier for each interval that has ended. */\n #update(): void {\n const now = this.#now();\n const ended = Math.floor((now - this.#intervalStart) / UPDATE_INTERVAL_MS);\n if (ended <= 0) {\n return;\n }\n this.#intervalStart += ended * UPDATE_INTERVAL_MS;\n // If more than one interval has ended, nothing asked the pacer for a\n // wait in the meantime, and the utilization since then is that of each.\n const latest = this.#utilization();\n // Once every client group is told to wait the longest it can, raising\n // the multiplier changes nothing, and only makes it take longer to come\n // back down. A worker that is still over its target then is busy with\n // something that making its client groups wait does not relieve.\n const allAtMax = this.#waitsAtMax > 0 && this.#waitsBelowMax === 0;\n this.#waitsAtMax = 0;\n this.#waitsBelowMax = 0;\n for (let i = Math.min(ended, MAX_CATCH_UP_UPDATES); i > 0; i--) {\n this.#share += SHARE_SMOOTHING * (latest - this.#share);\n // A worker that is allowed to be this busy is only raised the\n // multiplier for what it is over its target by.\n this.#saturatedSeconds =\n latest >= SATURATED_SHARE && latest > this.#shareTarget\n ? this.#saturatedSeconds + 1\n : 0;\n let correction =\n this.#saturatedSeconds >= SATURATED_SECONDS\n ? SATURATED_CORRECTION\n : (this.#share / this.#shareTarget) ** CORRECTION_EXPONENT;\n if (allAtMax && correction > 1) {\n correction = 1;\n }\n this.#multiplier = Math.min(\n Math.max((1 + this.#multiplier) * correction - 1, 0),\n MAX_MULTIPLIER,\n );\n }\n this.#logPacing(now, allAtMax);\n }\n\n /**\n * Says in the worker's log when it starts to pace its client groups, once\n * a minute while it does, and when it has stopped. The pacing of a client\n * group is logged by its view syncer.\n */\n #logPacing(now: number, allAtMax: boolean): void {\n const lc = this.#lc;\n if (lc === undefined) {\n return;\n }\n const state = {\n busyShare: this.#share,\n shareTarget: this.#shareTarget,\n waitMultiplier: this.#multiplier,\n maxBatchIntervalMs: this.#maxIntervalMs,\n };\n if (this.#multiplier > 0) {\n this.#notPacingSince = undefined;\n if (this.#pacingLoggedAt === undefined) {\n lc.info?.(\n 'Sync worker is over its share target: its client groups wait between batches',\n state,\n );\n this.#pacingLoggedAt = now;\n } else if (now - this.#pacingLoggedAt >= PACING_LOG_INTERVAL_MS) {\n if (allAtMax && this.#share > this.#shareTarget) {\n // Waiting longer is not an option, so pacing cannot bring the\n // worker to its target: it has more to do than it has time for.\n lc.warn?.(\n 'Sync worker is over its share target although every client group waits the longest it may',\n state,\n );\n } else {\n lc.info?.('Sync worker is pacing its client groups', state);\n }\n this.#pacingLoggedAt = now;\n }\n } else if (this.#pacingLoggedAt !== undefined) {\n // A worker around its target goes in and out of pacing. It only says\n // that it has stopped once it has stayed out.\n this.#notPacingSince ??= now;\n if (now - this.#notPacingSince >= PACING_LOG_INTERVAL_MS) {\n lc.info?.(\n 'Sync worker is under its share target: its client groups no longer wait',\n state,\n );\n this.#pacingLoggedAt = undefined;\n this.#notPacingSince = undefined;\n }\n }\n }\n\n /**\n * Yields the worker to the other work that is waiting for a time slice,\n * and resolves when it is the caller's turn again. One piece of work runs\n * per turn of the event loop, as when the work of a worker's client groups\n * simply takes turns, except that the hydration of new queries goes first:\n * while both kinds of work are waiting, `HYDRATION_PRIORITY` slices go to\n * hydrations for each one that goes to a batch.\n *\n * Batches are never made to wait for anything but a slice. If no\n * hydration is waiting for one (because none is running, or because those\n * that are running are waiting for I/O), batches get every slice. And\n * however many hydrations there are, batches keep a share of the slices,\n * so they keep moving, and so do the snapshots of the replica that they\n * hold.\n */\n yieldSlice(kind: 'hydration' | 'batch'): Promise<void> {\n return new Promise(resolve => {\n this.#waiting[kind].push(resolve);\n if (!this.#handingOutSlices) {\n this.#handingOutSlices = true;\n setImmediate(this.#nextSlice);\n }\n });\n }\n\n readonly #nextSlice = () => {\n const {hydration, batch} = this.#waiting;\n let next: (() => void) | undefined;\n if (\n hydration.length > 0 &&\n (batch.length === 0 || this.#hydrationSlices < HYDRATION_PRIORITY)\n ) {\n next = hydration.shift();\n this.#hydrationSlices++;\n } else if (batch.length > 0) {\n next = batch.shift();\n this.#hydrationSlices = 0;\n }\n if (next === undefined) {\n this.#handingOutSlices = false;\n return;\n }\n next();\n // After the work that was just resumed has run its slice.\n setImmediate(this.#nextSlice);\n };\n}\n\n/**\n * How many time slices go to the hydration of new queries for each one that\n * goes to a batch, while both are waiting for one.\n */\nconst HYDRATION_PRIORITY = 9;\n"],"mappings":";;;AAIA,IAAM,qBAAqB;;;;;;;AAQ3B,IAAM,kBAAkB;;;;;;;AAQxB,IAAM,sBAAsB;;;;;;AAO5B,IAAM,kBAAkB;;AAGxB,IAAM,oBAAoB;;;;;;;AAQ1B,IAAM,uBAAuB;;;;;AAM7B,IAAM,yBAAyB;;;;;AAM/B,IAAM,uBAAuB;;;;;AAM7B,IAAM,iBAAiB;AAoBvB,SAAS,uBAAqC;CAC5C,IAAI,OAAO,YAAY,qBAAqB;CAC5C,aAAa;EACX,MAAM,UAAU,YAAY,qBAAqB;EACjD,MAAM,EAAC,gBAAe,YAAY,qBAAqB,SAAS,IAAI;EACpE,OAAO;EACP,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,IAAa,aAAb,MAAwB;CACtB;CACA;CACA;CACA;CACA;;CAGA;;CAEA;CAEA;CACA,cAAc;;CAEd,SAAS;CACT,oBAAoB;;CAEpB,iBAAiB;CACjB,cAAc;;CAGd,WAAoB;EAClB,WAAW,CAAC;EACZ,OAAO,CAAC;CACV;CACA,oBAAoB;;CAEpB,mBAAmB;CAEnB,YAAY,EACV,aACA,eACA,KACA,aACA,MACoB;EACpB,KAAKA,eAAe;EACpB,KAAKC,iBAAiB;EACtB,KAAKC,OAAO,cAAc,YAAY,IAAI;EAC1C,KAAKC,eAAe,eAAe,qBAAqB;EACxD,KAAKC,MAAM;EACX,KAAKE,iBAAiB,KAAKJ,KAAK;CAClC;;;;;;;CAQA,OAAO,cAA8B;EACnC,KAAKK,QAAQ;EACb,MAAM,OAAO,KAAKC,cAAc;EAChC,IAAI,OAAO,KAAKP,gBAAgB;GAC9B,KAAKQ;GACL,OAAO;EACT;EACA,KAAKC;EACL,OAAO,KAAKT;CACd;;CAGA,IAAI,aAAqB;EACvB,KAAKM,QAAQ;EACb,OAAO,KAAKC;CACd;;;;;CAMA,IAAI,QAAgB;EAClB,KAAKD,QAAQ;EACb,OAAO,KAAKI;CACd;;CAGA,UAAgB;EACd,MAAM,MAAM,KAAKT,KAAK;EACtB,MAAM,QAAQ,KAAK,OAAO,MAAM,KAAKI,kBAAkB,kBAAkB;EACzE,IAAI,SAAS,GACX;EAEF,KAAKA,kBAAkB,QAAQ;EAG/B,MAAM,SAAS,KAAKH,aAAa;EAKjC,MAAM,WAAW,KAAKO,cAAc,KAAK,KAAKD,mBAAmB;EACjE,KAAKC,cAAc;EACnB,KAAKD,iBAAiB;EACtB,KAAK,IAAI,IAAI,KAAK,IAAI,OAAO,oBAAoB,GAAG,IAAI,GAAG,KAAK;GAC9D,KAAKE,UAAU,mBAAmB,SAAS,KAAKA;GAGhD,KAAKC,oBACH,UAAU,mBAAmB,SAAS,KAAKZ,eACvC,KAAKY,oBAAoB,IACzB;GACN,IAAI,aACF,KAAKA,qBAAqB,oBACtB,wBACC,KAAKD,SAAS,KAAKX,iBAAiB;GAC3C,IAAI,YAAY,aAAa,GAC3B,aAAa;GAEf,KAAKQ,cAAc,KAAK,IACtB,KAAK,KAAK,IAAI,KAAKA,eAAe,aAAa,GAAG,CAAC,GACnD,cACF;EACF;EACA,KAAKK,WAAW,KAAK,QAAQ;CAC/B;;;;;;CAOA,WAAW,KAAa,UAAyB;EAC/C,MAAM,KAAK,KAAKT;EAChB,IAAI,OAAO,KAAA,GACT;EAEF,MAAM,QAAQ;GACZ,WAAW,KAAKO;GAChB,aAAa,KAAKX;GAClB,gBAAgB,KAAKQ;GACrB,oBAAoB,KAAKP;EAC3B;EACA,IAAI,KAAKO,cAAc,GAAG;GACxB,KAAKM,kBAAkB,KAAA;GACvB,IAAI,KAAKC,oBAAoB,KAAA,GAAW;IACtC,GAAG,OACD,gFACA,KACF;IACA,KAAKA,kBAAkB;GACzB,OAAO,IAAI,MAAM,KAAKA,mBAAmB,wBAAwB;IAC/D,IAAI,YAAY,KAAKJ,SAAS,KAAKX,cAGjC,GAAG,OACD,6FACA,KACF;SAEA,GAAG,OAAO,2CAA2C,KAAK;IAE5D,KAAKe,kBAAkB;GACzB;EACF,OAAO,IAAI,KAAKA,oBAAoB,KAAA,GAAW;GAG7C,KAAKD,oBAAoB;GACzB,IAAI,MAAM,KAAKA,mBAAmB,wBAAwB;IACxD,GAAG,OACD,2EACA,KACF;IACA,KAAKC,kBAAkB,KAAA;IACvB,KAAKD,kBAAkB,KAAA;GACzB;EACF;CACF;;;;;;;;;;;;;;;;CAiBA,WAAW,MAA4C;EACrD,OAAO,IAAI,SAAQ,YAAW;GAC5B,KAAKT,SAAS,MAAM,KAAK,OAAO;GAChC,IAAI,CAAC,KAAKW,mBAAmB;IAC3B,KAAKA,oBAAoB;IACzB,aAAa,KAAKC,UAAU;GAC9B;EACF,CAAC;CACH;CAEA,mBAA4B;EAC1B,MAAM,EAAC,WAAW,UAAS,KAAKZ;EAChC,IAAI;EACJ,IACE,UAAU,SAAS,MAClB,MAAM,WAAW,KAAK,KAAKa,mBAAmB,qBAC/C;GACA,OAAO,UAAU,MAAM;GACvB,KAAKA;EACP,OAAO,IAAI,MAAM,SAAS,GAAG;GAC3B,OAAO,MAAM,MAAM;GACnB,KAAKA,mBAAmB;EAC1B;EACA,IAAI,SAAS,KAAA,GAAW;GACtB,KAAKF,oBAAoB;GACzB;EACF;EACA,KAAK;EAEL,aAAa,KAAKC,UAAU;CAC9B;AACF;;;;;AAMA,IAAM,qBAAqB"}
|
|
@@ -204,6 +204,32 @@ export declare class CVRQueryDrivenUpdater extends CVRUpdater {
|
|
|
204
204
|
* rows of the aborted queries have been {@link received}.
|
|
205
205
|
*/
|
|
206
206
|
abortExecutedQueries(lc: LogContext, queryIDs: readonly string[]): Promise<PatchToVersion[]>;
|
|
207
|
+
/**
|
|
208
|
+
* Retracts what queries have contributed to this update, so that they can
|
|
209
|
+
* be executed again from scratch in the same update. This is for a query
|
|
210
|
+
* whose changes were being received incrementally (see
|
|
211
|
+
* `PipelineDriver.advance()`) until its pipeline was abandoned in favor of
|
|
212
|
+
* hydrating it again.
|
|
213
|
+
*
|
|
214
|
+
* Rows received so far in this update have the queries' references
|
|
215
|
+
* stripped, as in {@link abortExecutedQueries}, except that a row left
|
|
216
|
+
* with no references is only deleted by {@link deleteUnreferencedRows}, if
|
|
217
|
+
* it is still unreferenced by then. Until then the clients keep the row
|
|
218
|
+
* and the patches they received for it in this update, so a row that is
|
|
219
|
+
* received again unchanged needs no patch at all. The queries are then
|
|
220
|
+
* tracked as executed, without changing them in the CVR: the references
|
|
221
|
+
* they held before this update are replaced, rather than adjusted, by the
|
|
222
|
+
* rows subsequently {@link received} for them, and
|
|
223
|
+
* {@link deleteUnreferencedRows} drops the references of rows that are not
|
|
224
|
+
* received again.
|
|
225
|
+
*
|
|
226
|
+
* Unlike {@link trackQueries}, this can be called on an updater that has
|
|
227
|
+
* not tracked any queries, and after rows have been received. All rows
|
|
228
|
+
* that the queries' pipelines have output must have been {@link received}
|
|
229
|
+
* first, and {@link deleteUnreferencedRows} must be called once the
|
|
230
|
+
* queries' rows have been received again.
|
|
231
|
+
*/
|
|
232
|
+
retractQueries(lc: LogContext, queryIDs: readonly string[]): Promise<PatchToVersion[]>;
|
|
207
233
|
updatedVersion(): CVRVersion;
|
|
208
234
|
/**
|
|
209
235
|
* Public alias for {@link _ensureNewVersion}. Forces a `configVersion` bump
|
|
@@ -221,7 +247,7 @@ export declare class CVRQueryDrivenUpdater extends CVRUpdater {
|
|
|
221
247
|
* returns (put) patches to be returned to update their state, versioned by
|
|
222
248
|
* patchVersion so that only the patches new to the clients are sent.
|
|
223
249
|
*/
|
|
224
|
-
received(
|
|
250
|
+
received(lc: LogContext, rows: Map<RowID, RowUpdate>): Promise<PatchToVersion[]>;
|
|
225
251
|
/**
|
|
226
252
|
* Computes and updates the row records based on:
|
|
227
253
|
* * The {@link #executed} queries
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cvr.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/cvr.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"cvr.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/cvr.ts"],"names":[],"mappings":"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"}
|
|
@@ -365,6 +365,13 @@ var CVRQueryDrivenUpdater = class extends CVRUpdater {
|
|
|
365
365
|
#executedQueryIDs = /* @__PURE__ */ new Set();
|
|
366
366
|
#receivedRows = new CustomKeyMap(rowIDString);
|
|
367
367
|
#lastPatches = new CustomKeyMap(rowIDString);
|
|
368
|
+
/**
|
|
369
|
+
* Rows that {@link retractQueries} left without references, and the patch
|
|
370
|
+
* version of their deletion. The 'del' patches are held back until
|
|
371
|
+
* {@link deleteUnreferencedRows}, because the retracted queries are
|
|
372
|
+
* executed again and usually reference most of these rows again.
|
|
373
|
+
*/
|
|
374
|
+
#retractedRows = new CustomKeyMap(rowIDString);
|
|
368
375
|
#rowSetSignature;
|
|
369
376
|
#existingRows = void 0;
|
|
370
377
|
/**
|
|
@@ -398,7 +405,7 @@ var CVRQueryDrivenUpdater = class extends CVRUpdater {
|
|
|
398
405
|
assert(this.#existingRows === void 0, `trackQueries already called`);
|
|
399
406
|
const queryPatches = [executed.map((q) => this.#trackExecuted(q.id, q.transformationHash)), removed.map((q) => this.#trackRemoved(q.id))].flat(2);
|
|
400
407
|
this.#existingRows = this.#lookupRowsForExecutedAndRemovedQueries(lc);
|
|
401
|
-
this.#existingRows.
|
|
408
|
+
this.#existingRows.catch(() => {});
|
|
402
409
|
const versionBumped = cmpVersions(this._orig.version, this._cvr.version) < 0;
|
|
403
410
|
lc.info?.(`trackQueries: ${executed.length} executed, ${removed.length} removed, version ${versionBumped ? "bumped" : "unchanged"}`);
|
|
404
411
|
return {
|
|
@@ -518,12 +525,37 @@ var CVRQueryDrivenUpdater = class extends CVRUpdater {
|
|
|
518
525
|
assert(this.#executedQueryIDs.has(queryID), `Query ${queryID} was not tracked as executed`);
|
|
519
526
|
assertNotInternal(must(this._cvr.queries[queryID]));
|
|
520
527
|
}
|
|
528
|
+
const patches = await this.#unrefReceivedRows(lc, aborted);
|
|
529
|
+
for (const queryID of aborted) {
|
|
530
|
+
delete this._mutableQueries()[queryID];
|
|
531
|
+
const patch = {
|
|
532
|
+
type: "query",
|
|
533
|
+
op: "del",
|
|
534
|
+
id: queryID
|
|
535
|
+
};
|
|
536
|
+
this._cvrStore.markQueryAsDeleted(this._cvr.version, patch);
|
|
537
|
+
patches.push({
|
|
538
|
+
patch,
|
|
539
|
+
toVersion: this._cvr.version
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
return patches;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Strips the references of `queryIDs` from every row received so far in
|
|
546
|
+
* this update. A row left with no references is deleted, and the 'del'
|
|
547
|
+
* patch cancels the 'put' the client has already received in this poke. The
|
|
548
|
+
* version of each unref is the version the row currently has in this
|
|
549
|
+
* update, so a row that stays referenced does not get a spurious patch
|
|
550
|
+
* version bump.
|
|
551
|
+
*/
|
|
552
|
+
async #unrefReceivedRows(lc, queryIDs, holdDeletes = false) {
|
|
521
553
|
const existingRows = await this._cvrStore.getRowRecords();
|
|
522
554
|
const unrefs = new CustomKeyMap(rowIDString);
|
|
523
555
|
for (const [id, refCounts] of this.#receivedRows) {
|
|
524
556
|
if (refCounts === null) continue;
|
|
525
557
|
let unref;
|
|
526
|
-
for (const queryID of
|
|
558
|
+
for (const queryID of queryIDs) {
|
|
527
559
|
const count = refCounts[queryID];
|
|
528
560
|
if (!count) continue;
|
|
529
561
|
if (!unref) {
|
|
@@ -537,19 +569,50 @@ var CVRQueryDrivenUpdater = class extends CVRUpdater {
|
|
|
537
569
|
unref.refCounts[queryID] = -count;
|
|
538
570
|
}
|
|
539
571
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
572
|
+
return unrefs.size > 0 ? this.#receive(lc, unrefs, holdDeletes) : [];
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Retracts what queries have contributed to this update, so that they can
|
|
576
|
+
* be executed again from scratch in the same update. This is for a query
|
|
577
|
+
* whose changes were being received incrementally (see
|
|
578
|
+
* `PipelineDriver.advance()`) until its pipeline was abandoned in favor of
|
|
579
|
+
* hydrating it again.
|
|
580
|
+
*
|
|
581
|
+
* Rows received so far in this update have the queries' references
|
|
582
|
+
* stripped, as in {@link abortExecutedQueries}, except that a row left
|
|
583
|
+
* with no references is only deleted by {@link deleteUnreferencedRows}, if
|
|
584
|
+
* it is still unreferenced by then. Until then the clients keep the row
|
|
585
|
+
* and the patches they received for it in this update, so a row that is
|
|
586
|
+
* received again unchanged needs no patch at all. The queries are then
|
|
587
|
+
* tracked as executed, without changing them in the CVR: the references
|
|
588
|
+
* they held before this update are replaced, rather than adjusted, by the
|
|
589
|
+
* rows subsequently {@link received} for them, and
|
|
590
|
+
* {@link deleteUnreferencedRows} drops the references of rows that are not
|
|
591
|
+
* received again.
|
|
592
|
+
*
|
|
593
|
+
* Unlike {@link trackQueries}, this can be called on an updater that has
|
|
594
|
+
* not tracked any queries, and after rows have been received. All rows
|
|
595
|
+
* that the queries' pipelines have output must have been {@link received}
|
|
596
|
+
* first, and {@link deleteUnreferencedRows} must be called once the
|
|
597
|
+
* queries' rows have been received again.
|
|
598
|
+
*/
|
|
599
|
+
async retractQueries(lc, queryIDs) {
|
|
600
|
+
assert(cmpVersions(this._orig.version, this._cvr.version) < 0, "A final CVR version must be set before retracting queries");
|
|
601
|
+
const retracted = new Set(queryIDs);
|
|
602
|
+
for (const queryID of retracted) {
|
|
603
|
+
assert(this._cvr.queries[queryID] !== void 0, () => `Query ${queryID} is not in the CVR`);
|
|
604
|
+
assert(this.#executedQueryIDs.has(queryID) || !this.#removedOrExecutedQueryIDs.has(queryID), () => `Query ${queryID} was tracked as removed`);
|
|
605
|
+
}
|
|
606
|
+
const patches = await this.#unrefReceivedRows(lc, retracted, true);
|
|
607
|
+
let tracked = false;
|
|
608
|
+
for (const queryID of retracted) if (!this.#executedQueryIDs.has(queryID)) {
|
|
609
|
+
this.#removedOrExecutedQueryIDs.add(queryID);
|
|
610
|
+
this.#executedQueryIDs.add(queryID);
|
|
611
|
+
tracked = true;
|
|
612
|
+
}
|
|
613
|
+
if (tracked) {
|
|
614
|
+
this.#existingRows = this.#lookupRowsForExecutedAndRemovedQueries(lc);
|
|
615
|
+
this.#existingRows.then(() => {});
|
|
553
616
|
}
|
|
554
617
|
return patches;
|
|
555
618
|
}
|
|
@@ -622,7 +685,14 @@ var CVRQueryDrivenUpdater = class extends CVRUpdater {
|
|
|
622
685
|
* returns (put) patches to be returned to update their state, versioned by
|
|
623
686
|
* patchVersion so that only the patches new to the clients are sent.
|
|
624
687
|
*/
|
|
625
|
-
received(
|
|
688
|
+
received(lc, rows) {
|
|
689
|
+
return this.#receive(lc, rows, false);
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* @param holdDeletes Whether to hold back the 'del' patches of rows that
|
|
693
|
+
* are left without references until {@link deleteUnreferencedRows}.
|
|
694
|
+
*/
|
|
695
|
+
#receive(_lc, rows, holdDeletes) {
|
|
626
696
|
return startAsyncSpan(tracer, "CVRQueryDrivenUpdater.received", async () => {
|
|
627
697
|
const patches = [];
|
|
628
698
|
const existingRows = await this._cvrStore.getRowRecords();
|
|
@@ -632,6 +702,7 @@ var CVRQueryDrivenUpdater = class extends CVRUpdater {
|
|
|
632
702
|
const previouslyReceived = this.#receivedRows.get(id);
|
|
633
703
|
const merged = previouslyReceived !== void 0 ? mergeRefCounts(previouslyReceived, refCounts) : mergeRefCounts(existing?.refCounts, refCounts, this.#removedOrExecutedQueryIDs);
|
|
634
704
|
this.#receivedRows.set(id, merged);
|
|
705
|
+
if (merged !== null) this.#retractedRows.delete(id);
|
|
635
706
|
const newRowVersion = merged === null ? void 0 : version;
|
|
636
707
|
const patchVersion = existing && existing.rowVersion === newRowVersion ? existing.patchVersion : this.#assertNewVersion(id, existing?.rowVersion, newRowVersion, refCounts);
|
|
637
708
|
const rowVersion = version ?? existing?.rowVersion;
|
|
@@ -646,7 +717,8 @@ var CVRQueryDrivenUpdater = class extends CVRUpdater {
|
|
|
646
717
|
const toVersion = maxVersion(patchVersion, lastPatch?.toVersion);
|
|
647
718
|
if (merged === null) {
|
|
648
719
|
if (existing || previouslyReceived) {
|
|
649
|
-
if (
|
|
720
|
+
if (holdDeletes) this.#retractedRows.set(id, toVersion);
|
|
721
|
+
else if (lastPatch?.rowVersion !== null) {
|
|
650
722
|
patches.push({
|
|
651
723
|
patch: {
|
|
652
724
|
type: "row",
|
|
@@ -704,6 +776,24 @@ var CVRQueryDrivenUpdater = class extends CVRUpdater {
|
|
|
704
776
|
const patches = [];
|
|
705
777
|
const start = Date.now();
|
|
706
778
|
assert(this.#existingRows, `trackQueries() was not called`);
|
|
779
|
+
for (const [id, deletedVersion] of this.#retractedRows) {
|
|
780
|
+
const lastPatch = this.#lastPatches.get(id);
|
|
781
|
+
if (lastPatch?.rowVersion === null) continue;
|
|
782
|
+
const toVersion = maxVersion(deletedVersion, lastPatch?.toVersion);
|
|
783
|
+
patches.push({
|
|
784
|
+
toVersion,
|
|
785
|
+
patch: {
|
|
786
|
+
type: "row",
|
|
787
|
+
op: "del",
|
|
788
|
+
id
|
|
789
|
+
}
|
|
790
|
+
});
|
|
791
|
+
this.#lastPatches.set(id, {
|
|
792
|
+
rowVersion: null,
|
|
793
|
+
toVersion
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
this.#retractedRows.clear();
|
|
707
797
|
for (const existing of await this.#existingRows) {
|
|
708
798
|
const deletedID = this.#deleteUnreferencedRow(existing);
|
|
709
799
|
if (deletedID === null) continue;
|