@rocicorp/zero 1.11.0-canary.0 → 1.11.0-canary.10

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 (577) hide show
  1. package/out/analyze-query/src/bin-analyze.js +1 -1
  2. package/out/analyze-query/src/bin-analyze.js.map +1 -1
  3. package/out/replicache/src/btree/read.d.ts +12 -1
  4. package/out/replicache/src/btree/read.d.ts.map +1 -1
  5. package/out/replicache/src/btree/read.js +24 -12
  6. package/out/replicache/src/btree/read.js.map +1 -1
  7. package/out/replicache/src/dag/gc.d.ts.map +1 -1
  8. package/out/replicache/src/dag/gc.js +2 -1
  9. package/out/replicache/src/dag/gc.js.map +1 -1
  10. package/out/replicache/src/dag/invalid-ref-count-error.d.ts +12 -0
  11. package/out/replicache/src/dag/invalid-ref-count-error.d.ts.map +1 -0
  12. package/out/replicache/src/dag/invalid-ref-count-error.js +19 -0
  13. package/out/replicache/src/dag/invalid-ref-count-error.js.map +1 -0
  14. package/out/replicache/src/dag/store-impl.d.ts +12 -2
  15. package/out/replicache/src/dag/store-impl.d.ts.map +1 -1
  16. package/out/replicache/src/dag/store-impl.js +22 -8
  17. package/out/replicache/src/dag/store-impl.js.map +1 -1
  18. package/out/replicache/src/db/rebase.d.ts +14 -2
  19. package/out/replicache/src/db/rebase.d.ts.map +1 -1
  20. package/out/replicache/src/db/rebase.js +27 -5
  21. package/out/replicache/src/db/rebase.js.map +1 -1
  22. package/out/replicache/src/db/write.js.map +1 -1
  23. package/out/replicache/src/kv/expo-sqlite/store.d.ts.map +1 -1
  24. package/out/replicache/src/kv/expo-sqlite/store.js +28 -5
  25. package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
  26. package/out/replicache/src/kv/idb-store-with-mem-fallback.d.ts +15 -5
  27. package/out/replicache/src/kv/idb-store-with-mem-fallback.d.ts.map +1 -1
  28. package/out/replicache/src/kv/idb-store-with-mem-fallback.js +25 -25
  29. package/out/replicache/src/kv/idb-store-with-mem-fallback.js.map +1 -1
  30. package/out/replicache/src/kv/idb-store.d.ts +9 -0
  31. package/out/replicache/src/kv/idb-store.d.ts.map +1 -1
  32. package/out/replicache/src/kv/idb-store.js +14 -2
  33. package/out/replicache/src/kv/idb-store.js.map +1 -1
  34. package/out/replicache/src/kv/mem-store.d.ts +1 -0
  35. package/out/replicache/src/kv/mem-store.d.ts.map +1 -1
  36. package/out/replicache/src/kv/mem-store.js +3 -0
  37. package/out/replicache/src/kv/mem-store.js.map +1 -1
  38. package/out/replicache/src/kv/op-sqlite/store.d.ts.map +1 -1
  39. package/out/replicache/src/kv/op-sqlite/store.js +1 -1
  40. package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
  41. package/out/replicache/src/kv/sqlite-store.d.ts +13 -1
  42. package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
  43. package/out/replicache/src/kv/sqlite-store.js +49 -4
  44. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  45. package/out/replicache/src/kv/store.d.ts +6 -0
  46. package/out/replicache/src/kv/store.d.ts.map +1 -1
  47. package/out/replicache/src/persist/collect-idb-databases.d.ts +10 -5
  48. package/out/replicache/src/persist/collect-idb-databases.d.ts.map +1 -1
  49. package/out/replicache/src/persist/collect-idb-databases.js +16 -9
  50. package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
  51. package/out/replicache/src/persist/persist.d.ts.map +1 -1
  52. package/out/replicache/src/persist/persist.js +12 -5
  53. package/out/replicache/src/persist/persist.js.map +1 -1
  54. package/out/replicache/src/persist/refresh.js +6 -2
  55. package/out/replicache/src/persist/refresh.js.map +1 -1
  56. package/out/replicache/src/replicache-impl.d.ts +14 -3
  57. package/out/replicache/src/replicache-impl.d.ts.map +1 -1
  58. package/out/replicache/src/replicache-impl.js +118 -11
  59. package/out/replicache/src/replicache-impl.js.map +1 -1
  60. package/out/replicache/src/replicache-options.d.ts +9 -0
  61. package/out/replicache/src/replicache-options.d.ts.map +1 -1
  62. package/out/replicache/src/transactions.d.ts.map +1 -1
  63. package/out/replicache/src/transactions.js +1 -1
  64. package/out/replicache/src/transactions.js.map +1 -1
  65. package/out/shared/src/error.d.ts +11 -0
  66. package/out/shared/src/error.d.ts.map +1 -1
  67. package/out/shared/src/error.js +35 -1
  68. package/out/shared/src/error.js.map +1 -1
  69. package/out/shared/src/iterables.d.ts +2 -0
  70. package/out/shared/src/iterables.d.ts.map +1 -1
  71. package/out/shared/src/iterables.js +18 -1
  72. package/out/shared/src/iterables.js.map +1 -1
  73. package/out/shared/src/map.d.ts +7 -2
  74. package/out/shared/src/map.d.ts.map +1 -1
  75. package/out/shared/src/map.js.map +1 -1
  76. package/out/shared/src/ring-buffer.d.ts +8 -0
  77. package/out/shared/src/ring-buffer.d.ts.map +1 -1
  78. package/out/shared/src/ring-buffer.js +22 -1
  79. package/out/shared/src/ring-buffer.js.map +1 -1
  80. package/out/zero/package.js +3 -4
  81. package/out/zero/package.js.map +1 -1
  82. package/out/zero-cache/src/config/normalize.d.ts.map +1 -1
  83. package/out/zero-cache/src/config/normalize.js +25 -1
  84. package/out/zero-cache/src/config/normalize.js.map +1 -1
  85. package/out/zero-cache/src/config/zero-config.d.ts +48 -2
  86. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  87. package/out/zero-cache/src/config/zero-config.js +171 -29
  88. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  89. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  90. package/out/zero-cache/src/server/change-streamer.js +25 -9
  91. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  92. package/out/zero-cache/src/server/inspector-delegate.d.ts +18 -2
  93. package/out/zero-cache/src/server/inspector-delegate.d.ts.map +1 -1
  94. package/out/zero-cache/src/server/inspector-delegate.js +20 -7
  95. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  96. package/out/zero-cache/src/server/main.d.ts.map +1 -1
  97. package/out/zero-cache/src/server/main.js +8 -1
  98. package/out/zero-cache/src/server/main.js.map +1 -1
  99. package/out/zero-cache/src/server/mutator.js +2 -0
  100. package/out/zero-cache/src/server/mutator.js.map +1 -1
  101. package/out/zero-cache/src/server/reaper.d.ts.map +1 -1
  102. package/out/zero-cache/src/server/reaper.js +2 -0
  103. package/out/zero-cache/src/server/reaper.js.map +1 -1
  104. package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
  105. package/out/zero-cache/src/server/replicator.js +8 -2
  106. package/out/zero-cache/src/server/replicator.js.map +1 -1
  107. package/out/zero-cache/src/server/runner/run-worker.js +1 -1
  108. package/out/zero-cache/src/server/runner/zero-dispatcher.d.ts.map +1 -1
  109. package/out/zero-cache/src/server/runner/zero-dispatcher.js +3 -0
  110. package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
  111. package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
  112. package/out/zero-cache/src/server/syncer.js +2 -0
  113. package/out/zero-cache/src/server/syncer.js.map +1 -1
  114. package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
  115. package/out/zero-cache/src/services/analyze.js +3 -2
  116. package/out/zero-cache/src/services/analyze.js.map +1 -1
  117. package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts.map +1 -1
  118. package/out/zero-cache/src/services/change-source/common/backfill-manager.js +14 -1
  119. package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
  120. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.d.ts.map +1 -1
  121. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js +15 -3
  122. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
  123. package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts.map +1 -1
  124. package/out/zero-cache/src/services/change-source/common/replica-schema.js +2 -39
  125. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  126. package/out/zero-cache/src/services/change-source/pg/backfill-resume.d.ts +156 -0
  127. package/out/zero-cache/src/services/change-source/pg/backfill-resume.d.ts.map +1 -0
  128. package/out/zero-cache/src/services/change-source/pg/backfill-resume.js +15 -0
  129. package/out/zero-cache/src/services/change-source/pg/backfill-resume.js.map +1 -0
  130. package/out/zero-cache/src/services/change-source/pg/change-source-init.d.ts +25 -0
  131. package/out/zero-cache/src/services/change-source/pg/change-source-init.d.ts.map +1 -0
  132. package/out/zero-cache/src/services/change-source/pg/change-source-init.js +202 -0
  133. package/out/zero-cache/src/services/change-source/pg/change-source-init.js.map +1 -0
  134. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts +14 -16
  135. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  136. package/out/zero-cache/src/services/change-source/pg/change-source.js +64 -143
  137. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  138. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +28 -2
  139. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  140. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +44 -21
  141. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  142. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.d.ts +16 -0
  143. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.d.ts.map +1 -0
  144. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js +113 -0
  145. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js.map +1 -0
  146. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +21 -15
  147. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -1
  148. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +86 -23
  149. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  150. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts.map +1 -1
  151. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js +68 -8
  152. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js.map +1 -1
  153. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
  154. package/out/zero-cache/src/services/change-source/pg/schema/init.js +32 -1
  155. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  156. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.d.ts +10 -0
  157. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.d.ts.map +1 -0
  158. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.js +6 -0
  159. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.js.map +1 -0
  160. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +50 -27
  161. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
  162. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +100 -34
  163. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  164. package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts.map +1 -1
  165. package/out/zero-cache/src/services/change-streamer/backup-monitor.js +58 -7
  166. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
  167. package/out/zero-cache/src/services/change-streamer/broadcast.d.ts +8 -2
  168. package/out/zero-cache/src/services/change-streamer/broadcast.d.ts.map +1 -1
  169. package/out/zero-cache/src/services/change-streamer/broadcast.js +24 -4
  170. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  171. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts +19 -4
  172. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts.map +1 -1
  173. package/out/zero-cache/src/services/change-streamer/change-log-codec.js +28 -8
  174. package/out/zero-cache/src/services/change-streamer/change-log-codec.js.map +1 -1
  175. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +5 -2
  176. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  177. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +21 -14
  178. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  179. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +6 -0
  180. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
  181. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +97 -63
  182. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  183. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +12 -8
  184. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
  185. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  186. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts +3 -3
  187. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts.map +1 -1
  188. package/out/zero-cache/src/services/change-streamer/forwarder.js +36 -5
  189. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  190. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts +1 -1
  191. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts.map +1 -1
  192. package/out/zero-cache/src/services/change-streamer/schema/tables.js +4 -4
  193. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  194. package/out/zero-cache/src/services/change-streamer/snapshot.d.ts +3 -1
  195. package/out/zero-cache/src/services/change-streamer/snapshot.d.ts.map +1 -1
  196. package/out/zero-cache/src/services/change-streamer/snapshot.js +54 -20
  197. package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
  198. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts +6 -1
  199. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts.map +1 -1
  200. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +27 -9
  201. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js.map +1 -1
  202. package/out/zero-cache/src/services/change-streamer/storer.d.ts +2 -2
  203. package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
  204. package/out/zero-cache/src/services/change-streamer/storer.js +8 -5
  205. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  206. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +4 -1
  207. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
  208. package/out/zero-cache/src/services/change-streamer/subscriber.js +42 -0
  209. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  210. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts +17 -1
  211. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts.map +1 -1
  212. package/out/zero-cache/src/services/change-streamer/upstream-acker.js +19 -3
  213. package/out/zero-cache/src/services/change-streamer/upstream-acker.js.map +1 -1
  214. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts.map +1 -1
  215. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js +1 -0
  216. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js.map +1 -1
  217. package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
  218. package/out/zero-cache/src/services/life-cycle.js +3 -1
  219. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  220. package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
  221. package/out/zero-cache/src/services/litestream/commands.js +8 -3
  222. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  223. package/out/zero-cache/src/services/litestream/config-v5.yml +15 -1
  224. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js +131 -0
  225. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js.map +1 -0
  226. package/out/zero-cache/src/services/litestream/litestream-controller.d.ts +70 -0
  227. package/out/zero-cache/src/services/litestream/litestream-controller.d.ts.map +1 -0
  228. package/out/zero-cache/src/services/litestream/litestream-controller.js +101 -0
  229. package/out/zero-cache/src/services/litestream/litestream-controller.js.map +1 -0
  230. package/out/zero-cache/src/services/mutagen/mutagen.d.ts +5 -0
  231. package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
  232. package/out/zero-cache/src/services/mutagen/mutagen.js +7 -1
  233. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  234. package/out/zero-cache/src/services/mutagen/pusher.js +2 -1
  235. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  236. package/out/zero-cache/src/services/profz.d.ts +7 -0
  237. package/out/zero-cache/src/services/profz.d.ts.map +1 -0
  238. package/out/zero-cache/src/services/profz.js +105 -0
  239. package/out/zero-cache/src/services/profz.js.map +1 -0
  240. package/out/zero-cache/src/services/replicator/change-log-db.js.map +1 -1
  241. package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts +5 -4
  242. package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts.map +1 -1
  243. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js +20 -22
  244. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js.map +1 -1
  245. package/out/zero-cache/src/services/replicator/change-processor.js +1 -0
  246. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  247. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts +1 -1
  248. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
  249. package/out/zero-cache/src/services/replicator/incremental-sync.js +58 -17
  250. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  251. package/out/zero-cache/src/services/replicator/replication-status.d.ts +35 -1
  252. package/out/zero-cache/src/services/replicator/replication-status.d.ts.map +1 -1
  253. package/out/zero-cache/src/services/replicator/replication-status.js +96 -6
  254. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  255. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  256. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts +6 -6
  257. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts.map +1 -1
  258. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +19 -14
  259. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -1
  260. package/out/zero-cache/src/services/replicator/write-worker-client.d.ts +31 -6
  261. package/out/zero-cache/src/services/replicator/write-worker-client.d.ts.map +1 -1
  262. package/out/zero-cache/src/services/replicator/write-worker-client.js +5 -4
  263. package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
  264. package/out/zero-cache/src/services/replicator/write-worker.js +30 -5
  265. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  266. package/out/zero-cache/src/services/run-ast.d.ts +12 -0
  267. package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
  268. package/out/zero-cache/src/services/run-ast.js +26 -14
  269. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  270. package/out/zero-cache/src/services/runner.d.ts.map +1 -1
  271. package/out/zero-cache/src/services/runner.js +1 -1
  272. package/out/zero-cache/src/services/runner.js.map +1 -1
  273. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts +1 -1
  274. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +1 -1
  275. package/out/zero-cache/src/services/view-syncer/client-handler.js +36 -31
  276. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  277. package/out/zero-cache/src/services/view-syncer/cvr.d.ts +45 -0
  278. package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
  279. package/out/zero-cache/src/services/view-syncer/cvr.js +133 -1
  280. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  281. package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts +14 -1
  282. package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts.map +1 -1
  283. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js +30 -4
  284. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
  285. package/out/zero-cache/src/services/view-syncer/hydration-budget.d.ts +33 -0
  286. package/out/zero-cache/src/services/view-syncer/hydration-budget.d.ts.map +1 -0
  287. package/out/zero-cache/src/services/view-syncer/hydration-budget.js +62 -0
  288. package/out/zero-cache/src/services/view-syncer/hydration-budget.js.map +1 -0
  289. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts +51 -0
  290. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts.map +1 -0
  291. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js +78 -0
  292. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js.map +1 -0
  293. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts +1 -1
  294. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts.map +1 -1
  295. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +2 -2
  296. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  297. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +2 -1
  298. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  299. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +33 -18
  300. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  301. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +4 -1
  302. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
  303. package/out/zero-cache/src/services/view-syncer/snapshotter.js +9 -3
  304. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  305. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +11 -1
  306. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  307. package/out/zero-cache/src/services/view-syncer/view-syncer.js +507 -93
  308. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  309. package/out/zero-cache/src/types/processes.d.ts +32 -0
  310. package/out/zero-cache/src/types/processes.d.ts.map +1 -1
  311. package/out/zero-cache/src/types/processes.js +43 -4
  312. package/out/zero-cache/src/types/processes.js.map +1 -1
  313. package/out/zero-cache/src/types/profiler.d.ts +12 -0
  314. package/out/zero-cache/src/types/profiler.d.ts.map +1 -1
  315. package/out/zero-cache/src/types/profiler.js +46 -1
  316. package/out/zero-cache/src/types/profiler.js.map +1 -1
  317. package/out/zero-cache/src/types/streams.d.ts +25 -9
  318. package/out/zero-cache/src/types/streams.d.ts.map +1 -1
  319. package/out/zero-cache/src/types/streams.js +118 -34
  320. package/out/zero-cache/src/types/streams.js.map +1 -1
  321. package/out/zero-cache/src/types/subscription.d.ts +4 -0
  322. package/out/zero-cache/src/types/subscription.d.ts.map +1 -1
  323. package/out/zero-cache/src/types/subscription.js +71 -0
  324. package/out/zero-cache/src/types/subscription.js.map +1 -1
  325. package/out/zero-cache/src/types/ws.d.ts.map +1 -1
  326. package/out/zero-cache/src/types/ws.js +23 -8
  327. package/out/zero-cache/src/types/ws.js.map +1 -1
  328. package/out/zero-cache/src/workers/connection.js +2 -2
  329. package/out/zero-cache/src/workers/replicator.d.ts +1 -0
  330. package/out/zero-cache/src/workers/replicator.d.ts.map +1 -1
  331. package/out/zero-cache/src/workers/replicator.js +4 -2
  332. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  333. package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
  334. package/out/zero-cache/src/workers/syncer-ws-message-handler.js +21 -16
  335. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  336. package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
  337. package/out/zero-cache/src/workers/syncer.js +10 -1
  338. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  339. package/out/zero-client/src/client/connection-manager.d.ts +20 -0
  340. package/out/zero-client/src/client/connection-manager.d.ts.map +1 -1
  341. package/out/zero-client/src/client/connection-manager.js +77 -13
  342. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  343. package/out/zero-client/src/client/connection-status-enum.d.ts +2 -0
  344. package/out/zero-client/src/client/connection-status-enum.d.ts.map +1 -1
  345. package/out/zero-client/src/client/connection-status-enum.js +3 -1
  346. package/out/zero-client/src/client/connection-status-enum.js.map +1 -1
  347. package/out/zero-client/src/client/connection.d.ts +6 -0
  348. package/out/zero-client/src/client/connection.d.ts.map +1 -1
  349. package/out/zero-client/src/client/connection.js +2 -1
  350. package/out/zero-client/src/client/connection.js.map +1 -1
  351. package/out/zero-client/src/client/context.d.ts +27 -2
  352. package/out/zero-client/src/client/context.d.ts.map +1 -1
  353. package/out/zero-client/src/client/context.js +112 -1
  354. package/out/zero-client/src/client/context.js.map +1 -1
  355. package/out/zero-client/src/client/custom.js +2 -2
  356. package/out/zero-client/src/client/custom.js.map +1 -1
  357. package/out/zero-client/src/client/error.js +1 -1
  358. package/out/zero-client/src/client/inspector/lazy-inspector.js +2 -2
  359. package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
  360. package/out/zero-client/src/client/ivm-branch.d.ts.map +1 -1
  361. package/out/zero-client/src/client/ivm-branch.js +20 -3
  362. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  363. package/out/zero-client/src/client/mutator-proxy.d.ts.map +1 -1
  364. package/out/zero-client/src/client/mutator-proxy.js +2 -1
  365. package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
  366. package/out/zero-client/src/client/options.d.ts +3 -0
  367. package/out/zero-client/src/client/options.d.ts.map +1 -1
  368. package/out/zero-client/src/client/options.js.map +1 -1
  369. package/out/zero-client/src/client/query-manager.d.ts.map +1 -1
  370. package/out/zero-client/src/client/query-manager.js +42 -6
  371. package/out/zero-client/src/client/query-manager.js.map +1 -1
  372. package/out/zero-client/src/client/version.js +1 -1
  373. package/out/zero-client/src/client/zero-rep.d.ts +1 -0
  374. package/out/zero-client/src/client/zero-rep.d.ts.map +1 -1
  375. package/out/zero-client/src/client/zero-rep.js +9 -6
  376. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  377. package/out/zero-client/src/client/zero.d.ts +18 -3
  378. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  379. package/out/zero-client/src/client/zero.js +122 -34
  380. package/out/zero-client/src/client/zero.js.map +1 -1
  381. package/out/zero-client/src/types/query-result.d.ts +2 -0
  382. package/out/zero-client/src/types/query-result.d.ts.map +1 -1
  383. package/out/zero-events/src/status.d.ts +26 -0
  384. package/out/zero-events/src/status.d.ts.map +1 -1
  385. package/out/zero-protocol/src/analyze-query-result.d.ts +5 -5
  386. package/out/zero-protocol/src/analyze-query-result.d.ts.map +1 -1
  387. package/out/zero-protocol/src/analyze-query-result.js +1 -1
  388. package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
  389. package/out/zero-protocol/src/down.d.ts +4 -4
  390. package/out/zero-protocol/src/inspect-down.d.ts +6 -6
  391. package/out/zero-protocol/src/protocol-version.d.ts +1 -1
  392. package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
  393. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  394. package/out/zero-react/src/use-query.d.ts +5 -4
  395. package/out/zero-react/src/use-query.d.ts.map +1 -1
  396. package/out/zero-react/src/use-query.js +86 -9
  397. package/out/zero-react/src/use-query.js.map +1 -1
  398. package/out/zero-server/src/adapters/drizzle.d.ts +2 -1
  399. package/out/zero-server/src/adapters/drizzle.d.ts.map +1 -1
  400. package/out/zero-server/src/adapters/drizzle.js +12 -7
  401. package/out/zero-server/src/adapters/drizzle.js.map +1 -1
  402. package/out/zero-server/src/adapters/kysely.d.ts +2 -1
  403. package/out/zero-server/src/adapters/kysely.d.ts.map +1 -1
  404. package/out/zero-server/src/adapters/kysely.js +8 -2
  405. package/out/zero-server/src/adapters/kysely.js.map +1 -1
  406. package/out/zero-server/src/adapters/pg.d.ts +1 -0
  407. package/out/zero-server/src/adapters/pg.d.ts.map +1 -1
  408. package/out/zero-server/src/adapters/pg.js +8 -2
  409. package/out/zero-server/src/adapters/pg.js.map +1 -1
  410. package/out/zero-server/src/adapters/postgresjs.d.ts +1 -0
  411. package/out/zero-server/src/adapters/postgresjs.d.ts.map +1 -1
  412. package/out/zero-server/src/adapters/postgresjs.js +7 -1
  413. package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
  414. package/out/zero-server/src/adapters/prisma.d.ts +3 -2
  415. package/out/zero-server/src/adapters/prisma.d.ts.map +1 -1
  416. package/out/zero-server/src/adapters/prisma.js +8 -2
  417. package/out/zero-server/src/adapters/prisma.js.map +1 -1
  418. package/out/zero-server/src/custom.d.ts +5 -1
  419. package/out/zero-server/src/custom.d.ts.map +1 -1
  420. package/out/zero-server/src/custom.js +6 -3
  421. package/out/zero-server/src/custom.js.map +1 -1
  422. package/out/zero-server/src/mod.d.ts +1 -1
  423. package/out/zero-server/src/mod.d.ts.map +1 -1
  424. package/out/zero-server/src/pg-query-executor.d.ts +2 -2
  425. package/out/zero-server/src/pg-query-executor.d.ts.map +1 -1
  426. package/out/zero-server/src/pg-query-executor.js +2 -2
  427. package/out/zero-server/src/pg-query-executor.js.map +1 -1
  428. package/out/zero-server/src/schema.d.ts +2 -2
  429. package/out/zero-server/src/schema.d.ts.map +1 -1
  430. package/out/zero-server/src/schema.js +2 -2
  431. package/out/zero-server/src/schema.js.map +1 -1
  432. package/out/zero-server/src/zql-database.d.ts +21 -1
  433. package/out/zero-server/src/zql-database.d.ts.map +1 -1
  434. package/out/zero-server/src/zql-database.js +33 -2
  435. package/out/zero-server/src/zql-database.js.map +1 -1
  436. package/out/zero-solid/src/solid-view.d.ts +17 -0
  437. package/out/zero-solid/src/solid-view.d.ts.map +1 -1
  438. package/out/zero-solid/src/solid-view.js +39 -1
  439. package/out/zero-solid/src/solid-view.js.map +1 -1
  440. package/out/zero-solid/src/use-query.js +5 -3
  441. package/out/zero-solid/src/use-query.js.map +1 -1
  442. package/out/zql/src/builder/builder.d.ts.map +1 -1
  443. package/out/zql/src/builder/builder.js +16 -10
  444. package/out/zql/src/builder/builder.js.map +1 -1
  445. package/out/zql/src/builder/debug-delegate.d.ts +6 -1
  446. package/out/zql/src/builder/debug-delegate.d.ts.map +1 -1
  447. package/out/zql/src/builder/debug-delegate.js +14 -2
  448. package/out/zql/src/builder/debug-delegate.js.map +1 -1
  449. package/out/zql/src/ivm/array-view.d.ts +29 -0
  450. package/out/zql/src/ivm/array-view.d.ts.map +1 -1
  451. package/out/zql/src/ivm/array-view.js +63 -12
  452. package/out/zql/src/ivm/array-view.js.map +1 -1
  453. package/out/zql/src/ivm/cap.d.ts.map +1 -1
  454. package/out/zql/src/ivm/cap.js +5 -3
  455. package/out/zql/src/ivm/cap.js.map +1 -1
  456. package/out/zql/src/ivm/data.d.ts.map +1 -1
  457. package/out/zql/src/ivm/data.js +8 -3
  458. package/out/zql/src/ivm/data.js.map +1 -1
  459. package/out/zql/src/ivm/deferred-input.d.ts +44 -0
  460. package/out/zql/src/ivm/deferred-input.d.ts.map +1 -0
  461. package/out/zql/src/ivm/deferred-input.js +89 -0
  462. package/out/zql/src/ivm/deferred-input.js.map +1 -0
  463. package/out/zql/src/ivm/exists.d.ts +1 -1
  464. package/out/zql/src/ivm/exists.d.ts.map +1 -1
  465. package/out/zql/src/ivm/exists.js.map +1 -1
  466. package/out/zql/src/ivm/fan-in.d.ts +2 -2
  467. package/out/zql/src/ivm/fan-in.d.ts.map +1 -1
  468. package/out/zql/src/ivm/fan-in.js +6 -5
  469. package/out/zql/src/ivm/fan-in.js.map +1 -1
  470. package/out/zql/src/ivm/fan-out.d.ts +2 -2
  471. package/out/zql/src/ivm/fan-out.d.ts.map +1 -1
  472. package/out/zql/src/ivm/fan-out.js.map +1 -1
  473. package/out/zql/src/ivm/filter-operators.d.ts +7 -6
  474. package/out/zql/src/ivm/filter-operators.d.ts.map +1 -1
  475. package/out/zql/src/ivm/filter-operators.js +29 -12
  476. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  477. package/out/zql/src/ivm/filter.d.ts +2 -2
  478. package/out/zql/src/ivm/filter.d.ts.map +1 -1
  479. package/out/zql/src/ivm/filter.js +7 -4
  480. package/out/zql/src/ivm/filter.js.map +1 -1
  481. package/out/zql/src/ivm/flipped-join.js +2 -2
  482. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  483. package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts +1 -1
  484. package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts.map +1 -1
  485. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js +6 -4
  486. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
  487. package/out/zql/src/ivm/memory-source.d.ts +15 -3
  488. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  489. package/out/zql/src/ivm/memory-source.js +57 -23
  490. package/out/zql/src/ivm/memory-source.js.map +1 -1
  491. package/out/zql/src/ivm/operator.d.ts +40 -0
  492. package/out/zql/src/ivm/operator.d.ts.map +1 -1
  493. package/out/zql/src/ivm/operator.js.map +1 -1
  494. package/out/zql/src/ivm/take.d.ts.map +1 -1
  495. package/out/zql/src/ivm/take.js +3 -4
  496. package/out/zql/src/ivm/take.js.map +1 -1
  497. package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
  498. package/out/zql/src/ivm/union-fan-in.js +9 -7
  499. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  500. package/out/zql/src/ivm/view-apply-change.d.ts.map +1 -1
  501. package/out/zql/src/ivm/view-apply-change.js +9 -4
  502. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  503. package/out/zql/src/ivm/view.d.ts +15 -0
  504. package/out/zql/src/ivm/view.d.ts.map +1 -1
  505. package/out/zql/src/mutate/custom.d.ts +24 -1
  506. package/out/zql/src/mutate/custom.d.ts.map +1 -1
  507. package/out/zql/src/mutate/custom.js.map +1 -1
  508. package/out/zql/src/mutate/mutator-registry.d.ts.map +1 -1
  509. package/out/zql/src/mutate/mutator-registry.js +2 -2
  510. package/out/zql/src/mutate/mutator-registry.js.map +1 -1
  511. package/out/zql/src/planner/planner-builder.d.ts.map +1 -1
  512. package/out/zql/src/planner/planner-builder.js +14 -4
  513. package/out/zql/src/planner/planner-builder.js.map +1 -1
  514. package/out/zql/src/planner/planner-connection.d.ts +13 -0
  515. package/out/zql/src/planner/planner-connection.d.ts.map +1 -1
  516. package/out/zql/src/planner/planner-connection.js +42 -7
  517. package/out/zql/src/planner/planner-connection.js.map +1 -1
  518. package/out/zql/src/planner/planner-debug.d.ts +1 -1
  519. package/out/zql/src/planner/planner-debug.d.ts.map +1 -1
  520. package/out/zql/src/planner/planner-debug.js.map +1 -1
  521. package/out/zql/src/planner/planner-fan-out.d.ts +1 -0
  522. package/out/zql/src/planner/planner-fan-out.d.ts.map +1 -1
  523. package/out/zql/src/planner/planner-fan-out.js +3 -0
  524. package/out/zql/src/planner/planner-fan-out.js.map +1 -1
  525. package/out/zql/src/planner/planner-filter.d.ts +59 -0
  526. package/out/zql/src/planner/planner-filter.d.ts.map +1 -0
  527. package/out/zql/src/planner/planner-filter.js +116 -0
  528. package/out/zql/src/planner/planner-filter.js.map +1 -0
  529. package/out/zql/src/planner/planner-graph.d.ts +2 -0
  530. package/out/zql/src/planner/planner-graph.d.ts.map +1 -1
  531. package/out/zql/src/planner/planner-graph.js +2 -0
  532. package/out/zql/src/planner/planner-graph.js.map +1 -1
  533. package/out/zql/src/planner/planner-join.d.ts +2 -0
  534. package/out/zql/src/planner/planner-join.d.ts.map +1 -1
  535. package/out/zql/src/planner/planner-join.js +7 -0
  536. package/out/zql/src/planner/planner-join.js.map +1 -1
  537. package/out/zql/src/planner/planner-node.d.ts +2 -1
  538. package/out/zql/src/planner/planner-node.d.ts.map +1 -1
  539. package/out/zql/src/planner/planner-node.js.map +1 -1
  540. package/out/zql/src/query/query-delegate-base.d.ts +9 -1
  541. package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
  542. package/out/zql/src/query/query-delegate-base.js +129 -33
  543. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  544. package/out/zql/src/query/query-delegate.d.ts +50 -1
  545. package/out/zql/src/query/query-delegate.d.ts.map +1 -1
  546. package/out/zql/src/query/query-impl.d.ts +13 -11
  547. package/out/zql/src/query/query-impl.d.ts.map +1 -1
  548. package/out/zql/src/query/query-impl.js +383 -60
  549. package/out/zql/src/query/query-impl.js.map +1 -1
  550. package/out/zql/src/query/query-transitions.d.ts +171 -0
  551. package/out/zql/src/query/query-transitions.d.ts.map +1 -0
  552. package/out/zql/src/query/query-transitions.js +207 -0
  553. package/out/zql/src/query/query-transitions.js.map +1 -0
  554. package/out/zql/src/query/query.d.ts +11 -2
  555. package/out/zql/src/query/query.d.ts.map +1 -1
  556. package/out/zql/src/query/runnable-query-impl.d.ts +1 -0
  557. package/out/zql/src/query/runnable-query-impl.d.ts.map +1 -1
  558. package/out/zql/src/query/runnable-query-impl.js +21 -1
  559. package/out/zql/src/query/runnable-query-impl.js.map +1 -1
  560. package/out/zql/src/query/typed-view.d.ts +1 -1
  561. package/out/zql/src/query/typed-view.d.ts.map +1 -1
  562. package/out/zqlite/src/db.d.ts +9 -1
  563. package/out/zqlite/src/db.d.ts.map +1 -1
  564. package/out/zqlite/src/db.js +8 -2
  565. package/out/zqlite/src/db.js.map +1 -1
  566. package/out/zqlite/src/query-builder.d.ts +1 -1
  567. package/out/zqlite/src/query-builder.d.ts.map +1 -1
  568. package/out/zqlite/src/query-builder.js +2 -1
  569. package/out/zqlite/src/query-builder.js.map +1 -1
  570. package/out/zqlite/src/sqlite-cost-model.d.ts.map +1 -1
  571. package/out/zqlite/src/sqlite-cost-model.js +2 -30
  572. package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
  573. package/out/zqlite/src/table-source.d.ts +1 -0
  574. package/out/zqlite/src/table-source.d.ts.map +1 -1
  575. package/out/zqlite/src/table-source.js +20 -8
  576. package/out/zqlite/src/table-source.js.map +1 -1
  577. package/package.json +3 -4
@@ -10,12 +10,11 @@ import { manualSpan, startAsyncSpan, startSpan } from "../../../../otel/src/span
10
10
  import { ZERO_VERSION_COLUMN_NAME } from "../replicator/schema/constants.js";
11
11
  import { transformAndHashQuery } from "../../auth/read-authorizer.js";
12
12
  import { randInt } from "../../../../shared/src/rand.js";
13
- import { getOrCreateCounter, getOrCreateLatencyHistogram, getOrCreateNativeHistogram, getOrCreateUpDownCounter } from "../../observability/metrics.js";
13
+ import { getOrCreateCounter, getOrCreateLatencyHistogram, getOrCreateNativeHistogram, getOrCreateUpDownCounter, getOrCreateValueHistogram } from "../../observability/metrics.js";
14
14
  import { rowIDString } from "../../types/row-key.js";
15
15
  import "../replicator/schema/replication-state.js";
16
16
  import { parseSignature } from "./row-set-signature.js";
17
17
  import { ResetPipelinesSignal } from "./snapshotter.js";
18
- import "./pipeline-driver.js";
19
18
  import { Subscription } from "../../types/subscription.js";
20
19
  import { CustomKeyMap } from "../../../../shared/src/custom-key-map.js";
21
20
  import { ttlClockAsNumber, ttlClockFromNumber } from "./ttl-clock.js";
@@ -25,8 +24,10 @@ import { isAuthErrorBody } from "../../auth/auth.js";
25
24
  import { ClientHandler, startPoke } from "./client-handler.js";
26
25
  import { tracer } from "./tracer.js";
27
26
  import { CVRStore, ClientNotFoundError } from "./cvr-store.js";
28
- import { CVRConfigDrivenUpdater, CVRQueryDrivenUpdater, nextEvictionTime } from "./cvr.js";
27
+ import { CVRConfigDrivenUpdater, CVRQueryDrivenUpdater, classifyQueriesForHydration, nextEvictionTime } from "./cvr.js";
29
28
  import { E2EServingLagTracker } from "./e2e-serving-lag.js";
29
+ import { HydrationBudget } from "./hydration-budget.js";
30
+ import { HydrationCircuitBreaker } from "./hydration-circuit-breaker.js";
30
31
  import { handleInspect } from "./inspect-handler.js";
31
32
  import { QueryCoveringIndex } from "./query-covering.js";
32
33
  import { resolver } from "@rocicorp/resolver";
@@ -61,6 +62,7 @@ var ViewSyncerService = class {
61
62
  #drainCoordinator;
62
63
  #keepaliveMs;
63
64
  #slowHydrateThreshold;
65
+ #hydrationCircuitBreaker;
64
66
  #lastConnectTime = Date.now();
65
67
  /**
66
68
  * The TTL clock is used to determine the time at which queries are considered
@@ -88,14 +90,37 @@ var ViewSyncerService = class {
88
90
  #lock = new Lock();
89
91
  #cvrStore;
90
92
  #stopped = resolver();
93
+ /**
94
+ * Set when {@link #cleanup} begins. Lock tasks that were in flight when the
95
+ * view-syncer was stopped may still complete after the timers have been
96
+ * cleared; this flag prevents them from scheduling new timers, which would
97
+ * otherwise outlive the service (and retain everything it references).
98
+ */
99
+ #shuttingDown = false;
91
100
  #initialized = resolver();
92
101
  #cvr;
93
- #pipelinesSynced = false;
102
+ /**
103
+ * Indicates whether the query pipelines have completed initial catch-up and
104
+ * hydration with the CVR snapshot, and are ready for steady-state operation.
105
+ *
106
+ * - When `false`: The syncer is in its initial catch-up phase. The replica
107
+ * advances without diffs until it reaches `cvr.version.stateVersion`,
108
+ * after which `#maybeHydratePipelines` hydrates queries.
109
+ * - When `true`: Pipelines are fully populated and in steady-state; replica
110
+ * changes advance incrementally via `#advancePipelines`, and client query
111
+ * updates are processed via `#syncQueryPipelineSet`.
112
+ *
113
+ * Resets to `false` if `#advancePipelines` returns a `ResetPipelinesSignal`
114
+ * and pipelines must be reset and rehydrated, or if a reloaded CVR is ahead
115
+ * of the pipelines (see `#resetPipelinesIfBehindCVR`).
116
+ */
117
+ #pipelinesHydrated = false;
94
118
  #servedVersion = null;
95
119
  #e2eServingLagTracker = new E2EServingLagTracker();
96
120
  #expiredQueriesTimer = 0;
97
121
  #authMaintenanceTimer = 0;
98
122
  #setTimeout;
123
+ #now;
99
124
  #customQueryTransformer;
100
125
  #queryReplacements = /* @__PURE__ */ new Map();
101
126
  #activeClients = getOrCreateUpDownCounter("sync", "active-clients", "Number of active sync clients");
@@ -122,10 +147,64 @@ var ViewSyncerService = class {
122
147
  #pipelineResets = getOrCreateCounter("sync", "pipeline-resets", "Number of pipeline resets");
123
148
  #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).");
124
149
  #sameHashRehydrationVersionBumps = getOrCreateCounter("sync", "query.same-hash-rehydrations-forced-bump", "Number of times query-set reconciliation forced a configVersion bump for already-gotten same-transformation-hash query rehydration because trackQueries would not otherwise bump. Expected to be near-zero; non-zero values indicate pipeline/CVR row-set drift reached query-set reconciliation.");
150
+ #hydrationBudgetExhaustions = getOrCreateCounter("sync", "hydration_budget_exhaustions", {
151
+ description: "Number of hydration passes that exhaust their budget.",
152
+ unit: "{pass}"
153
+ });
154
+ #hydrationBudgetEvictions = getOrCreateCounter("sync", "hydration_budget_evictions", {
155
+ description: "Number of inactive queries removed after hydration budget exhaustion.",
156
+ unit: "{query}"
157
+ });
158
+ #hydrationBudgetElapsed = getOrCreateValueHistogram("sync", "hydration_budget_elapsed", {
159
+ description: "Elapsed milliseconds when optional query hydration stopped.",
160
+ unit: "ms",
161
+ bucketBoundaries: [
162
+ 1,
163
+ 2,
164
+ 5,
165
+ 10,
166
+ 20,
167
+ 50,
168
+ 100,
169
+ 200,
170
+ 500,
171
+ 1e3,
172
+ 5e3
173
+ ]
174
+ });
175
+ #hydrationBudgetOvershoot = getOrCreateValueHistogram("sync", "hydration_budget_overshoot", {
176
+ description: "Milliseconds elapsed beyond the configured hydration budget.",
177
+ unit: "ms",
178
+ bucketBoundaries: [
179
+ 0,
180
+ 1,
181
+ 2,
182
+ 5,
183
+ 10,
184
+ 20,
185
+ 50,
186
+ 100,
187
+ 200,
188
+ 500,
189
+ 1e3
190
+ ]
191
+ });
192
+ #queryEvictions = getOrCreateCounter("sync", "query_evictions", {
193
+ description: "Number of queries evicted from the CVR ahead of their removal by the client, grouped by reason. Inactive queries are evicted by ttl and hydration-budget; hydration-timeout and hydration-circuit-breaker evict active queries as well.",
194
+ unit: "{query}"
195
+ });
196
+ #hydrationTimeouts = getOrCreateCounter("sync", "hydration_timeouts", {
197
+ description: "Number of query hydrations aborted for exceeding the query hydration timeout.",
198
+ unit: "{query}"
199
+ });
200
+ #hydrationCircuitBreakerRejections = getOrCreateCounter("sync", "hydration_circuit_breaker_rejections", {
201
+ description: "Number of queries rejected without hydration because their hydration circuit breaker was open.",
202
+ unit: "{query}"
203
+ });
125
204
  #inspectorDelegate;
126
205
  #config;
127
206
  #runPriorityOp;
128
- constructor(config, lc, shard, taskID, clientGroupID, cvrDb, pipelineDriver, versionChanges, drainCoordinator, slowHydrateThreshold, inspectorDelegate, connContextManager, customQueryTransformer, runPriorityOp, keepaliveMs = DEFAULT_KEEPALIVE_MS, setTimeoutFn = setTimeout.bind(globalThis)) {
207
+ constructor(config, lc, shard, taskID, clientGroupID, cvrDb, pipelineDriver, versionChanges, drainCoordinator, slowHydrateThreshold, inspectorDelegate, connContextManager, customQueryTransformer, runPriorityOp, keepaliveMs = DEFAULT_KEEPALIVE_MS, setTimeoutFn = setTimeout.bind(globalThis), now = performance.now.bind(performance)) {
129
208
  this.#config = config;
130
209
  this.id = clientGroupID;
131
210
  this.connContextManager = connContextManager;
@@ -140,6 +219,8 @@ var ViewSyncerService = class {
140
219
  this.#customQueryTransformer = customQueryTransformer;
141
220
  this.#cvrStore = new CVRStore(lc, cvrDb, shard, taskID, clientGroupID, () => this.#stateChanges.cancel());
142
221
  this.#setTimeout = setTimeoutFn;
222
+ this.#now = now;
223
+ this.#hydrationCircuitBreaker = new HydrationCircuitBreaker(config.viewSyncerQueryHydrationTimeoutMs ?? 0, void 0, now);
143
224
  this.#runPriorityOp = runPriorityOp;
144
225
  this.keepalive();
145
226
  }
@@ -166,11 +247,13 @@ var ViewSyncerService = class {
166
247
  this.#stateChanges.cancel();
167
248
  return;
168
249
  }
250
+ let reloaded = false;
169
251
  if (!this.#cvr) {
170
252
  this.#lc.debug?.("loading cvr");
171
253
  this.#cvr = await this.#runPriorityOp(lc, "loading cvr", () => this.#cvrStore.load(lc, this.#lastConnectTime));
172
254
  this.#ttlClock = this.#cvr.ttlClock;
173
255
  this.#ttlClockBase = Date.now();
256
+ reloaded = true;
174
257
  } else {
175
258
  const now = Date.now();
176
259
  this.#cvr = {
@@ -179,6 +262,10 @@ var ViewSyncerService = class {
179
262
  };
180
263
  }
181
264
  try {
265
+ if (reloaded && this.#resetPipelinesIfBehindCVR(lc, this.#cvr)) {
266
+ const connCtx = this.connContextManager.getBackgroundConnectionContext();
267
+ if (connCtx) await this.#maybeHydratePipelines(lc, this.#cvr, connCtx);
268
+ }
182
269
  await fn(lc, this.#cvr);
183
270
  } catch (e) {
184
271
  this.#cvr = void 0;
@@ -188,11 +275,47 @@ var ViewSyncerService = class {
188
275
  }
189
276
  });
190
277
  }
278
+ /**
279
+ * The CVR is reloaded after an error clears the cached copy. By then another
280
+ * view-syncer may have taken over the client group and flushed the CVR at a
281
+ * version ahead of this instance's hydrated pipelines (e.g. its replica was
282
+ * further ahead). The pipelines can no longer be diffed against the CVR, so
283
+ * reset them and let `#maybeHydratePipelines` rehydrate once the replica has
284
+ * caught up to the CVR.
285
+ *
286
+ * A CVR behind the pipelines is expected (advancements that do not change
287
+ * the CVR are not flushed) and is handled by the normal update path.
288
+ *
289
+ * Returns whether the pipelines were reset.
290
+ *
291
+ * Must be called from within the #lock.
292
+ */
293
+ #resetPipelinesIfBehindCVR(lc, cvr) {
294
+ if (!this.#pipelinesHydrated) return false;
295
+ const pipelineVersion = this.#pipelines.currentVersion();
296
+ if (pipelineVersion >= cvr.version.stateVersion) return false;
297
+ lc.info?.(`resetting pipelines: pipelines@${pipelineVersion} are behind reloaded cvr@${versionString(cvr.version)}`);
298
+ this.#pipelineResets.add(1, { reason: "behind-cvr" });
299
+ this.#pipelinesHydrated = false;
300
+ this.connContextManager.setSharedRetransformReady(false);
301
+ this.#pipelines.reset(must(cvr.clientSchema, "cvr.clientSchema missing after initialization"));
302
+ return true;
303
+ }
191
304
  readyState() {
192
- return Promise.race([this.#initialized.promise, this.#drainCoordinator.draining]);
305
+ return new Promise((resolve, reject) => {
306
+ const unsubscribe = this.#drainCoordinator.onDraining(() => resolve("draining"));
307
+ this.#initialized.promise.then((state) => {
308
+ unsubscribe();
309
+ resolve(state);
310
+ }, (err) => {
311
+ unsubscribe();
312
+ reject(err);
313
+ });
314
+ });
193
315
  }
194
316
  async run() {
195
317
  try {
318
+ this.#scheduleShutdown(this.#keepaliveMs);
196
319
  if (await this.readyState() === "draining") {
197
320
  this.#lc.debug?.(`draining view-syncer ${this.id} before running`);
198
321
  this.stop();
@@ -213,26 +336,29 @@ var ViewSyncerService = class {
213
336
  lc.info?.(`resetting CVR: ${message}`);
214
337
  throw new ClientNotFoundError(message);
215
338
  }
216
- if (this.#pipelinesSynced) {
339
+ let previousQueries;
340
+ if (this.#pipelinesHydrated) {
217
341
  const result = await this.#advancePipelines(lc, cvr);
218
342
  if (result === "success") return;
219
343
  lc.info?.(`resetting pipelines: ${result.message}`);
220
344
  this.#pipelineResets.add(1, { reason: result.reason });
345
+ switch (result.reason) {
346
+ case "advancement-timeout":
347
+ case "scalar-subquery":
348
+ case "truncation":
349
+ case "schema-change":
350
+ if (!Object.values(cvr.queries).some((q) => q.type === "client")) previousQueries = new Map(this.#pipelines.queries());
351
+ break;
352
+ case "permissions-change": break;
353
+ default: unreachable(result.reason);
354
+ }
221
355
  this.#pipelines.reset(clientSchema);
222
- this.#pipelinesSynced = false;
356
+ this.#pipelinesHydrated = false;
223
357
  this.connContextManager.setSharedRetransformReady(false);
224
358
  }
225
- const version = this.#pipelines.advanceWithoutDiff();
226
- const cvrVer = versionString(cvr.version);
227
- if (version < cvr.version.stateVersion) {
228
- lc.debug?.(`replica@${version} is behind cvr@${cvrVer}`);
229
- return;
230
- }
231
- lc.info?.(`init pipelines@${version} (cvr@${cvrVer})`);
232
- const driftedQueryIDs = await this.#hydrateUnchangedQueries(lc, cvr);
233
- await this.#syncQueryPipelineSet(lc, cvr, "missing", void 0, driftedQueryIDs);
234
- this.#pipelinesSynced = true;
235
- this.connContextManager.setSharedRetransformReady(true);
359
+ const backgroundConnCtx = this.connContextManager.getBackgroundConnectionContext();
360
+ if (backgroundConnCtx) await this.#maybeHydratePipelines(lc, cvr, backgroundConnCtx, previousQueries);
361
+ else lc.info?.("No validated background connection; deferring pipeline init");
236
362
  });
237
363
  }
238
364
  if (this.#drainCoordinator.shouldDrain()) this.#drainCoordinator.drainNextIn(this.#totalHydrationTimeMs());
@@ -246,11 +372,57 @@ var ViewSyncerService = class {
246
372
  this.#stopped.resolve();
247
373
  }
248
374
  }
375
+ /**
376
+ * Ensures that query pipelines are hydrated and ready for steady-state
377
+ * operation.
378
+ *
379
+ * Preconditions:
380
+ * 1. Must be called from within the `#lock`.
381
+ * 2. The caller must provide an active, validated `ConnectionContext` for
382
+ * evaluating custom query transforms and permissions.
383
+ *
384
+ * Readiness prerequisites:
385
+ * - The underlying `QueryPipelineDriver` must be initialized with the schema
386
+ * (i.e. `this.#pipelines.initialized()` is true).
387
+ * - The SQLite replica must have caught up to at least the CVR's
388
+ * `stateVersion` (`version >= cvr.version.stateVersion`).
389
+ *
390
+ * If either prerequisite is not met, this method returns early as a no-op,
391
+ * waiting for the replica to catch up or the driver to initialize.
392
+ *
393
+ * Once ready:
394
+ * - Hydrates unchanged queries from the CVR snapshot without full row diffing.
395
+ * - Syncs missing, errored, or drifted queries via `#syncQueryPipelineSet`.
396
+ * - Sets `#pipelinesHydrated = true` and enables shared retransformations.
397
+ */
398
+ async #maybeHydratePipelines(lc, cvr, connCtx, previousQueries) {
399
+ if (!this.#pipelines.initialized()) return;
400
+ const version = this.#pipelines.advanceWithoutDiff();
401
+ const cvrVer = versionString(cvr.version);
402
+ if (version < cvr.version.stateVersion) {
403
+ lc.debug?.(`replica@${version} is behind cvr@${cvrVer}`);
404
+ return;
405
+ }
406
+ lc.info?.(`init pipelines@${version} (cvr@${cvrVer})`);
407
+ const hydrationBudget = new HydrationBudget(this.#config.viewSyncerHydrationBudgetMs ?? 0, this.#now);
408
+ const hydrationPassStats = {
409
+ activeHydratedQueries: 0,
410
+ inactiveHydratedQueries: 0
411
+ };
412
+ const driftedQueryIDs = await this.#hydrateUnchangedQueries(lc, cvr, connCtx, hydrationPassStats, hydrationBudget, previousQueries);
413
+ await this.#syncQueryPipelineSet(lc, cvr, "missing", connCtx, driftedQueryIDs, hydrationBudget, hydrationPassStats, previousQueries);
414
+ this.#pipelinesHydrated = true;
415
+ this.connContextManager.setSharedRetransformReady(true);
416
+ }
249
417
  #removeExpiredQueries = async (lc, cvr) => {
250
418
  if (hasExpiredQueries(cvr)) {
251
419
  lc = lc.withContext("method", "#removeExpiredQueries");
252
420
  lc.debug?.("Queries have expired");
253
- if (this.#pipelinesSynced) await this.#syncQueryPipelineSet(lc, cvr, "missing", void 0);
421
+ if (this.#pipelinesHydrated) {
422
+ const connCtx = this.connContextManager.getBackgroundConnectionContext();
423
+ if (connCtx) await this.#syncQueryPipelineSet(lc, cvr, "missing", connCtx);
424
+ else lc.info?.("No validated background connection to remove expired queries; deferring");
425
+ }
254
426
  }
255
427
  this.#scheduleExpireEviction(lc, cvr);
256
428
  };
@@ -332,7 +504,14 @@ var ViewSyncerService = class {
332
504
  return true;
333
505
  }
334
506
  #shutdownTimer = null;
507
+ #stopShutdownTimer() {
508
+ if (this.#shutdownTimer !== null) {
509
+ clearTimeout(this.#shutdownTimer);
510
+ this.#shutdownTimer = null;
511
+ }
512
+ }
335
513
  #scheduleShutdown(delayMs = 0) {
514
+ if (this.#shuttingDown) return;
336
515
  this.#shutdownTimer ??= this.#setTimeout(() => {
337
516
  this.#shutdownTimer = null;
338
517
  this.#runInLockWithCVR(() => {}).catch((e) => this.#stateChanges.fail(e));
@@ -379,6 +558,7 @@ var ViewSyncerService = class {
379
558
  */
380
559
  #scheduleAuthMaintenance(lc) {
381
560
  this.#stopAuthMaintenanceTimer();
561
+ if (this.#shuttingDown) return;
382
562
  const plan = this.connContextManager.planMaintenance();
383
563
  if (plan.earliestDeadlineAt === void 0) {
384
564
  lc.debug?.("No auth maintenance wakeup scheduled");
@@ -466,7 +646,7 @@ var ViewSyncerService = class {
466
646
  }
467
647
  lc.debug?.("Auth changed, re-validating and re-transforming queries");
468
648
  const connCtx = this.connContextManager.mustGetConnectionContext(selector);
469
- if (!this.#pipelinesSynced) {
649
+ if (!this.#pipelinesHydrated) {
470
650
  if (!await this.#validateConnection(connCtx)) return;
471
651
  }
472
652
  return await this.#handleConfigUpdate(lc, clientID, {}, cvr, "all", void 0, connCtx);
@@ -495,6 +675,7 @@ var ViewSyncerService = class {
495
675
  }
496
676
  #startTTLClockInterval(lc) {
497
677
  this.#stopTTLClockInterval();
678
+ if (this.#shuttingDown) return;
498
679
  this.#ttlClockInterval = this.#setTimeout(() => {
499
680
  this.#updateTTLClockInCVRWithoutLock(lc);
500
681
  this.#startTTLClockInterval(lc);
@@ -523,12 +704,13 @@ var ViewSyncerService = class {
523
704
  if (cmpVersions(cvr.version, this.#cvr.version) < 0) {
524
705
  const newCVR = this.#cvr;
525
706
  await startAsyncSpan(tracer, "vs.#updateCVRConfig.pokeClients", async () => {
526
- const pokers = startPoke(this.#getClients(cvr.version), newCVR.version);
707
+ const pokers = startPoke(lc, this.#getClients(cvr.version), newCVR.version);
527
708
  for (const patch of patches) await pokers.addPatch(patch);
528
709
  await pokers.end(newCVR.version);
529
710
  });
530
711
  }
531
- if (this.#pipelinesSynced) await this.#syncQueryPipelineSet(lc, this.#cvr, customQueryTransformMode, connCtx);
712
+ if (!this.#pipelinesHydrated) await this.#maybeHydratePipelines(lc, this.#cvr, connCtx);
713
+ else await this.#syncQueryPipelineSet(lc, this.#cvr, customQueryTransformMode, connCtx);
532
714
  return this.#cvr;
533
715
  }
534
716
  /**
@@ -624,6 +806,7 @@ var ViewSyncerService = class {
624
806
  #scheduleExpireEviction(lc, cvr) {
625
807
  const { ttlClock } = cvr;
626
808
  this.#stopExpireTimer();
809
+ if (this.#shuttingDown) return;
627
810
  const next = nextEvictionTime(cvr);
628
811
  if (next === void 0) {
629
812
  lc.debug?.("no inactive queries with ttl");
@@ -651,7 +834,7 @@ var ViewSyncerService = class {
651
834
  *
652
835
  * This must be called from within the #lock.
653
836
  */
654
- async #hydrateUnchangedQueries(lc, cvr) {
837
+ async #hydrateUnchangedQueries(lc, cvr, connCtx, hydrationPassStats, hydrationBudget, previousQueries) {
655
838
  assert(this.#pipelines.initialized(), "pipelines must be initialized");
656
839
  const dbVersion = this.#pipelines.currentVersion();
657
840
  const cvrVersion = cvr.version;
@@ -660,33 +843,34 @@ var ViewSyncerService = class {
660
843
  return /* @__PURE__ */ new Set();
661
844
  }
662
845
  const gotQueries = Object.entries(cvr.queries).filter(([_, state]) => state.transformationHash !== void 0);
846
+ const { required: requiredGotQueries } = classifyQueriesForHydration(gotQueries.map(([, query]) => query));
663
847
  const customQueries = /* @__PURE__ */ new Map();
664
848
  const otherQueries = [];
665
- let inactivatedCount = 0;
666
- for (const [, query] of gotQueries) {
667
- if (query.type !== "internal" && Object.values(query.clientState).every(({ inactivatedAt }) => inactivatedAt !== void 0)) {
668
- inactivatedCount++;
669
- continue;
670
- }
671
- if (query.type === "custom") customQueries.set(query.id, query);
672
- else otherQueries.push(query);
673
- }
849
+ const inactivatedCount = gotQueries.length - requiredGotQueries.length;
674
850
  const transformedQueries = [];
851
+ for (const query of requiredGotQueries) if (query.type === "custom") {
852
+ const previous = previousQueries?.get(query.id);
853
+ if (previous && previous.transformationHash === query.transformationHash) transformedQueries.push({
854
+ id: query.id,
855
+ transformationHash: previous.transformationHash,
856
+ transformedAst: previous.originalAst ?? previous.transformedAst
857
+ });
858
+ else customQueries.set(query.id, query);
859
+ } else otherQueries.push(query);
675
860
  let customErrorCount = 0;
676
861
  let customHashMismatchCount = 0;
677
862
  let otherHashMismatchCount = 0;
678
863
  if (customQueries.size > 0 && !this.#customQueryTransformer) lc.warn?.("Custom/named queries were requested but no `ZERO_QUERY_URL` is configured for Zero Cache.");
679
- const backgroundContext = this.connContextManager.mustGetBackgroundConnectionContext();
680
864
  const customQueryTransformer = this.#customQueryTransformer;
681
865
  if (customQueryTransformer && customQueries.size > 0) {
682
- const transformedCustomQueries = await this.#runPriorityOp(lc, "#hydrateUnchangedQueries transforming custom queries", () => customQueryTransformer.transform(backgroundContext, customQueries.values()));
683
- if (transformedCustomQueries.kind === "success" && !transformedCustomQueries.cached) this.connContextManager.validateConnection(backgroundContext, backgroundContext.revision, transformedCustomQueries.validation);
866
+ const transformedCustomQueries = await hydrationBudget.excluding(() => this.#runPriorityOp(lc, "#hydrateUnchangedQueries transforming custom queries", () => customQueryTransformer.transform(connCtx, customQueries.values())));
867
+ if (transformedCustomQueries.kind === "success" && !transformedCustomQueries.cached) this.connContextManager.validateConnection(connCtx, connCtx.revision, transformedCustomQueries.validation);
684
868
  if (Array.isArray(transformedCustomQueries.result)) for (const q of transformedCustomQueries.result) if ("error" in q) customErrorCount++;
685
869
  else if (q.transformationHash !== customQueries.get(q.id)?.transformationHash) customHashMismatchCount++;
686
870
  else transformedQueries.push(q);
687
871
  }
688
872
  for (const q of otherQueries) {
689
- const transformed = transformAndHashQuery(lc, q.id, q.ast, must(this.#pipelines.currentPermissions()).permissions ?? { tables: {} }, backgroundContext.auth?.type === "jwt" ? backgroundContext.auth : void 0, q.type === "internal");
873
+ const transformed = transformAndHashQuery(lc, q.id, q.ast, must(this.#pipelines.currentPermissions()).permissions ?? { tables: {} }, connCtx.auth?.type === "jwt" ? connCtx.auth : void 0, q.type === "internal");
690
874
  if (transformed.transformationHash === q.transformationHash) transformedQueries.push(transformed);
691
875
  else otherHashMismatchCount++;
692
876
  }
@@ -699,6 +883,10 @@ var ViewSyncerService = class {
699
883
  for (const { id: queryID, transformationHash, transformedAst } of transformedQueries) {
700
884
  const query = cvr.queries[queryID];
701
885
  const queryName = query.type === "custom" ? query.name : void 0;
886
+ if (query.type !== "internal" && this.#hydrationCircuitBreaker.isOpen(transformationHash)) {
887
+ lc.info?.(`skipping hydration of ${queryID}: hydration circuit breaker open`);
888
+ continue;
889
+ }
702
890
  const covered = queryCoveringIndex ? this.#findQueryCoverageShadowHit(queryCoveringIndex, queryID, transformationHash, transformedAst, queryName) : void 0;
703
891
  totalHydratedQueries++;
704
892
  if (covered) {
@@ -707,19 +895,35 @@ var ViewSyncerService = class {
707
895
  }
708
896
  const timer = new TimeSliceTimer(lc);
709
897
  let count = 0;
898
+ let timedOut = false;
710
899
  await startAsyncSpan(tracer, "vs.#hydrateUnchangedQueries.addQuery", async (span) => {
711
900
  span.setAttribute("queryHash", queryID);
712
901
  span.setAttribute("transformationHash", transformationHash);
713
902
  span.setAttribute("table", transformedAst.table);
714
903
  if (queryName !== void 0) span.setAttribute("queryName", queryName);
715
- for (const change of this.#pipelines.addQuery(transformationHash, queryID, transformedAst, await timer.start(), queryName, "unchanged-query-rehydrate")) if (change === "yield") await timer.yieldProcess("yield in hydrateUnchangedQueries");
716
- else count++;
904
+ for (const change of this.#pipelines.addQuery(transformationHash, queryID, transformedAst, await timer.start(), queryName, "unchanged-query-rehydrate")) if (change === "yield") {
905
+ if (query.type !== "internal" && this.#hydrationCircuitBreaker.exceeded(timer.totalElapsed())) {
906
+ timedOut = true;
907
+ await timer.yieldProcess("yield in hydrateUnchangedQueries");
908
+ break;
909
+ }
910
+ await timer.yieldProcess("yield in hydrateUnchangedQueries");
911
+ } else count++;
717
912
  });
718
913
  const elapsed = timer.totalElapsed();
914
+ if (timedOut) {
915
+ this.#recordHydrationTimeout(lc, {
916
+ id: queryID,
917
+ transformationHash,
918
+ name: queryName
919
+ }, elapsed, count);
920
+ continue;
921
+ }
922
+ hydrationPassStats.activeHydratedQueries++;
719
923
  this.#hydrations.add(1);
720
924
  this.#hydrationTime.recordMs(elapsed);
721
- this.#addQueryMaterializationServerMetric(transformationHash, elapsed);
722
- this.#inspectorDelegate.addQuery(transformationHash, transformedAst);
925
+ this.#addQueryMaterializationServerMetric(queryID, elapsed);
926
+ this.#inspectorDelegate.addQuery(queryID, transformedAst);
723
927
  lc.debug?.(`hydrated ${count} rows for ${queryID} (${elapsed} ms)`);
724
928
  let drifted = false;
725
929
  const storedSigHex = cvr.queries[queryID]?.rowSetSignature;
@@ -820,7 +1024,10 @@ var ViewSyncerService = class {
820
1024
  *
821
1025
  * This must be called from within the #lock.
822
1026
  */
823
- #syncQueryPipelineSet(lc, cvr, customQueryTransformMode, connCtx, driftedQueryIDs = /* @__PURE__ */ new Set()) {
1027
+ #syncQueryPipelineSet(lc, cvr, customQueryTransformMode, connCtx, driftedQueryIDs = /* @__PURE__ */ new Set(), hydrationBudget = new HydrationBudget(this.#config.viewSyncerHydrationBudgetMs ?? 0, this.#now), hydrationPassStats = {
1028
+ activeHydratedQueries: 0,
1029
+ inactiveHydratedQueries: 0
1030
+ }, previousQueries) {
824
1031
  return startAsyncSpan(tracer, "vs.#syncQueryPipelineSet", async (span) => {
825
1032
  const start = performance.now();
826
1033
  span.setAttribute("clientGroupID", this.id);
@@ -829,38 +1036,45 @@ var ViewSyncerService = class {
829
1036
  const now = Date.now();
830
1037
  const ttlClock = this.#getTTLClock(now);
831
1038
  const cvrQueryEntires = Object.entries(cvr.queries);
832
- const customQueries = /* @__PURE__ */ new Map();
833
- const otherQueries = [];
834
1039
  const transformedQueries = [];
835
- const resolvedConnCtx = connCtx ?? this.connContextManager.mustGetBackgroundConnectionContext();
836
- for (const [id, query] of cvrQueryEntires) if (query.type === "custom") {
837
- assert(id === query.id, "custom query id mismatch");
838
- customQueries.set(id, query);
839
- } else otherQueries.push({
840
- id,
841
- query
842
- });
843
- for (const { id, query: origQuery } of otherQueries) {
844
- assert(id === origQuery.id, "query id mismatch");
845
- 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");
846
- transformedQueries.push({
847
- id,
848
- origQuery,
849
- transformed
850
- });
851
- }
852
- if (customQueries.size > 0 && !this.#customQueryTransformer) lc.warn?.("Custom/named queries were requested but no `ZERO_QUERY_URL` is configured for Zero Cache.");
853
- let erroredQueryIDs;
854
- const customQueriesToTransform = customQueryTransformMode === "all" ? [...customQueries.values()] : customQueryTransformMode && [...customQueries.values()].filter((q) => !this.#pipelines.queries().has(q.id));
1040
+ const naturallyExpiredQueryIDs = new Set(cvrQueryEntires.map(([, query]) => query).filter((query) => expired(ttlClock, query)).map((query) => query.id));
1041
+ if (naturallyExpiredQueryIDs.size > 0) this.#queryEvictions.add(naturallyExpiredQueryIDs.size, { reason: "ttl" });
1042
+ const hydrationCandidates = cvrQueryEntires.map(([, query]) => query).filter((query) => !naturallyExpiredQueryIDs.has(query.id));
1043
+ const { required, optional } = classifyQueriesForHydration(hydrationCandidates);
1044
+ const requiredQueryIDs = new Set(required.map((query) => query.id));
1045
+ const optionalToHydrate = hydrationBudget.limitMs === 0 ? optional : optional.filter((query) => query.patchVersion !== void 0);
1046
+ if (!this.#customQueryTransformer && hydrationCandidates.some((query) => query.type === "custom")) lc.warn?.("Custom/named queries were requested but no `ZERO_QUERY_URL` is configured for Zero Cache.");
855
1047
  const customQueryTransformer = this.#customQueryTransformer;
856
- if (customQueryTransformer && customQueriesToTransform.length > 0) {
1048
+ const erroredQueryIDs = [];
1049
+ let transformedCustomQueryCount = 0;
1050
+ const transformOtherQueries = (queries) => {
1051
+ for (const origQuery of queries) {
1052
+ if (origQuery.type === "custom") continue;
1053
+ const transformed = transformAndHashQuery(lc, origQuery.id, origQuery.ast, must(this.#pipelines.currentPermissions()).permissions ?? { tables: {} }, connCtx.auth?.type === "jwt" ? connCtx.auth : void 0, origQuery.type === "internal");
1054
+ transformedQueries.push({
1055
+ id: origQuery.id,
1056
+ origQuery,
1057
+ transformed
1058
+ });
1059
+ }
1060
+ };
1061
+ const shouldTransformCustomQuery = (query) => {
1062
+ if (customQueryTransformMode === "all") return true;
1063
+ if (this.#pipelines.queries().has(query.id)) return false;
1064
+ const previous = previousQueries?.get(query.id);
1065
+ if (previous && previous.transformationHash === query.transformationHash) return false;
1066
+ return true;
1067
+ };
1068
+ const transformCustomQueries = async (queries) => {
1069
+ if (!customQueryTransformer || queries.length === 0) return;
1070
+ transformedCustomQueryCount += queries.length;
857
1071
  const transformStart = performance.now();
858
1072
  let transformedCustomQueries;
859
1073
  try {
860
- transformedCustomQueries = await this.#runPriorityOp(lc, "#syncQueryPipelineSet transforming custom queries", () => customQueryTransformer.transform(resolvedConnCtx, customQueriesToTransform));
1074
+ transformedCustomQueries = await hydrationBudget.excluding(() => this.#runPriorityOp(lc, "#syncQueryPipelineSet transforming custom queries", () => customQueryTransformer.transform(connCtx, queries)));
861
1075
  if (transformedCustomQueries.kind === "failed") throw new ProtocolErrorWithLevel(transformedCustomQueries.result, "warn");
862
1076
  else {
863
- if (!transformedCustomQueries.cached) this.connContextManager.validateConnection(resolvedConnCtx, resolvedConnCtx.revision, transformedCustomQueries.validation);
1077
+ if (!transformedCustomQueries.cached) this.connContextManager.validateConnection(connCtx, connCtx.revision, transformedCustomQueries.validation);
864
1078
  this.#queryTransformations.add(1, { result: "success" });
865
1079
  }
866
1080
  } catch (e) {
@@ -871,8 +1085,9 @@ var ViewSyncerService = class {
871
1085
  this.#queryTransformationTime.recordMs(transformDuration);
872
1086
  }
873
1087
  const successfullyTransformedCustomQueries = /* @__PURE__ */ new Map();
874
- erroredQueryIDs = this.#processTransformedCustomQueries(lc, transformedCustomQueries.result, (q) => {
875
- const origQuery = customQueries.get(q.id);
1088
+ const customQueryMap = new Map(queries.map((query) => [query.id, query]));
1089
+ erroredQueryIDs.push(...this.#processTransformedCustomQueries(lc, transformedCustomQueries.result, (q) => {
1090
+ const origQuery = customQueryMap.get(q.id);
876
1091
  if (origQuery) {
877
1092
  successfullyTransformedCustomQueries.set(q.id, q);
878
1093
  transformedQueries.push({
@@ -881,7 +1096,7 @@ var ViewSyncerService = class {
881
1096
  transformed: q
882
1097
  });
883
1098
  }
884
- }, customQueries);
1099
+ }, customQueryMap));
885
1100
  for (const [queryID, newTransform] of successfullyTransformedCustomQueries) {
886
1101
  const existingTransformHash = cvr.queries[queryID]?.transformationHash;
887
1102
  if (existingTransformHash) {
@@ -894,21 +1109,55 @@ var ViewSyncerService = class {
894
1109
  } else this.#queryTransformationNoOps.add(1);
895
1110
  }
896
1111
  }
1112
+ };
1113
+ const queriesToTransform = [...required, ...optionalToHydrate];
1114
+ transformOtherQueries(queriesToTransform);
1115
+ if (customQueryTransformMode === "missing" && previousQueries) {
1116
+ for (const query of queriesToTransform) if (query.type === "custom" && !this.#pipelines.queries().has(query.id)) {
1117
+ const previous = previousQueries.get(query.id);
1118
+ if (previous && previous.transformationHash === query.transformationHash) {
1119
+ this.#queryTransformationNoOps.add(1);
1120
+ transformedQueries.push({
1121
+ id: query.id,
1122
+ origQuery: query,
1123
+ transformed: {
1124
+ id: query.id,
1125
+ transformationHash: previous.transformationHash,
1126
+ transformedAst: previous.originalAst ?? previous.transformedAst
1127
+ }
1128
+ });
1129
+ }
1130
+ }
897
1131
  }
898
- const removeQueriesQueryIds = new Set([...Object.values(cvr.queries).filter((q) => expired(ttlClock, q)).map((q) => q.id), ...erroredQueryIDs || []]);
1132
+ await transformCustomQueries(queriesToTransform.filter((query) => query.type === "custom" && shouldTransformCustomQuery(query)));
1133
+ const circuitBrokenQueries = transformedQueries.filter(({ id, origQuery, transformed }) => origQuery.type !== "internal" && this.#pipelines.queries().get(id)?.transformationHash !== transformed.transformationHash && this.#hydrationCircuitBreaker.isOpen(transformed.transformationHash)).map(({ id, origQuery, transformed }) => ({
1134
+ id,
1135
+ transformationHash: transformed.transformationHash,
1136
+ name: origQuery.type === "custom" ? origQuery.name : void 0
1137
+ }));
1138
+ if (circuitBrokenQueries.length > 0) this.#rejectCircuitBrokenQueries(lc, cvr, circuitBrokenQueries);
1139
+ const removeQueriesQueryIds = new Set([
1140
+ ...naturallyExpiredQueryIDs,
1141
+ ...erroredQueryIDs,
1142
+ ...circuitBrokenQueries.map(({ id }) => id)
1143
+ ]);
899
1144
  const addQueries = transformedQueries.map(({ id, origQuery, transformed }) => ({
900
1145
  id,
901
1146
  ast: transformed.transformedAst,
902
1147
  transformationHash: transformed.transformationHash,
903
1148
  name: origQuery.type === "custom" ? origQuery.name : void 0
904
1149
  })).filter((q) => !removeQueriesQueryIds.has(q.id) && this.#pipelines.queries().get(q.id)?.transformationHash !== q.transformationHash);
905
- lc.info?.(`syncQueryPipelineSet: ${cvrQueryEntires.length} CVR queries, ${customQueriesToTransform.length} custom re-transformed, ${erroredQueryIDs?.length ?? 0} errored, ${removeQueriesQueryIds.size} to remove, ${addQueries.length} to add`);
1150
+ const hydrationOrder = new Map([...required, ...optional].map((query, index) => [query.id, index]));
1151
+ addQueries.sort((a, b) => must(hydrationOrder.get(a.id)) - must(hydrationOrder.get(b.id)));
1152
+ const requiredAddQueries = addQueries.filter((query) => requiredQueryIDs.has(query.id));
1153
+ const optionalAddQueries = addQueries.filter((query) => !requiredQueryIDs.has(query.id));
1154
+ lc.info?.(`syncQueryPipelineSet: ${cvrQueryEntires.length} CVR queries, ${transformedCustomQueryCount} custom re-transformed, ${erroredQueryIDs.length} errored, ${circuitBrokenQueries.length} circuit-broken, ${removeQueriesQueryIds.size} to remove, ${addQueries.length} to add`);
906
1155
  for (const q of addQueries) {
907
1156
  const orig = cvr.queries[q.id];
908
1157
  lc.debug?.("ViewSyncer adding query", q.ast, "transformed from", orig.type === "custom" ? orig.name : orig.ast);
909
1158
  }
910
1159
  if (addQueries.length > 0 || removeQueriesQueryIds.size > 0) {
911
- await this.#addAndRemoveQueries(lc, cvr, addQueries, Array.from(removeQueriesQueryIds, (id) => ({ id })), driftedQueryIDs);
1160
+ await this.#addAndRemoveQueries(lc, cvr, requiredAddQueries, optionalAddQueries, Array.from(removeQueriesQueryIds, (id) => ({ id })), hydrationBudget, hydrationPassStats, driftedQueryIDs);
912
1161
  if (addQueries.length > 0) this.#viewSyncerHydration.recordMs(performance.now() - start);
913
1162
  } else await this.#catchupClients(lc, cvr);
914
1163
  });
@@ -941,8 +1190,100 @@ var ViewSyncerService = class {
941
1190
  record.count++;
942
1191
  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.`);
943
1192
  }
944
- #addAndRemoveQueries(lc, cvr, addQueries, removeQueries, driftedQueryIDs = /* @__PURE__ */ new Set()) {
1193
+ /**
1194
+ * Records that hydrating `query` was aborted for exceeding the query
1195
+ * hydration timeout, and opens its circuit breaker.
1196
+ */
1197
+ #recordHydrationTimeout(lc, query, elapsedMs, rowCount) {
1198
+ this.#hydrationCircuitBreaker.trip(query.transformationHash);
1199
+ this.#hydrationTimeouts.add(1);
1200
+ lc.warn?.("Query hydration aborted for exceeding the hydration timeout", {
1201
+ clientGroupID: this.id,
1202
+ queryHash: query.id,
1203
+ transformationHash: query.transformationHash,
1204
+ ...query.name !== void 0 && { queryName: query.name },
1205
+ hydrationTimeoutMs: this.#hydrationCircuitBreaker.timeoutMs,
1206
+ hydrationElapsedMs: elapsedMs,
1207
+ ...rowCount !== void 0 && { hydrationRowCount: rowCount },
1208
+ circuitBreakerOpenMs: this.#hydrationCircuitBreaker.openMs
1209
+ });
1210
+ }
1211
+ /** Records that `query` was rejected without hydration by its open breaker. */
1212
+ #recordCircuitBreakerRejection(lc, query) {
1213
+ this.#hydrationCircuitBreakerRejections.add(1);
1214
+ lc.warn?.("Query rejected by its open hydration circuit breaker", {
1215
+ clientGroupID: this.id,
1216
+ queryHash: query.id,
1217
+ transformationHash: query.transformationHash,
1218
+ ...query.name !== void 0 && { queryName: query.name },
1219
+ hydrationTimeoutMs: this.#hydrationCircuitBreaker.timeoutMs,
1220
+ circuitBreakerOpenMs: this.#hydrationCircuitBreaker.openMs
1221
+ });
1222
+ }
1223
+ /**
1224
+ * Handles queries whose hydration circuit breaker is open: they are counted,
1225
+ * logged, and errored to the affected clients. The caller removes them from
1226
+ * the CVR.
1227
+ */
1228
+ #rejectCircuitBrokenQueries(lc, cvr, queries) {
1229
+ for (const query of queries) this.#recordCircuitBreakerRejection(lc, query);
1230
+ this.#queryEvictions.add(queries.length, { reason: "hydration-circuit-breaker" });
1231
+ this.#sendHydrationTimeoutErrors(cvr, queries);
1232
+ }
1233
+ /**
1234
+ * Sends a per-query error to every client that desires one of `queries`.
1235
+ * The error goes out as a `transformError` application error, which every
1236
+ * client understands as "this query errored" without affecting the
1237
+ * connection or the client's other queries.
1238
+ */
1239
+ #sendHydrationTimeoutErrors(cvr, queries) {
1240
+ const timeoutMs = this.#hydrationCircuitBreaker.timeoutMs;
1241
+ const errorsByClient = /* @__PURE__ */ new Map();
1242
+ for (const { id, name } of queries) {
1243
+ const query = cvr.queries[id];
1244
+ if (query === void 0 || query.type === "internal") continue;
1245
+ const error = {
1246
+ error: "app",
1247
+ id,
1248
+ name: name ?? (query.type === "custom" ? query.name : "legacy"),
1249
+ message: `Query hydration exceeded the ${timeoutMs}ms limit (ZERO_VIEW_SYNCER_QUERY_HYDRATION_TIMEOUT_MS) and was aborted`,
1250
+ details: {
1251
+ kind: "HydrationTimeout",
1252
+ timeoutMs
1253
+ }
1254
+ };
1255
+ for (const clientID of Object.keys(query.clientState)) {
1256
+ let errors = errorsByClient.get(clientID);
1257
+ if (!errors) {
1258
+ errors = [];
1259
+ errorsByClient.set(clientID, errors);
1260
+ }
1261
+ errors.push(error);
1262
+ }
1263
+ }
1264
+ for (const [clientID, errors] of errorsByClient) this.#clients.get(clientID)?.sendQueryTransformApplicationErrors(errors);
1265
+ }
1266
+ #recordHydrationBudgetExhaustion(lc, hydrationBudget, activeHydratedQueries, inactiveHydratedQueries, inactiveEvictedQueryIDs) {
1267
+ const elapsedMs = hydrationBudget.exhaustedAtMs;
1268
+ if (elapsedMs === void 0) return;
1269
+ this.#hydrationBudgetExhaustions.add(1);
1270
+ this.#hydrationBudgetEvictions.add(inactiveEvictedQueryIDs.length);
1271
+ this.#hydrationBudgetElapsed.record(elapsedMs);
1272
+ this.#hydrationBudgetOvershoot.record(Math.max(0, elapsedMs - hydrationBudget.limitMs));
1273
+ if (inactiveEvictedQueryIDs.length > 0) this.#queryEvictions.add(inactiveEvictedQueryIDs.length, { reason: "hydration-budget" });
1274
+ lc.info?.("view-syncer hydration budget exhausted", {
1275
+ clientGroupID: this.id,
1276
+ hydrationBudgetMs: hydrationBudget.limitMs,
1277
+ hydrationElapsedMs: elapsedMs,
1278
+ activeHydratedQueries,
1279
+ inactiveHydratedQueries,
1280
+ inactiveEvictedQueries: inactiveEvictedQueryIDs.length,
1281
+ firstEvictedQueryHash: inactiveEvictedQueryIDs[0] ?? null
1282
+ });
1283
+ }
1284
+ #addAndRemoveQueries(lc, cvr, requiredQueries, optionalQueries, removeQueries, hydrationBudget, hydrationPassStats, driftedQueryIDs = /* @__PURE__ */ new Set()) {
945
1285
  return startAsyncSpan(tracer, "vs.#addAndRemoveQueries", async () => {
1286
+ const addQueries = [...requiredQueries, ...optionalQueries];
946
1287
  assert(addQueries.length > 0 || removeQueries.length > 0, "Must have queries to add or remove");
947
1288
  const start = performance.now();
948
1289
  const stateVersion = this.#pipelines.currentVersion();
@@ -959,7 +1300,9 @@ var ViewSyncerService = class {
959
1300
  updater.ensureNewVersion();
960
1301
  }
961
1302
  const { queryPatches, newVersion } = updater.trackQueries(lc, addQueries, removeQueries);
962
- const pokers = startPoke(this.#getClients(), newVersion);
1303
+ if (hydrationBudget.limitMs !== 0 && optionalQueries.length > 0) assert(cmpVersions(cvr.version, newVersion) < 0, "Optional hydration requires a final poke version before row processing so a removal is actually poked");
1304
+ const clients = this.#getClients();
1305
+ const pokers = startPoke(lc, clients, newVersion);
963
1306
  for (const patch of queryPatches) await pokers.addPatch(patch);
964
1307
  for (const q of removeQueries) {
965
1308
  this.#pipelines.removeQuery(q.id);
@@ -975,22 +1318,54 @@ var ViewSyncerService = class {
975
1318
  let totalHydratedQueries = 0;
976
1319
  let coveredHydratedQueries = 0;
977
1320
  let firstCoveredQuery;
1321
+ const hydratedQueryIDs = [];
1322
+ const budgetEvictedQueryIDs = [];
1323
+ const timedOutQueries = [];
1324
+ const rejectedQueries = [];
1325
+ const circuitBreaker = this.#hydrationCircuitBreaker;
978
1326
  const self = this;
979
1327
  await yieldProcess(lc);
980
- function* generateRowChanges(slowHydrateThreshold) {
981
- for (const q of addQueries) {
1328
+ function* hydrateQueries(queries, optional, slowHydrateThreshold) {
1329
+ for (let i = 0; i < queries.length; i++) {
1330
+ if (optional && hydrationBudget.exhausted()) {
1331
+ budgetEvictedQueryIDs.push(...queries.slice(i).map((query) => query.id));
1332
+ return;
1333
+ }
1334
+ const q = must(queries[i]);
982
1335
  let queryLC = lc.withContext("hash", q.id).withContext("queryHash", q.id).withContext("transformationHash", q.transformationHash);
983
1336
  if (q.name !== void 0) queryLC = queryLC.withContext("queryName", q.name);
984
1337
  queryLC.debug?.(`adding pipeline for query`, q.ast);
1338
+ const breakable = cvr.queries[q.id]?.type !== "internal";
1339
+ if (breakable && circuitBreaker.isOpen(q.transformationHash)) {
1340
+ rejectedQueries.push(q);
1341
+ self.#recordCircuitBreakerRejection(queryLC, q);
1342
+ continue;
1343
+ }
985
1344
  const covered = queryCoveringIndex ? self.#findQueryCoverageShadowHit(queryCoveringIndex, q.id, q.transformationHash, q.ast, q.name) : void 0;
986
1345
  totalHydratedQueries++;
987
1346
  if (covered) {
988
1347
  coveredHydratedQueries++;
989
1348
  firstCoveredQuery ??= covered;
990
1349
  }
991
- yield* pipelines.addQuery(q.transformationHash, q.id, q.ast, timer.startWithoutYielding(), q.name, "query-set-sync");
1350
+ let timedOut = false;
1351
+ for (const change of pipelines.addQuery(q.transformationHash, q.id, q.ast, timer.startWithoutYielding(), q.name, "query-set-sync")) {
1352
+ if (change === "yield" && breakable && circuitBreaker.exceeded(timer.totalElapsed())) {
1353
+ timedOut = true;
1354
+ yield change;
1355
+ break;
1356
+ }
1357
+ yield change;
1358
+ }
992
1359
  const elapsed = timer.stop();
993
1360
  totalProcessTime += elapsed;
1361
+ if (timedOut) {
1362
+ timedOutQueries.push(q);
1363
+ self.#recordHydrationTimeout(queryLC, q, elapsed);
1364
+ continue;
1365
+ }
1366
+ hydratedQueryIDs.push(q.id);
1367
+ if (optional) hydrationPassStats.inactiveHydratedQueries++;
1368
+ else hydrationPassStats.activeHydratedQueries++;
994
1369
  self.#addQueryMaterializationServerMetric(q.id, elapsed);
995
1370
  self.#inspectorDelegate.addQuery(q.id, q.ast);
996
1371
  queryCoveringIndex?.add(q.id, {
@@ -1004,18 +1379,43 @@ var ViewSyncerService = class {
1004
1379
  transformationHash: q.transformationHash,
1005
1380
  ...q.name !== void 0 && { name: q.name }
1006
1381
  });
1382
+ hydrations.add(1);
1383
+ hydrationTime.recordMs(elapsed);
1007
1384
  }
1008
- hydrations.add(1);
1009
- hydrationTime.recordMs(totalProcessTime);
1385
+ }
1386
+ function* generateRowChanges(slowHydrateThreshold) {
1387
+ yield* hydrateQueries(requiredQueries, false, slowHydrateThreshold);
1388
+ yield* hydrateQueries(optionalQueries, true, slowHydrateThreshold);
1010
1389
  }
1011
1390
  await this.#processChanges(lc, timer, generateRowChanges(this.#slowHydrateThreshold), updater, pokers);
1391
+ const abortedQueries = [...timedOutQueries, ...rejectedQueries];
1392
+ if (abortedQueries.length > 0) {
1393
+ const abortedQueryIDs = abortedQueries.map(({ id }) => id);
1394
+ for (const patch of await updater.abortExecutedQueries(lc, abortedQueryIDs)) await pokers.addPatch(patch);
1395
+ for (const queryID of abortedQueryIDs) {
1396
+ this.#pipelines.removeQuery(queryID);
1397
+ this.#inspectorDelegate.removeQuery(queryID);
1398
+ this.#queryReplacements.delete(queryID);
1399
+ }
1400
+ if (timedOutQueries.length > 0) this.#queryEvictions.add(timedOutQueries.length, { reason: "hydration-timeout" });
1401
+ if (rejectedQueries.length > 0) this.#queryEvictions.add(rejectedQueries.length, { reason: "hydration-circuit-breaker" });
1402
+ this.#sendHydrationTimeoutErrors(cvr, abortedQueries);
1403
+ }
1404
+ for (const patch of updater.removeTrackedQueries(budgetEvictedQueryIDs)) await pokers.addPatch(patch);
1405
+ for (const queryID of budgetEvictedQueryIDs) {
1406
+ this.#pipelines.removeQuery(queryID);
1407
+ this.#inspectorDelegate.removeQuery(queryID);
1408
+ this.#queryReplacements.delete(queryID);
1409
+ }
1410
+ this.#recordHydrationBudgetExhaustion(lc, hydrationBudget, hydrationPassStats.activeHydratedQueries, hydrationPassStats.inactiveHydratedQueries, budgetEvictedQueryIDs);
1012
1411
  this.#logQueryCoverageShadowSummary(lc, "add", totalHydratedQueries, coveredHydratedQueries, firstCoveredQuery);
1013
1412
  await startAsyncSpan(tracer, "vs.#syncQueryPipelineSet.deleteUnreferencedRows", async () => {
1014
1413
  for (const patch of await updater.deleteUnreferencedRows(lc)) await pokers.addPatch(patch);
1015
1414
  });
1016
1415
  this.#cvr = await this.#flushUpdater(lc, updater);
1416
+ if (budgetEvictedQueryIDs.length > 0) this.#scheduleExpireEviction(lc, this.#cvr);
1017
1417
  const finalVersion = this.#cvr.version;
1018
- await this.#catchupClients(lc, cvr, finalVersion, addQueries.map((q) => q.id), pokers);
1418
+ await this.#catchupClients(lc, cvr, finalVersion, hydratedQueryIDs, pokers);
1019
1419
  await startAsyncSpan(tracer, "vs.#syncQueryPipelineSet.pokeEnd", () => pokers.end(finalVersion));
1020
1420
  this.#markVersionServed(stateVersion);
1021
1421
  const wallTime = performance.now() - start;
@@ -1044,7 +1444,7 @@ var ViewSyncerService = class {
1044
1444
  return startAsyncSpan(tracer, "vs.#catchupClients", async (span) => {
1045
1445
  current ??= cvr.version;
1046
1446
  const clients = this.#getClients();
1047
- const pokers = usePokers ?? startPoke(clients, cvr.version);
1447
+ const pokers = usePokers ?? startPoke(lc, clients, cvr.version);
1048
1448
  span.setAttribute("numClients", clients.length);
1049
1449
  const catchupFrom = clients.map((c) => c.version()).reduce((a, b) => cmpVersions(a, b) < 0 ? a : b, cvr.version);
1050
1450
  const rowPatches = this.#cvrStore.catchupRowPatches(lc, catchupFrom, cvr, current, excludeQueryHashes);
@@ -1156,7 +1556,9 @@ var ViewSyncerService = class {
1156
1556
  *
1157
1557
  * Must be called from within the #lock.
1158
1558
  *
1159
- * Returns false if the advancement failed due to a schema change.
1559
+ * Returns 'success' if changes were successfully processed and poked,
1560
+ * or a `ResetPipelinesSignal` if advancement aborted (e.g. schema change,
1561
+ * timeout) and pipelines need to be reset and rehydrated.
1160
1562
  */
1161
1563
  #advancePipelines(lc, cvr) {
1162
1564
  return startAsyncSpan(tracer, "vs.#advancePipelines", async (span) => {
@@ -1164,20 +1566,27 @@ var ViewSyncerService = class {
1164
1566
  assert(this.#pipelines.initialized(), "pipelines must be initialized (advancePipelines");
1165
1567
  const start = performance.now();
1166
1568
  const timer = new TimeSliceTimer(lc);
1167
- const { version, numChanges, changes } = this.#pipelines.advance(timer);
1168
- lc = lc.withContext("newVersion", version);
1169
- const updater = new CVRQueryDrivenUpdater(this.#cvrStore, cvr, version, this.#pipelines.replicaVersion, (queryID) => this.#pipelines.rowSetSignature(queryID));
1170
- const pokers = startPoke(this.#getClients(cvr.version), updater.updatedVersion());
1171
- lc.debug?.(`applying ${numChanges} to advance to ${version}`);
1569
+ let pokers;
1570
+ let updater;
1571
+ let version;
1572
+ let numChanges = 0;
1172
1573
  try {
1173
- await this.#processChanges(lc, await timer.start(), changes, updater, pokers);
1574
+ const advancement = this.#pipelines.advance(timer);
1575
+ version = advancement.version;
1576
+ numChanges = advancement.numChanges;
1577
+ lc = lc.withContext("newVersion", version);
1578
+ updater = new CVRQueryDrivenUpdater(this.#cvrStore, cvr, version, this.#pipelines.replicaVersion, (queryID) => this.#pipelines.rowSetSignature(queryID));
1579
+ pokers = startPoke(lc, this.#getClients(cvr.version), updater.updatedVersion());
1580
+ lc.debug?.(`applying ${numChanges} to advance to ${version}`);
1581
+ await this.#processChanges(lc, await timer.start(), advancement.changes, updater, pokers);
1174
1582
  } catch (e) {
1175
1583
  if (e instanceof ResetPipelinesSignal) {
1176
- await pokers.cancel();
1584
+ await pokers?.cancel();
1177
1585
  return e;
1178
1586
  }
1179
1587
  throw e;
1180
1588
  }
1589
+ assert(updater && pokers && version !== void 0, "advancement state missing");
1181
1590
  this.#cvr = await this.#flushUpdater(lc, updater);
1182
1591
  const finalVersion = this.#cvr.version;
1183
1592
  await startAsyncSpan(tracer, "vs.#advancePipelines.pokeEnd", () => pokers.end(finalVersion));
@@ -1198,7 +1607,7 @@ var ViewSyncerService = class {
1198
1607
  clientID,
1199
1608
  wsID: client.wsID
1200
1609
  });
1201
- return handleInspect(lc, body, cvr, client, this.#inspectorDelegate, this.id, this.#cvrStore, this.#config, connCtx);
1610
+ return handleInspect(lc, body, cvr, client, this.#inspectorDelegate, this.id, this, this.#cvrStore, this.#config, connCtx);
1202
1611
  };
1203
1612
  async #runBackgroundRetransform(lc) {
1204
1613
  const attemptRetransform = async (connCtx) => {
@@ -1285,15 +1694,18 @@ var ViewSyncerService = class {
1285
1694
  return this.#stopped.promise;
1286
1695
  }
1287
1696
  async #cleanup(err) {
1697
+ this.#shuttingDown = true;
1288
1698
  this.connContextManager.setSharedRetransformReady(false);
1289
1699
  this.#stopTTLClockInterval();
1290
1700
  this.#stopExpireTimer();
1291
1701
  this.#stopAuthMaintenanceTimer();
1702
+ this.#stopShutdownTimer();
1292
1703
  this.#customQueryTransformer?.destroy();
1293
1704
  for (const client of this.#clients.values()) if (err) client.fail(err);
1294
1705
  else client.close(`closed clientGroupID=${this.id}`);
1295
1706
  await this.#lock.withLock(() => {});
1296
1707
  this.#pipelines.destroy();
1708
+ this.#inspectorDelegate.clearAuthenticated(this.id, this);
1297
1709
  }
1298
1710
  /**
1299
1711
  * Test helper: Manually mark initialization as complete.
@@ -1329,8 +1741,10 @@ function isTransformFailedError(error) {
1329
1741
  return error.errorBody.kind === "TransformFailed" && !isAuthErrorBody(error.errorBody);
1330
1742
  }
1331
1743
  /**
1332
- * A query must be expired for all clients in order to be considered
1333
- * expired.
1744
+ * Whether a query's TTL has elapsed. A query must be expired for all clients
1745
+ * in order to be considered expired.
1746
+ *
1747
+ * Exported for testing.
1334
1748
  */
1335
1749
  function expired(ttlClock, q) {
1336
1750
  if (q.type === "internal") return false;