@rocicorp/zero 1.4.0 → 1.5.0-canary.1
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/zero/package.js +1 -1
- package/out/zero/package.js.map +1 -1
- package/out/zero-cache/src/auth/auth.d.ts +1 -1
- package/out/zero-cache/src/auth/auth.d.ts.map +1 -1
- package/out/zero-cache/src/auth/auth.js +1 -1
- package/out/zero-cache/src/auth/auth.js.map +1 -1
- package/out/zero-cache/src/auth/write-authorizer.d.ts +1 -1
- package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
- package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
- package/out/zero-cache/src/config/normalize.d.ts.map +1 -1
- package/out/zero-cache/src/config/normalize.js +8 -0
- package/out/zero-cache/src/config/normalize.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +8 -4
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +28 -6
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/custom/fetch.d.ts +1 -1
- package/out/zero-cache/src/custom/fetch.d.ts.map +1 -1
- package/out/zero-cache/src/custom/fetch.js +2 -2
- package/out/zero-cache/src/custom/fetch.js.map +1 -1
- package/out/zero-cache/src/custom-queries/transform-query.d.ts +21 -7
- package/out/zero-cache/src/custom-queries/transform-query.d.ts.map +1 -1
- package/out/zero-cache/src/custom-queries/transform-query.js +26 -9
- package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
- package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +2 -1
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/runner/run-worker.d.ts.map +1 -1
- package/out/zero-cache/src/server/runner/run-worker.js +5 -2
- package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
- package/out/zero-cache/src/server/syncer.js +3 -3
- package/out/zero-cache/src/server/syncer.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/change-source.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.js +24 -20
- package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts +258 -45
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +119 -83
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js +2 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +1 -0
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +3 -3
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
- package/out/zero-cache/src/services/http-service.d.ts +1 -0
- package/out/zero-cache/src/services/http-service.d.ts.map +1 -1
- package/out/zero-cache/src/services/http-service.js +5 -4
- package/out/zero-cache/src/services/http-service.js.map +1 -1
- package/out/zero-cache/src/services/life-cycle.d.ts +1 -1
- package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
- package/out/zero-cache/src/services/life-cycle.js +1 -2
- package/out/zero-cache/src/services/life-cycle.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.d.ts +4 -3
- package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.js +57 -38
- package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
- package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js +2 -1
- package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.js +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.d.ts +41 -27
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.js +147 -104
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts +6 -0
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js +8 -0
- package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +3 -3
- 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 +119 -86
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/workers/connection.js +2 -2
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js +7 -7
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.d.ts +1 -1
- package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/workers/syncer.js +11 -10
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/connection.d.ts +15 -7
- package/out/zero-client/src/client/connection.d.ts.map +1 -1
- package/out/zero-client/src/client/connection.js.map +1 -1
- package/out/zero-client/src/client/crud-impl.d.ts +1 -1
- package/out/zero-client/src/client/crud-impl.d.ts.map +1 -1
- package/out/zero-client/src/client/crud-impl.js +1 -1
- package/out/zero-client/src/client/crud-impl.js.map +1 -1
- package/out/zero-client/src/client/crud.d.ts +1 -1
- package/out/zero-client/src/client/crud.d.ts.map +1 -1
- package/out/zero-client/src/client/crud.js +1 -1
- package/out/zero-client/src/client/crud.js.map +1 -1
- package/out/zero-client/src/client/keys.d.ts +1 -1
- package/out/zero-client/src/client/keys.d.ts.map +1 -1
- package/out/zero-client/src/client/keys.js.map +1 -1
- package/out/zero-client/src/client/make-replicache-mutators.js +1 -1
- package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
- package/out/zero-client/src/client/mutation-tracker.d.ts +2 -1
- package/out/zero-client/src/client/mutation-tracker.d.ts.map +1 -1
- package/out/zero-client/src/client/mutation-tracker.js +3 -3
- package/out/zero-client/src/client/mutation-tracker.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 +2 -2
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-client/src/types/client-state.d.ts +1 -1
- package/out/zero-client/src/types/client-state.d.ts.map +1 -1
- package/out/zero-protocol/src/custom-queries.js +1 -1
- package/out/zero-protocol/src/down.js +1 -1
- package/out/zero-protocol/src/error-kind-enum.d.ts +1 -2
- package/out/zero-protocol/src/error-kind-enum.d.ts.map +1 -1
- package/out/zero-protocol/src/error-kind-enum.js.map +1 -1
- package/out/zero-protocol/src/mutate-server.d.ts +165 -0
- package/out/zero-protocol/src/mutate-server.d.ts.map +1 -0
- package/out/zero-protocol/src/mutate-server.js +24 -0
- package/out/zero-protocol/src/mutate-server.js.map +1 -0
- package/out/zero-protocol/src/mutation.d.ts +229 -0
- package/out/zero-protocol/src/mutation.d.ts.map +1 -0
- package/out/zero-protocol/src/mutation.js +112 -0
- package/out/zero-protocol/src/mutation.js.map +1 -0
- package/out/zero-protocol/src/mutations-patch.js +1 -1
- package/out/zero-protocol/src/mutations-patch.js.map +1 -1
- package/out/zero-protocol/src/push.d.ts +3 -234
- package/out/zero-protocol/src/push.d.ts.map +1 -1
- package/out/zero-protocol/src/push.js +3 -114
- package/out/zero-protocol/src/push.js.map +1 -1
- package/out/zero-protocol/src/query-server.d.ts +150 -0
- package/out/zero-protocol/src/query-server.d.ts.map +1 -0
- package/out/zero-protocol/src/query-server.js +16 -0
- package/out/zero-protocol/src/query-server.js.map +1 -0
- package/out/zero-protocol/src/up.js +1 -1
- package/out/zero-server/src/mod.d.ts +4 -2
- package/out/zero-server/src/mod.d.ts.map +1 -1
- package/out/zero-server/src/process-mutations.d.ts +50 -4
- package/out/zero-server/src/process-mutations.d.ts.map +1 -1
- package/out/zero-server/src/process-mutations.js +73 -36
- package/out/zero-server/src/process-mutations.js.map +1 -1
- package/out/zero-server/src/push-processor.d.ts +3 -3
- package/out/zero-server/src/push-processor.d.ts.map +1 -1
- package/out/zero-server/src/push-processor.js.map +1 -1
- package/out/zero-server/src/queries/process-queries.d.ts +45 -53
- package/out/zero-server/src/queries/process-queries.d.ts.map +1 -1
- package/out/zero-server/src/queries/process-queries.js +72 -53
- package/out/zero-server/src/queries/process-queries.js.map +1 -1
- package/out/zero-server/src/zql-database.js.map +1 -1
- package/out/zero-types/src/default-types.d.ts +1 -0
- package/out/zero-types/src/default-types.d.ts.map +1 -1
- package/out/zql/src/builder/builder.d.ts.map +1 -1
- package/out/zql/src/builder/builder.js +17 -7
- package/out/zql/src/builder/builder.js.map +1 -1
- package/out/zql/src/ivm/cap.d.ts +32 -0
- package/out/zql/src/ivm/cap.d.ts.map +1 -0
- package/out/zql/src/ivm/cap.js +205 -0
- package/out/zql/src/ivm/cap.js.map +1 -0
- package/out/zql/src/ivm/constraint.js +1 -1
- package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
- package/out/zql/src/ivm/flipped-join.js +61 -15
- package/out/zql/src/ivm/flipped-join.js.map +1 -1
- package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
- package/out/zql/src/ivm/memory-source.js +3 -4
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/ivm/schema.d.ts +8 -0
- package/out/zql/src/ivm/schema.d.ts.map +1 -1
- package/out/zql/src/ivm/take.js +2 -2
- package/out/zql/src/mutate/mutator-registry.js.map +1 -1
- package/out/zql/src/mutate/mutator.d.ts +11 -2
- package/out/zql/src/mutate/mutator.d.ts.map +1 -1
- package/out/zql/src/mutate/mutator.js.map +1 -1
- package/out/zql/src/query/query-registry.d.ts +9 -2
- package/out/zql/src/query/query-registry.d.ts.map +1 -1
- package/out/zql/src/query/query-registry.js.map +1 -1
- package/out/zqlite/src/table-source.d.ts.map +1 -1
- package/out/zqlite/src/table-source.js +4 -1
- package/out/zqlite/src/table-source.js.map +1 -1
- package/package.json +1 -1
|
@@ -11,6 +11,7 @@ import { transformAndHashQuery } from "../../auth/read-authorizer.js";
|
|
|
11
11
|
import { getOrCreateCounter, getOrCreateLatencyHistogram, getOrCreateUpDownCounter } from "../../observability/metrics.js";
|
|
12
12
|
import { rowIDString } from "../../types/row-key.js";
|
|
13
13
|
import "../replicator/schema/replication-state.js";
|
|
14
|
+
import { parseSignature } from "./row-set-signature.js";
|
|
14
15
|
import { ResetPipelinesSignal } from "./snapshotter.js";
|
|
15
16
|
import "./pipeline-driver.js";
|
|
16
17
|
import { randInt } from "../../../../shared/src/rand.js";
|
|
@@ -41,7 +42,7 @@ function randomID() {
|
|
|
41
42
|
var TTL_CLOCK_INTERVAL = 6e4;
|
|
42
43
|
var ViewSyncerService = class {
|
|
43
44
|
id;
|
|
44
|
-
|
|
45
|
+
connContextManager;
|
|
45
46
|
#shard;
|
|
46
47
|
#lc;
|
|
47
48
|
#pipelines;
|
|
@@ -94,13 +95,14 @@ var ViewSyncerService = class {
|
|
|
94
95
|
#queryTransformationNoOps = getOrCreateCounter("sync", "query.transformation-no-ops", "Number of times query transformation resulted in no-op (hash unchanged)");
|
|
95
96
|
#lockWaitTime = getOrCreateLatencyHistogram("sync", "lock-wait-time", "Time spent waiting to acquire the ViewSyncer lock.");
|
|
96
97
|
#pipelineResets = getOrCreateCounter("sync", "pipeline-resets", "Number of pipeline resets");
|
|
98
|
+
#rowSetSignatureDrifts = getOrCreateCounter("sync", "query.row-set-signature-drifts", "Number of times re-hydration of an unchanged query produced a different row-set signature than what is stored in the CVR (forcing a configVersion bump and full re-execution). Expected to be near-zero in steady state; persistent non-zero values indicate non-deterministic query execution (e.g. Cap operator picking different N-row subsets).");
|
|
97
99
|
#inspectorDelegate;
|
|
98
100
|
#config;
|
|
99
101
|
#runPriorityOp;
|
|
100
|
-
constructor(config, lc, shard, taskID, clientGroupID, cvrDb, pipelineDriver, versionChanges, drainCoordinator, slowHydrateThreshold, inspectorDelegate,
|
|
102
|
+
constructor(config, lc, shard, taskID, clientGroupID, cvrDb, pipelineDriver, versionChanges, drainCoordinator, slowHydrateThreshold, inspectorDelegate, connContextManager, customQueryTransformer, runPriorityOp, keepaliveMs = DEFAULT_KEEPALIVE_MS, setTimeoutFn = setTimeout.bind(globalThis)) {
|
|
101
103
|
this.#config = config;
|
|
102
104
|
this.id = clientGroupID;
|
|
103
|
-
this.
|
|
105
|
+
this.connContextManager = connContextManager;
|
|
104
106
|
this.#shard = shard;
|
|
105
107
|
this.#lc = lc;
|
|
106
108
|
this.#pipelines = pipelineDriver;
|
|
@@ -189,6 +191,7 @@ var ViewSyncerService = class {
|
|
|
189
191
|
this.#pipelineResets.add(1, { reason: result.reason });
|
|
190
192
|
this.#pipelines.reset(clientSchema);
|
|
191
193
|
this.#pipelinesSynced = false;
|
|
194
|
+
this.connContextManager.setSharedRetransformReady(false);
|
|
192
195
|
}
|
|
193
196
|
const version = this.#pipelines.advanceWithoutDiff();
|
|
194
197
|
const cvrVer = versionString(cvr.version);
|
|
@@ -197,9 +200,10 @@ var ViewSyncerService = class {
|
|
|
197
200
|
return;
|
|
198
201
|
}
|
|
199
202
|
lc.info?.(`init pipelines@${version} (cvr@${cvrVer})`);
|
|
200
|
-
await this.#hydrateUnchangedQueries(lc, cvr);
|
|
201
|
-
await this.#syncQueryPipelineSet(lc, cvr, "missing", void 0);
|
|
203
|
+
const driftedQueryIDs = await this.#hydrateUnchangedQueries(lc, cvr);
|
|
204
|
+
await this.#syncQueryPipelineSet(lc, cvr, "missing", void 0, driftedQueryIDs);
|
|
202
205
|
this.#pipelinesSynced = true;
|
|
206
|
+
this.connContextManager.setSharedRetransformReady(true);
|
|
203
207
|
});
|
|
204
208
|
}
|
|
205
209
|
if (this.#drainCoordinator.shouldDrain()) this.#drainCoordinator.drainNextIn(this.#totalHydrationTimeMs());
|
|
@@ -262,7 +266,7 @@ var ViewSyncerService = class {
|
|
|
262
266
|
return this.#clients.size === 0;
|
|
263
267
|
}
|
|
264
268
|
#deleteClientDueToDisconnect(clientID, client) {
|
|
265
|
-
this.
|
|
269
|
+
this.connContextManager.closeConnection({
|
|
266
270
|
clientID,
|
|
267
271
|
wsID: client.wsID
|
|
268
272
|
});
|
|
@@ -290,13 +294,10 @@ var ViewSyncerService = class {
|
|
|
290
294
|
* deadlines. The timer plumbing is intentionally separate from the actual
|
|
291
295
|
* revalidation/retransform work so future policy changes only need to update
|
|
292
296
|
* the maintenance workers, not the wakeup logic.
|
|
293
|
-
*
|
|
294
|
-
* This is intentionally cheap & idempotent, so it can be called frequently
|
|
295
|
-
* when upstream state might have changed.
|
|
296
297
|
*/
|
|
297
298
|
#scheduleAuthMaintenance(lc) {
|
|
298
299
|
this.#stopAuthMaintenanceTimer();
|
|
299
|
-
const plan = this.
|
|
300
|
+
const plan = this.connContextManager.planMaintenance();
|
|
300
301
|
if (plan.earliestDeadlineAt === void 0) {
|
|
301
302
|
lc.debug?.("No auth maintenance wakeup scheduled");
|
|
302
303
|
return;
|
|
@@ -306,13 +307,17 @@ var ViewSyncerService = class {
|
|
|
306
307
|
delay,
|
|
307
308
|
earliestDeadlineAt: plan.earliestDeadlineAt
|
|
308
309
|
});
|
|
309
|
-
this.#authMaintenanceTimer = this.#setTimeout(() => {
|
|
310
|
-
|
|
311
|
-
|
|
310
|
+
this.#authMaintenanceTimer = this.#setTimeout(async () => {
|
|
311
|
+
try {
|
|
312
|
+
this.#authMaintenanceTimer = 0;
|
|
313
|
+
await this.#runInLockWithCVR((lc, cvr) => this.#runAuthMaintenance(lc, cvr));
|
|
314
|
+
} catch (e) {
|
|
315
|
+
this.#stateChanges.fail(e instanceof Error ? e : new Error(String(e)));
|
|
316
|
+
}
|
|
312
317
|
}, delay);
|
|
313
318
|
}
|
|
314
319
|
async #runAuthMaintenance(lc, _cvr) {
|
|
315
|
-
const plan = this.
|
|
320
|
+
const plan = this.connContextManager.planMaintenance();
|
|
316
321
|
if (plan.dueRevalidations.length === 0 && !plan.dueRetransform) {
|
|
317
322
|
lc.debug?.("Auth maintenance woke up with no due work");
|
|
318
323
|
return;
|
|
@@ -321,52 +326,52 @@ var ViewSyncerService = class {
|
|
|
321
326
|
dueRevalidations: plan.dueRevalidations.length,
|
|
322
327
|
dueRetransform: plan.dueRetransform
|
|
323
328
|
});
|
|
324
|
-
for (const
|
|
325
|
-
await this.#validateConnection(
|
|
329
|
+
for (const connCtx of plan.dueRevalidations) try {
|
|
330
|
+
await this.#validateConnection(connCtx);
|
|
326
331
|
} catch (e) {
|
|
327
332
|
if (isProtocolError(e) && isTransformFailedError(e)) {
|
|
328
333
|
lc.warn?.("Scheduled auth revalidation failed; deferring auth maintenance", {
|
|
329
|
-
clientID:
|
|
330
|
-
wsID:
|
|
334
|
+
clientID: connCtx.clientID,
|
|
335
|
+
wsID: connCtx.wsID,
|
|
331
336
|
message: e.message
|
|
332
337
|
});
|
|
333
|
-
this.
|
|
338
|
+
this.connContextManager.deferMaintenance("revalidate");
|
|
334
339
|
return;
|
|
335
340
|
}
|
|
336
341
|
throw e;
|
|
337
342
|
}
|
|
338
|
-
if (this.
|
|
343
|
+
if (this.connContextManager.planMaintenance().dueRetransform) await this.#runBackgroundRetransform(lc);
|
|
339
344
|
}
|
|
340
345
|
initConnection(selector, initConnectionMessage) {
|
|
341
346
|
this.#lc.debug?.("viewSyncer.initConnection");
|
|
342
347
|
return startSpan(tracer, "vs.initConnection", () => {
|
|
343
|
-
const
|
|
344
|
-
const lc = this.#lc.withContext("clientID",
|
|
348
|
+
const connCtx = this.connContextManager.mustGetConnectionContext(selector);
|
|
349
|
+
const lc = this.#lc.withContext("clientID", connCtx.clientID).withContext("wsID", connCtx.wsID);
|
|
345
350
|
const downstream = Subscription.create({ cleanup: (_, err) => {
|
|
346
351
|
err ? lc[getLogLevel(err)]?.(`client closed with error`, err) : lc.info?.("client closed");
|
|
347
|
-
this.#deleteClientDueToDisconnect(
|
|
348
|
-
this.#activeClients.add(-1, { [PROTOCOL_VERSION_ATTR]:
|
|
352
|
+
this.#deleteClientDueToDisconnect(connCtx.clientID, newClient);
|
|
353
|
+
this.#activeClients.add(-1, { [PROTOCOL_VERSION_ATTR]: connCtx.protocolVersion });
|
|
349
354
|
} });
|
|
350
|
-
this.#activeClients.add(1, { [PROTOCOL_VERSION_ATTR]:
|
|
355
|
+
this.#activeClients.add(1, { [PROTOCOL_VERSION_ATTR]: connCtx.protocolVersion });
|
|
351
356
|
if (this.#clients.size === 0) this.#ttlClockBase = Date.now();
|
|
352
|
-
const newClient = new ClientHandler(lc, this.id,
|
|
353
|
-
this.#clients.get(
|
|
354
|
-
this.#clients.set(
|
|
355
|
-
startAsyncSpan(tracer, "vs.initConnection.async", () => this.#runInLockForClient(
|
|
357
|
+
const newClient = new ClientHandler(lc, this.id, connCtx.clientID, connCtx.wsID, this.#shard, connCtx.baseCookie, downstream);
|
|
358
|
+
this.#clients.get(connCtx.clientID)?.close(`replaced by wsID: ${connCtx.wsID}`);
|
|
359
|
+
this.#clients.set(connCtx.clientID, newClient);
|
|
360
|
+
startAsyncSpan(tracer, "vs.initConnection.async", () => this.#runInLockForClient(connCtx, initConnectionMessage, async (lc, clientID, msg, cvr) => {
|
|
356
361
|
if (cvr.clientSchema === null && !msg.clientSchema) throw new ProtocolErrorWithLevel({
|
|
357
362
|
kind: InvalidConnectionRequest,
|
|
358
363
|
message: "The initConnection message for a new client group must include client schema.",
|
|
359
364
|
origin: ZeroCache
|
|
360
365
|
}, "warn");
|
|
361
|
-
if (!await this.#validateConnection(
|
|
362
|
-
await this.#handleConfigUpdate(lc, clientID, msg, cvr, "all",
|
|
366
|
+
if (!await this.#validateConnection(connCtx)) return;
|
|
367
|
+
await this.#handleConfigUpdate(lc, clientID, msg, cvr, "all", connCtx.profileID ?? `cg${this.id}`, connCtx);
|
|
363
368
|
this.#initialized.resolve("initialized");
|
|
364
369
|
}, newClient)).catch((e) => newClient.fail(e));
|
|
365
370
|
return downstream;
|
|
366
371
|
});
|
|
367
372
|
}
|
|
368
373
|
async changeDesiredQueries(selector, msg) {
|
|
369
|
-
await this.#runInLockForClient(selector, msg, (lc, clientID, msg, cvr) => this.#handleConfigUpdate(lc, clientID, msg, cvr, "missing", void 0, this.
|
|
374
|
+
await this.#runInLockForClient(selector, msg, (lc, clientID, msg, cvr) => this.#handleConfigUpdate(lc, clientID, msg, cvr, "missing", void 0, this.connContextManager.mustGetConnectionContext(selector)));
|
|
370
375
|
}
|
|
371
376
|
async updateAuth(selector, msg, authRevisionChanged) {
|
|
372
377
|
await this.#runInLockForClient(selector, msg, async (lc, clientID, _, cvr) => {
|
|
@@ -375,15 +380,15 @@ var ViewSyncerService = class {
|
|
|
375
380
|
return;
|
|
376
381
|
}
|
|
377
382
|
lc.debug?.("Auth changed, re-validating and re-transforming queries");
|
|
378
|
-
const
|
|
383
|
+
const connCtx = this.connContextManager.mustGetConnectionContext(selector);
|
|
379
384
|
if (!this.#pipelinesSynced) {
|
|
380
|
-
if (!await this.#validateConnection(
|
|
385
|
+
if (!await this.#validateConnection(connCtx)) return;
|
|
381
386
|
}
|
|
382
|
-
return await this.#handleConfigUpdate(lc, clientID, {}, cvr, "all", void 0,
|
|
387
|
+
return await this.#handleConfigUpdate(lc, clientID, {}, cvr, "all", void 0, connCtx);
|
|
383
388
|
});
|
|
384
389
|
}
|
|
385
390
|
async deleteClients(selector, msg) {
|
|
386
|
-
return await this.#runInLockForClient(selector, [msg[0], { deleted: msg[1] }], (lc, clientID, msg, cvr) => this.#handleConfigUpdate(lc, clientID, msg, cvr, "missing", void 0, this.
|
|
391
|
+
return await this.#runInLockForClient(selector, [msg[0], { deleted: msg[1] }], (lc, clientID, msg, cvr) => this.#handleConfigUpdate(lc, clientID, msg, cvr, "missing", void 0, this.connContextManager.mustGetConnectionContext(selector))) ?? [];
|
|
387
392
|
}
|
|
388
393
|
#getTTLClock(now) {
|
|
389
394
|
const delta = now - this.#ttlClockBase;
|
|
@@ -425,7 +430,7 @@ var ViewSyncerService = class {
|
|
|
425
430
|
lc.warn?.("failed to update TTL clock", rid, `after ${Date.now() - start} ms`, e);
|
|
426
431
|
});
|
|
427
432
|
}
|
|
428
|
-
async #updateCVRConfig(lc, cvr, clientID, customQueryTransformMode,
|
|
433
|
+
async #updateCVRConfig(lc, cvr, clientID, customQueryTransformMode, connCtx, fn) {
|
|
429
434
|
const updater = new CVRConfigDrivenUpdater(this.#cvrStore, cvr, this.#shard);
|
|
430
435
|
updater.ensureClient(clientID);
|
|
431
436
|
const patches = fn(updater);
|
|
@@ -438,7 +443,7 @@ var ViewSyncerService = class {
|
|
|
438
443
|
await pokers.end(newCVR.version);
|
|
439
444
|
});
|
|
440
445
|
}
|
|
441
|
-
if (this.#pipelinesSynced) await this.#syncQueryPipelineSet(lc, this.#cvr, customQueryTransformMode,
|
|
446
|
+
if (this.#pipelinesSynced) await this.#syncQueryPipelineSet(lc, this.#cvr, customQueryTransformMode, connCtx);
|
|
442
447
|
return this.#cvr;
|
|
443
448
|
}
|
|
444
449
|
/**
|
|
@@ -455,7 +460,7 @@ var ViewSyncerService = class {
|
|
|
455
460
|
span.setAttribute("clientID", clientID);
|
|
456
461
|
let client;
|
|
457
462
|
let result;
|
|
458
|
-
let
|
|
463
|
+
let connCtx;
|
|
459
464
|
try {
|
|
460
465
|
await this.#runInLockWithCVR(async (lc, cvr) => {
|
|
461
466
|
lc = lc.withContext("clientID", clientID).withContext("wsID", wsID).withContext("cmd", cmd);
|
|
@@ -465,7 +470,7 @@ var ViewSyncerService = class {
|
|
|
465
470
|
lc.debug?.("mismatched wsID", client?.wsID, wsID);
|
|
466
471
|
return;
|
|
467
472
|
}
|
|
468
|
-
|
|
473
|
+
connCtx = this.connContextManager.getConnectionContext(selector);
|
|
469
474
|
if (newClient) {
|
|
470
475
|
assert(newClient === client, "newClient must match existing client");
|
|
471
476
|
checkClientAndCVRVersions(client.version(), cvr.version);
|
|
@@ -475,7 +480,7 @@ var ViewSyncerService = class {
|
|
|
475
480
|
});
|
|
476
481
|
} catch (e) {
|
|
477
482
|
this.#lc.withContext("clientID", clientID).withContext("wsID", wsID).withContext("cmd", cmd)[getLogLevel(e)]?.(`closing connection with error`, e);
|
|
478
|
-
if (
|
|
483
|
+
if (connCtx) this.connContextManager.failConnection(selector, connCtx.revision);
|
|
479
484
|
if (client) client.fail(e);
|
|
480
485
|
else throw e;
|
|
481
486
|
}
|
|
@@ -486,10 +491,10 @@ var ViewSyncerService = class {
|
|
|
486
491
|
const clients = [...this.#clients.values()];
|
|
487
492
|
return atVersion ? clients.filter((c) => cmpVersions(c.version() ?? EMPTY_CVR_VERSION, atVersion) === 0) : clients;
|
|
488
493
|
}
|
|
489
|
-
#handleConfigUpdate = (lc, clientID, { clientSchema, deleted, desiredQueriesPatch, activeClients }, cvr, customQueryTransformMode, profileID,
|
|
494
|
+
#handleConfigUpdate = (lc, clientID, { clientSchema, deleted, desiredQueriesPatch, activeClients }, cvr, customQueryTransformMode, profileID, connCtx) => startAsyncSpan(tracer, "vs.#handleConfigUpdate", async () => {
|
|
490
495
|
const deletedClientIDs = [];
|
|
491
496
|
const deletedClientGroupIDs = [];
|
|
492
|
-
cvr = await this.#updateCVRConfig(lc, cvr, clientID, customQueryTransformMode,
|
|
497
|
+
cvr = await this.#updateCVRConfig(lc, cvr, clientID, customQueryTransformMode, connCtx, (updater) => {
|
|
493
498
|
const { ttlClock } = cvr;
|
|
494
499
|
const patches = [];
|
|
495
500
|
if (clientSchema) updater.setClientSchema(lc, clientSchema);
|
|
@@ -567,7 +572,7 @@ var ViewSyncerService = class {
|
|
|
567
572
|
const cvrVersion = cvr.version;
|
|
568
573
|
if (cvrVersion.stateVersion !== dbVersion) {
|
|
569
574
|
lc.info?.(`CVR (${versionToCookie(cvrVersion)}) is behind db ${dbVersion}`);
|
|
570
|
-
return;
|
|
575
|
+
return /* @__PURE__ */ new Set();
|
|
571
576
|
}
|
|
572
577
|
const gotQueries = Object.entries(cvr.queries).filter(([_, state]) => state.transformationHash !== void 0);
|
|
573
578
|
const customQueries = /* @__PURE__ */ new Map();
|
|
@@ -586,11 +591,11 @@ var ViewSyncerService = class {
|
|
|
586
591
|
let customHashMismatchCount = 0;
|
|
587
592
|
let otherHashMismatchCount = 0;
|
|
588
593
|
if (customQueries.size > 0 && !this.#customQueryTransformer) lc.warn?.("Custom/named queries were requested but no `ZERO_QUERY_URL` is configured for Zero Cache.");
|
|
589
|
-
const backgroundContext = this.
|
|
594
|
+
const backgroundContext = this.connContextManager.mustGetBackgroundConnectionContext();
|
|
590
595
|
const customQueryTransformer = this.#customQueryTransformer;
|
|
591
596
|
if (customQueryTransformer && customQueries.size > 0) {
|
|
592
597
|
const transformedCustomQueries = await this.#runPriorityOp(lc, "#hydrateUnchangedQueries transforming custom queries", () => customQueryTransformer.transform(backgroundContext, customQueries.values()));
|
|
593
|
-
if (!transformedCustomQueries.cached) this.
|
|
598
|
+
if (transformedCustomQueries.kind === "success" && !transformedCustomQueries.cached) this.connContextManager.validateConnection(backgroundContext, backgroundContext.revision, transformedCustomQueries.validation);
|
|
594
599
|
if (Array.isArray(transformedCustomQueries.result)) for (const q of transformedCustomQueries.result) if ("error" in q) customErrorCount++;
|
|
595
600
|
else if (q.transformationHash !== customQueries.get(q.id)?.transformationHash) customHashMismatchCount++;
|
|
596
601
|
else transformedQueries.push(q);
|
|
@@ -601,6 +606,7 @@ var ViewSyncerService = class {
|
|
|
601
606
|
else otherHashMismatchCount++;
|
|
602
607
|
}
|
|
603
608
|
lc.info?.(`hydrateUnchangedQueries: ${gotQueries.length} got queries, ${inactivatedCount} inactivated, ${customErrorCount} custom transform errors, ${customHashMismatchCount} custom hash mismatches, ${otherHashMismatchCount} other hash mismatches, ${transformedQueries.length} hydrated`);
|
|
609
|
+
const driftedQueryIDs = /* @__PURE__ */ new Set();
|
|
604
610
|
for (const { id: queryID, transformationHash, transformedAst } of transformedQueries) {
|
|
605
611
|
const timer = new TimeSliceTimer(lc);
|
|
606
612
|
let count = 0;
|
|
@@ -616,7 +622,19 @@ var ViewSyncerService = class {
|
|
|
616
622
|
this.#hydrationTime.recordMs(elapsed);
|
|
617
623
|
this.#addQueryMaterializationServerMetric(transformationHash, elapsed);
|
|
618
624
|
lc.debug?.(`hydrated ${count} rows for ${queryID} (${elapsed} ms)`);
|
|
625
|
+
const storedSigHex = cvr.queries[queryID]?.rowSetSignature;
|
|
626
|
+
if (storedSigHex !== void 0 && storedSigHex !== null) {
|
|
627
|
+
const priorSig = parseSignature(storedSigHex);
|
|
628
|
+
const candidateSig = this.#pipelines.rowSetSignature(queryID) ?? 0n;
|
|
629
|
+
if (priorSig !== candidateSig) {
|
|
630
|
+
lc.warn?.(`rowSetSignature drift for query ${queryID}: prior=${priorSig.toString(16)} new=${candidateSig.toString(16)} (${count} rows). Removing from pipelines for full re-execution.`);
|
|
631
|
+
this.#rowSetSignatureDrifts.add(1);
|
|
632
|
+
this.#pipelines.removeQuery(queryID);
|
|
633
|
+
driftedQueryIDs.add(queryID);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
619
636
|
}
|
|
637
|
+
return driftedQueryIDs;
|
|
620
638
|
}
|
|
621
639
|
#processTransformedCustomQueries(lc, transformedCustomQueries, cb, customQueryMap) {
|
|
622
640
|
if ("kind" in transformedCustomQueries) {
|
|
@@ -669,7 +687,7 @@ var ViewSyncerService = class {
|
|
|
669
687
|
*
|
|
670
688
|
* This must be called from within the #lock.
|
|
671
689
|
*/
|
|
672
|
-
#syncQueryPipelineSet(lc, cvr, customQueryTransformMode,
|
|
690
|
+
#syncQueryPipelineSet(lc, cvr, customQueryTransformMode, connCtx, driftedQueryIDs = /* @__PURE__ */ new Set()) {
|
|
673
691
|
return startAsyncSpan(tracer, "vs.#syncQueryPipelineSet", async (span) => {
|
|
674
692
|
span.setAttribute("clientGroupID", this.id);
|
|
675
693
|
assert(this.#pipelines.initialized(), "pipelines must be initialized (syncQueryPipelineSet)");
|
|
@@ -680,7 +698,7 @@ var ViewSyncerService = class {
|
|
|
680
698
|
const customQueries = /* @__PURE__ */ new Map();
|
|
681
699
|
const otherQueries = [];
|
|
682
700
|
const transformedQueries = [];
|
|
683
|
-
const
|
|
701
|
+
const resolvedConnCtx = connCtx ?? this.connContextManager.mustGetBackgroundConnectionContext();
|
|
684
702
|
for (const [id, query] of cvrQueryEntires) if (query.type === "custom") {
|
|
685
703
|
assert(id === query.id, "custom query id mismatch");
|
|
686
704
|
customQueries.set(id, query);
|
|
@@ -690,7 +708,7 @@ var ViewSyncerService = class {
|
|
|
690
708
|
});
|
|
691
709
|
for (const { id, query: origQuery } of otherQueries) {
|
|
692
710
|
assert(id === origQuery.id, "query id mismatch");
|
|
693
|
-
const transformed = transformAndHashQuery(lc, origQuery.id, origQuery.ast, must(this.#pipelines.currentPermissions()).permissions ?? { tables: {} },
|
|
711
|
+
const transformed = transformAndHashQuery(lc, origQuery.id, origQuery.ast, must(this.#pipelines.currentPermissions()).permissions ?? { tables: {} }, resolvedConnCtx.auth?.type === "jwt" ? resolvedConnCtx.auth : void 0, origQuery.type === "internal");
|
|
694
712
|
transformedQueries.push({
|
|
695
713
|
id,
|
|
696
714
|
origQuery,
|
|
@@ -705,10 +723,10 @@ var ViewSyncerService = class {
|
|
|
705
723
|
const transformStart = performance.now();
|
|
706
724
|
let transformedCustomQueries;
|
|
707
725
|
try {
|
|
708
|
-
transformedCustomQueries = await this.#runPriorityOp(lc, "#syncQueryPipelineSet transforming custom queries", () => customQueryTransformer.transform(
|
|
709
|
-
if (
|
|
726
|
+
transformedCustomQueries = await this.#runPriorityOp(lc, "#syncQueryPipelineSet transforming custom queries", () => customQueryTransformer.transform(resolvedConnCtx, customQueriesToTransform));
|
|
727
|
+
if (transformedCustomQueries.kind === "failed") throw new ProtocolErrorWithLevel(transformedCustomQueries.result, "warn");
|
|
710
728
|
else {
|
|
711
|
-
if (!transformedCustomQueries.cached) this.
|
|
729
|
+
if (!transformedCustomQueries.cached) this.connContextManager.validateConnection(resolvedConnCtx, resolvedConnCtx.revision, transformedCustomQueries.validation);
|
|
712
730
|
this.#queryTransformations.add(1, { result: "success" });
|
|
713
731
|
}
|
|
714
732
|
} catch (e) {
|
|
@@ -755,7 +773,7 @@ var ViewSyncerService = class {
|
|
|
755
773
|
const orig = cvr.queries[q.id];
|
|
756
774
|
lc.debug?.("ViewSyncer adding query", q.ast, "transformed from", orig.type === "custom" ? orig.name : orig.ast);
|
|
757
775
|
}
|
|
758
|
-
if (addQueries.length > 0 || removeQueriesQueryIds.size > 0) await this.#addAndRemoveQueries(lc, cvr, addQueries, Array.from(removeQueriesQueryIds, (id) => ({ id })));
|
|
776
|
+
if (addQueries.length > 0 || removeQueriesQueryIds.size > 0) await this.#addAndRemoveQueries(lc, cvr, addQueries, Array.from(removeQueriesQueryIds, (id) => ({ id })), driftedQueryIDs);
|
|
759
777
|
else await this.#catchupClients(lc, cvr);
|
|
760
778
|
});
|
|
761
779
|
}
|
|
@@ -787,7 +805,7 @@ var ViewSyncerService = class {
|
|
|
787
805
|
record.count++;
|
|
788
806
|
if (record.count >= THRASH_THRESHOLD) this.#lc.warn?.(`Query thrashing detected for query ${queryID}. ${record.count} replacements in 60s. This may indicate clients with different auth contexts connecting to the same client group.`);
|
|
789
807
|
}
|
|
790
|
-
#addAndRemoveQueries(lc, cvr, addQueries, removeQueries) {
|
|
808
|
+
#addAndRemoveQueries(lc, cvr, addQueries, removeQueries, driftedQueryIDs = /* @__PURE__ */ new Set()) {
|
|
791
809
|
return startAsyncSpan(tracer, "vs.#addAndRemoveQueries", async () => {
|
|
792
810
|
assert(addQueries.length > 0 || removeQueries.length > 0, "Must have queries to add or remove");
|
|
793
811
|
const start = performance.now();
|
|
@@ -795,9 +813,14 @@ var ViewSyncerService = class {
|
|
|
795
813
|
lc = lc.withContext("stateVersion", stateVersion);
|
|
796
814
|
lc.info?.(`hydrating ${addQueries.length} queries`);
|
|
797
815
|
const updater = new CVRQueryDrivenUpdater(this.#cvrStore, cvr, stateVersion, this.#pipelines.replicaVersion, (queryID) => this.#pipelines.rowSetSignature(queryID));
|
|
798
|
-
const {
|
|
816
|
+
const { queryPatches } = updater.trackQueries(lc, addQueries, removeQueries);
|
|
817
|
+
if (addQueries.some((q) => driftedQueryIDs.has(q.id))) updater.ensureNewVersion();
|
|
818
|
+
const newVersion = updater.updatedVersion();
|
|
799
819
|
const pokers = startPoke(this.#getClients(), newVersion);
|
|
800
|
-
for (const patch of queryPatches) await pokers.addPatch(
|
|
820
|
+
for (const patch of queryPatches) await pokers.addPatch({
|
|
821
|
+
...patch,
|
|
822
|
+
toVersion: newVersion
|
|
823
|
+
});
|
|
801
824
|
for (const q of removeQueries) {
|
|
802
825
|
this.#pipelines.removeQuery(q.id);
|
|
803
826
|
this.#inspectorDelegate.removeQuery(q.id);
|
|
@@ -1008,88 +1031,98 @@ var ViewSyncerService = class {
|
|
|
1008
1031
|
}
|
|
1009
1032
|
#handleInspect = async (lc, clientID, body, cvr) => {
|
|
1010
1033
|
const client = must(this.#clients.get(clientID));
|
|
1011
|
-
const
|
|
1034
|
+
const connCtx = this.connContextManager.mustGetConnectionContext({
|
|
1012
1035
|
clientID,
|
|
1013
1036
|
wsID: client.wsID
|
|
1014
1037
|
});
|
|
1015
|
-
return handleInspect(lc, body, cvr, client, this.#inspectorDelegate, this.id, this.#cvrStore, this.#config,
|
|
1038
|
+
return handleInspect(lc, body, cvr, client, this.#inspectorDelegate, this.id, this.#cvrStore, this.#config, connCtx);
|
|
1016
1039
|
};
|
|
1017
1040
|
async #runBackgroundRetransform(lc) {
|
|
1018
|
-
const attemptRetransform = async (
|
|
1019
|
-
await this.#syncQueryPipelineSet(lc, must(this.#cvr, "cvr missing during auth maintenance retransform"), "all",
|
|
1020
|
-
this.
|
|
1021
|
-
clientID:
|
|
1022
|
-
wsID:
|
|
1023
|
-
},
|
|
1041
|
+
const attemptRetransform = async (connCtx) => {
|
|
1042
|
+
await this.#syncQueryPipelineSet(lc, must(this.#cvr, "cvr missing during auth maintenance retransform"), "all", connCtx);
|
|
1043
|
+
this.connContextManager.markBackgroundRetransformSuccess({
|
|
1044
|
+
clientID: connCtx.clientID,
|
|
1045
|
+
wsID: connCtx.wsID
|
|
1046
|
+
}, connCtx.revision);
|
|
1024
1047
|
};
|
|
1025
|
-
let
|
|
1026
|
-
if (!
|
|
1048
|
+
let backgroundConnCtx = this.connContextManager.getBackgroundConnectionContext();
|
|
1049
|
+
if (!backgroundConnCtx) {
|
|
1027
1050
|
lc.debug?.("Skipping background retransform with no selected connection");
|
|
1028
1051
|
return;
|
|
1029
1052
|
}
|
|
1030
1053
|
for (;;) {
|
|
1031
1054
|
try {
|
|
1032
|
-
await attemptRetransform(
|
|
1055
|
+
await attemptRetransform(backgroundConnCtx);
|
|
1033
1056
|
return;
|
|
1034
1057
|
} catch (e) {
|
|
1035
1058
|
if (isProtocolError(e)) {
|
|
1036
1059
|
if (isAuthErrorBody(e.errorBody)) {
|
|
1037
1060
|
lc.warn?.("Background retransform auth failed; failing connection and searching for replacement", {
|
|
1038
|
-
clientID:
|
|
1061
|
+
clientID: backgroundConnCtx.clientID,
|
|
1039
1062
|
message: e.message
|
|
1040
1063
|
});
|
|
1041
|
-
this.#failMaintenanceConnection(
|
|
1064
|
+
this.#failMaintenanceConnection(backgroundConnCtx, e);
|
|
1042
1065
|
} else if (isTransformFailedError(e)) {
|
|
1043
1066
|
lc.warn?.("Background retransform failed; deferring auth maintenance", {
|
|
1044
|
-
clientID:
|
|
1067
|
+
clientID: backgroundConnCtx.clientID,
|
|
1045
1068
|
message: e.message
|
|
1046
1069
|
});
|
|
1047
|
-
this.
|
|
1070
|
+
this.connContextManager.deferMaintenance("retransform");
|
|
1048
1071
|
return;
|
|
1049
1072
|
}
|
|
1050
1073
|
} else throw e;
|
|
1051
1074
|
}
|
|
1052
|
-
const
|
|
1053
|
-
if (!
|
|
1075
|
+
const replacementConnCtx = this.connContextManager.getBackgroundConnectionContext();
|
|
1076
|
+
if (!replacementConnCtx) {
|
|
1054
1077
|
lc.debug?.("No replacement connection available for background retransform");
|
|
1055
1078
|
return;
|
|
1056
1079
|
}
|
|
1057
1080
|
lc.debug?.("Retrying background retransform with replacement connection", {
|
|
1058
|
-
clientID:
|
|
1059
|
-
wsID:
|
|
1081
|
+
clientID: replacementConnCtx.clientID,
|
|
1082
|
+
wsID: replacementConnCtx.wsID
|
|
1060
1083
|
});
|
|
1061
|
-
|
|
1084
|
+
backgroundConnCtx = replacementConnCtx;
|
|
1062
1085
|
}
|
|
1063
1086
|
}
|
|
1064
|
-
async #validateConnection(
|
|
1087
|
+
async #validateConnection(connCtx) {
|
|
1065
1088
|
try {
|
|
1089
|
+
let validation = void 0;
|
|
1066
1090
|
if (this.#customQueryTransformer) {
|
|
1067
|
-
const
|
|
1068
|
-
if (
|
|
1069
|
-
|
|
1070
|
-
|
|
1091
|
+
const response = await this.#customQueryTransformer.validate(connCtx);
|
|
1092
|
+
if (response.kind === "TransformFailed") throw new ProtocolErrorWithLevel(response, "warn");
|
|
1093
|
+
validation = response.validation;
|
|
1094
|
+
} else validation = { kind: "client-fallback" };
|
|
1095
|
+
this.connContextManager.validateConnection(connCtx, connCtx.revision, validation);
|
|
1071
1096
|
return true;
|
|
1072
1097
|
} catch (e) {
|
|
1073
1098
|
if (isProtocolError(e) && isAuthErrorBody(e.errorBody)) {
|
|
1074
|
-
this.#
|
|
1099
|
+
this.#lc.warn?.("Connection auth validation failed; invalidating connection", {
|
|
1100
|
+
clientID: connCtx.clientID,
|
|
1101
|
+
wsID: connCtx.wsID,
|
|
1102
|
+
revision: connCtx.revision,
|
|
1103
|
+
message: e.message
|
|
1104
|
+
});
|
|
1105
|
+
this.#failMaintenanceConnection(connCtx, e);
|
|
1075
1106
|
return false;
|
|
1076
1107
|
}
|
|
1077
1108
|
throw e;
|
|
1078
1109
|
}
|
|
1079
1110
|
}
|
|
1080
|
-
#failMaintenanceConnection(
|
|
1081
|
-
if (!this.
|
|
1111
|
+
#failMaintenanceConnection(connCtx, error) {
|
|
1112
|
+
if (!this.connContextManager.failConnection(connCtx, connCtx.revision)) return;
|
|
1082
1113
|
const wrapped = wrapWithProtocolError(error);
|
|
1083
|
-
const client = this.#clients.get(
|
|
1084
|
-
if (client?.wsID ===
|
|
1114
|
+
const client = this.#clients.get(connCtx.clientID);
|
|
1115
|
+
if (client?.wsID === connCtx.wsID) client.fail(wrapped);
|
|
1085
1116
|
}
|
|
1086
1117
|
stop() {
|
|
1087
1118
|
this.#lc.info?.("stopping view syncer");
|
|
1119
|
+
this.connContextManager.setSharedRetransformReady(false);
|
|
1088
1120
|
this.#initialized.reject("shut down before initialization completed");
|
|
1089
1121
|
this.#stateChanges.cancel();
|
|
1090
1122
|
return this.#stopped.promise;
|
|
1091
1123
|
}
|
|
1092
1124
|
async #cleanup(err) {
|
|
1125
|
+
this.connContextManager.setSharedRetransformReady(false);
|
|
1093
1126
|
this.#stopTTLClockInterval();
|
|
1094
1127
|
this.#stopExpireTimer();
|
|
1095
1128
|
this.#stopAuthMaintenanceTimer();
|