@rocicorp/zero 1.12.0-canary.12 → 1.12.0-canary.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/out/replicache/src/btree/diff.d.ts.map +1 -1
  2. package/out/replicache/src/btree/diff.js +1 -2
  3. package/out/replicache/src/btree/diff.js.map +1 -1
  4. package/out/replicache/src/btree/read.d.ts +31 -1
  5. package/out/replicache/src/btree/read.d.ts.map +1 -1
  6. package/out/replicache/src/btree/read.js +74 -19
  7. package/out/replicache/src/btree/read.js.map +1 -1
  8. package/out/replicache/src/btree/splice.d.ts.map +1 -1
  9. package/out/replicache/src/btree/splice.js +2 -1
  10. package/out/replicache/src/btree/splice.js.map +1 -1
  11. package/out/replicache/src/btree/write.d.ts.map +1 -1
  12. package/out/replicache/src/btree/write.js +2 -1
  13. package/out/replicache/src/btree/write.js.map +1 -1
  14. package/out/replicache/src/persist/collect-idb-databases.d.ts +1 -0
  15. package/out/replicache/src/persist/collect-idb-databases.d.ts.map +1 -1
  16. package/out/replicache/src/persist/collect-idb-databases.js +13 -5
  17. package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
  18. package/out/replicache/src/scan-iterator.js +1 -1
  19. package/out/replicache/src/sync/request-id.js +2 -2
  20. package/out/replicache/src/sync/request-id.js.map +1 -1
  21. package/out/shared/src/bigint-json.d.ts.map +1 -1
  22. package/out/shared/src/bigint-json.js +1 -0
  23. package/out/shared/src/bigint-json.js.map +1 -1
  24. package/out/shared/src/btree-set.d.ts +21 -0
  25. package/out/shared/src/btree-set.d.ts.map +1 -1
  26. package/out/shared/src/btree-set.js +54 -0
  27. package/out/shared/src/btree-set.js.map +1 -1
  28. package/out/shared/src/headers.js +20 -0
  29. package/out/shared/src/headers.js.map +1 -0
  30. package/out/shared/src/natural-runs.d.ts +191 -0
  31. package/out/shared/src/natural-runs.d.ts.map +1 -0
  32. package/out/shared/src/natural-runs.js +337 -0
  33. package/out/shared/src/natural-runs.js.map +1 -0
  34. package/out/shared/src/random-uint64.d.ts.map +1 -1
  35. package/out/shared/src/random-uint64.js +2 -2
  36. package/out/shared/src/random-uint64.js.map +1 -1
  37. package/out/shared/src/random-values.d.ts +19 -1
  38. package/out/shared/src/random-values.d.ts.map +1 -1
  39. package/out/shared/src/random-values.js +38 -4
  40. package/out/shared/src/random-values.js.map +1 -1
  41. package/out/shared/src/tuple-map.d.ts +22 -0
  42. package/out/shared/src/tuple-map.d.ts.map +1 -0
  43. package/out/shared/src/tuple-map.js +48 -0
  44. package/out/shared/src/tuple-map.js.map +1 -0
  45. package/out/zero/package.js +1 -1
  46. package/out/zero/package.js.map +1 -1
  47. package/out/zero-cache/src/auth/auth.d.ts.map +1 -1
  48. package/out/zero-cache/src/auth/auth.js +5 -0
  49. package/out/zero-cache/src/auth/auth.js.map +1 -1
  50. package/out/zero-cache/src/config/zero-config.d.ts +21 -0
  51. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  52. package/out/zero-cache/src/config/zero-config.js +47 -9
  53. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  54. package/out/zero-cache/src/custom-queries/transform-query.js +2 -2
  55. package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
  56. package/out/zero-cache/src/db/pg-to-lite.d.ts.map +1 -1
  57. package/out/zero-cache/src/db/pg-to-lite.js +3 -2
  58. package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
  59. package/out/zero-cache/src/db/pg-type-parser.js +55 -2
  60. package/out/zero-cache/src/db/pg-type-parser.js.map +1 -1
  61. package/out/zero-cache/src/server/change-streamer.js +5 -2
  62. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  63. package/out/zero-cache/src/server/inspector-delegate.js +6 -29
  64. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  65. package/out/zero-cache/src/server/main.js +5 -0
  66. package/out/zero-cache/src/server/main.js.map +1 -1
  67. package/out/zero-cache/src/server/replicator.js +3 -2
  68. package/out/zero-cache/src/server/replicator.js.map +1 -1
  69. package/out/zero-cache/src/server/syncer.js +5 -4
  70. package/out/zero-cache/src/server/syncer.js.map +1 -1
  71. package/out/zero-cache/src/server/worker-dispatcher.d.ts.map +1 -1
  72. package/out/zero-cache/src/server/worker-dispatcher.js +2 -1
  73. package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
  74. package/out/zero-cache/src/services/admin-auth.d.ts +22 -0
  75. package/out/zero-cache/src/services/admin-auth.d.ts.map +1 -0
  76. package/out/zero-cache/src/services/admin-auth.js +42 -0
  77. package/out/zero-cache/src/services/admin-auth.js.map +1 -0
  78. package/out/zero-cache/src/services/auth-attempt-counter.d.ts +33 -0
  79. package/out/zero-cache/src/services/auth-attempt-counter.d.ts.map +1 -0
  80. package/out/zero-cache/src/services/auth-attempt-counter.js +61 -0
  81. package/out/zero-cache/src/services/auth-attempt-counter.js.map +1 -0
  82. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +1 -1
  83. package/out/zero-cache/src/services/change-source/pg/change-source.js +110 -13
  84. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  85. package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts +13 -7
  86. package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts.map +1 -1
  87. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +38 -5
  88. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
  89. package/out/zero-cache/src/services/change-source/pg/schema/init.js +10 -1
  90. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  91. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +4 -0
  92. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
  93. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +10 -3
  94. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  95. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +23 -2
  96. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  97. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +2 -2
  98. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  99. package/out/zero-cache/src/services/change-streamer/change-streamer.js +10 -3
  100. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  101. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  102. package/out/zero-cache/src/services/heapz.d.ts.map +1 -1
  103. package/out/zero-cache/src/services/heapz.js +4 -5
  104. package/out/zero-cache/src/services/heapz.js.map +1 -1
  105. package/out/zero-cache/src/services/mutagen/error.d.ts +5 -0
  106. package/out/zero-cache/src/services/mutagen/error.d.ts.map +1 -1
  107. package/out/zero-cache/src/services/mutagen/error.js +7 -1
  108. package/out/zero-cache/src/services/mutagen/error.js.map +1 -1
  109. package/out/zero-cache/src/services/mutagen/mutagen.js +1 -1
  110. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  111. package/out/zero-cache/src/services/mutagen/pusher.js +1 -1
  112. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  113. package/out/zero-cache/src/services/plannerz.d.ts.map +1 -1
  114. package/out/zero-cache/src/services/plannerz.js +3 -7
  115. package/out/zero-cache/src/services/plannerz.js.map +1 -1
  116. package/out/zero-cache/src/services/profz.d.ts.map +1 -1
  117. package/out/zero-cache/src/services/profz.js +4 -13
  118. package/out/zero-cache/src/services/profz.js.map +1 -1
  119. package/out/zero-cache/src/services/replicator/change-processor.js +29 -2
  120. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  121. package/out/zero-cache/src/services/replicator/incremental-sync.js +3 -3
  122. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  123. package/out/zero-cache/src/services/statz.d.ts.map +1 -1
  124. package/out/zero-cache/src/services/statz.js +2 -7
  125. package/out/zero-cache/src/services/statz.js.map +1 -1
  126. package/out/zero-cache/src/services/view-syncer/client-handler.js +22 -1
  127. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  128. package/out/zero-cache/src/services/view-syncer/connection-context-manager.js +40 -2
  129. package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
  130. package/out/zero-cache/src/services/view-syncer/cvr-store.js +54 -17
  131. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  132. package/out/zero-cache/src/services/view-syncer/cvr.js +11 -0
  133. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  134. package/out/zero-cache/src/services/view-syncer/inspect-auth-limiter.js +107 -0
  135. package/out/zero-cache/src/services/view-syncer/inspect-auth-limiter.js.map +1 -0
  136. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +11 -6
  137. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  138. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +2 -1
  139. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  140. package/out/zero-cache/src/services/view-syncer/row-record-cache.js +16 -14
  141. package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
  142. package/out/zero-cache/src/services/view-syncer/schema/cvr.js +4 -1
  143. package/out/zero-cache/src/services/view-syncer/schema/cvr.js.map +1 -1
  144. package/out/zero-cache/src/services/view-syncer/schema/init.js +3 -0
  145. package/out/zero-cache/src/services/view-syncer/schema/init.js.map +1 -1
  146. package/out/zero-cache/src/services/view-syncer/view-syncer.js +89 -51
  147. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  148. package/out/zero-cache/src/types/client-group-log-id.d.ts +9 -0
  149. package/out/zero-cache/src/types/client-group-log-id.d.ts.map +1 -0
  150. package/out/zero-cache/src/types/client-group-log-id.js +16 -0
  151. package/out/zero-cache/src/types/client-group-log-id.js.map +1 -0
  152. package/out/zero-cache/src/types/lite.d.ts.map +1 -1
  153. package/out/zero-cache/src/types/lite.js +1 -0
  154. package/out/zero-cache/src/types/lite.js.map +1 -1
  155. package/out/zero-cache/src/types/pg.d.ts +13 -2
  156. package/out/zero-cache/src/types/pg.d.ts.map +1 -1
  157. package/out/zero-cache/src/types/pg.js +2 -2
  158. package/out/zero-cache/src/types/pg.js.map +1 -1
  159. package/out/zero-cache/src/types/poke-chunk.d.ts +25 -4
  160. package/out/zero-cache/src/types/poke-chunk.d.ts.map +1 -1
  161. package/out/zero-cache/src/types/poke-chunk.js +46 -5
  162. package/out/zero-cache/src/types/poke-chunk.js.map +1 -1
  163. package/out/zero-cache/src/workers/connection.d.ts.map +1 -1
  164. package/out/zero-cache/src/workers/connection.js +10 -3
  165. package/out/zero-cache/src/workers/connection.js.map +1 -1
  166. package/out/zero-cache/src/workers/rehome.d.ts +10 -0
  167. package/out/zero-cache/src/workers/rehome.d.ts.map +1 -1
  168. package/out/zero-cache/src/workers/rehome.js +4 -2
  169. package/out/zero-cache/src/workers/rehome.js.map +1 -1
  170. package/out/zero-cache/src/workers/syncer-ws-message-handler.js +3 -2
  171. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  172. package/out/zero-cache/src/workers/syncer.js +15 -15
  173. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  174. package/out/zero-client/src/client/inspector/inspector.d.ts +4 -0
  175. package/out/zero-client/src/client/inspector/inspector.d.ts.map +1 -1
  176. package/out/zero-client/src/client/inspector/inspector.js +4 -0
  177. package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
  178. package/out/zero-client/src/client/inspector/lazy-inspector.d.ts +10 -2
  179. package/out/zero-client/src/client/inspector/lazy-inspector.d.ts.map +1 -1
  180. package/out/zero-client/src/client/inspector/lazy-inspector.js +38 -31
  181. package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
  182. package/out/zero-client/src/client/inspector/query.d.ts +2 -2
  183. package/out/zero-client/src/client/inspector/query.d.ts.map +1 -1
  184. package/out/zero-client/src/client/inspector/query.js +4 -4
  185. package/out/zero-client/src/client/inspector/query.js.map +1 -1
  186. package/out/zero-client/src/client/ivm-branch.d.ts +3 -0
  187. package/out/zero-client/src/client/ivm-branch.d.ts.map +1 -1
  188. package/out/zero-client/src/client/ivm-branch.js +10 -5
  189. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  190. package/out/zero-client/src/client/reconnect-backoff.d.ts +31 -0
  191. package/out/zero-client/src/client/reconnect-backoff.d.ts.map +1 -0
  192. package/out/zero-client/src/client/reconnect-backoff.js +44 -0
  193. package/out/zero-client/src/client/reconnect-backoff.js.map +1 -0
  194. package/out/zero-client/src/client/version.js +1 -1
  195. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  196. package/out/zero-client/src/client/zero.js +7 -0
  197. package/out/zero-client/src/client/zero.js.map +1 -1
  198. package/out/zero-client/src/util/nanoid.js +2 -2
  199. package/out/zero-client/src/util/nanoid.js.map +1 -1
  200. package/out/zero-protocol/src/up.d.ts +42 -42
  201. package/out/zero-server/src/process-mutations.d.ts +10 -0
  202. package/out/zero-server/src/process-mutations.d.ts.map +1 -1
  203. package/out/zero-server/src/process-mutations.js +39 -6
  204. package/out/zero-server/src/process-mutations.js.map +1 -1
  205. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  206. package/out/zql/src/ivm/memory-source.js +89 -6
  207. package/out/zql/src/ivm/memory-source.js.map +1 -1
  208. package/out/zql/src/planner/planner-connection.d.ts.map +1 -1
  209. package/out/zql/src/planner/planner-connection.js +37 -3
  210. package/out/zql/src/planner/planner-connection.js.map +1 -1
  211. package/package.json +1 -1
@@ -17,6 +17,7 @@ import { ZERO_VERSION_COLUMN_NAME } from "../replicator/schema/constants.js";
17
17
  import { rowIDString } from "../../types/row-key.js";
18
18
  import "../replicator/schema/replication-state.js";
19
19
  import { ProtocolErrorWithLevel, getLogLevel, wrapWithProtocolError } from "../../types/error-with-level.js";
20
+ import { clientGroupLogID } from "../../types/client-group-log-id.js";
20
21
  import { Subscription } from "../../types/subscription.js";
21
22
  import { CustomKeyMap } from "../../../../shared/src/custom-key-map.js";
22
23
  import { ttlClockAsNumber, ttlClockFromNumber } from "./ttl-clock.js";
@@ -30,8 +31,8 @@ import { ResetPipelinesSignal } from "./snapshotter.js";
30
31
  import { AdvancementMetrics } from "./advancement-metrics.js";
31
32
  import { ClientHandler, startPoke } from "./client-handler.js";
32
33
  import { tracer } from "./tracer.js";
33
- import { CVRStore, ClientNotFoundError } from "./cvr-store.js";
34
- import { CVRConfigDrivenUpdater, CVRQueryDrivenUpdater, classifyQueriesForHydration, nextEvictionTime } from "./cvr.js";
34
+ import { CVRStore, ClientNotFoundError, PinnedUserMismatchError } from "./cvr-store.js";
35
+ import { CVRConfigDrivenUpdater, CVRQueryDrivenUpdater, CVRUpdater, classifyQueriesForHydration, nextEvictionTime } from "./cvr.js";
35
36
  import { E2EServingLagTracker } from "./e2e-serving-lag.js";
36
37
  import { HydrationBudget } from "./hydration-budget.js";
37
38
  import { HydrationCircuitBreaker } from "./hydration-circuit-breaker.js";
@@ -147,8 +148,8 @@ var ViewSyncerService = class {
147
148
  * updates are processed via `#syncQueryPipelineSet`.
148
149
  *
149
150
  * Resets to `false` if `#advancePipelines` returns a `ResetPipelinesSignal`
150
- * and pipelines must be reset and rehydrated, or if a reloaded CVR is ahead
151
- * of the pipelines (see `#resetPipelinesIfBehindCVR`).
151
+ * and pipelines must be reset and rehydrated, or if the CVR is reloaded
152
+ * (see `#resetPipelinesForReloadedCVR`).
152
153
  */
153
154
  #pipelinesHydrated = false;
154
155
  #servedVersion = null;
@@ -335,7 +336,7 @@ var ViewSyncerService = class {
335
336
  }, "info");
336
337
  }
337
338
  if (await this.#checkForShutdownConditionsInLock()) {
338
- this.#lc.info?.(`closing clientGroupID=${this.id}`);
339
+ this.#lc.info?.(`closing clientGroupID=${clientGroupLogID(this.id)}`);
339
340
  this.#initialized.reject(shutdownBeforeInitializationError());
340
341
  this.#stateChanges.cancel();
341
342
  return;
@@ -355,13 +356,20 @@ var ViewSyncerService = class {
355
356
  };
356
357
  }
357
358
  try {
358
- if (reloaded && this.#resetPipelinesIfBehindCVR(lc, this.#cvr)) {
359
+ if (reloaded && this.#cvr.pinnedUser !== null) try {
360
+ this.connContextManager.restorePinnedUser(this.#cvr.pinnedUser);
361
+ } catch (e) {
362
+ this.#stateChanges.fail(e instanceof Error ? e : new Error(String(e)));
363
+ throw e;
364
+ }
365
+ if (reloaded && this.#resetPipelinesForReloadedCVR(lc, this.#cvr)) {
359
366
  const connCtx = this.connContextManager.getBackgroundConnectionContext();
360
367
  if (connCtx) await this.#maybeHydratePipelines(lc, this.#cvr, connCtx);
361
368
  }
362
369
  await fn(lc, this.#cvr);
363
370
  } catch (e) {
364
371
  this.#cvr = void 0;
372
+ if (e instanceof PinnedUserMismatchError) this.#stateChanges.fail(e);
365
373
  throw e;
366
374
  } finally {
367
375
  this.#scheduleAuthMaintenance(lc);
@@ -369,26 +377,29 @@ var ViewSyncerService = class {
369
377
  });
370
378
  }
371
379
  /**
372
- * The CVR is reloaded after an error clears the cached copy. By then another
373
- * view-syncer may have taken over the client group and flushed the CVR at a
374
- * version ahead of this instance's hydrated pipelines (e.g. its replica was
375
- * further ahead). The pipelines can no longer be diffed against the CVR, so
376
- * reset them and let `#maybeHydratePipelines` rehydrate once the replica has
377
- * caught up to the CVR.
380
+ * The CVR is reloaded after an error clears the cached copy. The pipelines
381
+ * may no longer correspond to it:
382
+ *
383
+ * - Another view-syncer may have taken over the client group and flushed
384
+ * the CVR, adding or removing queries (e.g. evicting them when its
385
+ * hydration budget ran out), or at a version ahead of the pipelines.
386
+ * - The failed operation may have changed the pipelines before the CVR
387
+ * flush that would have recorded the change, e.g. a hydration that failed
388
+ * after some of its queries were added.
378
389
  *
379
- * A CVR behind the pipelines is expected (advancements that do not change
380
- * the CVR are not flushed) and is handled by the normal update path.
390
+ * Rather than reconcile them, reset the pipelines and let
391
+ * `#maybeHydratePipelines` rehydrate them from the reloaded CVR once the
392
+ * replica has caught up to it, as when a view-syncer takes over a client
393
+ * group.
381
394
  *
382
395
  * Returns whether the pipelines were reset.
383
396
  *
384
397
  * Must be called from within the #lock.
385
398
  */
386
- #resetPipelinesIfBehindCVR(lc, cvr) {
399
+ #resetPipelinesForReloadedCVR(lc, cvr) {
387
400
  if (!this.#pipelinesHydrated) return false;
388
- const pipelineVersion = this.#pipelines.currentVersion();
389
- if (pipelineVersion >= cvr.version.stateVersion) return false;
390
- lc.info?.(`resetting pipelines: pipelines@${pipelineVersion} are behind reloaded cvr@${versionString(cvr.version)}`);
391
- this.#pipelineResets.add(1, { reason: "behind-cvr" });
401
+ lc.info?.(`resetting pipelines@${this.#pipelines.currentVersion()} for reloaded cvr@${versionString(cvr.version)}`);
402
+ this.#pipelineResets.add(1, { reason: "cvr-reloaded" });
392
403
  this.#pipelinesHydrated = false;
393
404
  this.connContextManager.setSharedRetransformReady(false);
394
405
  this.#pipelines.reset(must(cvr.clientSchema, "cvr.clientSchema missing after initialization"));
@@ -410,13 +421,13 @@ var ViewSyncerService = class {
410
421
  try {
411
422
  this.#scheduleShutdown(this.#keepaliveMs);
412
423
  if (await this.readyState() === "draining") {
413
- this.#lc.debug?.(`draining view-syncer ${this.id} before running`);
424
+ this.#lc.debug?.(`draining view-syncer ${clientGroupLogID(this.id)} before running`);
414
425
  this.stop();
415
426
  }
416
427
  for await (const replicaState of this.#stateChanges) {
417
428
  const { state } = replicaState;
418
429
  if (this.#drainCoordinator.shouldDrain()) {
419
- this.#lc.debug?.(`draining view-syncer ${this.id} (elective)`);
430
+ this.#lc.debug?.(`draining view-syncer ${clientGroupLogID(this.id)} (elective)`);
420
431
  break;
421
432
  }
422
433
  assert(state === "version-ready", "state should be version-ready");
@@ -460,11 +471,11 @@ var ViewSyncerService = class {
460
471
  if (this.#drainCoordinator.shouldDrain()) this.#drainCoordinator.drainNextIn(this.#totalHydrationTimeMs());
461
472
  await this.#cleanup();
462
473
  } catch (e) {
463
- this.#lc[getLogLevel(e)]?.(`stopping view-syncer ${this.id}: ${String(e)}`, e);
474
+ this.#lc[getLogLevel(e)]?.(`stopping view-syncer ${clientGroupLogID(this.id)}: ${String(e)}`, e);
464
475
  await this.#cleanup(e);
465
476
  } finally {
466
477
  await this.#cvrStore.flushed(this.#lc).catch((e) => this.#lc[getLogLevel(e)]?.(e));
467
- this.#lc.info?.(`view-syncer ${this.id} finished`);
478
+ this.#lc.info?.(`view-syncer ${clientGroupLogID(this.id)} finished`);
468
479
  this.#stopped.resolve();
469
480
  }
470
481
  }
@@ -740,6 +751,7 @@ var ViewSyncerService = class {
740
751
  }, "warn");
741
752
  const validatedConnCtx = await this.#validateCurrentConnection(connCtx);
742
753
  if (!validatedConnCtx) return;
754
+ newClient.markValidated();
743
755
  await this.#handleConfigUpdate(lc, clientID, msg, cvr, "all", validatedConnCtx.profileID ?? `cg${this.id}`, validatedConnCtx);
744
756
  this.#initialized.resolve("initialized");
745
757
  }, newClient)).catch((e) => newClient.fail(e));
@@ -756,11 +768,9 @@ var ViewSyncerService = class {
756
768
  return;
757
769
  }
758
770
  lc.debug?.("Auth changed, re-validating and re-transforming queries");
759
- const connCtx = this.connContextManager.mustGetConnectionContext(selector);
760
- if (!this.#pipelinesHydrated) {
761
- if (!await this.#validateConnection(connCtx)) return;
762
- }
763
- return await this.#handleConfigUpdate(lc, clientID, {}, cvr, "all", void 0, connCtx);
771
+ const validatedConnCtx = await this.#validateCurrentConnection(selector);
772
+ if (!validatedConnCtx) return;
773
+ return await this.#handleConfigUpdate(lc, clientID, {}, cvr, "all", void 0, validatedConnCtx);
764
774
  });
765
775
  }
766
776
  async deleteClients(selector, msg) {
@@ -785,6 +795,8 @@ var ViewSyncerService = class {
785
795
  */
786
796
  #flushUpdater(lc, updater, patchesPoked = false) {
787
797
  return startAsyncSpan(tracer, "vs.#flushUpdater", () => this.#runPriorityOp(lc, "flushing cvr", async () => {
798
+ const { pinnedUser } = this.connContextManager.getGroupState();
799
+ if (pinnedUser !== void 0) updater.setPinnedUser(pinnedUser);
788
800
  const now = Date.now();
789
801
  const ttlClock = this.#getTTLClock(now);
790
802
  const { cvr, flushed } = await updater.flush(lc, this.#lastConnectTime, now, ttlClock, patchesPoked);
@@ -863,7 +875,7 @@ var ViewSyncerService = class {
863
875
  const [cmd, body] = msg;
864
876
  if (newClient || !this.#clients.has(clientID)) this.#lastConnectTime = Date.now();
865
877
  return startAsyncSpan(tracer, `vs.#runInLockForClient(${cmd})`, async (span) => {
866
- span.setAttribute("clientGroupID", this.id);
878
+ span.setAttribute("clientGroupID", clientGroupLogID(this.id));
867
879
  span.setAttribute("clientID", clientID);
868
880
  let client;
869
881
  let result;
@@ -885,17 +897,32 @@ var ViewSyncerService = class {
885
897
  });
886
898
  } catch (e) {
887
899
  this.#lc.withContext("clientID", clientID).withContext("wsID", wsID).withContext("cmd", cmd)[getLogLevel(e)]?.(`closing connection with error`, e);
888
- if (connCtx) this.connContextManager.failConnection(selector, connCtx.revision);
900
+ const current = connCtx && this.#currentRegistration(connCtx);
901
+ if (current) this.connContextManager.failConnection(selector, current.revision);
889
902
  if (client) client.fail(e);
890
903
  else throw e;
891
904
  }
892
905
  return result;
893
906
  });
894
907
  }
908
+ /**
909
+ * The clients that pokes and catch-up are sent to. A client whose
910
+ * connection has not been validated yet is left out: its `initConnection`
911
+ * validates it and then catches it up from its baseCookie, so lock-held
912
+ * work for other clients that runs in the meantime must not send it data.
913
+ */
895
914
  #getClients(atVersion) {
896
- const clients = [...this.#clients.values()];
915
+ const clients = [...this.#clients.values()].filter((c) => c.validated());
897
916
  return atVersion ? clients.filter((c) => cmpVersions(c.version() ?? EMPTY_CVR_VERSION, atVersion) === 0) : clients;
898
917
  }
918
+ /**
919
+ * The client to send a targeted message (e.g. a query error) to, or
920
+ * `undefined` while its connection is still unvalidated; see #getClients().
921
+ */
922
+ #getValidatedClient(clientID) {
923
+ const client = this.#clients.get(clientID);
924
+ return client?.validated() ? client : void 0;
925
+ }
899
926
  #handleConfigUpdate = (lc, clientID, { clientSchema, deleted, desiredQueriesPatch, activeClients }, cvr, customQueryTransformMode, profileID, connCtx) => startAsyncSpan(tracer, "vs.#handleConfigUpdate", async () => {
900
927
  const deletedClientIDs = [];
901
928
  const deletedClientGroupIDs = [];
@@ -1122,12 +1149,12 @@ var ViewSyncerService = class {
1122
1149
  return clientIds;
1123
1150
  };
1124
1151
  if ("queryIDs" in errorOrErrors) {
1125
- for (const clientId of getAffectedClientIDs(errorOrErrors.queryIDs)) this.#clients.get(clientId)?.sendQueryTransformFailedError(errorOrErrors);
1152
+ for (const clientId of getAffectedClientIDs(errorOrErrors.queryIDs)) this.#getValidatedClient(clientId)?.sendQueryTransformFailedError(errorOrErrors);
1126
1153
  return;
1127
1154
  }
1128
1155
  const appErrorGroups = /* @__PURE__ */ new Map();
1129
1156
  for (const err of errorOrErrors) for (const clientId of getAffectedClientIDs([err.id])) getOrInsertComputed(appErrorGroups, clientId, newArray).push(err);
1130
- for (const [clientId, errors] of appErrorGroups) this.#clients.get(clientId)?.sendQueryTransformApplicationErrors(errors);
1157
+ for (const [clientId, errors] of appErrorGroups) this.#getValidatedClient(clientId)?.sendQueryTransformApplicationErrors(errors);
1131
1158
  }
1132
1159
  #addQueryMaterializationServerMetric(queryID, elapsed) {
1133
1160
  this.#inspectorDelegate.addMetric("query-materialization-server", elapsed, queryID);
@@ -1146,7 +1173,7 @@ var ViewSyncerService = class {
1146
1173
  }
1147
1174
  #logQueryCoverageShadowSummary(lc, hydrationPath, totalHydratedQueries, coveredHydratedQueries, firstCoveredQuery) {
1148
1175
  if (!this.#config.enableQueryCovering || totalHydratedQueries === 0) return;
1149
- let coverageLC = lc.withContext("appID", this.#shard.appID).withContext("shardNum", this.#shard.shardNum).withContext("clientGroupID", this.id).withContext("queryCoverageMode", "shadow").withContext("hydrationPath", hydrationPath).withContext("totalHydratedQueries", totalHydratedQueries).withContext("coveredHydratedQueries", coveredHydratedQueries).withContext("uncoveredHydratedQueries", totalHydratedQueries - coveredHydratedQueries);
1176
+ let coverageLC = lc.withContext("appID", this.#shard.appID).withContext("shardNum", this.#shard.shardNum).withContext("clientGroupID", clientGroupLogID(this.id)).withContext("queryCoverageMode", "shadow").withContext("hydrationPath", hydrationPath).withContext("totalHydratedQueries", totalHydratedQueries).withContext("coveredHydratedQueries", coveredHydratedQueries).withContext("uncoveredHydratedQueries", totalHydratedQueries - coveredHydratedQueries);
1150
1177
  if (firstCoveredQuery) {
1151
1178
  coverageLC = coverageLC.withContext("firstCoveredQueryHash", firstCoveredQuery.coveredQueryHash).withContext("firstCoveredTransformationHash", firstCoveredQuery.coveredTransformationHash).withContext("firstCoveringQueryHash", firstCoveredQuery.coveringQueryHash).withContext("firstCoveringTransformationHash", firstCoveredQuery.coveringTransformationHash);
1152
1179
  if (firstCoveredQuery.coveredQueryName !== void 0) coverageLC = coverageLC.withContext("firstCoveredQueryName", firstCoveredQuery.coveredQueryName);
@@ -1168,7 +1195,7 @@ var ViewSyncerService = class {
1168
1195
  }, previousQueries) {
1169
1196
  return startAsyncSpan(tracer, "vs.#syncQueryPipelineSet", async (span) => {
1170
1197
  const start = performance.now();
1171
- span.setAttribute("clientGroupID", this.id);
1198
+ span.setAttribute("clientGroupID", clientGroupLogID(this.id));
1172
1199
  assert(this.#pipelines.initialized(), "pipelines must be initialized (syncQueryPipelineSet)");
1173
1200
  if (this.#ttlClock === void 0) this.#ttlClock = cvr.ttlClock;
1174
1201
  const now = Date.now();
@@ -1338,7 +1365,7 @@ var ViewSyncerService = class {
1338
1365
  if (!lc.warn) return;
1339
1366
  const shape = queryShape(query.ast);
1340
1367
  lc.warn("Query hydration aborted for exceeding the hydration timeout", {
1341
- clientGroupID: this.id,
1368
+ clientGroupID: clientGroupLogID(this.id),
1342
1369
  queryHash: query.id,
1343
1370
  transformationHash: query.transformationHash,
1344
1371
  ...query.name !== void 0 && { queryName: query.name },
@@ -1366,7 +1393,7 @@ var ViewSyncerService = class {
1366
1393
  if (suppressed === void 0) return;
1367
1394
  lc.warn("Slow query materialization", {
1368
1395
  zeroEvent: "query-slow-hydration",
1369
- clientGroupID: this.id,
1396
+ clientGroupID: clientGroupLogID(this.id),
1370
1397
  queryHash: query.id,
1371
1398
  transformationHash: query.transformationHash,
1372
1399
  ...query.name !== void 0 && { queryName: query.name },
@@ -1385,7 +1412,7 @@ var ViewSyncerService = class {
1385
1412
  #recordCircuitBreakerRejection(lc, query) {
1386
1413
  this.#hydrationCircuitBreakerRejections.add(1);
1387
1414
  lc.warn?.("Query rejected by its open hydration circuit breaker", {
1388
- clientGroupID: this.id,
1415
+ clientGroupID: clientGroupLogID(this.id),
1389
1416
  queryHash: query.id,
1390
1417
  transformationHash: query.transformationHash,
1391
1418
  ...query.name !== void 0 && { queryName: query.name },
@@ -1427,7 +1454,7 @@ var ViewSyncerService = class {
1427
1454
  };
1428
1455
  for (const clientID of Object.keys(query.clientState)) getOrInsertComputed(errorsByClient, clientID, newArray).push(error);
1429
1456
  }
1430
- for (const [clientID, errors] of errorsByClient) this.#clients.get(clientID)?.sendQueryTransformApplicationErrors(errors);
1457
+ for (const [clientID, errors] of errorsByClient) this.#getValidatedClient(clientID)?.sendQueryTransformApplicationErrors(errors);
1431
1458
  }
1432
1459
  #recordHydrationBudgetExhaustion(lc, hydrationBudget, activeHydratedQueries, inactiveHydratedQueries, inactiveEvictedQueryIDs) {
1433
1460
  const elapsedMs = hydrationBudget.exhaustedAtMs;
@@ -1438,7 +1465,7 @@ var ViewSyncerService = class {
1438
1465
  this.#hydrationBudgetOvershoot.record(Math.max(0, elapsedMs - hydrationBudget.limitMs));
1439
1466
  if (inactiveEvictedQueryIDs.length > 0) this.#queryEvictions.add(inactiveEvictedQueryIDs.length, { reason: "hydration-budget" });
1440
1467
  lc.info?.("view-syncer hydration budget exhausted", {
1441
- clientGroupID: this.id,
1468
+ clientGroupID: clientGroupLogID(this.id),
1442
1469
  hydrationBudgetMs: hydrationBudget.limitMs,
1443
1470
  hydrationElapsedMs: elapsedMs,
1444
1471
  activeHydratedQueries,
@@ -1630,7 +1657,8 @@ var ViewSyncerService = class {
1630
1657
  if (!row.refCounts) patch = {
1631
1658
  type: "row",
1632
1659
  op: "del",
1633
- id
1660
+ id,
1661
+ catchup: true
1634
1662
  };
1635
1663
  else {
1636
1664
  const { contents } = contentsAndVersion(must(this.#pipelines.getRow(table, rowKey), `Missing row in ${table} keyed by ${Object.keys(rowKey).join()}`));
@@ -1728,7 +1756,7 @@ var ViewSyncerService = class {
1728
1756
  */
1729
1757
  #advancePipelines(lc, cvr) {
1730
1758
  return startAsyncSpan(tracer, "vs.#advancePipelines", async (span) => {
1731
- span.setAttribute("clientGroupID", this.id);
1759
+ span.setAttribute("clientGroupID", clientGroupLogID(this.id));
1732
1760
  assert(this.#pipelines.initialized(), "pipelines must be initialized (advancePipelines");
1733
1761
  const start = performance.now();
1734
1762
  const timer = this.#timer(lc, "batch");
@@ -1948,7 +1976,7 @@ var ViewSyncerService = class {
1948
1976
  const now = this.#now();
1949
1977
  if (log.hydrationTimeMs < REBUILD_LOG_MIN_MS || now - this.#rebuildsLoggedAt < REBUILD_LOG_INTERVAL_MS) return;
1950
1978
  lc.info?.("Client group hydrated queries again in its batches of changes instead of pushing the changes to them", {
1951
- clientGroupID: this.id,
1979
+ clientGroupID: clientGroupLogID(this.id),
1952
1980
  ...log
1953
1981
  });
1954
1982
  this.#rebuildsLoggedAt = now;
@@ -2016,7 +2044,7 @@ var ViewSyncerService = class {
2016
2044
  const now = this.#now();
2017
2045
  if (log.waitedMs < BATCH_WAIT_LOG_MIN_MS || now - log.loggedAt < BATCH_WAIT_LOG_INTERVAL_MS) return;
2018
2046
  this.#lc.info?.("Client group is waiting between batches: its sync worker is over its share target", {
2019
- clientGroupID: this.id,
2047
+ clientGroupID: clientGroupLogID(this.id),
2020
2048
  waits: log.waits,
2021
2049
  waitedMs: log.waitedMs,
2022
2050
  lastWaitMs: waitedMs,
@@ -2038,7 +2066,7 @@ var ViewSyncerService = class {
2038
2066
  clientID,
2039
2067
  wsID: client.wsID
2040
2068
  });
2041
- return handleInspect(lc, body, cvr, client, this.#inspectorDelegate, this.id, this, this.#cvrStore, this.#config, connCtx);
2069
+ return handleInspect(lc, body, cvr, client, this.#inspectorDelegate, this.id, this.#cvrStore, this.#config, connCtx);
2042
2070
  };
2043
2071
  async #runBackgroundRetransform(lc) {
2044
2072
  const attemptRetransform = async (connCtx) => {
@@ -2095,7 +2123,8 @@ var ViewSyncerService = class {
2095
2123
  if (response.kind === "TransformFailed") throw new ProtocolErrorWithLevel(response, "warn");
2096
2124
  validation = response.validation;
2097
2125
  } else validation = { kind: "client-fallback" };
2098
- this.connContextManager.validateConnection(connCtx, connCtx.revision, validation);
2126
+ if (!this.#currentRegistration(connCtx)) return false;
2127
+ if (this.connContextManager.validateConnection(connCtx, connCtx.revision, validation) && this.#cvr?.pinnedUser === null) this.#cvr = await this.#flushUpdater(this.#lc, new CVRUpdater(this.#cvrStore, this.#cvr, this.#cvr.replicaVersion));
2099
2128
  return true;
2100
2129
  } catch (e) {
2101
2130
  if (isProtocolError(e) && isAuthErrorBody(e.errorBody)) {
@@ -2105,13 +2134,22 @@ var ViewSyncerService = class {
2105
2134
  revision: connCtx.revision,
2106
2135
  message: e.message
2107
2136
  });
2108
- this.#failMaintenanceConnection(connCtx, e);
2137
+ if (this.#currentRegistration(connCtx)) this.#failMaintenanceConnection(connCtx, e);
2109
2138
  return false;
2110
2139
  }
2111
2140
  throw e;
2112
2141
  }
2113
2142
  }
2114
2143
  /**
2144
+ * The live context registered for `connCtx`'s selector, or `undefined` if
2145
+ * that socket is gone or has been replaced by one reusing the selector
2146
+ * (which starts over at revision 0, so revisions alone cannot tell).
2147
+ */
2148
+ #currentRegistration(connCtx) {
2149
+ const current = this.connContextManager.getConnectionContext(connCtx);
2150
+ return current?.insertionOrder === connCtx.insertionOrder ? current : void 0;
2151
+ }
2152
+ /**
2115
2153
  * Validates the connection's credential as it stands once validation ends.
2116
2154
  * An `updateAuth` handled while a validation is in flight moves the
2117
2155
  * connection to a newer revision, which makes a failure of the older one
@@ -2122,8 +2160,9 @@ var ViewSyncerService = class {
2122
2160
  let connCtx = this.connContextManager.getConnectionContext(selector);
2123
2161
  while (connCtx) {
2124
2162
  const valid = await this.#validateConnection(connCtx);
2125
- const current = this.connContextManager.getConnectionContext(selector);
2126
- if (current?.revision === connCtx.revision) return valid ? connCtx : void 0;
2163
+ const current = this.#currentRegistration(connCtx);
2164
+ if (!current) return;
2165
+ if (current.revision === connCtx.revision) return valid ? connCtx : void 0;
2127
2166
  connCtx = current;
2128
2167
  }
2129
2168
  }
@@ -2150,10 +2189,9 @@ var ViewSyncerService = class {
2150
2189
  this.#stopShutdownTimer();
2151
2190
  this.#customQueryTransformer?.destroy();
2152
2191
  for (const client of this.#clients.values()) if (err) client.fail(err);
2153
- else client.close(`closed clientGroupID=${this.id}`);
2192
+ else client.close(`closed clientGroupID=${clientGroupLogID(this.id)}`);
2154
2193
  await this.#lock.withLock(() => {});
2155
2194
  this.#pipelines.destroy();
2156
- this.#inspectorDelegate.clearAuthenticated(this.id, this);
2157
2195
  }
2158
2196
  /**
2159
2197
  * Test helper: Manually mark initialization as complete.