@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
|
@@ -15,13 +15,18 @@ import { resolveSimpleScalarSubqueries } from "../../../../zqlite/src/resolve-sc
|
|
|
15
15
|
import { randInt } from "../../../../shared/src/rand.js";
|
|
16
16
|
import { createSQLiteCostModel } from "../../../../zqlite/src/sqlite-cost-model.js";
|
|
17
17
|
import { reloadPermissionsIfChanged } from "../../auth/load-permissions.js";
|
|
18
|
+
import { LogThrottle } from "../../observability/log-throttle.js";
|
|
18
19
|
import { getOrCreateCounter, getOrCreateLatencyHistogram } from "../../observability/metrics.js";
|
|
19
20
|
import "../../types/row-key.js";
|
|
20
21
|
import { getSubscriptionState } from "../replicator/schema/replication-state.js";
|
|
21
22
|
import { checkClientSchema } from "./client-schema.js";
|
|
23
|
+
import { planWarningMessage } from "./plan-warnings.js";
|
|
24
|
+
import { queryShape } from "./query-shape.js";
|
|
22
25
|
import { rowIDSignatureUnit } from "./row-set-signature.js";
|
|
23
26
|
import { ResetPipelinesSignal } from "./snapshotter.js";
|
|
24
27
|
//#region ../zero-cache/src/services/view-syncer/pipeline-driver.ts
|
|
28
|
+
/** The most queries a log of an advancement timeout lists. */
|
|
29
|
+
var ADVANCE_TIMEOUT_LOG_MAX_QUERIES = 3;
|
|
25
30
|
/**
|
|
26
31
|
* No matter how fast hydration is, advancement is given at least this long to
|
|
27
32
|
* complete before doing a pipeline reset.
|
|
@@ -34,6 +39,8 @@ var MIN_PROJECTED_ADVANCEMENT_SAMPLE_MS = 5;
|
|
|
34
39
|
var MIN_PROJECTED_ADVANCEMENT_CHANGES = 16;
|
|
35
40
|
var PROJECTED_ADVANCEMENT_RESET_MULTIPLIER = 1.5;
|
|
36
41
|
var LATE_ADVANCEMENT_FINISH_PROGRESS = .8;
|
|
42
|
+
var planWarningLogThrottle = new LogThrottle({ windowMs: 60 * 6e4 });
|
|
43
|
+
var slowAdvanceLogThrottle = new LogThrottle({ windowMs: 5 * 6e4 });
|
|
37
44
|
function randomID() {
|
|
38
45
|
return randInt(1, Number.MAX_SAFE_INTEGER).toString(36);
|
|
39
46
|
}
|
|
@@ -78,9 +85,11 @@ var PipelineDriver = class {
|
|
|
78
85
|
#shardID;
|
|
79
86
|
#logConfig;
|
|
80
87
|
#config;
|
|
88
|
+
#deferredWrites;
|
|
81
89
|
#tableSpecs = /* @__PURE__ */ new Map();
|
|
82
90
|
#allTableNames = /* @__PURE__ */ new Set();
|
|
83
91
|
#costModels;
|
|
92
|
+
#planWarningThresholds;
|
|
84
93
|
#yieldThresholdMs;
|
|
85
94
|
#streamer = null;
|
|
86
95
|
#hydrateContext = null;
|
|
@@ -90,17 +99,34 @@ var PipelineDriver = class {
|
|
|
90
99
|
#permissions = null;
|
|
91
100
|
#advanceTime = getOrCreateLatencyHistogram("sync", "ivm.advance-time", "Time to advance all queries for a given client group in response to a single change.");
|
|
92
101
|
#conflictRowsDeleted = getOrCreateCounter("sync", "ivm.conflict-rows-deleted", "Number of rows deleted because they conflicted with added row");
|
|
102
|
+
#deferredWritesFallbacks = getOrCreateCounter("sync", "ivm.deferred-writes-fallbacks", "Number of advancements written through to the replica snapshot because their changes did not fit in the deferred IVM writes budget, from the start or partway (because of the bytes held, a change that needed more rows than were left, or more rows held than reserved)");
|
|
93
103
|
#inspectorDelegate;
|
|
94
|
-
|
|
104
|
+
#queryStats;
|
|
105
|
+
/**
|
|
106
|
+
* Passes the pipelines' metrics on to the inspector, and accounts the time
|
|
107
|
+
* spent pushing to each query to the advancement in progress.
|
|
108
|
+
*/
|
|
109
|
+
#metricsDelegate = { addMetric: (metric, value, ...args) => {
|
|
110
|
+
this.#inspectorDelegate.addMetric(metric, value, ...args);
|
|
111
|
+
if (metric === "query-update-server") this.#recordAdvancePush(args[0], value);
|
|
112
|
+
} };
|
|
113
|
+
constructor(lc, logConfig, snapshotter, shardID, storage, clientGroupID, inspectorDelegate, yieldThresholdMs, enablePlanner, config, deferredWrites, queryStats) {
|
|
95
114
|
this.#lc = lc.withContext("clientGroupID", clientGroupID);
|
|
96
115
|
this.#snapshotter = snapshotter;
|
|
97
116
|
this.#storage = storage;
|
|
98
117
|
this.#shardID = shardID;
|
|
99
118
|
this.#logConfig = logConfig;
|
|
100
119
|
this.#config = config;
|
|
120
|
+
this.#deferredWrites = deferredWrites;
|
|
101
121
|
this.#inspectorDelegate = inspectorDelegate;
|
|
102
122
|
this.#costModels = enablePlanner ? /* @__PURE__ */ new WeakMap() : void 0;
|
|
123
|
+
const planWarningThresholds = {
|
|
124
|
+
rows: logConfig.planWarningRowThreshold,
|
|
125
|
+
cost: logConfig.planWarningCostThreshold
|
|
126
|
+
};
|
|
127
|
+
this.#planWarningThresholds = planWarningThresholds.rows > 0 || planWarningThresholds.cost > 0 ? planWarningThresholds : void 0;
|
|
103
128
|
this.#yieldThresholdMs = yieldThresholdMs;
|
|
129
|
+
this.#queryStats = queryStats;
|
|
104
130
|
}
|
|
105
131
|
/**
|
|
106
132
|
* Initializes the PipelineDriver to the current head of the database.
|
|
@@ -219,18 +245,76 @@ var PipelineDriver = class {
|
|
|
219
245
|
queries() {
|
|
220
246
|
return this.#pipelines;
|
|
221
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* Stats from the hydration of the pipeline for `queryID`, or `undefined` if
|
|
250
|
+
* the query has no pipeline.
|
|
251
|
+
*/
|
|
252
|
+
hydrationStats(queryID) {
|
|
253
|
+
const pipeline = this.#pipelines.get(queryID);
|
|
254
|
+
return pipeline ? {
|
|
255
|
+
rowCount: pipeline.hydrationRowCount,
|
|
256
|
+
rowsRead: pipeline.hydrationRowsRead,
|
|
257
|
+
planWarnings: pipeline.planWarnings,
|
|
258
|
+
shape: pipeline.shape
|
|
259
|
+
} : void 0;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Logs what the planner warned about the plan it chose for `query`, at
|
|
263
|
+
* most once per query shape per {@link PLAN_WARNING_LOG_WINDOW_MS}.
|
|
264
|
+
*/
|
|
265
|
+
#logPlanWarnings(shape, { queryHash, transformationHash, queryName }, warnings) {
|
|
266
|
+
if (!this.#lc.warn) return;
|
|
267
|
+
const suppressed = planWarningLogThrottle.admit(`${queryName ?? ""}:${shape.hash}`);
|
|
268
|
+
if (suppressed === void 0) return;
|
|
269
|
+
const messages = warnings.map(planWarningMessage);
|
|
270
|
+
this.#lc.warn(`Query plan warning${queryName === void 0 ? "" : ` for ${queryName}`}: ` + messages.join(" "), {
|
|
271
|
+
zeroEvent: "query-plan-warning",
|
|
272
|
+
queryHash,
|
|
273
|
+
transformationHash,
|
|
274
|
+
...queryName !== void 0 && { queryName },
|
|
275
|
+
queryShape: shape.hash,
|
|
276
|
+
warnings,
|
|
277
|
+
...suppressed > 0 && { suppressedSinceLastLog: suppressed },
|
|
278
|
+
zql: shape.zql
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
#totalRowsRead() {
|
|
282
|
+
let total = 0;
|
|
283
|
+
for (const table of this.#tables.values()) total += table.rowsRead;
|
|
284
|
+
return total;
|
|
285
|
+
}
|
|
222
286
|
totalHydrationTimeMs() {
|
|
223
287
|
let total = 0;
|
|
224
288
|
for (const pipeline of this.#pipelines.values()) total += pipeline.hydrationTimeMs;
|
|
225
289
|
return total;
|
|
226
290
|
}
|
|
227
|
-
|
|
291
|
+
/**
|
|
292
|
+
* The rows the sources hold in memory for the advancement in progress.
|
|
293
|
+
* The advancement's reservation from the {@link DeferredWritesBudget}
|
|
294
|
+
* bounds this.
|
|
295
|
+
*/
|
|
296
|
+
get pendingRows() {
|
|
297
|
+
let total = 0;
|
|
298
|
+
for (const source of this.#tables.values()) total += source.pendingRows;
|
|
299
|
+
return total;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* The estimated bytes of {@link pendingRows}, which are added to the
|
|
303
|
+
* {@link DeferredWritesBudget}'s held bytes as the advancement goes.
|
|
304
|
+
*/
|
|
305
|
+
get pendingBytes() {
|
|
306
|
+
let total = 0;
|
|
307
|
+
for (const source of this.#tables.values()) total += source.pendingBytes;
|
|
308
|
+
return total;
|
|
309
|
+
}
|
|
310
|
+
#logQueryPipelineLifecycle({ zeroEvent, pipelineRunID, queryHash, transformationHash, queryName, hydrationReason, stopReason, hydrationTimeMs, hydrationRowCount, hydrationRowsRead, pipelineLifetimeMs }) {
|
|
228
311
|
let lc = this.#lc.withContext("zeroEvent", zeroEvent).withContext("pipelineRunID", pipelineRunID).withContext("queryHash", queryHash).withContext("transformationHash", transformationHash);
|
|
229
312
|
if (queryName !== void 0) lc = lc.withContext("queryName", queryName);
|
|
230
313
|
if (hydrationReason !== void 0) lc = lc.withContext("hydrationReason", hydrationReason);
|
|
231
314
|
if (stopReason !== void 0) lc = lc.withContext("stopReason", stopReason);
|
|
232
315
|
if (hydrationTimeMs !== void 0) lc = lc.withContext("hydrationTimeMs", hydrationTimeMs);
|
|
233
316
|
if (hydrationRowCount !== void 0) lc = lc.withContext("hydrationRowCount", hydrationRowCount);
|
|
317
|
+
if (hydrationRowsRead !== void 0) lc = lc.withContext("hydrationRowsRead", hydrationRowsRead);
|
|
234
318
|
if (pipelineLifetimeMs !== void 0) lc = lc.withContext("pipelineLifetimeMs", pipelineLifetimeMs);
|
|
235
319
|
lc.info?.("query pipeline lifecycle");
|
|
236
320
|
}
|
|
@@ -312,6 +396,7 @@ var PipelineDriver = class {
|
|
|
312
396
|
assert(this.initialized(), "Pipeline driver must be initialized before adding queries");
|
|
313
397
|
this.removeQuery(queryID, "replace-query");
|
|
314
398
|
const pipelineRunID = randomID();
|
|
399
|
+
const shape = queryShape(query);
|
|
315
400
|
this.#logQueryPipelineLifecycle({
|
|
316
401
|
zeroEvent: "query-pipeline-hydrate-start",
|
|
317
402
|
pipelineRunID,
|
|
@@ -324,9 +409,12 @@ var PipelineDriver = class {
|
|
|
324
409
|
const costModel = this.#ensureCostModelExistsIfEnabled(this.#snapshotter.current().db.db);
|
|
325
410
|
assert(this.#advanceContext === null, "Cannot hydrate while advance is in progress");
|
|
326
411
|
this.#hydrateContext = { timer };
|
|
412
|
+
const rowsReadAtStart = this.#totalRowsRead();
|
|
327
413
|
let hydrationFinished = false;
|
|
328
414
|
let hydrationFailed = false;
|
|
329
415
|
let hydrationRowCount = 0;
|
|
416
|
+
let planWarnings = [];
|
|
417
|
+
const planWarningThresholds = this.#planWarningThresholds;
|
|
330
418
|
let builtInputs = [];
|
|
331
419
|
try {
|
|
332
420
|
const { ast: resolvedQuery, companionRows, companions: companionMeta, companionInputs, ignoredScalarHints } = this.#resolveScalarSubqueries(query);
|
|
@@ -339,12 +427,23 @@ var PipelineDriver = class {
|
|
|
339
427
|
enablePlannerAwarePushdown: this.#config?.enablePlannerAwarePushdown !== false,
|
|
340
428
|
getSource: (name) => this.#getSource(name),
|
|
341
429
|
createStorage: () => this.#createStorage(),
|
|
342
|
-
decorateSourceInput: (input, _queryID) => new MeasurePushOperator(new QueryFailureLoggingOperator(this.#lc, input, queryID, transformationHash, queryName), queryID, this.#
|
|
430
|
+
decorateSourceInput: (input, _queryID) => new MeasurePushOperator(new QueryFailureLoggingOperator(this.#lc, input, queryID, transformationHash, queryName), queryID, this.#metricsDelegate, "query-update-server"),
|
|
343
431
|
decorateInput: (input) => input,
|
|
344
432
|
addEdge() {},
|
|
345
|
-
decorateFilterInput: (input) => input
|
|
433
|
+
decorateFilterInput: (input) => input,
|
|
434
|
+
planWarnings: planWarningThresholds && {
|
|
435
|
+
thresholds: planWarningThresholds,
|
|
436
|
+
report: (warnings) => {
|
|
437
|
+
planWarnings = warnings;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
346
440
|
}, queryID, costModel);
|
|
347
441
|
builtInputs.push(input);
|
|
442
|
+
if (planWarnings.length > 0) this.#logPlanWarnings(shape, {
|
|
443
|
+
queryHash: queryID,
|
|
444
|
+
transformationHash,
|
|
445
|
+
queryName
|
|
446
|
+
}, planWarnings);
|
|
348
447
|
const schema = input.getSchema();
|
|
349
448
|
input.setOutput({ push: (change) => this.#streamPushed(queryID, schema, change) });
|
|
350
449
|
for (const change of hydrateInternal(input, queryID, must(this.#primaryKeys), this.#tableSpecs)) {
|
|
@@ -363,6 +462,7 @@ var PipelineDriver = class {
|
|
|
363
462
|
};
|
|
364
463
|
}
|
|
365
464
|
const hydrationTimeMs = timer.totalElapsed();
|
|
465
|
+
const hydrationRowsRead = this.#totalRowsRead() - rowsReadAtStart;
|
|
366
466
|
if (runtimeDebugFlags.trackRowCountsVended) {
|
|
367
467
|
if (hydrationTimeMs > this.#logConfig.slowHydrateThreshold) {
|
|
368
468
|
let totalRowsConsidered = 0;
|
|
@@ -408,16 +508,29 @@ var PipelineDriver = class {
|
|
|
408
508
|
input,
|
|
409
509
|
hydrationTimeMs,
|
|
410
510
|
hydrationRowCount,
|
|
511
|
+
hydrationRowsRead,
|
|
512
|
+
planWarnings,
|
|
411
513
|
hydrationReason,
|
|
412
514
|
pipelineRunID,
|
|
413
515
|
pipelineReadyAtMs,
|
|
414
516
|
transformedAst: resolvedQuery,
|
|
415
517
|
originalAst: query,
|
|
518
|
+
shape,
|
|
416
519
|
transformationHash,
|
|
417
520
|
...queryName !== void 0 && { queryName },
|
|
418
521
|
companions: liveCompanions
|
|
419
522
|
});
|
|
420
523
|
hydrationFinished = true;
|
|
524
|
+
this.#queryStats?.recordHydration({
|
|
525
|
+
queryName,
|
|
526
|
+
shape
|
|
527
|
+
}, {
|
|
528
|
+
outcome: "finished",
|
|
529
|
+
timeMs: hydrationTimeMs,
|
|
530
|
+
rowCount: hydrationRowCount,
|
|
531
|
+
rowsRead: hydrationRowsRead,
|
|
532
|
+
planWarnings
|
|
533
|
+
});
|
|
421
534
|
this.#logQueryPipelineLifecycle({
|
|
422
535
|
zeroEvent: "query-pipeline-hydrate-finish",
|
|
423
536
|
pipelineRunID,
|
|
@@ -426,10 +539,18 @@ var PipelineDriver = class {
|
|
|
426
539
|
queryName,
|
|
427
540
|
hydrationReason,
|
|
428
541
|
hydrationTimeMs,
|
|
429
|
-
hydrationRowCount
|
|
542
|
+
hydrationRowCount,
|
|
543
|
+
hydrationRowsRead
|
|
430
544
|
});
|
|
431
545
|
} catch (e) {
|
|
432
546
|
hydrationFailed = true;
|
|
547
|
+
this.#queryStats?.recordHydration({
|
|
548
|
+
queryName,
|
|
549
|
+
shape
|
|
550
|
+
}, {
|
|
551
|
+
outcome: "failed",
|
|
552
|
+
timeMs: timer.totalElapsed()
|
|
553
|
+
});
|
|
433
554
|
this.#logQueryPipelineLifecycle({
|
|
434
555
|
zeroEvent: "query-pipeline-hydrate-failed",
|
|
435
556
|
pipelineRunID,
|
|
@@ -447,16 +568,25 @@ var PipelineDriver = class {
|
|
|
447
568
|
}, "query hydration failed", e);
|
|
448
569
|
throw e;
|
|
449
570
|
} finally {
|
|
450
|
-
if (!hydrationFinished && !hydrationFailed)
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
571
|
+
if (!hydrationFinished && !hydrationFailed) {
|
|
572
|
+
this.#queryStats?.recordHydration({
|
|
573
|
+
queryName,
|
|
574
|
+
shape
|
|
575
|
+
}, {
|
|
576
|
+
outcome: "aborted",
|
|
577
|
+
timeMs: timer.totalElapsed()
|
|
578
|
+
});
|
|
579
|
+
this.#logQueryPipelineLifecycle({
|
|
580
|
+
zeroEvent: "query-pipeline-hydrate-aborted",
|
|
581
|
+
pipelineRunID,
|
|
582
|
+
queryHash: queryID,
|
|
583
|
+
transformationHash,
|
|
584
|
+
queryName,
|
|
585
|
+
hydrationReason,
|
|
586
|
+
hydrationTimeMs: timer.totalElapsed(),
|
|
587
|
+
hydrationRowCount
|
|
588
|
+
});
|
|
589
|
+
}
|
|
460
590
|
if (!hydrationFinished) {
|
|
461
591
|
for (const input of builtInputs) input.destroy();
|
|
462
592
|
this.#pruneUnusedTables();
|
|
@@ -559,25 +689,38 @@ var PipelineDriver = class {
|
|
|
559
689
|
*#advance(diff, timer, numChanges) {
|
|
560
690
|
assert(this.#hydrateContext === null, "Cannot advance while hydration is in progress");
|
|
561
691
|
const totalHydrationTimeMs = this.totalHydrationTimeMs();
|
|
562
|
-
|
|
692
|
+
const advanceContext = {
|
|
563
693
|
timer,
|
|
564
694
|
totalHydrationTimeMs,
|
|
565
695
|
numChanges,
|
|
696
|
+
reservedRows: void 0,
|
|
697
|
+
heldBytes: 0,
|
|
566
698
|
currentChangeStartMs: void 0,
|
|
567
|
-
pos: 0
|
|
699
|
+
pos: 0,
|
|
700
|
+
currentTable: void 0,
|
|
701
|
+
queryStats: /* @__PURE__ */ new Map()
|
|
568
702
|
};
|
|
569
|
-
this.#
|
|
703
|
+
this.#advanceContext = advanceContext;
|
|
704
|
+
let timedOut = false;
|
|
570
705
|
try {
|
|
571
|
-
|
|
706
|
+
advanceContext.reservedRows = this.#reserveDeferredWrites(numChanges);
|
|
707
|
+
const deferWrites = advanceContext.reservedRows !== void 0;
|
|
708
|
+
if (deferWrites) diff.setPrevWrites("none");
|
|
709
|
+
for (const table of this.#tables.values()) table.setDeferWrites(deferWrites);
|
|
710
|
+
this.#lc.debug?.(`starting pipeline advancement of ${numChanges} changes with an advancement time limited based on total hydration time of ${totalHydrationTimeMs} ms (${deferWrites ? "deferred" : "write-through"}).`);
|
|
711
|
+
for (const { table, prevValues: probedPrevValues, nextValue, rowKey } of diff) {
|
|
572
712
|
if (this.#shouldAdvanceYieldMaybeAbortAdvance()) yield "yield";
|
|
573
713
|
const start = timer.totalElapsed();
|
|
574
|
-
const advanceContext = must(this.#advanceContext);
|
|
575
714
|
advanceContext.currentChangeStartMs = start;
|
|
715
|
+
advanceContext.currentTable = table;
|
|
716
|
+
let holds = "fits";
|
|
576
717
|
try {
|
|
577
718
|
try {
|
|
578
719
|
const tableSource = this.#tables.get(table);
|
|
579
720
|
if (!tableSource) continue;
|
|
580
721
|
const primaryKey = mustGetPrimaryKey(this.#primaryKeys, table);
|
|
722
|
+
if (nextValue !== null && !this.#reserveSecondRow(advanceContext, rowKey, primaryKey)) holds = "rows";
|
|
723
|
+
const prevValues = tableSource.reconcilePendingConflicts(probedPrevValues, nextValue, rowKey, this.#tableSpecs.get(table)?.tableSpec.uniqueKeys ?? []);
|
|
581
724
|
let editOldRow = void 0;
|
|
582
725
|
for (const prevValue of prevValues) if (nextValue && deepEqual(getRowKey(primaryKey, prevValue), getRowKey(primaryKey, nextValue))) editOldRow = prevValue;
|
|
583
726
|
else {
|
|
@@ -590,20 +733,188 @@ var PipelineDriver = class {
|
|
|
590
733
|
advanceContext.pos++;
|
|
591
734
|
}
|
|
592
735
|
this.#shouldAdvanceYieldMaybeAbortAdvance(false);
|
|
736
|
+
if (holds === "fits") holds = this.#holdPendingRows(advanceContext);
|
|
593
737
|
} finally {
|
|
594
738
|
advanceContext.currentChangeStartMs = void 0;
|
|
595
739
|
}
|
|
596
740
|
const elapsed = timer.totalElapsed() - start;
|
|
597
741
|
this.#advanceTime.recordMs(elapsed, { table });
|
|
742
|
+
if (holds !== "fits") yield* this.#writeThrough(advanceContext, diff, holds);
|
|
598
743
|
}
|
|
599
744
|
const { curr } = diff;
|
|
600
745
|
for (const table of this.#tables.values()) table.setDB(curr.db.db);
|
|
601
746
|
this.#ensureCostModelExistsIfEnabled(curr.db.db);
|
|
602
747
|
this.#lc.debug?.(`Advanced to ${curr.version}`);
|
|
748
|
+
this.#logSlowAdvances(advanceContext);
|
|
749
|
+
} catch (e) {
|
|
750
|
+
if (e instanceof ResetPipelinesSignal && e.reason === "advancement-timeout") {
|
|
751
|
+
timedOut = true;
|
|
752
|
+
this.#logAdvanceTimeout(advanceContext, e);
|
|
753
|
+
}
|
|
754
|
+
throw e;
|
|
603
755
|
} finally {
|
|
756
|
+
if (advanceContext.reservedRows !== void 0) for (const table of this.#tables.values()) table.discardPendingChanges();
|
|
757
|
+
this.#releaseDeferredWrites(advanceContext);
|
|
604
758
|
this.#advanceContext = null;
|
|
759
|
+
this.#recordAdvanceStats(advanceContext, timedOut);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
/** Accounts the time each query took in an advancement to its shape. */
|
|
763
|
+
#recordAdvanceStats({ queryStats }, timedOut) {
|
|
764
|
+
if (this.#queryStats === void 0) return;
|
|
765
|
+
for (const [queryID, { timeMs, changes }] of queryStats) {
|
|
766
|
+
const pipeline = this.#pipelines.get(queryID);
|
|
767
|
+
if (pipeline !== void 0) this.#queryStats.recordAdvance(pipeline, {
|
|
768
|
+
timeMs,
|
|
769
|
+
changes,
|
|
770
|
+
timedOut
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Decides how the sources apply an advancement's changes: in memory if
|
|
776
|
+
* they fit in the worker's {@link DeferredWritesBudget}, and otherwise
|
|
777
|
+
* written through to the `prev` snapshot. Returns the number of rows
|
|
778
|
+
* reserved if they are held in memory.
|
|
779
|
+
*/
|
|
780
|
+
#reserveDeferredWrites(numChanges) {
|
|
781
|
+
const budget = this.#deferredWrites;
|
|
782
|
+
if (!budget) return;
|
|
783
|
+
if (budget.tryReserve(numChanges)) return numChanges;
|
|
784
|
+
this.#deferredWritesFallbacks.add(1, { stage: "start" });
|
|
785
|
+
this.#lc.debug?.(`writing through ${numChanges} changes: ${budget.reservedRows} rows of the deferred writes budget are reserved`);
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* If the change log identifies the rows of a table by a key other than its
|
|
789
|
+
* primary key here, an entry that sets a row can change its primary key,
|
|
790
|
+
* which is two rows to a source: the old and the new. (An entry that removes
|
|
791
|
+
* a row removes the one row its key identifies.) Reserves the second row for
|
|
792
|
+
* such an entry, if the advancement holds its changes in memory, and
|
|
793
|
+
* returns whether it fit.
|
|
794
|
+
*/
|
|
795
|
+
#reserveSecondRow(advanceContext, rowKey, primaryKey) {
|
|
796
|
+
if (advanceContext.reservedRows === void 0 || isKeyedBy(rowKey, primaryKey)) return true;
|
|
797
|
+
if (!must(this.#deferredWrites).tryReserveMore(1)) return false;
|
|
798
|
+
advanceContext.reservedRows++;
|
|
799
|
+
return true;
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* Writes the changes that an advancement holds in memory through to the
|
|
803
|
+
* `prev` snapshot, and the rest of its changes after them, when they no
|
|
804
|
+
* longer fit (see {@link HeldRows}). The advancement then continues as if
|
|
805
|
+
* it had written through from the start.
|
|
806
|
+
*/
|
|
807
|
+
*#writeThrough(advanceContext, diff, reason) {
|
|
808
|
+
const budget = must(this.#deferredWrites);
|
|
809
|
+
this.#deferredWritesFallbacks.add(1, {
|
|
810
|
+
stage: "partway",
|
|
811
|
+
reason
|
|
812
|
+
});
|
|
813
|
+
this.#lc.debug?.(`writing through (${reason}) at ${advanceContext.pos} of ${advanceContext.numChanges} changes, holding ${advanceContext.heldBytes} of the ${budget.heldBytes} estimated bytes held on this worker (budget: ${budget.maxBytes})`);
|
|
814
|
+
diff.setPrevWrites("divergent");
|
|
815
|
+
for (const source of this.#tables.values()) yield* source.writePendingChanges();
|
|
816
|
+
this.#releaseDeferredWrites(advanceContext);
|
|
817
|
+
}
|
|
818
|
+
#releaseDeferredWrites(advanceContext) {
|
|
819
|
+
const { reservedRows, heldBytes } = advanceContext;
|
|
820
|
+
if (reservedRows !== void 0) {
|
|
821
|
+
must(this.#deferredWrites).release(reservedRows, heldBytes);
|
|
822
|
+
advanceContext.reservedRows = void 0;
|
|
823
|
+
advanceContext.heldBytes = 0;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
#recordAdvancePush(queryID, timeMs) {
|
|
827
|
+
const advance = this.#advanceContext;
|
|
828
|
+
if (advance === null) return;
|
|
829
|
+
const stats = getOrInsertComputed(advance.queryStats, queryID, () => ({
|
|
830
|
+
timeMs: 0,
|
|
831
|
+
changes: 0,
|
|
832
|
+
lastPos: -1,
|
|
833
|
+
timeMsByTable: /* @__PURE__ */ new Map()
|
|
834
|
+
}));
|
|
835
|
+
stats.timeMs += timeMs;
|
|
836
|
+
if (stats.lastPos !== advance.pos) {
|
|
837
|
+
stats.lastPos = advance.pos;
|
|
838
|
+
stats.changes++;
|
|
839
|
+
}
|
|
840
|
+
const table = advance.currentTable;
|
|
841
|
+
if (table !== void 0) stats.timeMsByTable.set(table, (stats.timeMsByTable.get(table) ?? 0) + timeMs);
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* The identity of the query of the pipeline for `queryID` and its
|
|
845
|
+
* {@link queryShape}, for logging.
|
|
846
|
+
*/
|
|
847
|
+
#queryForLog(queryID) {
|
|
848
|
+
const pipeline = this.#pipelines.get(queryID);
|
|
849
|
+
if (pipeline === void 0) return;
|
|
850
|
+
const { transformationHash, queryName, shape } = pipeline;
|
|
851
|
+
return {
|
|
852
|
+
queryName,
|
|
853
|
+
shape,
|
|
854
|
+
fields: {
|
|
855
|
+
queryHash: queryID,
|
|
856
|
+
transformationHash,
|
|
857
|
+
...queryName !== void 0 && { queryName },
|
|
858
|
+
queryShape: shape.hash
|
|
859
|
+
}
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Logs each query whose pushes took longer than the slow advance
|
|
864
|
+
* threshold, at most once per query shape per
|
|
865
|
+
* {@link SLOW_ADVANCE_LOG_WINDOW_MS}.
|
|
866
|
+
*/
|
|
867
|
+
#logSlowAdvances({ queryStats, numChanges }) {
|
|
868
|
+
if (!this.#lc.warn) return;
|
|
869
|
+
for (const [queryID, stats] of queryStats) {
|
|
870
|
+
if (stats.timeMs <= this.#logConfig.slowAdvanceThreshold) continue;
|
|
871
|
+
const query = this.#queryForLog(queryID);
|
|
872
|
+
if (query === void 0) continue;
|
|
873
|
+
const suppressed = slowAdvanceLogThrottle.admit(`${query.queryName ?? ""}:${query.shape.hash}`);
|
|
874
|
+
if (suppressed === void 0) continue;
|
|
875
|
+
this.#lc.warn(`Slow query advancement${query.queryName === void 0 ? "" : ` for ${query.queryName}`}: ${Math.round(stats.timeMs)} ms for ${stats.changes} of ${numChanges} changes`, {
|
|
876
|
+
zeroEvent: "query-slow-advance",
|
|
877
|
+
...query.fields,
|
|
878
|
+
advanceTimeMs: stats.timeMs,
|
|
879
|
+
changes: stats.changes,
|
|
880
|
+
advancementChanges: numChanges,
|
|
881
|
+
timeMsByTable: Object.fromEntries(stats.timeMsByTable),
|
|
882
|
+
...suppressed > 0 && { suppressedSinceLastLog: suppressed },
|
|
883
|
+
zql: query.shape.zql
|
|
884
|
+
});
|
|
605
885
|
}
|
|
606
886
|
}
|
|
887
|
+
/**
|
|
888
|
+
* Logs the queries that took the most time in an advancement that timed
|
|
889
|
+
* out, since the reset that follows would otherwise not say what was slow.
|
|
890
|
+
* Throttled per query shape of the slowest query.
|
|
891
|
+
*/
|
|
892
|
+
#logAdvanceTimeout({ queryStats, numChanges }, reset) {
|
|
893
|
+
if (!this.#lc.warn) return;
|
|
894
|
+
const slowest = [...queryStats].toSorted(([, a], [, b]) => b.timeMs - a.timeMs).slice(0, ADVANCE_TIMEOUT_LOG_MAX_QUERIES).flatMap(([queryID, stats]) => {
|
|
895
|
+
const query = this.#queryForLog(queryID);
|
|
896
|
+
return query ? [{
|
|
897
|
+
query,
|
|
898
|
+
stats
|
|
899
|
+
}] : [];
|
|
900
|
+
});
|
|
901
|
+
if (slowest.length === 0) return;
|
|
902
|
+
const suppressed = slowAdvanceLogThrottle.admit(`timeout:${slowest[0].query.queryName ?? ""}:${slowest[0].query.shape.hash}`);
|
|
903
|
+
if (suppressed === void 0) return;
|
|
904
|
+
const summary = slowest.map(({ query, stats }) => `${query.queryName ?? query.fields.queryHash} (${Math.round(stats.timeMs)} ms)`).join(", ");
|
|
905
|
+
this.#lc.warn(`Advancement of ${numChanges} changes timed out. The queries that took the most time: ${summary}`, {
|
|
906
|
+
zeroEvent: "query-advance-timeout",
|
|
907
|
+
reason: reset.message,
|
|
908
|
+
advancementChanges: numChanges,
|
|
909
|
+
queries: slowest.map(({ query, stats }) => ({
|
|
910
|
+
...query.fields,
|
|
911
|
+
advanceTimeMs: stats.timeMs,
|
|
912
|
+
changes: stats.changes,
|
|
913
|
+
zql: query.shape.zql
|
|
914
|
+
})),
|
|
915
|
+
...suppressed > 0 && { suppressedSinceLastLog: suppressed }
|
|
916
|
+
});
|
|
917
|
+
}
|
|
607
918
|
/** Implements `BuilderDelegate.getSource()` */
|
|
608
919
|
#getSource(tableName) {
|
|
609
920
|
return getOrInsertComputed(this.#tables, tableName, (tableName) => {
|
|
@@ -637,6 +948,28 @@ var PipelineDriver = class {
|
|
|
637
948
|
if (!shouldFinish && elapsed > MIN_ADVANCEMENT_TIME_LIMIT_MS && (elapsed > totalHydrationTimeMs || elapsed > totalHydrationTimeMs / 2 && pos <= numChanges / 2)) throw new ResetPipelinesSignal(`Advancement exceeded timeout at ${pos} of ${numChanges} changes after ${elapsed} ms. Advancement time limited based on total hydration time of ${totalHydrationTimeMs} ms.`, "advancement-timeout");
|
|
638
949
|
return checkYield && advanceTimer.elapsedLap() > this.#yieldThresholdMs();
|
|
639
950
|
}
|
|
951
|
+
/**
|
|
952
|
+
* The rows an advancement holds in memory are bounded by its reservation,
|
|
953
|
+
* but their width is not known in advance. So the bytes they are estimated
|
|
954
|
+
* to hold are added to those held by the other advancements on this worker.
|
|
955
|
+
* Says whether that takes them past the budget, or if the rows are not
|
|
956
|
+
* bounded by the reservation after all.
|
|
957
|
+
*/
|
|
958
|
+
#holdPendingRows(advanceContext) {
|
|
959
|
+
const { reservedRows, heldBytes, pos, numChanges } = advanceContext;
|
|
960
|
+
if (reservedRows === void 0) return "fits";
|
|
961
|
+
const budget = must(this.#deferredWrites);
|
|
962
|
+
const rows = this.pendingRows;
|
|
963
|
+
const bytes = this.pendingBytes;
|
|
964
|
+
advanceContext.heldBytes = bytes;
|
|
965
|
+
const bytesFit = budget.holdBytes(bytes - heldBytes);
|
|
966
|
+
if (rows > reservedRows) {
|
|
967
|
+
budget.recordRowOverrun();
|
|
968
|
+
this.#lc.error?.(`Advancement holds ${rows} rows at ${pos} of ${numChanges} changes, more than the ${reservedRows} it reserved. Writing through the rest.`);
|
|
969
|
+
return "row-overrun";
|
|
970
|
+
}
|
|
971
|
+
return bytesFit ? "fits" : "bytes";
|
|
972
|
+
}
|
|
640
973
|
#throwSlowCurrentChangeReset(pos, numChanges, elapsed, currentChangeElapsedMs, totalHydrationTimeMs) {
|
|
641
974
|
throw new ResetPipelinesSignal(`Advancement exceeded timeout processing current change at ${pos} of ${numChanges} changes after ${currentChangeElapsedMs} ms (${elapsed} ms total). Advancement time limited based on total hydration time of ${totalHydrationTimeMs} ms.`, "advancement-timeout");
|
|
642
975
|
}
|
|
@@ -875,6 +1208,15 @@ function* toAdds(nodes) {
|
|
|
875
1208
|
function getRowKey(cols, row) {
|
|
876
1209
|
return Object.fromEntries(cols.map((col) => [col, must(row[col])]));
|
|
877
1210
|
}
|
|
1211
|
+
/** Whether `rowKey` has exactly the columns of `primaryKey`. */
|
|
1212
|
+
function isKeyedBy(rowKey, primaryKey) {
|
|
1213
|
+
let cols = 0;
|
|
1214
|
+
for (const col in rowKey) {
|
|
1215
|
+
if (!primaryKey.includes(col)) return false;
|
|
1216
|
+
cols++;
|
|
1217
|
+
}
|
|
1218
|
+
return cols === primaryKey.length;
|
|
1219
|
+
}
|
|
878
1220
|
/**
|
|
879
1221
|
* Core hydration logic used by {@link PipelineDriver#addQuery}, extracted to a
|
|
880
1222
|
* function for reuse by the analyze-query RPC path so that analysis hydrates
|