@rocicorp/zero 1.11.0-canary.13 → 1.11.0-canary.14
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/ast-to-zql/src/ast-to-zql.d.ts +8 -1
- package/out/ast-to-zql/src/ast-to-zql.d.ts.map +1 -1
- package/out/ast-to-zql/src/ast-to-zql.js +25 -25
- package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
- package/out/otel/src/log-options.d.ts +16 -0
- package/out/otel/src/log-options.d.ts.map +1 -1
- package/out/otel/src/log-options.js +54 -1
- package/out/otel/src/log-options.js.map +1 -1
- package/out/replicache/src/get-kv-store-provider.d.ts +1 -1
- package/out/replicache/src/get-kv-store-provider.d.ts.map +1 -1
- package/out/replicache/src/get-kv-store-provider.js +4 -4
- package/out/replicache/src/get-kv-store-provider.js.map +1 -1
- package/out/replicache/src/kv/idb-store.d.ts +10 -1
- package/out/replicache/src/kv/idb-store.d.ts.map +1 -1
- package/out/replicache/src/kv/idb-store.js +18 -2
- package/out/replicache/src/kv/idb-store.js.map +1 -1
- package/out/replicache/src/kv/mem-fallback-store.d.ts +54 -0
- package/out/replicache/src/kv/mem-fallback-store.d.ts.map +1 -0
- package/out/replicache/src/kv/mem-fallback-store.js +177 -0
- package/out/replicache/src/kv/mem-fallback-store.js.map +1 -0
- package/out/replicache/src/kv/op-sqlite/store.js +3 -3
- package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
- package/out/replicache/src/kv/sqlite-store.d.ts +9 -0
- package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
- package/out/replicache/src/kv/sqlite-store.js +86 -6
- package/out/replicache/src/kv/sqlite-store.js.map +1 -1
- package/out/replicache/src/new-client-channel.js.map +1 -1
- package/out/replicache/src/persist/collect-idb-databases.d.ts +17 -3
- package/out/replicache/src/persist/collect-idb-databases.d.ts.map +1 -1
- package/out/replicache/src/persist/collect-idb-databases.js +81 -11
- package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
- package/out/replicache/src/persist/idb-databases-store-db-name.js +1 -1
- package/out/replicache/src/persist/idb-databases-store-db-name.js.map +1 -1
- package/out/replicache/src/persist/persist.d.ts.map +1 -1
- package/out/replicache/src/persist/persist.js +6 -3
- package/out/replicache/src/persist/persist.js.map +1 -1
- package/out/replicache/src/replicache-impl.d.ts +43 -3
- package/out/replicache/src/replicache-impl.d.ts.map +1 -1
- package/out/replicache/src/replicache-impl.js +135 -17
- package/out/replicache/src/replicache-impl.js.map +1 -1
- package/out/replicache/src/storage-failure.d.ts +37 -0
- package/out/replicache/src/storage-failure.d.ts.map +1 -0
- package/out/replicache/src/storage-failure.js +31 -0
- package/out/replicache/src/storage-failure.js.map +1 -0
- package/out/shared/src/random-values.js +12 -1
- package/out/shared/src/random-values.js.map +1 -1
- package/out/zero/package.js +1 -1
- package/out/zero/package.js.map +1 -1
- package/out/zero/src/zero.js +2 -1
- package/out/zero-cache/src/config/zero-config.d.ts +26 -0
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +36 -0
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/observability/log-throttle.d.ts +44 -0
- package/out/zero-cache/src/observability/log-throttle.d.ts.map +1 -0
- package/out/zero-cache/src/observability/log-throttle.js +76 -0
- package/out/zero-cache/src/observability/log-throttle.js.map +1 -0
- package/out/zero-cache/src/scripts/permissions.d.ts +16 -0
- package/out/zero-cache/src/scripts/permissions.d.ts.map +1 -1
- package/out/zero-cache/src/server/runner/zero-dispatcher.d.ts.map +1 -1
- package/out/zero-cache/src/server/runner/zero-dispatcher.js +2 -0
- package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
- package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/server/syncer.js +20 -2
- package/out/zero-cache/src/server/syncer.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 +27 -15
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts +1 -0
- package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.js +15 -3
- package/out/zero-cache/src/services/change-source/common/replica-schema.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-source/pg/backfill-stream.d.ts +9 -0
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +204 -18
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +11 -2
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js +11 -2
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/backfill-progress.d.ts +68 -0
- package/out/zero-cache/src/services/change-source/protocol/backfill-progress.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js +141 -0
- package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js.map +1 -0
- package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts +43 -0
- package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/data.js +10 -1
- package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +57 -0
- package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts +20 -6
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.js +17 -7
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current.js +2 -1
- package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.d.ts +66 -0
- package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js +154 -0
- package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/backfill-state.d.ts +50 -0
- package/out/zero-cache/src/services/change-streamer/backfill-state.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/backfill-state.js +268 -0
- package/out/zero-cache/src/services/change-streamer/backfill-state.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +7 -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.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +20 -4
- 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 +26 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscribe.d.ts +88 -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 +9 -1
- package/out/zero-cache/src/services/change-streamer/subscribe.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +29 -0
- package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.js +34 -0
- package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
- package/out/zero-cache/src/services/plannerz.d.ts +159 -0
- package/out/zero-cache/src/services/plannerz.d.ts.map +1 -0
- package/out/zero-cache/src/services/plannerz.js +263 -0
- package/out/zero-cache/src/services/plannerz.js.map +1 -0
- package/out/zero-cache/src/services/replicator/change-log-cookies.js +1 -1
- package/out/zero-cache/src/services/replicator/change-log-cookies.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.js +44 -12
- package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.js +4 -3
- package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts +23 -2
- package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/backfilling.js +54 -2
- package/out/zero-cache/src/services/replicator/schema/backfilling.js.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker-client.d.ts +11 -3
- package/out/zero-cache/src/services/replicator/write-worker-client.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker.js +5 -1
- package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.d.ts +81 -0
- package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js +128 -0
- package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +36 -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 +362 -20
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/plan-warnings.d.ts +7 -0
- package/out/zero-cache/src/services/view-syncer/plan-warnings.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/plan-warnings.js +35 -0
- package/out/zero-cache/src/services/view-syncer/plan-warnings.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/query-shape.d.ts +25 -0
- package/out/zero-cache/src/services/view-syncer/query-shape.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/query-shape.js +28 -0
- package/out/zero-cache/src/services/view-syncer/query-shape.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/query-stats.d.ts +77 -0
- package/out/zero-cache/src/services/view-syncer/query-stats.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/query-stats.js +206 -0
- package/out/zero-cache/src/services/view-syncer/query-stats.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +11 -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 +7 -1
- 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 +49 -3
- package/out/zero-cache/src/services/view-syncer/view-syncer.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/options.d.ts +29 -0
- package/out/zero-client/src/client/options.d.ts.map +1 -1
- package/out/zero-client/src/client/options.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 +8 -23
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-client/src/mod.d.ts +1 -0
- package/out/zero-client/src/mod.d.ts.map +1 -1
- package/out/zero-client/src/mod.js +1 -0
- package/out/zero-protocol/src/down.d.ts +2 -2
- package/out/zero-react/src/zero.js +1 -0
- package/out/zero-solid/src/zero.js +1 -0
- package/out/zql/src/builder/builder.d.ts +6 -0
- package/out/zql/src/builder/builder.d.ts.map +1 -1
- package/out/zql/src/builder/builder.js +4 -3
- package/out/zql/src/builder/builder.js.map +1 -1
- package/out/zql/src/ivm/memory-source.d.ts +13 -0
- package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
- package/out/zql/src/ivm/memory-source.js +1 -1
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/planner/planner-builder.d.ts +12 -1
- package/out/zql/src/planner/planner-builder.d.ts.map +1 -1
- package/out/zql/src/planner/planner-builder.js +8 -1
- package/out/zql/src/planner/planner-builder.js.map +1 -1
- package/out/zql/src/planner/planner-connection.d.ts +43 -0
- package/out/zql/src/planner/planner-connection.d.ts.map +1 -1
- package/out/zql/src/planner/planner-connection.js +31 -4
- package/out/zql/src/planner/planner-connection.js.map +1 -1
- package/out/zql/src/planner/planner-graph.d.ts +10 -0
- package/out/zql/src/planner/planner-graph.d.ts.map +1 -1
- package/out/zql/src/planner/planner-graph.js +14 -0
- package/out/zql/src/planner/planner-graph.js.map +1 -1
- package/out/zql/src/planner/planner-warnings.d.ts +83 -0
- package/out/zql/src/planner/planner-warnings.d.ts.map +1 -0
- package/out/zql/src/planner/planner-warnings.js +78 -0
- package/out/zql/src/planner/planner-warnings.js.map +1 -0
- package/out/zql/src/query/measure-push-operator.d.ts.map +1 -1
- package/out/zql/src/query/measure-push-operator.js +30 -8
- package/out/zql/src/query/measure-push-operator.js.map +1 -1
- package/out/zqlite/src/pending-delta.d.ts +110 -0
- package/out/zqlite/src/pending-delta.d.ts.map +1 -0
- package/out/zqlite/src/pending-delta.js +278 -0
- package/out/zqlite/src/pending-delta.js.map +1 -0
- package/out/zqlite/src/query-delegate.d.ts.map +1 -1
- package/out/zqlite/src/query-delegate.js +5 -1
- package/out/zqlite/src/query-delegate.js.map +1 -1
- package/out/zqlite/src/sqlite-cost-model.d.ts.map +1 -1
- package/out/zqlite/src/sqlite-cost-model.js +52 -1
- package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
- package/out/zqlite/src/sqlite-stat-fanout.d.ts.map +1 -1
- package/out/zqlite/src/sqlite-stat-fanout.js +2 -26
- package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
- package/out/zqlite/src/sqlite-stat4-sample.d.ts +37 -0
- package/out/zqlite/src/sqlite-stat4-sample.d.ts.map +1 -0
- package/out/zqlite/src/sqlite-stat4-sample.js +61 -0
- package/out/zqlite/src/sqlite-stat4-sample.js.map +1 -0
- package/out/zqlite/src/table-source.d.ts +68 -0
- package/out/zqlite/src/table-source.d.ts.map +1 -1
- package/out/zqlite/src/table-source.js +206 -7
- package/out/zqlite/src/table-source.js.map +1 -1
- package/package.json +1 -1
- package/out/replicache/src/kv/idb-store-with-mem-fallback.d.ts +0 -35
- package/out/replicache/src/kv/idb-store-with-mem-fallback.d.ts.map +0 -1
- package/out/replicache/src/kv/idb-store-with-mem-fallback.js +0 -83
- package/out/replicache/src/kv/idb-store-with-mem-fallback.js.map +0 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-resume.d.ts +0 -156
- package/out/zero-cache/src/services/change-source/pg/backfill-resume.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-resume.js +0 -15
- package/out/zero-cache/src/services/change-source/pg/backfill-resume.js.map +0 -1
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { assert } from "../../../shared/src/asserts.js";
|
|
2
|
+
//#region ../zero-cache/src/observability/log-throttle.ts
|
|
3
|
+
/**
|
|
4
|
+
* Rate limits a log statement per key, for logs that would otherwise be
|
|
5
|
+
* written for every client group, such as the slow hydration of a popular
|
|
6
|
+
* query.
|
|
7
|
+
*
|
|
8
|
+
* Deduplication alone (logging a key once and never again) is not enough:
|
|
9
|
+
* the one line is lost once the log rotates, and the set of logged keys
|
|
10
|
+
* grows without bound. Instead a key is admitted at most once per
|
|
11
|
+
* {@link LogThrottleOptions.windowMs}, and the occurrences suppressed in
|
|
12
|
+
* between are counted and reported with the next admitted one.
|
|
13
|
+
*/
|
|
14
|
+
var LogThrottle = class {
|
|
15
|
+
#windowMs;
|
|
16
|
+
#maxKeys;
|
|
17
|
+
#maxPerWindow;
|
|
18
|
+
#now;
|
|
19
|
+
#entries = /* @__PURE__ */ new Map();
|
|
20
|
+
#windowStart = -Infinity;
|
|
21
|
+
#admittedInWindow = 0;
|
|
22
|
+
constructor({ windowMs, maxKeys = 1e3, maxPerWindow = 100, now = performance.now.bind(performance) }) {
|
|
23
|
+
assert(windowMs >= 0, "windowMs must be nonnegative");
|
|
24
|
+
assert(maxKeys > 0, "maxKeys must be positive");
|
|
25
|
+
assert(maxPerWindow > 0, "maxPerWindow must be positive");
|
|
26
|
+
this.#windowMs = windowMs;
|
|
27
|
+
this.#maxKeys = maxKeys;
|
|
28
|
+
this.#maxPerWindow = maxPerWindow;
|
|
29
|
+
this.#now = now;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Records an occurrence of `key`.
|
|
33
|
+
*
|
|
34
|
+
* @returns `undefined` if the occurrence should not be logged, or else the
|
|
35
|
+
* number of occurrences of `key` suppressed since it was last logged.
|
|
36
|
+
*/
|
|
37
|
+
admit(key) {
|
|
38
|
+
const now = this.#now();
|
|
39
|
+
const entry = this.#entries.get(key);
|
|
40
|
+
if (entry && now - entry.admittedAt < this.#windowMs) {
|
|
41
|
+
entry.suppressed++;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (now - this.#windowStart >= this.#windowMs) {
|
|
45
|
+
this.#windowStart = now;
|
|
46
|
+
this.#admittedInWindow = 0;
|
|
47
|
+
}
|
|
48
|
+
if (this.#admittedInWindow >= this.#maxPerWindow) {
|
|
49
|
+
if (entry) entry.suppressed++;
|
|
50
|
+
else this.#set(key, {
|
|
51
|
+
admittedAt: -Infinity,
|
|
52
|
+
suppressed: 1
|
|
53
|
+
});
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
this.#admittedInWindow++;
|
|
57
|
+
const suppressed = entry?.suppressed ?? 0;
|
|
58
|
+
this.#set(key, {
|
|
59
|
+
admittedAt: now,
|
|
60
|
+
suppressed: 0
|
|
61
|
+
});
|
|
62
|
+
return suppressed;
|
|
63
|
+
}
|
|
64
|
+
#set(key, entry) {
|
|
65
|
+
this.#entries.delete(key);
|
|
66
|
+
this.#entries.set(key, entry);
|
|
67
|
+
if (this.#entries.size > this.#maxKeys) {
|
|
68
|
+
const oldest = this.#entries.keys().next();
|
|
69
|
+
if (!oldest.done) this.#entries.delete(oldest.value);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
//#endregion
|
|
74
|
+
export { LogThrottle };
|
|
75
|
+
|
|
76
|
+
//# sourceMappingURL=log-throttle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-throttle.js","names":["#windowMs","#maxKeys","#maxPerWindow","#now","#entries","#windowStart","#admittedInWindow","#set"],"sources":["../../../../../zero-cache/src/observability/log-throttle.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\n\nexport type LogThrottleOptions = {\n /**\n * An admitted key is suppressed for this long. A key that is still\n * occurring after the window is admitted again, so a log that is still\n * relevant keeps showing up (e.g. after the log has rotated) without\n * repeating for every occurrence.\n */\n readonly windowMs: number;\n\n /**\n * The most keys tracked at once. The least recently admitted key is\n * forgotten beyond this, which bounds memory when the key space is\n * unbounded (e.g. dynamically built query shapes).\n */\n readonly maxKeys?: number | undefined;\n\n /**\n * The most occurrences admitted per window across all keys, which bounds\n * log volume when many distinct keys occur at once.\n */\n readonly maxPerWindow?: number | undefined;\n\n readonly now?: (() => number) | undefined;\n};\n\ntype Entry = {\n admittedAt: number;\n suppressed: number;\n};\n\n/**\n * Rate limits a log statement per key, for logs that would otherwise be\n * written for every client group, such as the slow hydration of a popular\n * query.\n *\n * Deduplication alone (logging a key once and never again) is not enough:\n * the one line is lost once the log rotates, and the set of logged keys\n * grows without bound. Instead a key is admitted at most once per\n * {@link LogThrottleOptions.windowMs}, and the occurrences suppressed in\n * between are counted and reported with the next admitted one.\n */\nexport class LogThrottle {\n readonly #windowMs: number;\n readonly #maxKeys: number;\n readonly #maxPerWindow: number;\n readonly #now: () => number;\n\n // Insertion ordered by admission, so the first entry is the least recently\n // admitted key.\n readonly #entries = new Map<string, Entry>();\n #windowStart = -Infinity;\n #admittedInWindow = 0;\n\n constructor({\n windowMs,\n maxKeys = 1000,\n maxPerWindow = 100,\n now = performance.now.bind(performance),\n }: LogThrottleOptions) {\n assert(windowMs >= 0, 'windowMs must be nonnegative');\n assert(maxKeys > 0, 'maxKeys must be positive');\n assert(maxPerWindow > 0, 'maxPerWindow must be positive');\n this.#windowMs = windowMs;\n this.#maxKeys = maxKeys;\n this.#maxPerWindow = maxPerWindow;\n this.#now = now;\n }\n\n /**\n * Records an occurrence of `key`.\n *\n * @returns `undefined` if the occurrence should not be logged, or else the\n * number of occurrences of `key` suppressed since it was last logged.\n */\n admit(key: string): number | undefined {\n const now = this.#now();\n const entry = this.#entries.get(key);\n if (entry && now - entry.admittedAt < this.#windowMs) {\n entry.suppressed++;\n return undefined;\n }\n\n if (now - this.#windowStart >= this.#windowMs) {\n this.#windowStart = now;\n this.#admittedInWindow = 0;\n }\n if (this.#admittedInWindow >= this.#maxPerWindow) {\n // Over the global budget. The occurrence is counted against the key so\n // that it is reported once the key is admitted.\n if (entry) {\n entry.suppressed++;\n } else {\n this.#set(key, {admittedAt: -Infinity, suppressed: 1});\n }\n return undefined;\n }\n\n this.#admittedInWindow++;\n const suppressed = entry?.suppressed ?? 0;\n this.#set(key, {admittedAt: now, suppressed: 0});\n return suppressed;\n }\n\n #set(key: string, entry: Entry): void {\n // Delete first so that the key moves to the end of the insertion order.\n this.#entries.delete(key);\n this.#entries.set(key, entry);\n if (this.#entries.size > this.#maxKeys) {\n const oldest = this.#entries.keys().next();\n if (!oldest.done) {\n this.#entries.delete(oldest.value);\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;AA2CA,IAAa,cAAb,MAAyB;CACvB;CACA;CACA;CACA;CAIA,2BAAoB,IAAI,IAAmB;CAC3C,eAAe;CACf,oBAAoB;CAEpB,YAAY,EACV,UACA,UAAU,KACV,eAAe,KACf,MAAM,YAAY,IAAI,KAAK,WAAW,KACjB;EACrB,OAAO,YAAY,GAAG,8BAA8B;EACpD,OAAO,UAAU,GAAG,0BAA0B;EAC9C,OAAO,eAAe,GAAG,+BAA+B;EACxD,KAAKA,YAAY;EACjB,KAAKC,WAAW;EAChB,KAAKC,gBAAgB;EACrB,KAAKC,OAAO;CACd;;;;;;;CAQA,MAAM,KAAiC;EACrC,MAAM,MAAM,KAAKA,KAAK;EACtB,MAAM,QAAQ,KAAKC,SAAS,IAAI,GAAG;EACnC,IAAI,SAAS,MAAM,MAAM,aAAa,KAAKJ,WAAW;GACpD,MAAM;GACN;EACF;EAEA,IAAI,MAAM,KAAKK,gBAAgB,KAAKL,WAAW;GAC7C,KAAKK,eAAe;GACpB,KAAKC,oBAAoB;EAC3B;EACA,IAAI,KAAKA,qBAAqB,KAAKJ,eAAe;GAGhD,IAAI,OACF,MAAM;QAEN,KAAKK,KAAK,KAAK;IAAC,YAAY;IAAW,YAAY;GAAC,CAAC;GAEvD;EACF;EAEA,KAAKD;EACL,MAAM,aAAa,OAAO,cAAc;EACxC,KAAKC,KAAK,KAAK;GAAC,YAAY;GAAK,YAAY;EAAC,CAAC;EAC/C,OAAO;CACT;CAEA,KAAK,KAAa,OAAoB;EAEpC,KAAKH,SAAS,OAAO,GAAG;EACxB,KAAKA,SAAS,IAAI,KAAK,KAAK;EAC5B,IAAI,KAAKA,SAAS,OAAO,KAAKH,UAAU;GACtC,MAAM,SAAS,KAAKG,SAAS,KAAK,EAAE,KAAK;GACzC,IAAI,CAAC,OAAO,MACV,KAAKA,SAAS,OAAO,OAAO,KAAK;EAErC;CACF;AACF"}
|
|
@@ -51,6 +51,22 @@ export declare const deployPermissionsOptions: {
|
|
|
51
51
|
type: v.Type<number>;
|
|
52
52
|
desc: string[];
|
|
53
53
|
};
|
|
54
|
+
slowAdvanceThreshold: {
|
|
55
|
+
type: v.Type<number>;
|
|
56
|
+
desc: string[];
|
|
57
|
+
};
|
|
58
|
+
planWarningRowThreshold: {
|
|
59
|
+
type: v.Type<number>;
|
|
60
|
+
desc: string[];
|
|
61
|
+
};
|
|
62
|
+
planWarningCostThreshold: {
|
|
63
|
+
type: v.Type<number>;
|
|
64
|
+
desc: string[];
|
|
65
|
+
};
|
|
66
|
+
queryStatsIntervalSeconds: {
|
|
67
|
+
type: v.Type<number>;
|
|
68
|
+
desc: string[];
|
|
69
|
+
};
|
|
54
70
|
ivmSampling: {
|
|
55
71
|
type: v.Type<number>;
|
|
56
72
|
desc: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/scripts/permissions.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,CAAC,MAAM,+BAA+B,CAAC;AACnD,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,kDAAkD,CAAC;AAE1D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAG9D,eAAO,MAAM,wBAAwB;IACnC,MAAM;QACJ,IAAI;YACF,IAAI;YACJ,IAAI;YACJ,KAAK;;;IAIT,QAAQ;QACN,EAAE;YACA,IAAI;YACJ,IAAI;;QAMN,IAAI;;;;;;IAGN,GAAG;QAAG,EAAE;;;;;IAER,KAAK;;;;;;;;;;;IAEL,GAAG
|
|
1
|
+
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/scripts/permissions.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,CAAC,MAAM,+BAA+B,CAAC;AACnD,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,kDAAkD,CAAC;AAE1D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAG9D,eAAO,MAAM,wBAAwB;IACnC,MAAM;QACJ,IAAI;YACF,IAAI;YACJ,IAAI;YACJ,KAAK;;;IAIT,QAAQ;QACN,EAAE;YACA,IAAI;YACJ,IAAI;;QAMN,IAAI;;;;;;IAGN,GAAG;QAAG,EAAE;;;;;IAER,KAAK;;;;;;;;;;;IAEL,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAEH,MAAM;QACJ,IAAI;YACF,IAAI;YACJ,IAAI;;QAKN,MAAM;YACJ,IAAI;YACJ,IAAI;;;IAYR,KAAK;QACH,IAAI;QACJ,IAAI;QACJ,KAAK;;CAER,CAAC;AAEF,wBAAsB,wBAAwB,CAC5C,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,IAAI,GACjB,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,iBAAiB,CAAA;CAAC,GAAG,SAAS,CAAC,CAAC;AACzE,wBAAsB,wBAAwB,CAC5C,UAAU,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,KAAK,GACnB,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,iBAAiB,CAAA;CAAC,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zero-dispatcher.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/server/runner/zero-dispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,2BAA2B,CAAC;AAEpE,OAAO,EAAC,WAAW,EAAE,KAAK,OAAO,EAAC,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"zero-dispatcher.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/server/runner/zero-dispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,2BAA2B,CAAC;AAEpE,OAAO,EAAC,WAAW,EAAE,KAAK,OAAO,EAAC,MAAM,gCAAgC,CAAC;AAQzE,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,0BAA0B,CAAC;AAMrD,qBAAa,cAAe,SAAQ,WAAW;;IAC7C,QAAQ,CAAC,EAAE,qBAAqB;IAIhC,YACE,MAAM,EAAE,oBAAoB,EAC5B,EAAE,EAAE,UAAU,EACd,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,EAChC,OAAO,CAAC,EAAE,MAAM,IAAI,EAsBrB;IAED,UAAmB,QAAQ,SAE1B;CAYF"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { handleHeapzRequest } from "../../services/heapz.js";
|
|
2
2
|
import { HttpService } from "../../services/http-service.js";
|
|
3
|
+
import { handlePlannerzRequest } from "../../services/plannerz.js";
|
|
3
4
|
import { handleProfrmzRequest, handleProfzRequest } from "../../services/profz.js";
|
|
4
5
|
import { handleStatzRequest } from "../../services/statz.js";
|
|
5
6
|
import { installWebSocketHandoff } from "../../types/websocket-handoff.js";
|
|
@@ -11,6 +12,7 @@ var ZeroDispatcher = class extends HttpService {
|
|
|
11
12
|
constructor(config, lc, opts, getWorker, onStart) {
|
|
12
13
|
super(`zero-dispatcher`, lc, opts, (fastify) => {
|
|
13
14
|
fastify.get("/statz", (req, res) => handleStatzRequest(lc, config, req, res));
|
|
15
|
+
fastify.get("/plannerz", (req, res) => handlePlannerzRequest(lc, config, req, res));
|
|
14
16
|
fastify.get("/heapz", (req, res) => handleHeapzRequest(lc, config, req, res));
|
|
15
17
|
fastify.get("/profz", (req, res) => handleProfzRequest(lc, config, req, res, this.#getWorker));
|
|
16
18
|
fastify.get("/profrmz", (req, res) => handleProfrmzRequest(lc, config, req, res, this.#getWorker));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zero-dispatcher.js","names":["#getWorker","#onStart","#handoff"],"sources":["../../../../../../zero-cache/src/server/runner/zero-dispatcher.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {NormalizedZeroConfig} from '../../config/normalize.ts';\nimport {handleHeapzRequest} from '../../services/heapz.ts';\nimport {HttpService, type Options} from '../../services/http-service.ts';\nimport {\n handleProfrmzRequest,\n handleProfzRequest,\n} from '../../services/profz.ts';\nimport {handleStatzRequest} from '../../services/statz.ts';\nimport type {IncomingMessageSubset} from '../../types/http.ts';\nimport type {Worker} from '../../types/processes.ts';\nimport {\n installWebSocketHandoff,\n type HandoffSpec,\n} from '../../types/websocket-handoff.ts';\n\nexport class ZeroDispatcher extends HttpService {\n readonly id = 'zero-dispatcher';\n readonly #getWorker: () => Promise<Worker>;\n readonly #onStart: (() => void) | undefined;\n\n constructor(\n config: NormalizedZeroConfig,\n lc: LogContext,\n opts: Options,\n getWorker: () => Promise<Worker>,\n onStart?: () => void,\n ) {\n super(`zero-dispatcher`, lc, opts, fastify => {\n fastify.get('/statz', (req, res) =>\n handleStatzRequest(lc, config, req, res),\n );\n fastify.get('/heapz', (req, res) =>\n handleHeapzRequest(lc, config, req, res),\n );\n fastify.get('/profz', (req, res) =>\n handleProfzRequest(lc, config, req, res, this.#getWorker),\n );\n fastify.get('/profrmz', (req, res) =>\n handleProfrmzRequest(lc, config, req, res, this.#getWorker),\n );\n installWebSocketHandoff(lc, this.#handoff, fastify.server);\n });\n this.#getWorker = getWorker;\n this.#onStart = onStart;\n }\n\n protected override _onStart() {\n this.#onStart?.();\n }\n\n readonly #handoff = (\n _req: IncomingMessageSubset,\n dispatch: (h: HandoffSpec<string>) => void,\n onError: (error: unknown) => void,\n ) => {\n void this.#getWorker().then(\n sender => dispatch({payload: 'unused', sender}),\n onError,\n );\n };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"zero-dispatcher.js","names":["#getWorker","#onStart","#handoff"],"sources":["../../../../../../zero-cache/src/server/runner/zero-dispatcher.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {NormalizedZeroConfig} from '../../config/normalize.ts';\nimport {handleHeapzRequest} from '../../services/heapz.ts';\nimport {HttpService, type Options} from '../../services/http-service.ts';\nimport {handlePlannerzRequest} from '../../services/plannerz.ts';\nimport {\n handleProfrmzRequest,\n handleProfzRequest,\n} from '../../services/profz.ts';\nimport {handleStatzRequest} from '../../services/statz.ts';\nimport type {IncomingMessageSubset} from '../../types/http.ts';\nimport type {Worker} from '../../types/processes.ts';\nimport {\n installWebSocketHandoff,\n type HandoffSpec,\n} from '../../types/websocket-handoff.ts';\n\nexport class ZeroDispatcher extends HttpService {\n readonly id = 'zero-dispatcher';\n readonly #getWorker: () => Promise<Worker>;\n readonly #onStart: (() => void) | undefined;\n\n constructor(\n config: NormalizedZeroConfig,\n lc: LogContext,\n opts: Options,\n getWorker: () => Promise<Worker>,\n onStart?: () => void,\n ) {\n super(`zero-dispatcher`, lc, opts, fastify => {\n fastify.get('/statz', (req, res) =>\n handleStatzRequest(lc, config, req, res),\n );\n fastify.get('/plannerz', (req, res) =>\n handlePlannerzRequest(lc, config, req, res),\n );\n fastify.get('/heapz', (req, res) =>\n handleHeapzRequest(lc, config, req, res),\n );\n fastify.get('/profz', (req, res) =>\n handleProfzRequest(lc, config, req, res, this.#getWorker),\n );\n fastify.get('/profrmz', (req, res) =>\n handleProfrmzRequest(lc, config, req, res, this.#getWorker),\n );\n installWebSocketHandoff(lc, this.#handoff, fastify.server);\n });\n this.#getWorker = getWorker;\n this.#onStart = onStart;\n }\n\n protected override _onStart() {\n this.#onStart?.();\n }\n\n readonly #handoff = (\n _req: IncomingMessageSubset,\n dispatch: (h: HandoffSpec<string>) => void,\n onError: (error: unknown) => void,\n ) => {\n void this.#getWorker().then(\n sender => dispatch({payload: 'unused', sender}),\n onError,\n );\n };\n}\n"],"mappings":";;;;;;;AAiBA,IAAa,iBAAb,cAAoC,YAAY;CAC9C,KAAc;CACd;CACA;CAEA,YACE,QACA,IACA,MACA,WACA,SACA;EACA,MAAM,mBAAmB,IAAI,OAAM,YAAW;GAC5C,QAAQ,IAAI,WAAW,KAAK,QAC1B,mBAAmB,IAAI,QAAQ,KAAK,GAAG,CACzC;GACA,QAAQ,IAAI,cAAc,KAAK,QAC7B,sBAAsB,IAAI,QAAQ,KAAK,GAAG,CAC5C;GACA,QAAQ,IAAI,WAAW,KAAK,QAC1B,mBAAmB,IAAI,QAAQ,KAAK,GAAG,CACzC;GACA,QAAQ,IAAI,WAAW,KAAK,QAC1B,mBAAmB,IAAI,QAAQ,KAAK,KAAK,KAAKA,UAAU,CAC1D;GACA,QAAQ,IAAI,aAAa,KAAK,QAC5B,qBAAqB,IAAI,QAAQ,KAAK,KAAK,KAAKA,UAAU,CAC5D;GACA,wBAAwB,IAAI,KAAKE,UAAU,QAAQ,MAAM;EAC3D,CAAC;EACD,KAAKF,aAAa;EAClB,KAAKC,WAAW;CAClB;CAEA,WAA8B;EAC5B,KAAKA,WAAW;CAClB;CAEA,YACE,MACA,UACA,YACG;EACH,KAAUD,WAAW,EAAE,MACrB,WAAU,SAAS;GAAC,SAAS;GAAU;EAAM,CAAC,GAC9C,OACF;CACF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncer.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/syncer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"syncer.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/syncer.ts"],"names":[],"mappings":"AAqCA,OAAO,EAGL,KAAK,MAAM,EACZ,MAAM,uBAAuB,CAAC;AAqC/B,wBAA8B,SAAS,CACrC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,GAAG,IAAI,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,IAAI,CAAC,CA6Qf"}
|
|
@@ -6,6 +6,7 @@ import { parentWorker, singleProcessMode } from "../types/processes.js";
|
|
|
6
6
|
import { getShardID } from "../types/shards.js";
|
|
7
7
|
import { getNormalizedZeroConfig } from "../config/zero-config.js";
|
|
8
8
|
import { randInt } from "../../../shared/src/rand.js";
|
|
9
|
+
import { getOrCreateGauge } from "../observability/metrics.js";
|
|
9
10
|
import { Snapshotter } from "../services/view-syncer/snapshotter.js";
|
|
10
11
|
import { PipelineDriver } from "../services/view-syncer/pipeline-driver.js";
|
|
11
12
|
import { connectPgClient } from "../types/pg.js";
|
|
@@ -25,6 +26,8 @@ import { CustomQueryTransformer } from "../custom-queries/transform-query.js";
|
|
|
25
26
|
import { MutagenService } from "../services/mutagen/mutagen.js";
|
|
26
27
|
import { PusherService } from "../services/mutagen/pusher.js";
|
|
27
28
|
import { ConnectionContextManagerImpl } from "../services/view-syncer/connection-context-manager.js";
|
|
29
|
+
import { DeferredWritesBudget } from "../services/view-syncer/deferred-writes-budget.js";
|
|
30
|
+
import { QueryStats } from "../services/view-syncer/query-stats.js";
|
|
28
31
|
import { SnapshotRowCache } from "../services/view-syncer/snapshot-row-cache.js";
|
|
29
32
|
import { ViewSyncerService } from "../services/view-syncer/view-syncer.js";
|
|
30
33
|
import { Syncer } from "../workers/syncer.js";
|
|
@@ -102,6 +105,17 @@ async function runWorker(parent, env, ...args) {
|
|
|
102
105
|
};
|
|
103
106
|
};
|
|
104
107
|
const snapshotRowCache = config.snapshotRowCacheSize > 0 ? new SnapshotRowCache(config.snapshotRowCacheSize) : void 0;
|
|
108
|
+
const deferredWritesBudget = config.deferIvmWrites ? DeferredWritesBudget.forHeapProportion(config.deferIvmWritesHeapProportion) : void 0;
|
|
109
|
+
if (deferredWritesBudget) {
|
|
110
|
+
lc.info?.(`Deferred IVM writes may hold up to ${deferredWritesBudget.maxRows} rows (~${(deferredWritesBudget.maxBytes / 1024 ** 2).toFixed(2)} MB) across client groups`);
|
|
111
|
+
getOrCreateGauge("sync", "ivm.deferred-writes-reserved-rows", "Rows reserved by the client groups of a sync worker to hold IVM changes in memory (deferIvmWrites)").addCallback((o) => o.observe(deferredWritesBudget.reservedRows));
|
|
112
|
+
getOrCreateGauge("sync", "ivm.deferred-writes-held-bytes", {
|
|
113
|
+
description: "Estimated bytes of the IVM changes that the client groups of a sync worker hold in memory (deferIvmWrites)",
|
|
114
|
+
unit: "By"
|
|
115
|
+
}).addCallback((o) => o.observe(deferredWritesBudget.heldBytes));
|
|
116
|
+
}
|
|
117
|
+
const queryStats = config.log.queryStatsIntervalSeconds > 0 ? new QueryStats() : void 0;
|
|
118
|
+
const stopQueryStats = queryStats?.start(lc.withContext("taskID", config.taskID).withContext("component", "view-syncer"), config.log.queryStatsIntervalSeconds * 1e3);
|
|
105
119
|
const viewSyncerFactory = (id, sub, drainCoordinator) => {
|
|
106
120
|
const logger = lc.withContext("taskID", config.taskID).withContext("component", "view-syncer").withContext("appID", shard.appID).withContext("shardNum", shard.shardNum).withContext("clientGroupID", id).withContext("instance", randomID());
|
|
107
121
|
const customQueryTransformer = customQueryConfig && new CustomQueryTransformer(logger, shard);
|
|
@@ -110,12 +124,16 @@ async function runWorker(parent, env, ...args) {
|
|
|
110
124
|
const inspectorDelegate = new InspectorDelegate(customQueryTransformer);
|
|
111
125
|
const priorityOpRunningYieldThresholdMs = Math.max(config.yieldThresholdMs / 4, 2);
|
|
112
126
|
const normalYieldThresholdMs = Math.max(config.yieldThresholdMs, 2);
|
|
113
|
-
return new ViewSyncerService(config, logger, shard, config.taskID, id, cvrDB, new PipelineDriver(logger, config.log, new Snapshotter(logger, replicaFile, shard, void 0, snapshotRowCache), shard, operatorStorage.createClientGroupStorage(id), id, inspectorDelegate, () => isPriorityOpRunning() ? priorityOpRunningYieldThresholdMs : normalYieldThresholdMs, config.enableQueryPlanner, config), sub, drainCoordinator, config.log.slowHydrateThreshold, inspectorDelegate, connContextManager, customQueryTransformer, runPriorityOp);
|
|
127
|
+
return new ViewSyncerService(config, logger, shard, config.taskID, id, cvrDB, new PipelineDriver(logger, config.log, new Snapshotter(logger, replicaFile, shard, void 0, snapshotRowCache), shard, operatorStorage.createClientGroupStorage(id), id, inspectorDelegate, () => isPriorityOpRunning() ? priorityOpRunningYieldThresholdMs : normalYieldThresholdMs, config.enableQueryPlanner, config, deferredWritesBudget, queryStats), sub, drainCoordinator, config.log.slowHydrateThreshold, inspectorDelegate, connContextManager, customQueryTransformer, runPriorityOp);
|
|
114
128
|
};
|
|
115
129
|
const syncer = new Syncer(lc, config, viewSyncerFactory, upstreamDB ? (id) => new MutagenService(lc.withContext("component", "mutagen").withContext("clientGroupID", id), shard, id, upstreamDB, config, writeAuthzStorage) : void 0, pushConfig === void 0 ? void 0 : (id, connContextManager) => new PusherService(config, lc.withContext("clientGroupID", id), id, connContextManager), parent, validateLegacyJWT);
|
|
116
130
|
startAnonymousTelemetry(lc, config);
|
|
117
131
|
dbWarmup.then(() => parent.send(["ready", { ready: true }]));
|
|
118
|
-
|
|
132
|
+
try {
|
|
133
|
+
return await runUntilKilled(lc, parent, syncer);
|
|
134
|
+
} finally {
|
|
135
|
+
stopQueryStats?.();
|
|
136
|
+
}
|
|
119
137
|
}
|
|
120
138
|
if (!singleProcessMode()) exitAfter(() => lc, () => runWorker(must(parentWorker), process.env, ...process.argv.slice(2)));
|
|
121
139
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncer.js","names":[],"sources":["../../../../../zero-cache/src/server/syncer.ts"],"sourcesContent":["import {randomUUID} from 'node:crypto';\nimport {tmpdir} from 'node:os';\nimport path from 'node:path';\nimport {pid} from 'node:process';\nimport {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {randInt} from '../../../shared/src/rand.ts';\nimport {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {DatabaseStorage} from '../../../zqlite/src/database-storage.ts';\nimport type {ValidateLegacyJWT} from '../auth/auth.ts';\nimport {tokenConfigOptions, verifyToken} from '../auth/jwt.ts';\nimport type {NormalizedZeroConfig} from '../config/normalize.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {CustomQueryTransformer} from '../custom-queries/transform-query.ts';\nimport {registerSQLiteCorruptionDiagnosticTarget} from '../db/sqlite-corruption.ts';\nimport {warmupConnections} from '../db/warmup.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {exitAfter, runUntilKilled} from '../services/life-cycle.ts';\nimport {MutagenService} from '../services/mutagen/mutagen.ts';\nimport {PusherService} from '../services/mutagen/pusher.ts';\nimport type {ReplicaState} from '../services/replicator/replicator.ts';\nimport {\n type ConnectionContextManager,\n ConnectionContextManagerImpl,\n} from '../services/view-syncer/connection-context-manager.ts';\nimport type {DrainCoordinator} from '../services/view-syncer/drain-coordinator.ts';\nimport {PipelineDriver} from '../services/view-syncer/pipeline-driver.ts';\nimport {SnapshotRowCache} from '../services/view-syncer/snapshot-row-cache.ts';\nimport {Snapshotter} from '../services/view-syncer/snapshotter.ts';\nimport {ViewSyncerService} from '../services/view-syncer/view-syncer.ts';\nimport {ProtocolErrorWithLevel} from '../types/error-with-level.ts';\nimport {connectPgClient} from '../types/pg.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {installProfileHandler} from '../types/profiler.ts';\nimport {getShardID} from '../types/shards.ts';\nimport type {Subscription} from '../types/subscription.ts';\nimport {replicaFileModeSchema, replicaFileName} from '../workers/replicator.ts';\nimport {Syncer} from '../workers/syncer.ts';\nimport {startAnonymousTelemetry} from './anonymous-otel-start.ts';\nimport {InspectorDelegate} from './inspector-delegate.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\nimport {isPriorityOpRunning, runPriorityOp} from './priority-op.ts';\n\nfunction randomID() {\n return randInt(1, Number.MAX_SAFE_INTEGER).toString(36);\n}\n\nfunction getCustomQueryConfig(\n config: Pick<NormalizedZeroConfig, 'query' | 'getQueries'>,\n) {\n const queryConfig = config.query?.url ? config.query : config.getQueries;\n\n if (!queryConfig?.url) {\n return undefined;\n }\n\n return {\n url: queryConfig.url,\n apiKey: queryConfig.apiKey,\n allowedClientHeaders: queryConfig.allowedClientHeaders,\n allowedRequestHeaders: queryConfig.allowedRequestHeaders,\n forwardCookies: queryConfig.forwardCookies ?? false,\n };\n}\n\n// Default LogContext, overridden in runWorker\nlet lc = new LogContext('info', {}, consoleLogSink);\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...args: string[]\n): Promise<void> {\n assert(args.length >= 2, `expected [fileMode, workerIndex, ...flags]`);\n const fileMode = v.parse(args[0], replicaFileModeSchema);\n const workerIndex = Number(args[1]);\n installProfileHandler(parent, `syncer-${workerIndex}`, workerIndex);\n const config = getNormalizedZeroConfig({env, argv: args.slice(2)});\n\n startOtelAuto(\n createLogContext(config, 'syncer', workerIndex, false),\n 'syncer',\n workerIndex,\n );\n lc = createLogContext(config, 'syncer', workerIndex);\n initEventSink(lc, config);\n\n const {cvr, upstream, enableCrudMutations} = config;\n\n const replicaFile = replicaFileName(config.replica.file, fileMode);\n registerSQLiteCorruptionDiagnosticTarget(\n {\n debugName: 'syncer replica',\n dbPath: replicaFile,\n },\n config.sqliteCorruptionChecks,\n );\n lc.debug?.(`running view-syncer on ${replicaFile}`);\n\n const cvrDB = await connectPgClient(lc, cvr.db, `sync-worker-${pid}-cvr`, {\n max: must(cvr.maxConnsPerWorker, 'cvr.maxConnsPerWorker must be set'),\n });\n\n const upstreamDB =\n enableCrudMutations && upstream.type === 'pg'\n ? await connectPgClient(lc, upstream.db, `sync-worker-${pid}-upstream`, {\n max: must(\n upstream.maxConnsPerWorker,\n 'upstream.maxConnsPerWorker must be set',\n ),\n })\n : undefined;\n\n const dbWarmup = Promise.allSettled([\n warmupConnections(lc, cvrDB, 'cvr'),\n upstreamDB ? warmupConnections(lc, upstreamDB, 'upstream') : promiseVoid,\n ]);\n\n const tmpDir = config.storageDBTmpDir ?? tmpdir();\n const operatorStorage = DatabaseStorage.create(\n lc,\n path.join(tmpDir, `sync-worker-${randomUUID()}`),\n );\n const writeAuthzStorage = DatabaseStorage.create(\n lc,\n path.join(tmpDir, `mutagen-${randomUUID()}`),\n );\n\n const shard = getShardID(config);\n const customQueryConfig = getCustomQueryConfig(config);\n const pushConfig =\n config.push.url === undefined && config.mutate.url === undefined\n ? undefined\n : {\n ...config.push,\n ...config.mutate,\n url: must(\n config.push.url ?? config.mutate.url,\n 'No push or mutate URL configured',\n ),\n };\n\n /** @deprecated used in JWT validation */\n let validateLegacyJWT: ValidateLegacyJWT | undefined = undefined;\n\n const tokenOptions = tokenConfigOptions(config.auth ?? {});\n if (tokenOptions.length === 1) {\n validateLegacyJWT = async (token, {userID}) => {\n if (!userID) {\n throw new ProtocolErrorWithLevel(\n {\n kind: 'Unauthorized',\n message: 'UserID is required for JWT validation.',\n origin: 'zeroCache',\n },\n 'warn',\n );\n }\n\n const decoded = await verifyToken(config.auth, token, {\n subject: userID,\n ...(config.auth?.issuer && {issuer: config.auth.issuer}),\n ...(config.auth?.audience && {\n audience: config.auth.audience,\n }),\n });\n return {\n type: 'jwt',\n raw: token,\n decoded,\n };\n };\n }\n\n // Shared by all of the view-syncers on this worker so that the row reads\n // performed when advancing their pipelines are done once per worker rather\n // than once per client group.\n const snapshotRowCache =\n config.snapshotRowCacheSize > 0\n ? new SnapshotRowCache(config.snapshotRowCacheSize)\n : undefined;\n\n const viewSyncerFactory = (\n id: string,\n sub: Subscription<ReplicaState>,\n drainCoordinator: DrainCoordinator,\n ) => {\n const logger = lc\n .withContext('taskID', config.taskID)\n .withContext('component', 'view-syncer')\n .withContext('appID', shard.appID)\n .withContext('shardNum', shard.shardNum)\n .withContext('clientGroupID', id)\n .withContext('instance', randomID());\n\n const customQueryTransformer =\n customQueryConfig && new CustomQueryTransformer(logger, shard);\n const connContextManager = new ConnectionContextManagerImpl(\n logger,\n config.auth.revalidateIntervalSeconds,\n config.auth.retransformIntervalSeconds,\n customQueryConfig,\n pushConfig,\n validateLegacyJWT,\n );\n\n lc.debug?.(\n `creating view syncer. Query Planner Enabled: ${config.enableQueryPlanner}`,\n );\n\n const inspectorDelegate = new InspectorDelegate(customQueryTransformer);\n\n const priorityOpRunningYieldThresholdMs = Math.max(\n config.yieldThresholdMs / 4,\n 2,\n );\n const normalYieldThresholdMs = Math.max(config.yieldThresholdMs, 2);\n\n return new ViewSyncerService(\n config,\n logger,\n shard,\n config.taskID,\n id,\n cvrDB,\n new PipelineDriver(\n logger,\n config.log,\n new Snapshotter(\n logger,\n replicaFile,\n shard,\n undefined,\n snapshotRowCache,\n ),\n shard,\n operatorStorage.createClientGroupStorage(id),\n id,\n inspectorDelegate,\n () =>\n isPriorityOpRunning()\n ? priorityOpRunningYieldThresholdMs\n : normalYieldThresholdMs,\n config.enableQueryPlanner,\n config,\n ),\n sub,\n drainCoordinator,\n config.log.slowHydrateThreshold,\n inspectorDelegate,\n connContextManager,\n customQueryTransformer,\n runPriorityOp,\n );\n };\n\n const mutagenFactory = upstreamDB\n ? (id: string) =>\n new MutagenService(\n lc\n .withContext('component', 'mutagen')\n .withContext('clientGroupID', id),\n shard,\n id,\n upstreamDB,\n config,\n writeAuthzStorage,\n )\n : undefined;\n\n const pusherFactory =\n pushConfig === undefined\n ? undefined\n : (id: string, connContextManager: ConnectionContextManager) =>\n new PusherService(\n config,\n lc.withContext('clientGroupID', id),\n id,\n connContextManager,\n );\n\n const syncer = new Syncer(\n lc,\n config,\n viewSyncerFactory,\n mutagenFactory,\n pusherFactory,\n parent,\n validateLegacyJWT,\n );\n\n startAnonymousTelemetry(lc, config);\n\n void dbWarmup.then(() => parent.send(['ready', {ready: true}]));\n\n return runUntilKilled(lc, parent, syncer);\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(\n () => lc,\n () => runWorker(must(parentWorker), process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"syncer.js","names":[],"sources":["../../../../../zero-cache/src/server/syncer.ts"],"sourcesContent":["import {randomUUID} from 'node:crypto';\nimport {tmpdir} from 'node:os';\nimport path from 'node:path';\nimport {pid} from 'node:process';\nimport {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {randInt} from '../../../shared/src/rand.ts';\nimport {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {DatabaseStorage} from '../../../zqlite/src/database-storage.ts';\nimport type {ValidateLegacyJWT} from '../auth/auth.ts';\nimport {tokenConfigOptions, verifyToken} from '../auth/jwt.ts';\nimport type {NormalizedZeroConfig} from '../config/normalize.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {CustomQueryTransformer} from '../custom-queries/transform-query.ts';\nimport {registerSQLiteCorruptionDiagnosticTarget} from '../db/sqlite-corruption.ts';\nimport {warmupConnections} from '../db/warmup.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {getOrCreateGauge} from '../observability/metrics.ts';\nimport {exitAfter, runUntilKilled} from '../services/life-cycle.ts';\nimport {MutagenService} from '../services/mutagen/mutagen.ts';\nimport {PusherService} from '../services/mutagen/pusher.ts';\nimport type {ReplicaState} from '../services/replicator/replicator.ts';\nimport {\n type ConnectionContextManager,\n ConnectionContextManagerImpl,\n} from '../services/view-syncer/connection-context-manager.ts';\nimport {DeferredWritesBudget} from '../services/view-syncer/deferred-writes-budget.ts';\nimport type {DrainCoordinator} from '../services/view-syncer/drain-coordinator.ts';\nimport {PipelineDriver} from '../services/view-syncer/pipeline-driver.ts';\nimport {QueryStats} from '../services/view-syncer/query-stats.ts';\nimport {SnapshotRowCache} from '../services/view-syncer/snapshot-row-cache.ts';\nimport {Snapshotter} from '../services/view-syncer/snapshotter.ts';\nimport {ViewSyncerService} from '../services/view-syncer/view-syncer.ts';\nimport {ProtocolErrorWithLevel} from '../types/error-with-level.ts';\nimport {connectPgClient} from '../types/pg.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {installProfileHandler} from '../types/profiler.ts';\nimport {getShardID} from '../types/shards.ts';\nimport type {Subscription} from '../types/subscription.ts';\nimport {replicaFileModeSchema, replicaFileName} from '../workers/replicator.ts';\nimport {Syncer} from '../workers/syncer.ts';\nimport {startAnonymousTelemetry} from './anonymous-otel-start.ts';\nimport {InspectorDelegate} from './inspector-delegate.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\nimport {isPriorityOpRunning, runPriorityOp} from './priority-op.ts';\n\nfunction randomID() {\n return randInt(1, Number.MAX_SAFE_INTEGER).toString(36);\n}\n\nfunction getCustomQueryConfig(\n config: Pick<NormalizedZeroConfig, 'query' | 'getQueries'>,\n) {\n const queryConfig = config.query?.url ? config.query : config.getQueries;\n\n if (!queryConfig?.url) {\n return undefined;\n }\n\n return {\n url: queryConfig.url,\n apiKey: queryConfig.apiKey,\n allowedClientHeaders: queryConfig.allowedClientHeaders,\n allowedRequestHeaders: queryConfig.allowedRequestHeaders,\n forwardCookies: queryConfig.forwardCookies ?? false,\n };\n}\n\n// Default LogContext, overridden in runWorker\nlet lc = new LogContext('info', {}, consoleLogSink);\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...args: string[]\n): Promise<void> {\n assert(args.length >= 2, `expected [fileMode, workerIndex, ...flags]`);\n const fileMode = v.parse(args[0], replicaFileModeSchema);\n const workerIndex = Number(args[1]);\n installProfileHandler(parent, `syncer-${workerIndex}`, workerIndex);\n const config = getNormalizedZeroConfig({env, argv: args.slice(2)});\n\n startOtelAuto(\n createLogContext(config, 'syncer', workerIndex, false),\n 'syncer',\n workerIndex,\n );\n lc = createLogContext(config, 'syncer', workerIndex);\n initEventSink(lc, config);\n\n const {cvr, upstream, enableCrudMutations} = config;\n\n const replicaFile = replicaFileName(config.replica.file, fileMode);\n registerSQLiteCorruptionDiagnosticTarget(\n {\n debugName: 'syncer replica',\n dbPath: replicaFile,\n },\n config.sqliteCorruptionChecks,\n );\n lc.debug?.(`running view-syncer on ${replicaFile}`);\n\n const cvrDB = await connectPgClient(lc, cvr.db, `sync-worker-${pid}-cvr`, {\n max: must(cvr.maxConnsPerWorker, 'cvr.maxConnsPerWorker must be set'),\n });\n\n const upstreamDB =\n enableCrudMutations && upstream.type === 'pg'\n ? await connectPgClient(lc, upstream.db, `sync-worker-${pid}-upstream`, {\n max: must(\n upstream.maxConnsPerWorker,\n 'upstream.maxConnsPerWorker must be set',\n ),\n })\n : undefined;\n\n const dbWarmup = Promise.allSettled([\n warmupConnections(lc, cvrDB, 'cvr'),\n upstreamDB ? warmupConnections(lc, upstreamDB, 'upstream') : promiseVoid,\n ]);\n\n const tmpDir = config.storageDBTmpDir ?? tmpdir();\n const operatorStorage = DatabaseStorage.create(\n lc,\n path.join(tmpDir, `sync-worker-${randomUUID()}`),\n );\n const writeAuthzStorage = DatabaseStorage.create(\n lc,\n path.join(tmpDir, `mutagen-${randomUUID()}`),\n );\n\n const shard = getShardID(config);\n const customQueryConfig = getCustomQueryConfig(config);\n const pushConfig =\n config.push.url === undefined && config.mutate.url === undefined\n ? undefined\n : {\n ...config.push,\n ...config.mutate,\n url: must(\n config.push.url ?? config.mutate.url,\n 'No push or mutate URL configured',\n ),\n };\n\n /** @deprecated used in JWT validation */\n let validateLegacyJWT: ValidateLegacyJWT | undefined = undefined;\n\n const tokenOptions = tokenConfigOptions(config.auth ?? {});\n if (tokenOptions.length === 1) {\n validateLegacyJWT = async (token, {userID}) => {\n if (!userID) {\n throw new ProtocolErrorWithLevel(\n {\n kind: 'Unauthorized',\n message: 'UserID is required for JWT validation.',\n origin: 'zeroCache',\n },\n 'warn',\n );\n }\n\n const decoded = await verifyToken(config.auth, token, {\n subject: userID,\n ...(config.auth?.issuer && {issuer: config.auth.issuer}),\n ...(config.auth?.audience && {\n audience: config.auth.audience,\n }),\n });\n return {\n type: 'jwt',\n raw: token,\n decoded,\n };\n };\n }\n\n // Shared by all of the view-syncers on this worker so that the row reads\n // performed when advancing their pipelines are done once per worker rather\n // than once per client group.\n const snapshotRowCache =\n config.snapshotRowCacheSize > 0\n ? new SnapshotRowCache(config.snapshotRowCacheSize)\n : undefined;\n\n // Shared by all of the view-syncers on this worker, which each hold their\n // own copy of the changes they are advancing through.\n const deferredWritesBudget = config.deferIvmWrites\n ? DeferredWritesBudget.forHeapProportion(\n config.deferIvmWritesHeapProportion,\n )\n : undefined;\n if (deferredWritesBudget) {\n lc.info?.(\n `Deferred IVM writes may hold up to ${deferredWritesBudget.maxRows} ` +\n `rows (~${(deferredWritesBudget.maxBytes / 1024 ** 2).toFixed(2)} MB) ` +\n `across client groups`,\n );\n getOrCreateGauge(\n 'sync',\n 'ivm.deferred-writes-reserved-rows',\n 'Rows reserved by the client groups of a sync worker to hold IVM ' +\n 'changes in memory (deferIvmWrites)',\n ).addCallback(o => o.observe(deferredWritesBudget.reservedRows));\n getOrCreateGauge('sync', 'ivm.deferred-writes-held-bytes', {\n description:\n 'Estimated bytes of the IVM changes that the client groups of a sync ' +\n 'worker hold in memory (deferIvmWrites)',\n unit: 'By',\n }).addCallback(o => o.observe(deferredWritesBudget.heldBytes));\n }\n\n // Shared by all of the view-syncers on this worker, so that the work done\n // for each query shape is logged once per worker per interval.\n const queryStats =\n config.log.queryStatsIntervalSeconds > 0 ? new QueryStats() : undefined;\n const stopQueryStats = queryStats?.start(\n lc\n .withContext('taskID', config.taskID)\n .withContext('component', 'view-syncer'),\n config.log.queryStatsIntervalSeconds * 1000,\n );\n\n const viewSyncerFactory = (\n id: string,\n sub: Subscription<ReplicaState>,\n drainCoordinator: DrainCoordinator,\n ) => {\n const logger = lc\n .withContext('taskID', config.taskID)\n .withContext('component', 'view-syncer')\n .withContext('appID', shard.appID)\n .withContext('shardNum', shard.shardNum)\n .withContext('clientGroupID', id)\n .withContext('instance', randomID());\n\n const customQueryTransformer =\n customQueryConfig && new CustomQueryTransformer(logger, shard);\n const connContextManager = new ConnectionContextManagerImpl(\n logger,\n config.auth.revalidateIntervalSeconds,\n config.auth.retransformIntervalSeconds,\n customQueryConfig,\n pushConfig,\n validateLegacyJWT,\n );\n\n lc.debug?.(\n `creating view syncer. Query Planner Enabled: ${config.enableQueryPlanner}`,\n );\n\n const inspectorDelegate = new InspectorDelegate(customQueryTransformer);\n\n const priorityOpRunningYieldThresholdMs = Math.max(\n config.yieldThresholdMs / 4,\n 2,\n );\n const normalYieldThresholdMs = Math.max(config.yieldThresholdMs, 2);\n\n return new ViewSyncerService(\n config,\n logger,\n shard,\n config.taskID,\n id,\n cvrDB,\n new PipelineDriver(\n logger,\n config.log,\n new Snapshotter(\n logger,\n replicaFile,\n shard,\n undefined,\n snapshotRowCache,\n ),\n shard,\n operatorStorage.createClientGroupStorage(id),\n id,\n inspectorDelegate,\n () =>\n isPriorityOpRunning()\n ? priorityOpRunningYieldThresholdMs\n : normalYieldThresholdMs,\n config.enableQueryPlanner,\n config,\n deferredWritesBudget,\n queryStats,\n ),\n sub,\n drainCoordinator,\n config.log.slowHydrateThreshold,\n inspectorDelegate,\n connContextManager,\n customQueryTransformer,\n runPriorityOp,\n );\n };\n\n const mutagenFactory = upstreamDB\n ? (id: string) =>\n new MutagenService(\n lc\n .withContext('component', 'mutagen')\n .withContext('clientGroupID', id),\n shard,\n id,\n upstreamDB,\n config,\n writeAuthzStorage,\n )\n : undefined;\n\n const pusherFactory =\n pushConfig === undefined\n ? undefined\n : (id: string, connContextManager: ConnectionContextManager) =>\n new PusherService(\n config,\n lc.withContext('clientGroupID', id),\n id,\n connContextManager,\n );\n\n const syncer = new Syncer(\n lc,\n config,\n viewSyncerFactory,\n mutagenFactory,\n pusherFactory,\n parent,\n validateLegacyJWT,\n );\n\n startAnonymousTelemetry(lc, config);\n\n void dbWarmup.then(() => parent.send(['ready', {ready: true}]));\n\n try {\n return await runUntilKilled(lc, parent, syncer);\n } finally {\n // Logs the work done since the last interval.\n stopQueryStats?.();\n }\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(\n () => lc,\n () => runWorker(must(parentWorker), process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backfill-manager.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/change-source/common/backfill-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"backfill-manager.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/change-source/common/backfill-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAUjD,OAAO,EAAmB,KAAK,UAAU,EAAC,MAAM,wBAAwB,CAAC;AAKzE,OAAO,KAAK,EACV,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EAEnB,eAAe,EAChB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EACV,UAAU,EACV,uBAAuB,EACvB,QAAQ,EACT,MAAM,gCAAgC,CAAC;AAMxC;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,eAAe,GAAG,iBAAiB,CAAC;IAC7C,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,KAAK,gBAAgB,GAAG,CACtB,GAAG,EAAE,eAAe,KACjB,cAAc,CAAC,eAAe,CAAC,CAAC;AAkFrC;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,eAAgB,YAAW,UAAU,EAAE,QAAQ;;IA4C1D,YACE,EAAE,EAAE,UAAU,EACd,cAAc,EAAE,uBAAuB,EACvC,gBAAgB,EAAE,gBAAgB,EAClC,UAAU,GAAE,UAA6B,EACzC,YAAY,SAA0B,EACtC,YAAY,SAA0B,EACtC,oBAAoB,SAAyB,EAU9C;IAED,GAAG,CAAC,aAAa,EAAE,MAAM,EAAE,eAAe,EAAE,eAAe,EAAE,QAU5D;IA4bD;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CA6M3C;IAED,MAAM,IAAI,IAAI,CAab;CACF;AAED,uBAAe,mBAAoB,SAAQ,KAAK;IAC9C,YAAY,EAAE,EAAE,eAAe,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,EAM5D;CACF;AA6BD;;;;;GAKG;AACH,qBAAa,0BAA2B,SAAQ,mBAAmB;IACjE,QAAQ,CAAC,IAAI,gCAAgC;IAE7C,YAAY,EAAE,EAAE,eAAe,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,EAE5D;CACF"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { assert } from "../../../../../shared/src/asserts.js";
|
|
2
2
|
import { AbortError } from "../../../../../shared/src/abort-error.js";
|
|
3
3
|
import { must } from "../../../../../shared/src/must.js";
|
|
4
|
+
import { promiseOrAbort } from "../../../../../shared/src/promise-race.js";
|
|
4
5
|
import { stringify } from "../../../../../shared/src/bigint-json.js";
|
|
5
6
|
import "../../../types/lite.js";
|
|
6
7
|
import { randInt } from "../../../../../shared/src/rand.js";
|
|
@@ -147,7 +148,8 @@ var BackfillManager = class {
|
|
|
147
148
|
const request = candidates[randInt(0, candidates.length - 1)];
|
|
148
149
|
const state = {
|
|
149
150
|
request,
|
|
150
|
-
minWatermark: ""
|
|
151
|
+
minWatermark: "",
|
|
152
|
+
stopped: new AbortController()
|
|
151
153
|
};
|
|
152
154
|
const lc = this.#lc.withContext("table", request.table.name);
|
|
153
155
|
this.#runningBackfill = state;
|
|
@@ -186,7 +188,8 @@ var BackfillManager = class {
|
|
|
186
188
|
produceMs: 0,
|
|
187
189
|
pushMs: 0,
|
|
188
190
|
reserveMs: 0,
|
|
189
|
-
stallMs: 0
|
|
191
|
+
stallMs: 0,
|
|
192
|
+
waitMs: 0
|
|
190
193
|
};
|
|
191
194
|
/**
|
|
192
195
|
* @returns the new tx watermark, or null if backfill was cancelled
|
|
@@ -194,8 +197,8 @@ var BackfillManager = class {
|
|
|
194
197
|
const beginTxFor = async (msg) => {
|
|
195
198
|
assert(backfillTx === null, "Expected no active backfill transaction");
|
|
196
199
|
const lastWatermark = await changeStream.reserve("backfill");
|
|
197
|
-
if (state.
|
|
198
|
-
if (state.
|
|
200
|
+
if (state.stopped.signal.aborted || msg.tag === "backfill" && msg.watermark < state.minWatermark) {
|
|
201
|
+
if (!state.stopped.signal.aborted) {
|
|
199
202
|
assert(msg.tag === "backfill", "Expected backfill message tag");
|
|
200
203
|
this.#stopRunningBackfill(`row key change at ${state.minWatermark} postdates backfill watermark at ${msg.watermark}`, state);
|
|
201
204
|
}
|
|
@@ -207,7 +210,7 @@ var BackfillManager = class {
|
|
|
207
210
|
major,
|
|
208
211
|
minor: BigInt(minor) + 1n
|
|
209
212
|
});
|
|
210
|
-
if (
|
|
213
|
+
if (tx < msg.watermark) tx = msg.watermark;
|
|
211
214
|
changeStream.push([
|
|
212
215
|
"begin",
|
|
213
216
|
{
|
|
@@ -288,16 +291,22 @@ var BackfillManager = class {
|
|
|
288
291
|
return;
|
|
289
292
|
}
|
|
290
293
|
const { message: msg, byteSize } = item;
|
|
291
|
-
const
|
|
292
|
-
if (backfillTx && (changeStream.waiterDelay() > 0 ||
|
|
293
|
-
if (
|
|
294
|
+
const waitForChangeStream = this.#changeStreamReached(lc, msg.watermark);
|
|
295
|
+
if (backfillTx && (changeStream.waiterDelay() > 0 || waitForChangeStream || uncommittedBytes >= this.#commitThresholdBytes)) commitTx();
|
|
296
|
+
if (waitForChangeStream) {
|
|
297
|
+
const t0 = performance.now();
|
|
298
|
+
await promiseOrAbort(waitForChangeStream, state.stopped.signal);
|
|
299
|
+
const elapsed = performance.now() - t0;
|
|
300
|
+
stats.waitMs += elapsed;
|
|
301
|
+
lc.info?.(`change stream reached ${msg.watermark} after waiting ${Math.round(elapsed)} ms`);
|
|
302
|
+
}
|
|
294
303
|
if (msg.tag === "backfill" && msg.rowValues.length > 0 && msg.relation.rowKey.columns.length === 0) throw new MissingRowKeyError(state.request);
|
|
295
304
|
if (backfillTx === null) {
|
|
296
305
|
const t0 = performance.now();
|
|
297
306
|
backfillTx = await beginTxFor(msg);
|
|
298
307
|
stats.reserveMs += performance.now() - t0;
|
|
299
308
|
if (backfillTx === null) {
|
|
300
|
-
lc.info?.(`backfill stream canceled: ${state.
|
|
309
|
+
lc.info?.(`backfill stream canceled: ${state.stopped.signal.reason}`, state.request);
|
|
301
310
|
this.#checkAndStartBackfill();
|
|
302
311
|
return;
|
|
303
312
|
}
|
|
@@ -311,7 +320,7 @@ var BackfillManager = class {
|
|
|
311
320
|
this.#maybeLogBackfillStats(lc, stats);
|
|
312
321
|
}
|
|
313
322
|
if (backfillTx) commitTx();
|
|
314
|
-
lc.debug?.(`backfill stream exited
|
|
323
|
+
lc.debug?.(`backfill stream exited`);
|
|
315
324
|
} finally {
|
|
316
325
|
stopProducer = true;
|
|
317
326
|
credits.enqueue(1);
|
|
@@ -334,7 +343,9 @@ var BackfillManager = class {
|
|
|
334
343
|
* their sum can exceed `elapsedMs`. `reserveMs` is time spent (re)acquiring
|
|
335
344
|
* the change-stream reservation, and `stallMs` is time the consumer waited on
|
|
336
345
|
* an empty prefetch queue (i.e. blocked on the upstream) — a large `stallMs`
|
|
337
|
-
* indicates an upstream-bound backfill.
|
|
346
|
+
* indicates an upstream-bound backfill. `waitMs` is time spent waiting
|
|
347
|
+
* (without the reservation) for the change stream to reach the backfill
|
|
348
|
+
* watermark, during which the upstream snapshot is held open.
|
|
338
349
|
*/
|
|
339
350
|
#logBackfillStats(lc, stats) {
|
|
340
351
|
const elapsed = performance.now() - stats.start;
|
|
@@ -348,6 +359,7 @@ var BackfillManager = class {
|
|
|
348
359
|
pushMs: Math.round(stats.pushMs),
|
|
349
360
|
reserveMs: Math.round(stats.reserveMs),
|
|
350
361
|
stallMs: Math.round(stats.stallMs),
|
|
362
|
+
waitMs: Math.round(stats.waitMs),
|
|
351
363
|
throughputKBps: elapsed > 0 ? Math.round(stats.bytes / 1024 / (elapsed / 1e3)) : 0
|
|
352
364
|
});
|
|
353
365
|
}
|
|
@@ -364,7 +376,7 @@ var BackfillManager = class {
|
|
|
364
376
|
#stopRunningBackfill(reason, instance) {
|
|
365
377
|
const backfill = this.#runningBackfill;
|
|
366
378
|
if (backfill && backfill === (instance ?? backfill)) {
|
|
367
|
-
backfill.
|
|
379
|
+
backfill.stopped.abort(reason);
|
|
368
380
|
this.#runningBackfill = null;
|
|
369
381
|
reason && this.#lc.info?.(`canceling backfill:`, reason);
|
|
370
382
|
}
|
|
@@ -428,7 +440,7 @@ var BackfillManager = class {
|
|
|
428
440
|
name,
|
|
429
441
|
metadata
|
|
430
442
|
},
|
|
431
|
-
columns: backfill
|
|
443
|
+
columns: Object.fromEntries(Object.entries(backfill).map(([col, id]) => [col, { id }]))
|
|
432
444
|
});
|
|
433
445
|
break;
|
|
434
446
|
}
|
|
@@ -467,7 +479,7 @@ var BackfillManager = class {
|
|
|
467
479
|
...table,
|
|
468
480
|
metadata
|
|
469
481
|
},
|
|
470
|
-
columns: { [column.name]: backfill }
|
|
482
|
+
columns: { [column.name]: { id: backfill } }
|
|
471
483
|
});
|
|
472
484
|
else this.#setRequiredBackfill(tag, {
|
|
473
485
|
...backfillRequest,
|
|
@@ -477,7 +489,7 @@ var BackfillManager = class {
|
|
|
477
489
|
},
|
|
478
490
|
columns: {
|
|
479
491
|
...backfillRequest.columns,
|
|
480
|
-
[column.name]: backfill
|
|
492
|
+
[column.name]: { id: backfill }
|
|
481
493
|
}
|
|
482
494
|
});
|
|
483
495
|
}
|