@rocicorp/zero 0.26.0-canary.2 → 0.26.0-canary.3

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 (333) hide show
  1. package/README.md +1 -1
  2. package/out/replicache/src/persist/collect-idb-databases.d.ts +4 -4
  3. package/out/replicache/src/persist/collect-idb-databases.d.ts.map +1 -1
  4. package/out/replicache/src/persist/collect-idb-databases.js +22 -19
  5. package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
  6. package/out/replicache/src/persist/refresh.d.ts.map +1 -1
  7. package/out/replicache/src/persist/refresh.js +0 -8
  8. package/out/replicache/src/persist/refresh.js.map +1 -1
  9. package/out/replicache/src/process-scheduler.d.ts +23 -0
  10. package/out/replicache/src/process-scheduler.d.ts.map +1 -1
  11. package/out/replicache/src/process-scheduler.js +50 -1
  12. package/out/replicache/src/process-scheduler.js.map +1 -1
  13. package/out/replicache/src/replicache-impl.d.ts +8 -0
  14. package/out/replicache/src/replicache-impl.d.ts.map +1 -1
  15. package/out/replicache/src/replicache-impl.js +11 -2
  16. package/out/replicache/src/replicache-impl.js.map +1 -1
  17. package/out/shared/src/falsy.d.ts +3 -0
  18. package/out/shared/src/falsy.d.ts.map +1 -0
  19. package/out/zero/package.json.js +1 -1
  20. package/out/zero/src/adapters/drizzle.js +1 -2
  21. package/out/zero/src/adapters/prisma.d.ts +2 -0
  22. package/out/zero/src/adapters/prisma.d.ts.map +1 -0
  23. package/out/zero/src/adapters/prisma.js +6 -0
  24. package/out/zero/src/adapters/prisma.js.map +1 -0
  25. package/out/zero/src/pg.js +4 -7
  26. package/out/zero/src/react.js +3 -1
  27. package/out/zero/src/react.js.map +1 -1
  28. package/out/zero/src/server.js +5 -8
  29. package/out/zero-cache/src/auth/load-permissions.d.ts +3 -2
  30. package/out/zero-cache/src/auth/load-permissions.d.ts.map +1 -1
  31. package/out/zero-cache/src/auth/load-permissions.js +14 -8
  32. package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
  33. package/out/zero-cache/src/auth/write-authorizer.d.ts +6 -0
  34. package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
  35. package/out/zero-cache/src/auth/write-authorizer.js +16 -3
  36. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  37. package/out/zero-cache/src/config/zero-config.d.ts +44 -8
  38. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  39. package/out/zero-cache/src/config/zero-config.js +53 -13
  40. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  41. package/out/zero-cache/src/custom/fetch.d.ts +3 -0
  42. package/out/zero-cache/src/custom/fetch.d.ts.map +1 -1
  43. package/out/zero-cache/src/custom/fetch.js +26 -0
  44. package/out/zero-cache/src/custom/fetch.js.map +1 -1
  45. package/out/zero-cache/src/db/lite-tables.js +1 -1
  46. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  47. package/out/zero-cache/src/db/migration-lite.d.ts.map +1 -1
  48. package/out/zero-cache/src/db/migration-lite.js +9 -3
  49. package/out/zero-cache/src/db/migration-lite.js.map +1 -1
  50. package/out/zero-cache/src/db/migration.d.ts.map +1 -1
  51. package/out/zero-cache/src/db/migration.js +9 -3
  52. package/out/zero-cache/src/db/migration.js.map +1 -1
  53. package/out/zero-cache/src/db/specs.d.ts +4 -3
  54. package/out/zero-cache/src/db/specs.d.ts.map +1 -1
  55. package/out/zero-cache/src/db/specs.js +4 -1
  56. package/out/zero-cache/src/db/specs.js.map +1 -1
  57. package/out/zero-cache/src/db/transaction-pool.d.ts.map +1 -1
  58. package/out/zero-cache/src/db/transaction-pool.js +9 -3
  59. package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
  60. package/out/zero-cache/src/server/inspector-delegate.d.ts +1 -1
  61. package/out/zero-cache/src/server/inspector-delegate.d.ts.map +1 -1
  62. package/out/zero-cache/src/server/inspector-delegate.js +11 -30
  63. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  64. package/out/zero-cache/src/server/main.js +1 -1
  65. package/out/zero-cache/src/server/main.js.map +1 -1
  66. package/out/zero-cache/src/server/priority-op.d.ts +8 -0
  67. package/out/zero-cache/src/server/priority-op.d.ts.map +1 -0
  68. package/out/zero-cache/src/server/priority-op.js +29 -0
  69. package/out/zero-cache/src/server/priority-op.js.map +1 -0
  70. package/out/zero-cache/src/server/syncer.d.ts +0 -1
  71. package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
  72. package/out/zero-cache/src/server/syncer.js +3 -21
  73. package/out/zero-cache/src/server/syncer.js.map +1 -1
  74. package/out/zero-cache/src/services/analyze.js +1 -1
  75. package/out/zero-cache/src/services/analyze.js.map +1 -1
  76. package/out/zero-cache/src/services/change-source/custom/change-source.d.ts.map +1 -1
  77. package/out/zero-cache/src/services/change-source/custom/change-source.js +4 -3
  78. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  79. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  80. package/out/zero-cache/src/services/change-source/pg/change-source.js +68 -13
  81. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  82. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  83. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +7 -2
  84. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  85. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts.map +1 -1
  86. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +7 -4
  87. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
  88. package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts +125 -180
  89. package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts.map +1 -1
  90. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
  91. package/out/zero-cache/src/services/change-source/pg/schema/init.js +18 -10
  92. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  93. package/out/zero-cache/src/services/change-source/pg/schema/published.d.ts +36 -90
  94. package/out/zero-cache/src/services/change-source/pg/schema/published.d.ts.map +1 -1
  95. package/out/zero-cache/src/services/change-source/pg/schema/published.js +51 -14
  96. package/out/zero-cache/src/services/change-source/pg/schema/published.js.map +1 -1
  97. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +31 -36
  98. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
  99. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +24 -3
  100. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  101. package/out/zero-cache/src/services/change-source/pg/schema/validation.d.ts +2 -2
  102. package/out/zero-cache/src/services/change-source/pg/schema/validation.d.ts.map +1 -1
  103. package/out/zero-cache/src/services/change-source/pg/schema/validation.js +2 -4
  104. package/out/zero-cache/src/services/change-source/pg/schema/validation.js.map +1 -1
  105. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts +158 -53
  106. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts.map +1 -1
  107. package/out/zero-cache/src/services/change-source/protocol/current/data.js +55 -10
  108. package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
  109. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +210 -72
  110. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts.map +1 -1
  111. package/out/zero-cache/src/services/change-source/protocol/current.js +4 -2
  112. package/out/zero-cache/src/services/change-source/replica-schema.d.ts.map +1 -1
  113. package/out/zero-cache/src/services/change-source/replica-schema.js +19 -10
  114. package/out/zero-cache/src/services/change-source/replica-schema.js.map +1 -1
  115. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +1 -1
  116. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  117. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +71 -25
  118. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
  119. package/out/zero-cache/src/services/change-streamer/change-streamer.js +1 -1
  120. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  121. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts +1 -0
  122. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts.map +1 -1
  123. package/out/zero-cache/src/services/change-streamer/schema/tables.js +6 -5
  124. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  125. package/out/zero-cache/src/services/change-streamer/storer.js +1 -1
  126. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  127. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +2 -0
  128. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
  129. package/out/zero-cache/src/services/change-streamer/subscriber.js +14 -1
  130. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  131. package/out/zero-cache/src/services/heapz.d.ts.map +1 -1
  132. package/out/zero-cache/src/services/heapz.js +1 -0
  133. package/out/zero-cache/src/services/heapz.js.map +1 -1
  134. package/out/zero-cache/src/services/mutagen/error.d.ts.map +1 -1
  135. package/out/zero-cache/src/services/mutagen/error.js +4 -1
  136. package/out/zero-cache/src/services/mutagen/error.js.map +1 -1
  137. package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
  138. package/out/zero-cache/src/services/mutagen/mutagen.js +1 -0
  139. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  140. package/out/zero-cache/src/services/mutagen/pusher.d.ts +7 -4
  141. package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +1 -1
  142. package/out/zero-cache/src/services/mutagen/pusher.js +80 -8
  143. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  144. package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +1 -1
  145. package/out/zero-cache/src/services/replicator/change-processor.js +21 -29
  146. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  147. package/out/zero-cache/src/services/replicator/schema/change-log.d.ts +1 -2
  148. package/out/zero-cache/src/services/replicator/schema/change-log.d.ts.map +1 -1
  149. package/out/zero-cache/src/services/replicator/schema/change-log.js +2 -5
  150. package/out/zero-cache/src/services/replicator/schema/change-log.js.map +1 -1
  151. package/out/zero-cache/src/services/{change-source → replicator/schema}/column-metadata.d.ts +3 -3
  152. package/out/zero-cache/src/services/replicator/schema/column-metadata.d.ts.map +1 -0
  153. package/out/zero-cache/src/services/{change-source → replicator/schema}/column-metadata.js +3 -3
  154. package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -0
  155. package/out/zero-cache/src/services/replicator/schema/replication-state.d.ts.map +1 -1
  156. package/out/zero-cache/src/services/replicator/schema/replication-state.js +3 -1
  157. package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
  158. package/out/zero-cache/src/services/run-ast.js +1 -1
  159. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  160. package/out/zero-cache/src/services/statz.d.ts.map +1 -1
  161. package/out/zero-cache/src/services/statz.js +1 -0
  162. package/out/zero-cache/src/services/statz.js.map +1 -1
  163. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts +1 -1
  164. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
  165. package/out/zero-cache/src/services/view-syncer/cvr-store.js +59 -40
  166. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  167. package/out/zero-cache/src/services/view-syncer/cvr.d.ts +0 -1
  168. package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
  169. package/out/zero-cache/src/services/view-syncer/cvr.js +23 -6
  170. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  171. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +13 -14
  172. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  173. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +44 -56
  174. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  175. package/out/zero-cache/src/services/view-syncer/row-record-cache.d.ts +1 -1
  176. package/out/zero-cache/src/services/view-syncer/row-record-cache.d.ts.map +1 -1
  177. package/out/zero-cache/src/services/view-syncer/row-record-cache.js +22 -11
  178. package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
  179. package/out/zero-cache/src/services/view-syncer/snapshotter.js +1 -1
  180. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  181. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +6 -3
  182. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  183. package/out/zero-cache/src/services/view-syncer/view-syncer.js +192 -217
  184. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  185. package/out/zero-cache/src/types/lexi-version.d.ts.map +1 -1
  186. package/out/zero-cache/src/types/lexi-version.js +4 -1
  187. package/out/zero-cache/src/types/lexi-version.js.map +1 -1
  188. package/out/zero-cache/src/types/lite.d.ts.map +1 -1
  189. package/out/zero-cache/src/types/lite.js +8 -2
  190. package/out/zero-cache/src/types/lite.js.map +1 -1
  191. package/out/zero-cache/src/types/shards.js +1 -1
  192. package/out/zero-cache/src/types/shards.js.map +1 -1
  193. package/out/zero-cache/src/types/sql.d.ts +5 -0
  194. package/out/zero-cache/src/types/sql.d.ts.map +1 -1
  195. package/out/zero-cache/src/types/sql.js +5 -1
  196. package/out/zero-cache/src/types/sql.js.map +1 -1
  197. package/out/zero-cache/src/types/subscription.js +1 -1
  198. package/out/zero-cache/src/types/subscription.js.map +1 -1
  199. package/out/zero-cache/src/workers/connect-params.d.ts +1 -0
  200. package/out/zero-cache/src/workers/connect-params.d.ts.map +1 -1
  201. package/out/zero-cache/src/workers/connect-params.js +2 -1
  202. package/out/zero-cache/src/workers/connect-params.js.map +1 -1
  203. package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
  204. package/out/zero-cache/src/workers/syncer-ws-message-handler.js +14 -6
  205. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  206. package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
  207. package/out/zero-cache/src/workers/syncer.js +17 -10
  208. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  209. package/out/zero-client/src/client/connection-manager.d.ts +8 -0
  210. package/out/zero-client/src/client/connection-manager.d.ts.map +1 -1
  211. package/out/zero-client/src/client/connection-manager.js +33 -0
  212. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  213. package/out/zero-client/src/client/connection.d.ts.map +1 -1
  214. package/out/zero-client/src/client/connection.js +6 -3
  215. package/out/zero-client/src/client/connection.js.map +1 -1
  216. package/out/zero-client/src/client/error.js +1 -1
  217. package/out/zero-client/src/client/error.js.map +1 -1
  218. package/out/zero-client/src/client/mutator-proxy.d.ts.map +1 -1
  219. package/out/zero-client/src/client/mutator-proxy.js +15 -1
  220. package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
  221. package/out/zero-client/src/client/options.d.ts +10 -0
  222. package/out/zero-client/src/client/options.d.ts.map +1 -1
  223. package/out/zero-client/src/client/options.js.map +1 -1
  224. package/out/zero-client/src/client/query-manager.d.ts +4 -0
  225. package/out/zero-client/src/client/query-manager.d.ts.map +1 -1
  226. package/out/zero-client/src/client/query-manager.js +7 -0
  227. package/out/zero-client/src/client/query-manager.js.map +1 -1
  228. package/out/zero-client/src/client/version.js +1 -1
  229. package/out/zero-client/src/client/zero.d.ts +3 -1
  230. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  231. package/out/zero-client/src/client/zero.js +52 -7
  232. package/out/zero-client/src/client/zero.js.map +1 -1
  233. package/out/zero-client/src/mod.d.ts +1 -0
  234. package/out/zero-client/src/mod.d.ts.map +1 -1
  235. package/out/zero-protocol/src/connect.d.ts +4 -0
  236. package/out/zero-protocol/src/connect.d.ts.map +1 -1
  237. package/out/zero-protocol/src/connect.js +3 -1
  238. package/out/zero-protocol/src/connect.js.map +1 -1
  239. package/out/zero-protocol/src/protocol-version.d.ts +1 -1
  240. package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
  241. package/out/zero-protocol/src/protocol-version.js +1 -1
  242. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  243. package/out/zero-protocol/src/push.d.ts +11 -2
  244. package/out/zero-protocol/src/push.d.ts.map +1 -1
  245. package/out/zero-protocol/src/push.js +22 -6
  246. package/out/zero-protocol/src/push.js.map +1 -1
  247. package/out/zero-protocol/src/up.d.ts +2 -0
  248. package/out/zero-protocol/src/up.d.ts.map +1 -1
  249. package/out/zero-react/src/mod.d.ts +3 -1
  250. package/out/zero-react/src/mod.d.ts.map +1 -1
  251. package/out/zero-react/src/paging-reducer.d.ts +61 -0
  252. package/out/zero-react/src/paging-reducer.d.ts.map +1 -0
  253. package/out/zero-react/src/paging-reducer.js +77 -0
  254. package/out/zero-react/src/paging-reducer.js.map +1 -0
  255. package/out/zero-react/src/use-query.d.ts +11 -1
  256. package/out/zero-react/src/use-query.d.ts.map +1 -1
  257. package/out/zero-react/src/use-query.js +13 -11
  258. package/out/zero-react/src/use-query.js.map +1 -1
  259. package/out/zero-react/src/use-rows.d.ts +39 -0
  260. package/out/zero-react/src/use-rows.d.ts.map +1 -0
  261. package/out/zero-react/src/use-rows.js +130 -0
  262. package/out/zero-react/src/use-rows.js.map +1 -0
  263. package/out/zero-react/src/use-zero-virtualizer.d.ts +122 -0
  264. package/out/zero-react/src/use-zero-virtualizer.d.ts.map +1 -0
  265. package/out/zero-react/src/use-zero-virtualizer.js +342 -0
  266. package/out/zero-react/src/use-zero-virtualizer.js.map +1 -0
  267. package/out/zero-react/src/zero-provider.js +1 -1
  268. package/out/zero-react/src/zero-provider.js.map +1 -1
  269. package/out/zero-server/src/adapters/drizzle.d.ts +18 -18
  270. package/out/zero-server/src/adapters/drizzle.d.ts.map +1 -1
  271. package/out/zero-server/src/adapters/drizzle.js +8 -22
  272. package/out/zero-server/src/adapters/drizzle.js.map +1 -1
  273. package/out/zero-server/src/adapters/pg.d.ts +19 -13
  274. package/out/zero-server/src/adapters/pg.d.ts.map +1 -1
  275. package/out/zero-server/src/adapters/pg.js.map +1 -1
  276. package/out/zero-server/src/adapters/postgresjs.d.ts +19 -13
  277. package/out/zero-server/src/adapters/postgresjs.d.ts.map +1 -1
  278. package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
  279. package/out/zero-server/src/adapters/prisma.d.ts +66 -0
  280. package/out/zero-server/src/adapters/prisma.d.ts.map +1 -0
  281. package/out/zero-server/src/adapters/prisma.js +63 -0
  282. package/out/zero-server/src/adapters/prisma.js.map +1 -0
  283. package/out/zero-server/src/custom.js +1 -15
  284. package/out/zero-server/src/custom.js.map +1 -1
  285. package/out/zero-server/src/mod.d.ts +9 -8
  286. package/out/zero-server/src/mod.d.ts.map +1 -1
  287. package/out/zero-server/src/process-mutations.d.ts +2 -2
  288. package/out/zero-server/src/process-mutations.d.ts.map +1 -1
  289. package/out/zero-server/src/process-mutations.js +4 -8
  290. package/out/zero-server/src/process-mutations.js.map +1 -1
  291. package/out/zero-server/src/push-processor.js +1 -1
  292. package/out/zero-server/src/push-processor.js.map +1 -1
  293. package/out/zero-server/src/schema.d.ts.map +1 -1
  294. package/out/zero-server/src/schema.js +4 -1
  295. package/out/zero-server/src/schema.js.map +1 -1
  296. package/out/zero-server/src/zql-database.d.ts.map +1 -1
  297. package/out/zero-server/src/zql-database.js +17 -8
  298. package/out/zero-server/src/zql-database.js.map +1 -1
  299. package/out/zero-solid/src/mod.d.ts +1 -1
  300. package/out/zero-solid/src/mod.d.ts.map +1 -1
  301. package/out/zero-solid/src/use-query.d.ts +10 -1
  302. package/out/zero-solid/src/use-query.d.ts.map +1 -1
  303. package/out/zero-solid/src/use-query.js +21 -5
  304. package/out/zero-solid/src/use-query.js.map +1 -1
  305. package/out/zero-solid/src/use-zero.js +1 -1
  306. package/out/zero-solid/src/use-zero.js.map +1 -1
  307. package/out/zql/src/ivm/constraint.d.ts.map +1 -1
  308. package/out/zql/src/ivm/constraint.js +4 -1
  309. package/out/zql/src/ivm/constraint.js.map +1 -1
  310. package/out/zql/src/ivm/exists.d.ts.map +1 -1
  311. package/out/zql/src/ivm/exists.js +4 -1
  312. package/out/zql/src/ivm/exists.js.map +1 -1
  313. package/out/zql/src/ivm/join-utils.d.ts.map +1 -1
  314. package/out/zql/src/ivm/join-utils.js +8 -2
  315. package/out/zql/src/ivm/join-utils.js.map +1 -1
  316. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  317. package/out/zql/src/ivm/memory-source.js +12 -3
  318. package/out/zql/src/ivm/memory-source.js.map +1 -1
  319. package/out/zql/src/ivm/push-accumulated.d.ts.map +1 -1
  320. package/out/zql/src/ivm/push-accumulated.js +25 -2
  321. package/out/zql/src/ivm/push-accumulated.js.map +1 -1
  322. package/out/zql/src/ivm/take.d.ts.map +1 -1
  323. package/out/zql/src/ivm/take.js +24 -6
  324. package/out/zql/src/ivm/take.js.map +1 -1
  325. package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
  326. package/out/zql/src/ivm/union-fan-in.js +12 -3
  327. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  328. package/out/zqlite/src/table-source.d.ts.map +1 -1
  329. package/out/zqlite/src/table-source.js +1 -2
  330. package/out/zqlite/src/table-source.js.map +1 -1
  331. package/package.json +6 -2
  332. package/out/zero-cache/src/services/change-source/column-metadata.d.ts.map +0 -1
  333. package/out/zero-cache/src/services/change-source/column-metadata.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"row-record-cache.js","sources":["../../../../../../zero-cache/src/services/view-syncer/row-record-cache.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {type Resolver, resolver} from '@rocicorp/resolver';\nimport type {PendingQuery, Row} from 'postgres';\nimport {CustomKeyMap} from '../../../../shared/src/custom-key-map.ts';\nimport {must} from '../../../../shared/src/must.ts';\nimport {promiseVoid} from '../../../../shared/src/resolved-promises.ts';\nimport * as Mode from '../../db/mode-enum.ts';\nimport {TransactionPool} from '../../db/transaction-pool.ts';\nimport {\n getOrCreateCounter,\n getOrCreateHistogram,\n} from '../../observability/metrics.ts';\nimport {\n disableStatementTimeout,\n type PostgresDB,\n type PostgresTransaction,\n} from '../../types/pg.ts';\nimport {rowIDString} from '../../types/row-key.ts';\nimport {cvrSchema, type ShardID} from '../../types/shards.ts';\nimport {checkVersion, type CVRFlushStats} from './cvr-store.ts';\nimport type {CVRSnapshot} from './cvr.ts';\nimport {\n rowRecordToRowsRow,\n type RowsRow,\n rowsRowToRowRecord,\n} from './schema/cvr.ts';\nimport {\n cmpVersions,\n type CVRVersion,\n type NullableCVRVersion,\n type RowID,\n type RowRecord,\n versionString,\n versionToNullableCookie,\n} from './schema/types.ts';\n\nconst FLUSH_TYPE_ATTRIBUTE = 'flush.type';\n\n/**\n * The RowRecordCache is an in-memory cache of the `cvr.rows` tables that\n * operates as both a write-through and write-back cache.\n *\n * For \"small\" CVR updates (i.e. zero or small numbers of rows) the\n * RowRecordCache operates as write-through, executing commits in\n * {@link executeRowUpdates()} before they are {@link apply}-ed to the\n * in-memory state.\n *\n * For \"large\" CVR updates (i.e. with many rows), the cache switches to a\n * write-back mode of operation, in which {@link executeRowUpdates()} is a\n * no-op, and {@link apply()} initiates a background task to flush the pending\n * row changes to the store. This allows the client poke to be completed and\n * committed on the client without waiting for the heavyweight operation of\n * committing the row records to the CVR store.\n *\n * Note that when the cache is in write-back mode, all updates become\n * write-back (i.e. asynchronously flushed) until the pending update queue is\n * fully flushed. This is required because updates must be applied in version\n * order. As with all pending work systems in zero-cache, multiple pending\n * updates are coalesced to reduce buildup of work.\n *\n * ### High level consistency\n *\n * Note that the above caching scheme only applies to the row data in `cvr.rows`\n * and corresponding `cvr.rowsVersion` tables. CVR metadata and query\n * information, on the other hand, are always committed before completing the\n * client poke. In this manner, the difference between the `version` column in\n * `cvr.instances` and the analogous column in `cvr.rowsVersion` determines\n * whether the data in the store is consistent, or whether it is awaiting a\n * pending update.\n *\n * The logic in {@link CVRStore#load()} takes this into account by loading both\n * the `cvr.instances` version and the `cvr.rowsVersion` version and checking\n * if they are in sync, waiting for a configurable delay until they are.\n *\n * ### Eventual conversion\n *\n * In the event of a continual stream of mutations (e.g. an animation-style\n * app), it is conceivable that the row record data be continually behind\n * the CVR metadata. In order to effect eventual convergence, a new view-syncer\n * signals the current view-syncer to stop updating by writing new `owner`\n * information to the `cvr.instances` row. This effectively stops the mutation\n * processing (in {@link CVRStore.#checkVersionAndOwnership}) so that the row\n * data can eventually catch up, allowing the new view-syncer to take over.\n *\n * Of course, there is the pathological situation in which a view-syncer\n * process crashes before the pending row updates are flushed. In this case,\n * the wait timeout will elapse and the CVR considered invalid.\n */\nexport class RowRecordCache {\n // The state in the #cache is always in sync with the CVR metadata\n // (i.e. cvr.instances). It may contain information that has not yet\n // been flushed to cvr.rows.\n #cache: Promise<CustomKeyMap<RowID, RowRecord>> | undefined;\n readonly #lc: LogContext;\n readonly #db: PostgresDB;\n readonly #schema: string;\n readonly #cvrID: string;\n readonly #failService: (e: unknown) => void;\n readonly #deferredRowFlushThreshold: number;\n readonly #setTimeout: typeof setTimeout;\n\n // Write-back cache state.\n readonly #pending = new CustomKeyMap<RowID, RowRecord | null>(rowIDString);\n #pendingRowsVersion: CVRVersion | null = null;\n #flushedRowsVersion: CVRVersion | null = null;\n #flushing: Resolver<void> | null = null;\n\n readonly #cvrFlushTime = getOrCreateHistogram('sync', 'cvr.flush-time', {\n description:\n 'Time to flush a CVR transaction. This includes both synchronous ' +\n 'and asynchronous flushes, distinguished by the flush.type attribute',\n unit: 's',\n });\n readonly #cvrRowsFlushed = getOrCreateCounter(\n 'sync',\n 'cvr.rows-flushed',\n 'Number of (changed) rows flushed to a CVR',\n );\n\n constructor(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardID,\n cvrID: string,\n failService: (e: unknown) => void,\n deferredRowFlushThreshold = 100,\n setTimeoutFn = setTimeout,\n ) {\n this.#lc = lc;\n this.#db = db;\n this.#schema = cvrSchema(shard);\n this.#cvrID = cvrID;\n this.#failService = failService;\n this.#deferredRowFlushThreshold = deferredRowFlushThreshold;\n this.#setTimeout = setTimeoutFn;\n }\n\n recordSyncFlushStats(stats: CVRFlushStats, elapsedMs: number) {\n this.#cvrFlushTime.record(elapsedMs / 1000, {\n [FLUSH_TYPE_ATTRIBUTE]: 'sync',\n });\n if (stats.rowsDeferred === 0) {\n this.#cvrRowsFlushed.add(stats.rows);\n }\n }\n\n #recordAsyncFlushStats(rows: number, elapsedMs: number) {\n this.#cvrFlushTime.record(elapsedMs / 1000, {\n [FLUSH_TYPE_ATTRIBUTE]: 'async',\n });\n this.#cvrRowsFlushed.add(rows);\n }\n\n #cvr(table: string) {\n return this.#db(`${this.#schema}.${table}`);\n }\n\n async #ensureLoaded(): Promise<CustomKeyMap<RowID, RowRecord>> {\n if (this.#cache) {\n return this.#cache;\n }\n const r = resolver<CustomKeyMap<RowID, RowRecord>>();\n // Set this.#cache immediately (before await) so that only one db\n // query is made even if there are multiple callers.\n this.#cache = r.promise;\n\n const cache: CustomKeyMap<RowID, RowRecord> = new CustomKeyMap(rowIDString);\n for await (const rows of this.#db<RowsRow[]>`\n SELECT * FROM ${this.#cvr(`rows`)} \n WHERE \"clientGroupID\" = ${this.#cvrID} AND \"refCounts\" IS NOT NULL`\n // TODO(arv): Arbitrary page size\n .cursor(5000)) {\n for (const row of rows) {\n const rowRecord = rowsRowToRowRecord(row);\n cache.set(rowRecord.id, rowRecord);\n }\n }\n r.resolve(cache);\n return this.#cache;\n }\n\n getRowRecords(): Promise<ReadonlyMap<RowID, RowRecord>> {\n return this.#ensureLoaded();\n }\n\n /**\n * Applies the `rowRecords` corresponding to the `rowsVersion`\n * to the cache, indicating whether the corresponding updates\n * (generated by {@link executeRowUpdates}) were `flushed`.\n *\n * If `flushed` is false, the RowRecordCache will flush the records\n * asynchronously.\n *\n * Note that `apply()` indicates that the CVR metadata associated with\n * the `rowRecords` was successfully committed, which essentially means\n * that this process has the unconditional right (and responsibility) of\n * following up with a flush of the `rowRecords`. In particular, the\n * commit of row records are not conditioned on the version or ownership\n * columns of the `cvr.instances` row.\n */\n async apply(\n rowRecords: Map<RowID, RowRecord | null>,\n rowsVersion: CVRVersion,\n flushed: boolean,\n ): Promise<number> {\n const cache = await this.#ensureLoaded();\n for (const [id, row] of rowRecords.entries()) {\n if (row === null || row.refCounts === null) {\n cache.delete(id);\n } else {\n cache.set(id, row);\n }\n if (!flushed) {\n this.#pending.set(id, row);\n }\n }\n this.#pendingRowsVersion = rowsVersion;\n // Initiate a flush if not already flushing.\n if (!flushed && this.#flushing === null) {\n this.#flushing = resolver();\n this.#setTimeout(() => this.#flush(), 0);\n }\n return cache.size;\n }\n\n async #flush() {\n const flushing = must(this.#flushing);\n try {\n while (this.#pendingRowsVersion !== this.#flushedRowsVersion) {\n const start = performance.now();\n\n const {rows, rowsVersion} = await this.#db.begin(\n Mode.READ_COMMITTED,\n tx => {\n disableStatementTimeout(tx);\n\n // Note: This code block is synchronous, guaranteeing that the\n // #pendingRowsVersion is consistent with the #pending rows.\n const rows = this.#pending.size;\n const rowsVersion = must(this.#pendingRowsVersion);\n // Awaiting all of the individual statements incurs too much\n // overhead. Instead, just catch and log exception(s); the outer\n // transaction will properly fail.\n void Promise.all(\n this.executeRowUpdates(tx, rowsVersion, this.#pending, 'force'),\n ).catch(e => this.#lc.error?.(`error flushing cvr rows`, e));\n\n this.#pending.clear();\n return {rows, rowsVersion};\n },\n );\n const elapsed = performance.now() - start;\n this.#lc.debug?.(\n `flushed ${rows} rows@${versionString(rowsVersion)} (${elapsed} ms)`,\n );\n this.#recordAsyncFlushStats(rows, elapsed);\n this.#flushedRowsVersion = rowsVersion;\n // Note: apply() may have called while the transaction was committing,\n // which will result in looping to commit the next #pendingRowsVersion.\n }\n this.#lc.debug?.(\n `up to date rows@${versionToNullableCookie(this.#flushedRowsVersion)}`,\n );\n flushing.resolve();\n this.#flushing = null;\n } catch (e) {\n flushing.reject(e);\n this.#failService(e);\n }\n }\n\n hasPendingUpdates() {\n return this.#flushing !== null;\n }\n\n /**\n * Returns a promise that resolves when all outstanding row-records\n * have been committed.\n */\n flushed(lc: LogContext): Promise<void> {\n if (this.#flushing) {\n lc.debug?.('awaiting pending row flush');\n return this.#flushing.promise;\n }\n return promiseVoid;\n }\n\n clear() {\n // Note: Only the #cache is cleared. #pending updates, on the other hand,\n // comprise canonical (i.e. already flushed) data and must be flushed\n // even if the snapshot of the present state (the #cache) is cleared.\n this.#cache = undefined;\n }\n\n async *catchupRowPatches(\n lc: LogContext,\n afterVersion: NullableCVRVersion,\n upToCVR: CVRSnapshot,\n current: CVRVersion,\n excludeQueryHashes: string[] = [],\n ): AsyncGenerator<RowsRow[], void, undefined> {\n if (cmpVersions(afterVersion, upToCVR.version) >= 0) {\n return;\n }\n\n const startMs = Date.now();\n const start = afterVersion ? versionString(afterVersion) : '';\n const end = versionString(upToCVR.version);\n lc.debug?.(`scanning row patches for clients from ${start}`);\n\n // Before accessing the CVR db, pending row records must be flushed.\n // Note that because catchupRowPatches() is called from within the\n // view syncer lock, this flush is guaranteed to complete since no\n // new CVR updates can happen while the lock is held.\n await this.flushed(lc);\n const flushMs = Date.now() - startMs;\n\n const reader = new TransactionPool(lc, Mode.READONLY).run(this.#db);\n try {\n // Verify that we are reading the right version of the CVR.\n await reader.processReadTask(tx =>\n checkVersion(tx, this.#schema, this.#cvrID, current),\n );\n\n const {query} = await reader.processReadTask(tx => {\n const query =\n excludeQueryHashes.length === 0\n ? tx<RowsRow[]>`SELECT * FROM ${this.#cvr('rows')}\n WHERE \"clientGroupID\" = ${this.#cvrID}\n AND \"patchVersion\" > ${start}\n AND \"patchVersion\" <= ${end}`\n : // Exclude rows that were already sent as part of query hydration.\n tx<RowsRow[]>`SELECT * FROM ${this.#cvr('rows')}\n WHERE \"clientGroupID\" = ${this.#cvrID}\n AND \"patchVersion\" > ${start}\n AND \"patchVersion\" <= ${end}\n AND (\"refCounts\" IS NULL OR NOT \"refCounts\" ?| ${excludeQueryHashes})`;\n return {query};\n });\n\n yield* query.cursor(10000);\n } finally {\n reader.setDone();\n }\n\n const totalMs = Date.now() - startMs;\n lc.debug?.(\n `finished row catchup (flush: ${flushMs} ms, total: ${totalMs} ms)`,\n );\n }\n\n executeRowUpdates(\n tx: PostgresTransaction,\n version: CVRVersion,\n rowUpdates: Map<RowID, RowRecord | null>,\n mode: 'allow-defer' | 'force',\n ): PendingQuery<Row[]>[] {\n if (\n mode === 'allow-defer' &&\n // defer if pending rows are being flushed\n (this.#flushing !== null ||\n // or if the new batch is above the limit.\n rowUpdates.size > this.#deferredRowFlushThreshold)\n ) {\n return [];\n }\n const rowsVersion = {\n clientGroupID: this.#cvrID,\n version: versionString(version),\n };\n const pending: PendingQuery<Row[]>[] = [\n tx`INSERT INTO ${this.#cvr('rowsVersion')} ${tx(rowsVersion)}\n ON CONFLICT (\"clientGroupID\") \n DO UPDATE SET ${tx(rowsVersion)}`.execute(),\n ];\n\n const rowRecordRows: RowsRow[] = [];\n for (const [id, row] of rowUpdates.entries()) {\n if (row === null) {\n pending.push(\n tx`\n DELETE FROM ${this.#cvr('rows')}\n WHERE \"clientGroupID\" = ${this.#cvrID}\n AND \"schema\" = ${id.schema}\n AND \"table\" = ${id.table}\n AND \"rowKey\" = ${id.rowKey}\n `.execute(),\n );\n } else {\n rowRecordRows.push(rowRecordToRowsRow(this.#cvrID, row));\n }\n }\n if (rowRecordRows.length) {\n pending.push(\n tx`\n INSERT INTO ${this.#cvr('rows')}(\n \"clientGroupID\", \"schema\", \"table\", \"rowKey\", \"rowVersion\", \"patchVersion\", \"refCounts\"\n ) SELECT\n \"clientGroupID\", \"schema\", \"table\", \"rowKey\", \"rowVersion\", \"patchVersion\", \"refCounts\"\n FROM json_to_recordset(${rowRecordRows}) AS x(\n \"clientGroupID\" TEXT,\n \"schema\" TEXT,\n \"table\" TEXT,\n \"rowKey\" JSONB,\n \"rowVersion\" TEXT,\n \"patchVersion\" TEXT,\n \"refCounts\" JSONB\n ) ON CONFLICT (\"clientGroupID\", \"schema\", \"table\", \"rowKey\")\n DO UPDATE SET \"rowVersion\" = excluded.\"rowVersion\",\n \"patchVersion\" = excluded.\"patchVersion\",\n \"refCounts\" = excluded.\"refCounts\"\n `.execute(),\n );\n this.#lc.debug?.(\n `flushing ${rowUpdates.size} rows (${rowRecordRows.length} inserts, ${\n rowUpdates.size - rowRecordRows.length\n } deletes)`,\n );\n }\n return pending;\n }\n}\n"],"names":["Mode.READ_COMMITTED","rows","rowsVersion","Mode.READONLY","query"],"mappings":"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"}
1
+ {"version":3,"file":"row-record-cache.js","sources":["../../../../../../zero-cache/src/services/view-syncer/row-record-cache.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {type Resolver, resolver} from '@rocicorp/resolver';\nimport type {PendingQuery, Row} from 'postgres';\nimport {CustomKeyMap} from '../../../../shared/src/custom-key-map.ts';\nimport {must} from '../../../../shared/src/must.ts';\nimport {promiseVoid} from '../../../../shared/src/resolved-promises.ts';\nimport * as Mode from '../../db/mode-enum.ts';\nimport {TransactionPool} from '../../db/transaction-pool.ts';\nimport {\n getOrCreateCounter,\n getOrCreateHistogram,\n} from '../../observability/metrics.ts';\nimport {\n disableStatementTimeout,\n type PostgresDB,\n type PostgresTransaction,\n} from '../../types/pg.ts';\nimport {rowIDString} from '../../types/row-key.ts';\nimport {cvrSchema, type ShardID} from '../../types/shards.ts';\nimport {checkVersion, type CVRFlushStats} from './cvr-store.ts';\nimport type {CVRSnapshot} from './cvr.ts';\nimport {\n rowRecordToRowsRow,\n type RowsRow,\n rowsRowToRowRecord,\n} from './schema/cvr.ts';\nimport {\n cmpVersions,\n type CVRVersion,\n type NullableCVRVersion,\n type RowID,\n type RowRecord,\n versionString,\n versionToNullableCookie,\n} from './schema/types.ts';\n\nconst FLUSH_TYPE_ATTRIBUTE = 'flush.type';\n\n/**\n * The RowRecordCache is an in-memory cache of the `cvr.rows` tables that\n * operates as both a write-through and write-back cache.\n *\n * For \"small\" CVR updates (i.e. zero or small numbers of rows) the\n * RowRecordCache operates as write-through, executing commits in\n * {@link executeRowUpdates()} before they are {@link apply}-ed to the\n * in-memory state.\n *\n * For \"large\" CVR updates (i.e. with many rows), the cache switches to a\n * write-back mode of operation, in which {@link executeRowUpdates()} is a\n * no-op, and {@link apply()} initiates a background task to flush the pending\n * row changes to the store. This allows the client poke to be completed and\n * committed on the client without waiting for the heavyweight operation of\n * committing the row records to the CVR store.\n *\n * Note that when the cache is in write-back mode, all updates become\n * write-back (i.e. asynchronously flushed) until the pending update queue is\n * fully flushed. This is required because updates must be applied in version\n * order. As with all pending work systems in zero-cache, multiple pending\n * updates are coalesced to reduce buildup of work.\n *\n * ### High level consistency\n *\n * Note that the above caching scheme only applies to the row data in `cvr.rows`\n * and corresponding `cvr.rowsVersion` tables. CVR metadata and query\n * information, on the other hand, are always committed before completing the\n * client poke. In this manner, the difference between the `version` column in\n * `cvr.instances` and the analogous column in `cvr.rowsVersion` determines\n * whether the data in the store is consistent, or whether it is awaiting a\n * pending update.\n *\n * The logic in {@link CVRStore#load()} takes this into account by loading both\n * the `cvr.instances` version and the `cvr.rowsVersion` version and checking\n * if they are in sync, waiting for a configurable delay until they are.\n *\n * ### Eventual conversion\n *\n * In the event of a continual stream of mutations (e.g. an animation-style\n * app), it is conceivable that the row record data be continually behind\n * the CVR metadata. In order to effect eventual convergence, a new view-syncer\n * signals the current view-syncer to stop updating by writing new `owner`\n * information to the `cvr.instances` row. This effectively stops the mutation\n * processing (in {@link CVRStore.#checkVersionAndOwnership}) so that the row\n * data can eventually catch up, allowing the new view-syncer to take over.\n *\n * Of course, there is the pathological situation in which a view-syncer\n * process crashes before the pending row updates are flushed. In this case,\n * the wait timeout will elapse and the CVR considered invalid.\n */\nexport class RowRecordCache {\n // The state in the #cache is always in sync with the CVR metadata\n // (i.e. cvr.instances). It may contain information that has not yet\n // been flushed to cvr.rows.\n #cache: Promise<CustomKeyMap<RowID, RowRecord>> | undefined;\n readonly #lc: LogContext;\n readonly #db: PostgresDB;\n readonly #schema: string;\n readonly #cvrID: string;\n readonly #failService: (e: unknown) => void;\n readonly #deferredRowFlushThreshold: number;\n readonly #setTimeout: typeof setTimeout;\n\n // Write-back cache state.\n readonly #pending = new CustomKeyMap<RowID, RowRecord | null>(rowIDString);\n #pendingRowsVersion: CVRVersion | null = null;\n #flushedRowsVersion: CVRVersion | null = null;\n #flushing: Resolver<void> | null = null;\n\n readonly #cvrFlushTime = getOrCreateHistogram('sync', 'cvr.flush-time', {\n description:\n 'Time to flush a CVR transaction. This includes both synchronous ' +\n 'and asynchronous flushes, distinguished by the flush.type attribute',\n unit: 's',\n });\n readonly #cvrRowsFlushed = getOrCreateCounter(\n 'sync',\n 'cvr.rows-flushed',\n 'Number of (changed) rows flushed to a CVR',\n );\n\n constructor(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardID,\n cvrID: string,\n failService: (e: unknown) => void,\n deferredRowFlushThreshold = 100,\n setTimeoutFn = setTimeout,\n ) {\n this.#lc = lc;\n this.#db = db;\n this.#schema = cvrSchema(shard);\n this.#cvrID = cvrID;\n this.#failService = failService;\n this.#deferredRowFlushThreshold = deferredRowFlushThreshold;\n this.#setTimeout = setTimeoutFn;\n }\n\n recordSyncFlushStats(stats: CVRFlushStats, elapsedMs: number) {\n this.#cvrFlushTime.record(elapsedMs / 1000, {\n [FLUSH_TYPE_ATTRIBUTE]: 'sync',\n });\n if (stats.rowsDeferred === 0) {\n this.#cvrRowsFlushed.add(stats.rows);\n }\n }\n\n #recordAsyncFlushStats(rows: number, elapsedMs: number) {\n this.#cvrFlushTime.record(elapsedMs / 1000, {\n [FLUSH_TYPE_ATTRIBUTE]: 'async',\n });\n this.#cvrRowsFlushed.add(rows);\n }\n\n #cvr(table: string) {\n return this.#db(`${this.#schema}.${table}`);\n }\n\n async #ensureLoaded(): Promise<CustomKeyMap<RowID, RowRecord>> {\n if (this.#cache) {\n return this.#cache;\n }\n const start = Date.now();\n const r = resolver<CustomKeyMap<RowID, RowRecord>>();\n // Set this.#cache immediately (before await) so that only one db\n // query is made even if there are multiple callers.\n this.#cache = r.promise;\n try {\n const cache: CustomKeyMap<RowID, RowRecord> = new CustomKeyMap(\n rowIDString,\n );\n for await (const rows of this.#db<RowsRow[]>`\n SELECT * FROM ${this.#cvr(`rows`)}\n WHERE \"clientGroupID\" = ${this.#cvrID} AND \"refCounts\" IS NOT NULL`\n // TODO(arv): Arbitrary page size\n .cursor(5000)) {\n for (const row of rows) {\n const rowRecord = rowsRowToRowRecord(row);\n cache.set(rowRecord.id, rowRecord);\n }\n }\n this.#lc.debug?.(\n `Loaded ${cache.size} row records in ${Date.now() - start} ms`,\n );\n r.resolve(cache);\n return this.#cache;\n } catch (e) {\n r.reject(e); // Make sure the error is reflected in the cached promise\n throw e;\n }\n }\n\n getRowRecords(): Promise<ReadonlyMap<RowID, RowRecord>> {\n return this.#ensureLoaded();\n }\n\n /**\n * Applies the `rowRecords` corresponding to the `rowsVersion`\n * to the cache, indicating whether the corresponding updates\n * (generated by {@link executeRowUpdates}) were `flushed`.\n *\n * If `flushed` is false, the RowRecordCache will flush the records\n * asynchronously.\n *\n * Note that `apply()` indicates that the CVR metadata associated with\n * the `rowRecords` was successfully committed, which essentially means\n * that this process has the unconditional right (and responsibility) of\n * following up with a flush of the `rowRecords`. In particular, the\n * commit of row records are not conditioned on the version or ownership\n * columns of the `cvr.instances` row.\n */\n async apply(\n rowRecords: Map<RowID, RowRecord | null>,\n rowsVersion: CVRVersion,\n flushed: boolean,\n ): Promise<number> {\n const cache = await this.#ensureLoaded();\n for (const [id, row] of rowRecords.entries()) {\n if (row === null || row.refCounts === null) {\n cache.delete(id);\n } else {\n cache.set(id, row);\n }\n if (!flushed) {\n this.#pending.set(id, row);\n }\n }\n this.#pendingRowsVersion = rowsVersion;\n // Initiate a flush if not already flushing.\n if (!flushed && this.#flushing === null) {\n this.#flushing = resolver();\n this.#setTimeout(() => this.#flush(), 0);\n }\n return cache.size;\n }\n\n async #flush() {\n const flushing = must(this.#flushing);\n try {\n while (this.#pendingRowsVersion !== this.#flushedRowsVersion) {\n const start = performance.now();\n\n const {rows, rowsVersion} = await this.#db.begin(\n Mode.READ_COMMITTED,\n tx => {\n disableStatementTimeout(tx);\n\n // Note: This code block is synchronous, guaranteeing that the\n // #pendingRowsVersion is consistent with the #pending rows.\n const rows = this.#pending.size;\n const rowsVersion = must(this.#pendingRowsVersion);\n // Awaiting all of the individual statements incurs too much\n // overhead. Instead, just catch and log exception(s); the outer\n // transaction will properly fail.\n void Promise.all(\n this.executeRowUpdates(tx, rowsVersion, this.#pending, 'force'),\n ).catch(e => this.#lc.error?.(`error flushing cvr rows`, e));\n\n this.#pending.clear();\n return {rows, rowsVersion};\n },\n );\n const elapsed = performance.now() - start;\n this.#lc.debug?.(\n `flushed ${rows} rows@${versionString(rowsVersion)} (${elapsed} ms)`,\n );\n this.#recordAsyncFlushStats(rows, elapsed);\n this.#flushedRowsVersion = rowsVersion;\n // Note: apply() may have called while the transaction was committing,\n // which will result in looping to commit the next #pendingRowsVersion.\n }\n this.#lc.debug?.(\n `up to date rows@${versionToNullableCookie(this.#flushedRowsVersion)}`,\n );\n flushing.resolve();\n this.#flushing = null;\n } catch (e) {\n flushing.reject(e);\n this.#failService(e);\n }\n }\n\n hasPendingUpdates() {\n return this.#flushing !== null;\n }\n\n /**\n * Returns a promise that resolves when all outstanding row-records\n * have been committed.\n */\n flushed(lc: LogContext): Promise<void> {\n if (this.#flushing) {\n lc.debug?.('awaiting pending row flush');\n return this.#flushing.promise;\n }\n return promiseVoid;\n }\n\n clear() {\n // Note: Only the #cache is cleared. #pending updates, on the other hand,\n // comprise canonical (i.e. already flushed) data and must be flushed\n // even if the snapshot of the present state (the #cache) is cleared.\n this.#cache = undefined;\n }\n\n async *catchupRowPatches(\n lc: LogContext,\n afterVersion: NullableCVRVersion,\n upToCVR: CVRSnapshot,\n current: CVRVersion,\n excludeQueryHashes: string[] = [],\n ): AsyncGenerator<RowsRow[], void, undefined> {\n if (cmpVersions(afterVersion, upToCVR.version) >= 0) {\n return;\n }\n\n const startMs = Date.now();\n const start = afterVersion ? versionString(afterVersion) : '';\n const end = versionString(upToCVR.version);\n lc.debug?.(`scanning row patches for clients from ${start}`);\n\n // Before accessing the CVR db, pending row records must be flushed.\n // Note that because catchupRowPatches() is called from within the\n // view syncer lock, this flush is guaranteed to complete since no\n // new CVR updates can happen while the lock is held.\n await this.flushed(lc);\n const flushMs = Date.now() - startMs;\n\n const reader = new TransactionPool(lc, Mode.READONLY).run(this.#db);\n try {\n // Verify that we are reading the right version of the CVR.\n await reader.processReadTask(tx =>\n checkVersion(tx, this.#schema, this.#cvrID, current),\n );\n\n const {query} = await reader.processReadTask(tx => {\n const query =\n excludeQueryHashes.length === 0\n ? tx<RowsRow[]>`SELECT * FROM ${this.#cvr('rows')}\n WHERE \"clientGroupID\" = ${this.#cvrID}\n AND \"patchVersion\" > ${start}\n AND \"patchVersion\" <= ${end}`\n : // Exclude rows that were already sent as part of query hydration.\n tx<RowsRow[]>`SELECT * FROM ${this.#cvr('rows')}\n WHERE \"clientGroupID\" = ${this.#cvrID}\n AND \"patchVersion\" > ${start}\n AND \"patchVersion\" <= ${end}\n AND (\"refCounts\" IS NULL OR NOT \"refCounts\" ?| ${excludeQueryHashes})`;\n return {query};\n });\n\n yield* query.cursor(10000);\n } finally {\n reader.setDone();\n }\n\n const totalMs = Date.now() - startMs;\n lc.debug?.(\n `finished row catchup (flush: ${flushMs} ms, total: ${totalMs} ms)`,\n );\n }\n\n executeRowUpdates(\n tx: PostgresTransaction,\n version: CVRVersion,\n rowUpdates: Map<RowID, RowRecord | null>,\n mode: 'allow-defer' | 'force',\n lc = this.#lc,\n ): PendingQuery<Row[]>[] {\n if (\n mode === 'allow-defer' &&\n // defer if pending rows are being flushed\n (this.#flushing !== null ||\n // or if the new batch is above the limit.\n rowUpdates.size > this.#deferredRowFlushThreshold)\n ) {\n return [];\n }\n const rowsVersion = {\n clientGroupID: this.#cvrID,\n version: versionString(version),\n };\n const pending: PendingQuery<Row[]>[] = [\n tx`INSERT INTO ${this.#cvr('rowsVersion')} ${tx(rowsVersion)}\n ON CONFLICT (\"clientGroupID\") \n DO UPDATE SET ${tx(rowsVersion)}`.execute(),\n ];\n\n const rowRecordRows: RowsRow[] = [];\n for (const [id, row] of rowUpdates.entries()) {\n if (row === null) {\n pending.push(\n tx`\n DELETE FROM ${this.#cvr('rows')}\n WHERE \"clientGroupID\" = ${this.#cvrID}\n AND \"schema\" = ${id.schema}\n AND \"table\" = ${id.table}\n AND \"rowKey\" = ${id.rowKey}\n `.execute(),\n );\n } else {\n rowRecordRows.push(rowRecordToRowsRow(this.#cvrID, row));\n }\n }\n if (rowRecordRows.length) {\n pending.push(\n tx`\n INSERT INTO ${this.#cvr('rows')}(\n \"clientGroupID\", \"schema\", \"table\", \"rowKey\", \"rowVersion\", \"patchVersion\", \"refCounts\"\n ) SELECT\n \"clientGroupID\", \"schema\", \"table\", \"rowKey\", \"rowVersion\", \"patchVersion\", \"refCounts\"\n FROM json_to_recordset(${rowRecordRows}) AS x(\n \"clientGroupID\" TEXT,\n \"schema\" TEXT,\n \"table\" TEXT,\n \"rowKey\" JSONB,\n \"rowVersion\" TEXT,\n \"patchVersion\" TEXT,\n \"refCounts\" JSONB\n ) ON CONFLICT (\"clientGroupID\", \"schema\", \"table\", \"rowKey\")\n DO UPDATE SET \"rowVersion\" = excluded.\"rowVersion\",\n \"patchVersion\" = excluded.\"patchVersion\",\n \"refCounts\" = excluded.\"refCounts\"\n `.execute(),\n );\n lc.debug?.(\n `flushing ${rowUpdates.size} rows (${rowRecordRows.length} inserts, ${\n rowUpdates.size - rowRecordRows.length\n } deletes)`,\n );\n }\n return pending;\n }\n}\n"],"names":["Mode.READ_COMMITTED","rows","rowsVersion","Mode.READONLY","query"],"mappings":"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"}
@@ -238,7 +238,7 @@ class Diff {
238
238
  );
239
239
  }
240
240
  const { tableSpec, zqlSpec } = must(this.tables.get(table));
241
- assert(rowKey !== null);
241
+ assert(rowKey !== null, "rowKey must be present for row changes");
242
242
  const nextValue = op === SET_OP ? this.curr.getRow(tableSpec, rowKey) : null;
243
243
  let prevValues;
244
244
  if (nextValue) {
@@ -1 +1 @@
1
- {"version":3,"file":"snapshotter.js","sources":["../../../../../../zero-cache/src/services/view-syncer/snapshotter.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../../shared/src/asserts.ts';\nimport {stringify, type JSONValue} from '../../../../shared/src/bigint-json.ts';\nimport {must} from '../../../../shared/src/must.ts';\nimport * as v from '../../../../shared/src/valita.ts';\nimport type {Row} from '../../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../../zero-types/src/schema.ts';\nimport {Database} from '../../../../zqlite/src/db.ts';\nimport {fromSQLiteTypes} from '../../../../zqlite/src/table-source.ts';\nimport type {LiteAndZqlSpec, LiteTableSpecWithKeys} from '../../db/specs.ts';\nimport {StatementRunner} from '../../db/statements.ts';\nimport {\n normalizedKeyOrder,\n type RowKey,\n type RowValue,\n} from '../../types/row-key.ts';\nimport type {AppID} from '../../types/shards.ts';\nimport {id} from '../../types/sql.ts';\nimport {\n RESET_OP,\n changeLogEntrySchema as schema,\n SET_OP,\n TRUNCATE_OP,\n} from '../replicator/schema/change-log.ts';\nimport {\n getReplicationState,\n ZERO_VERSION_COLUMN_NAME as ROW_VERSION,\n} from '../replicator/schema/replication-state.ts';\n\n/**\n * A `Snapshotter` manages the progression of database snapshots for a\n * ViewSyncer.\n *\n * The Replicator and ViewSyncers operate on the same SQLite file, with the\n * Replicator being the sole writer to the database. The IVM logic in\n * ViewSyncers, however, rely on incrementally applying changes to the DB to\n * update the state of its pipelines.\n *\n * To avoid coupling the progress of the Replicator and all IVM pipelines on\n * each other, ViewSyncers operate on ephemeral forks of the database by holding\n * [concurrent](https://sqlite.org/src/doc/begin-concurrent/doc/begin_concurrent.md)\n * snapshots of the database and simulating (but ultimately rolling back)\n * mutations on these snapshots.\n *\n * Example:\n * 1. ViewSyncer takes `snapshot_a` at version `t1` of the database and\n * hydrates its pipeline(s).\n * 2. Replicator applies a new transaction to the database and notifies\n * subscribers.\n * 3. ViewSyncer takes `snapshot_b` at `t2`, and queries the `ChangeLog` at\n * that snapshot for changes since `t1`.\n * 4. ViewSyncer applies those changes to `snapshot_a` for IVM, but does not\n * commit them. (Recall that the Replicator is the sole writer to the db, so\n * the ViewSyncer never commits any writes.)\n * 5. Replicator applies the next transaction and advances the database to `t3`.\n * 6. ViewSyncer rolls back `snapshot_a` and opens `snapshot_c` at `t3`, using\n * `snapshot_b` to simulate changes from `t2` to `t3`.\n *\n * ```\n * Replicator: t1 --------------> t2 --------------> t3 --------------->\n * ViewSyncer: [snapshot_a] ----> [snapshot_b] ----> [snapshot_c]\n * ```\n *\n * Note that the Replicator (and ViewSyncers) do not wait on the progress of\n * other ViewSyncers. If a ViewSyncer is busy hydrating at `t1`, the Replicator\n * and other ViewSyncers can progress through `t2`, `t3`, etc. independently,\n * as the busy ViewSyncer simply takes its own snapshot when it is ready.\n *\n * ```\n * Replicator: t1 --------------> t2 --------------> t3 --------------->\n * ViewSyncer1: [snapshot_a] ----> [snapshot_b] ----> [snapshot_c]\n * ViewSyncer2: [.......... snapshot_a ..........] ----> [snapshot_b]\n * ```\n *\n * To minimize Database connections (and statement preparation, etc.), the\n * Snapshotter reuses the connection from the previous (rolled back)\n * snapshot when opening the new one.\n *\n * ```\n * Replicator: t1 --------------> t2 --------------> t3 --------------->\n * ViewSyncer: [snapshot_a] ----> [snapshot_b] ----> [snapshot_c]\n * (conn_1) (conn_2) (conn_1)\n * ```\n *\n * In this manner, each ViewSyncer uses two connections that continually\n * \"leapfrog\" each other to replay the timeline of changes in isolation from\n * the Replicator and other ViewSyncers.\n */\nexport class Snapshotter {\n readonly #lc: LogContext;\n readonly #dbFile: string;\n readonly #appID: string;\n readonly #pageCacheSizeKib: number | undefined;\n #curr: Snapshot | undefined;\n #prev: Snapshot | undefined;\n\n constructor(\n lc: LogContext,\n dbFile: string,\n {appID}: AppID,\n pageCacheSizeKib?: number | undefined,\n ) {\n this.#lc = lc;\n this.#dbFile = dbFile;\n this.#appID = appID;\n this.#pageCacheSizeKib = pageCacheSizeKib;\n }\n\n /**\n * Initializes the snapshot to the current head of the database. This must be\n * only be called once. The state of whether a Snapshotter has been initialized\n * can be determined by calling {@link initialized()}.\n */\n init(): this {\n assert(this.#curr === undefined, 'Already initialized');\n this.#curr = Snapshot.create(\n this.#lc,\n this.#dbFile,\n this.#appID,\n this.#pageCacheSizeKib,\n );\n this.#lc.debug?.(`Initial snapshot at version ${this.#curr.version}`);\n return this;\n }\n\n initialized(): boolean {\n return this.#curr !== undefined;\n }\n\n /** Returns the current snapshot. Asserts if {@link initialized()} is false. */\n current(): Snapshot {\n assert(this.#curr !== undefined, 'Snapshotter has not been initialized');\n return this.#curr;\n }\n\n /**\n * Advances to the head of the Database, returning a diff between the\n * previously current Snapshot and a new Snapshot at head. This is called\n * in response to a notification from a Replicator subscription. Subsequent\n * calls to {@link current()} return the new Snapshot. Note that the Snapshotter\n * must be initialized before advancing.\n *\n * The returned {@link SnapshotDiff} contains snapshots at the endpoints\n * of the database timeline. Iterating over the diff generates a sequence\n * of {@link Change}s between the two snapshots.\n *\n * Note that this sequence is not chronological; rather, the sequence is\n * ordered by `<table, row-key>`, such that a row can appear at most once\n * in the common case, or twice if its table is `TRUNCATE`'d and a new value\n * is subsequently `INSERT`'ed. This results in dropping most intermediate\n * changes to a row and bounds the amount of work needed to catch up;\n * however, as a consequence, a consistent database state is only guaranteed\n * when the sequence has been fully consumed.\n *\n * Note that Change generation relies on the state of the underlying\n * database connections, and because the connection for the previous snapshot\n * is reused to produce the next snapshot, the diff object is only valid\n * until the next call to `advance()`.\n *\n * It is okay for the caller to apply `Change`s to the `prev` snapshot\n * during the iteration (e.g. this is necessary for IVM); the remainder\n * of the iteration is not affected because a given row can appear at most\n * once in the sequence (with the exception being TRUNCATE, after which the\n * deleted rows can be re-inserted, but this will also behave correctly if\n * the changes are applied).\n *\n * Once the changes have been applied, however, a _subsequent_ iteration\n * will not produce the correct results. In order to perform multiple\n * change-applying iterations, the caller must (1) create a save point\n * on `prev` before each iteration, and (2) rollback to the save point after\n * the iteration.\n */\n advance(tables: Map<string, LiteAndZqlSpec>): SnapshotDiff {\n const {prev, curr} = this.advanceWithoutDiff();\n return new Diff(this.#appID, tables, prev, curr);\n }\n\n advanceWithoutDiff() {\n assert(this.#curr !== undefined, 'Snapshotter has not been initialized');\n const next = this.#prev\n ? this.#prev.resetToHead()\n : Snapshot.create(\n this.#lc,\n this.#curr.db.db.name,\n this.#appID,\n this.#pageCacheSizeKib,\n );\n this.#prev = this.#curr;\n this.#curr = next;\n return {prev: this.#prev, curr: this.#curr};\n }\n\n /**\n * Call this to close the database connections when the Snapshotter is\n * no longer needed.\n */\n destroy() {\n this.#curr?.db.db.close();\n this.#prev?.db.db.close();\n this.#lc.debug?.('closed database connections');\n }\n}\n\nexport type Change = {\n readonly table: string;\n /**\n * If this change represents a remove the row to remove,\n * if nextValue is not null then all rows that have a unique constraint\n * violation with nextValue.\n * In both cases these rows should be removed.\n */\n readonly prevValues: Readonly<Row>[];\n readonly nextValue: Readonly<Row> | null;\n readonly rowKey: RowKey;\n};\n\n/**\n * Represents the difference between two database Snapshots.\n * Iterating over the object will produce a sequence of {@link Change}s\n * between the two snapshots.\n *\n * See {@link Snapshotter.advance()} for semantics and usage.\n */\nexport interface SnapshotDiff extends Iterable<Change> {\n readonly prev: {\n readonly db: StatementRunner;\n readonly version: string;\n };\n readonly curr: {\n readonly db: StatementRunner;\n readonly version: string;\n };\n\n /**\n * The number of ChangeLog entries between the snapshots. Note that this\n * may not necessarily equal the number of `Change` objects that the iteration\n * will produce, as `TRUNCATE` entries are counted as a single log entry which\n * may be expanded into many changes (i.e. row deletes).\n *\n * TODO: Determine if it is worth changing the definition to count the\n * truncated rows. This would make diff computation more expensive\n * (requiring the count to be aggregated by operation type), which\n * may not be worth it for a presumable rare operation.\n */\n readonly changes: number;\n}\n\n/**\n * Thrown during an iteration of a {@link SnapshotDiff} when a schema\n * change or truncate is encountered, which result in aborting the\n * advancement and resetting / rehydrating the pipelines.\n */\nexport class ResetPipelinesSignal extends Error {\n readonly name = 'ResetPipelinesSignal';\n\n constructor(msg: string) {\n super(msg);\n }\n}\n\nclass Snapshot {\n static create(\n lc: LogContext,\n dbFile: string,\n appID: string,\n pageCacheSizeKib: number | undefined,\n ) {\n const conn = new Database(lc, dbFile);\n conn.pragma('synchronous = OFF'); // Applied changes are ephemeral; COMMIT is never called.\n if (pageCacheSizeKib !== undefined) {\n conn.pragma(`cache_size = -${pageCacheSizeKib}`); // Negative = size in KiB\n }\n const [{journal_mode: mode}] = conn.pragma('journal_mode') as [\n {journal_mode: string},\n ];\n // The Snapshotter operates on the replica file with BEGIN CONCURRENT,\n // which must be used in concert with the replicator using BEGIN CONCURRENT\n // on a db in the wal2 journal_mode.\n assert(\n mode === 'wal2',\n `replica db must be in wal2 mode (current: ${mode})`,\n );\n\n const db = new StatementRunner(conn);\n return new Snapshot(db, appID);\n }\n\n readonly db: StatementRunner;\n readonly #appID: string;\n readonly version: string;\n\n constructor(db: StatementRunner, appID: string) {\n db.beginConcurrent();\n // Note: The subsequent read is necessary to acquire the read lock\n // (which results in the logical creation of the snapshot). Calling\n // `BEGIN CONCURRENT` alone does not result in acquiring the read lock.\n const {stateVersion} = getReplicationState(db);\n\n this.db = db;\n this.#appID = appID;\n this.version = stateVersion;\n }\n\n numChangesSince(prevVersion: string) {\n const {count} = this.db.get(\n 'SELECT COUNT(*) AS count FROM \"_zero.changeLog2\" WHERE stateVersion > ?',\n prevVersion,\n );\n return count;\n }\n\n changesSince(prevVersion: string) {\n const cached = this.db.statementCache.get(\n 'SELECT * FROM \"_zero.changeLog2\" WHERE stateVersion > ? ORDER BY stateVersion ASC, pos ASC',\n );\n return {\n changes: cached.statement.iterate(prevVersion),\n cleanup: () => this.db.statementCache.return(cached),\n };\n }\n\n getRow(table: LiteTableSpecWithKeys, rowKey: JSONValue) {\n const key = normalizedKeyOrder(rowKey as RowKey);\n const conds = Object.keys(key).map(c => `${id(c)}=?`);\n const cols = Object.keys(table.columns);\n const cached = this.db.statementCache.get(\n `SELECT ${cols.map(c => id(c)).join(',')} FROM ${id(\n table.name,\n )} WHERE ${conds.join(' AND ')}`,\n );\n cached.statement.safeIntegers(true);\n try {\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n return cached.statement.get<any>(Object.values(key));\n } finally {\n this.db.statementCache.return(cached);\n }\n }\n\n getRows(table: LiteTableSpecWithKeys, keys: PrimaryKey[], row: RowValue) {\n const conds = keys.map(key => key.map(c => `${id(c)}=?`));\n const cols = Object.keys(table.columns);\n const cached = this.db.statementCache.get(\n `SELECT ${cols.map(c => id(c)).join(',')} FROM ${id(\n table.name,\n )} WHERE ${conds.map(cond => cond.join(' AND ')).join(' OR ')}`,\n );\n cached.statement.safeIntegers(true);\n try {\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n return cached.statement.all<any>(\n keys.flatMap(key => key.map(column => row[column])),\n );\n } finally {\n this.db.statementCache.return(cached);\n }\n }\n\n resetToHead(): Snapshot {\n this.db.rollback();\n return new Snapshot(this.db, this.#appID);\n }\n}\n\nclass Diff implements SnapshotDiff {\n readonly #permissionsTable: string;\n readonly tables: Map<string, LiteAndZqlSpec>;\n readonly prev: Snapshot;\n readonly curr: Snapshot;\n readonly changes: number;\n\n constructor(\n appID: string,\n tables: Map<string, LiteAndZqlSpec>,\n prev: Snapshot,\n curr: Snapshot,\n ) {\n this.#permissionsTable = `${appID}.permissions`;\n this.tables = tables;\n this.prev = prev;\n this.curr = curr;\n this.changes = curr.numChangesSince(prev.version);\n }\n\n [Symbol.iterator](): Iterator<Change> {\n const {changes, cleanup: done} = this.curr.changesSince(this.prev.version);\n\n const cleanup = () => {\n try {\n // Allow open iterators to clean up their state.\n changes.return?.(undefined);\n } finally {\n done();\n }\n };\n\n return {\n next: () => {\n try {\n for (;;) {\n const {value, done} = changes.next();\n if (done) {\n cleanup();\n return {value, done: true};\n }\n\n const {table, rowKey, op, stateVersion} = v.parse(value, schema);\n if (op === RESET_OP) {\n // The current map of `TableSpec`s may not have the correct or complete information.\n throw new ResetPipelinesSignal(\n `schema for table ${table} has changed`,\n );\n }\n if (op === TRUNCATE_OP) {\n // Truncates are also processed by rehydrating pipelines at current.\n throw new ResetPipelinesSignal(\n `table ${table} has been truncated`,\n );\n }\n const {tableSpec, zqlSpec} = must(this.tables.get(table));\n\n assert(rowKey !== null);\n const nextValue =\n op === SET_OP ? this.curr.getRow(tableSpec, rowKey) : null;\n let prevValues;\n if (nextValue) {\n prevValues = this.prev.getRows(\n tableSpec,\n tableSpec.uniqueKeys,\n nextValue,\n );\n } else {\n const prevValue = this.prev.getRow(tableSpec, rowKey);\n prevValues = prevValue ? [prevValue] : [];\n }\n if (nextValue === undefined) {\n throw new Error(\n `Missing value for ${table} ${stringify(rowKey)}`,\n );\n }\n // Sanity check detects if the diff is being accessed after the Snapshots have advanced.\n this.checkThatDiffIsValid(stateVersion, op, prevValues, nextValue);\n\n if (prevValues.length === 0 && nextValue === null) {\n // Filter out no-op changes (e.g. a delete of a row that does not exist in prev).\n // TODO: Consider doing this for deep-equal values.\n continue;\n }\n\n if (\n table === this.#permissionsTable &&\n prevValues.find(\n prevValue => prevValue.permissions !== nextValue.permissions,\n )\n ) {\n throw new ResetPipelinesSignal(\n `Permissions have changed ${\n prevValues.find(\n prevValue =>\n prevValue.permissions !== nextValue.permissions,\n ).hash\n } => ${nextValue.hash}`,\n );\n }\n\n // Modify the values in place when converting to ZQL rows\n // This is safe since we're the first node in the iterator chain.\n // TODO Can we get rid of these RowValue casts?\n return {\n value: {\n table,\n prevValues: prevValues.map(prevValue =>\n fromSQLiteTypes(zqlSpec, prevValue, table),\n ),\n nextValue: nextValue\n ? fromSQLiteTypes(zqlSpec, nextValue, table)\n : null,\n rowKey,\n } satisfies Change,\n };\n }\n } catch (e) {\n // This control flow path is not covered by the return() method (i.e. `break`).\n cleanup();\n throw e;\n }\n },\n\n return: (value: unknown) => {\n cleanup();\n return {value, done: true};\n },\n };\n }\n\n checkThatDiffIsValid(\n stateVersion: string,\n op: string,\n prevValues: RowValue[],\n nextValue: RowValue,\n ) {\n // Sanity checks to detect that the diff is not being accessed after\n // the Snapshots have advanced.\n if (stateVersion > this.curr.version) {\n throw new InvalidDiffError(\n `Diff is no longer valid. curr db has advanced past ${this.curr.version}`,\n );\n }\n if (\n prevValues.findIndex(\n prevValue => (prevValue[ROW_VERSION] ?? '~') > this.prev.version,\n ) !== -1\n ) {\n throw new InvalidDiffError(\n `Diff is no longer valid. prev db has advanced past ${this.prev.version}.`,\n );\n }\n if (op === SET_OP && nextValue[ROW_VERSION] !== stateVersion) {\n throw new InvalidDiffError(\n 'Diff is no longer valid. curr db has advanced.',\n );\n }\n }\n}\n\nexport class InvalidDiffError extends Error {\n constructor(msg: string) {\n super(msg);\n }\n}\n"],"names":["done","v.parse","schema","ROW_VERSION"],"mappings":"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"}
1
+ {"version":3,"file":"snapshotter.js","sources":["../../../../../../zero-cache/src/services/view-syncer/snapshotter.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../../shared/src/asserts.ts';\nimport {stringify, type JSONValue} from '../../../../shared/src/bigint-json.ts';\nimport {must} from '../../../../shared/src/must.ts';\nimport * as v from '../../../../shared/src/valita.ts';\nimport type {Row} from '../../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../../zero-types/src/schema.ts';\nimport {Database} from '../../../../zqlite/src/db.ts';\nimport {fromSQLiteTypes} from '../../../../zqlite/src/table-source.ts';\nimport type {LiteAndZqlSpec, LiteTableSpecWithKeys} from '../../db/specs.ts';\nimport {StatementRunner} from '../../db/statements.ts';\nimport {\n normalizedKeyOrder,\n type RowKey,\n type RowValue,\n} from '../../types/row-key.ts';\nimport type {AppID} from '../../types/shards.ts';\nimport {id} from '../../types/sql.ts';\nimport {\n RESET_OP,\n changeLogEntrySchema as schema,\n SET_OP,\n TRUNCATE_OP,\n} from '../replicator/schema/change-log.ts';\nimport {\n getReplicationState,\n ZERO_VERSION_COLUMN_NAME as ROW_VERSION,\n} from '../replicator/schema/replication-state.ts';\n\n/**\n * A `Snapshotter` manages the progression of database snapshots for a\n * ViewSyncer.\n *\n * The Replicator and ViewSyncers operate on the same SQLite file, with the\n * Replicator being the sole writer to the database. The IVM logic in\n * ViewSyncers, however, rely on incrementally applying changes to the DB to\n * update the state of its pipelines.\n *\n * To avoid coupling the progress of the Replicator and all IVM pipelines on\n * each other, ViewSyncers operate on ephemeral forks of the database by holding\n * [concurrent](https://sqlite.org/src/doc/begin-concurrent/doc/begin_concurrent.md)\n * snapshots of the database and simulating (but ultimately rolling back)\n * mutations on these snapshots.\n *\n * Example:\n * 1. ViewSyncer takes `snapshot_a` at version `t1` of the database and\n * hydrates its pipeline(s).\n * 2. Replicator applies a new transaction to the database and notifies\n * subscribers.\n * 3. ViewSyncer takes `snapshot_b` at `t2`, and queries the `ChangeLog` at\n * that snapshot for changes since `t1`.\n * 4. ViewSyncer applies those changes to `snapshot_a` for IVM, but does not\n * commit them. (Recall that the Replicator is the sole writer to the db, so\n * the ViewSyncer never commits any writes.)\n * 5. Replicator applies the next transaction and advances the database to `t3`.\n * 6. ViewSyncer rolls back `snapshot_a` and opens `snapshot_c` at `t3`, using\n * `snapshot_b` to simulate changes from `t2` to `t3`.\n *\n * ```\n * Replicator: t1 --------------> t2 --------------> t3 --------------->\n * ViewSyncer: [snapshot_a] ----> [snapshot_b] ----> [snapshot_c]\n * ```\n *\n * Note that the Replicator (and ViewSyncers) do not wait on the progress of\n * other ViewSyncers. If a ViewSyncer is busy hydrating at `t1`, the Replicator\n * and other ViewSyncers can progress through `t2`, `t3`, etc. independently,\n * as the busy ViewSyncer simply takes its own snapshot when it is ready.\n *\n * ```\n * Replicator: t1 --------------> t2 --------------> t3 --------------->\n * ViewSyncer1: [snapshot_a] ----> [snapshot_b] ----> [snapshot_c]\n * ViewSyncer2: [.......... snapshot_a ..........] ----> [snapshot_b]\n * ```\n *\n * To minimize Database connections (and statement preparation, etc.), the\n * Snapshotter reuses the connection from the previous (rolled back)\n * snapshot when opening the new one.\n *\n * ```\n * Replicator: t1 --------------> t2 --------------> t3 --------------->\n * ViewSyncer: [snapshot_a] ----> [snapshot_b] ----> [snapshot_c]\n * (conn_1) (conn_2) (conn_1)\n * ```\n *\n * In this manner, each ViewSyncer uses two connections that continually\n * \"leapfrog\" each other to replay the timeline of changes in isolation from\n * the Replicator and other ViewSyncers.\n */\nexport class Snapshotter {\n readonly #lc: LogContext;\n readonly #dbFile: string;\n readonly #appID: string;\n readonly #pageCacheSizeKib: number | undefined;\n #curr: Snapshot | undefined;\n #prev: Snapshot | undefined;\n\n constructor(\n lc: LogContext,\n dbFile: string,\n {appID}: AppID,\n pageCacheSizeKib?: number | undefined,\n ) {\n this.#lc = lc;\n this.#dbFile = dbFile;\n this.#appID = appID;\n this.#pageCacheSizeKib = pageCacheSizeKib;\n }\n\n /**\n * Initializes the snapshot to the current head of the database. This must be\n * only be called once. The state of whether a Snapshotter has been initialized\n * can be determined by calling {@link initialized()}.\n */\n init(): this {\n assert(this.#curr === undefined, 'Already initialized');\n this.#curr = Snapshot.create(\n this.#lc,\n this.#dbFile,\n this.#appID,\n this.#pageCacheSizeKib,\n );\n this.#lc.debug?.(`Initial snapshot at version ${this.#curr.version}`);\n return this;\n }\n\n initialized(): boolean {\n return this.#curr !== undefined;\n }\n\n /** Returns the current snapshot. Asserts if {@link initialized()} is false. */\n current(): Snapshot {\n assert(this.#curr !== undefined, 'Snapshotter has not been initialized');\n return this.#curr;\n }\n\n /**\n * Advances to the head of the Database, returning a diff between the\n * previously current Snapshot and a new Snapshot at head. This is called\n * in response to a notification from a Replicator subscription. Subsequent\n * calls to {@link current()} return the new Snapshot. Note that the Snapshotter\n * must be initialized before advancing.\n *\n * The returned {@link SnapshotDiff} contains snapshots at the endpoints\n * of the database timeline. Iterating over the diff generates a sequence\n * of {@link Change}s between the two snapshots.\n *\n * Note that this sequence is not chronological; rather, the sequence is\n * ordered by `<table, row-key>`, such that a row can appear at most once\n * in the common case, or twice if its table is `TRUNCATE`'d and a new value\n * is subsequently `INSERT`'ed. This results in dropping most intermediate\n * changes to a row and bounds the amount of work needed to catch up;\n * however, as a consequence, a consistent database state is only guaranteed\n * when the sequence has been fully consumed.\n *\n * Note that Change generation relies on the state of the underlying\n * database connections, and because the connection for the previous snapshot\n * is reused to produce the next snapshot, the diff object is only valid\n * until the next call to `advance()`.\n *\n * It is okay for the caller to apply `Change`s to the `prev` snapshot\n * during the iteration (e.g. this is necessary for IVM); the remainder\n * of the iteration is not affected because a given row can appear at most\n * once in the sequence (with the exception being TRUNCATE, after which the\n * deleted rows can be re-inserted, but this will also behave correctly if\n * the changes are applied).\n *\n * Once the changes have been applied, however, a _subsequent_ iteration\n * will not produce the correct results. In order to perform multiple\n * change-applying iterations, the caller must (1) create a save point\n * on `prev` before each iteration, and (2) rollback to the save point after\n * the iteration.\n */\n advance(tables: Map<string, LiteAndZqlSpec>): SnapshotDiff {\n const {prev, curr} = this.advanceWithoutDiff();\n return new Diff(this.#appID, tables, prev, curr);\n }\n\n advanceWithoutDiff() {\n assert(this.#curr !== undefined, 'Snapshotter has not been initialized');\n const next = this.#prev\n ? this.#prev.resetToHead()\n : Snapshot.create(\n this.#lc,\n this.#curr.db.db.name,\n this.#appID,\n this.#pageCacheSizeKib,\n );\n this.#prev = this.#curr;\n this.#curr = next;\n return {prev: this.#prev, curr: this.#curr};\n }\n\n /**\n * Call this to close the database connections when the Snapshotter is\n * no longer needed.\n */\n destroy() {\n this.#curr?.db.db.close();\n this.#prev?.db.db.close();\n this.#lc.debug?.('closed database connections');\n }\n}\n\nexport type Change = {\n readonly table: string;\n /**\n * If this change represents a remove the row to remove,\n * if nextValue is not null then all rows that have a unique constraint\n * violation with nextValue.\n * In both cases these rows should be removed.\n */\n readonly prevValues: Readonly<Row>[];\n readonly nextValue: Readonly<Row> | null;\n readonly rowKey: RowKey;\n};\n\n/**\n * Represents the difference between two database Snapshots.\n * Iterating over the object will produce a sequence of {@link Change}s\n * between the two snapshots.\n *\n * See {@link Snapshotter.advance()} for semantics and usage.\n */\nexport interface SnapshotDiff extends Iterable<Change> {\n readonly prev: {\n readonly db: StatementRunner;\n readonly version: string;\n };\n readonly curr: {\n readonly db: StatementRunner;\n readonly version: string;\n };\n\n /**\n * The number of ChangeLog entries between the snapshots. Note that this\n * may not necessarily equal the number of `Change` objects that the iteration\n * will produce, as `TRUNCATE` entries are counted as a single log entry which\n * may be expanded into many changes (i.e. row deletes).\n *\n * TODO: Determine if it is worth changing the definition to count the\n * truncated rows. This would make diff computation more expensive\n * (requiring the count to be aggregated by operation type), which\n * may not be worth it for a presumable rare operation.\n */\n readonly changes: number;\n}\n\n/**\n * Thrown during an iteration of a {@link SnapshotDiff} when a schema\n * change or truncate is encountered, which result in aborting the\n * advancement and resetting / rehydrating the pipelines.\n */\nexport class ResetPipelinesSignal extends Error {\n readonly name = 'ResetPipelinesSignal';\n\n constructor(msg: string) {\n super(msg);\n }\n}\n\nclass Snapshot {\n static create(\n lc: LogContext,\n dbFile: string,\n appID: string,\n pageCacheSizeKib: number | undefined,\n ) {\n const conn = new Database(lc, dbFile);\n conn.pragma('synchronous = OFF'); // Applied changes are ephemeral; COMMIT is never called.\n if (pageCacheSizeKib !== undefined) {\n conn.pragma(`cache_size = -${pageCacheSizeKib}`); // Negative = size in KiB\n }\n const [{journal_mode: mode}] = conn.pragma('journal_mode') as [\n {journal_mode: string},\n ];\n // The Snapshotter operates on the replica file with BEGIN CONCURRENT,\n // which must be used in concert with the replicator using BEGIN CONCURRENT\n // on a db in the wal2 journal_mode.\n assert(\n mode === 'wal2',\n `replica db must be in wal2 mode (current: ${mode})`,\n );\n\n const db = new StatementRunner(conn);\n return new Snapshot(db, appID);\n }\n\n readonly db: StatementRunner;\n readonly #appID: string;\n readonly version: string;\n\n constructor(db: StatementRunner, appID: string) {\n db.beginConcurrent();\n // Note: The subsequent read is necessary to acquire the read lock\n // (which results in the logical creation of the snapshot). Calling\n // `BEGIN CONCURRENT` alone does not result in acquiring the read lock.\n const {stateVersion} = getReplicationState(db);\n\n this.db = db;\n this.#appID = appID;\n this.version = stateVersion;\n }\n\n numChangesSince(prevVersion: string) {\n const {count} = this.db.get(\n 'SELECT COUNT(*) AS count FROM \"_zero.changeLog2\" WHERE stateVersion > ?',\n prevVersion,\n );\n return count;\n }\n\n changesSince(prevVersion: string) {\n const cached = this.db.statementCache.get(\n 'SELECT * FROM \"_zero.changeLog2\" WHERE stateVersion > ? ORDER BY stateVersion ASC, pos ASC',\n );\n return {\n changes: cached.statement.iterate(prevVersion),\n cleanup: () => this.db.statementCache.return(cached),\n };\n }\n\n getRow(table: LiteTableSpecWithKeys, rowKey: JSONValue) {\n const key = normalizedKeyOrder(rowKey as RowKey);\n const conds = Object.keys(key).map(c => `${id(c)}=?`);\n const cols = Object.keys(table.columns);\n const cached = this.db.statementCache.get(\n `SELECT ${cols.map(c => id(c)).join(',')} FROM ${id(\n table.name,\n )} WHERE ${conds.join(' AND ')}`,\n );\n cached.statement.safeIntegers(true);\n try {\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n return cached.statement.get<any>(Object.values(key));\n } finally {\n this.db.statementCache.return(cached);\n }\n }\n\n getRows(table: LiteTableSpecWithKeys, keys: PrimaryKey[], row: RowValue) {\n const conds = keys.map(key => key.map(c => `${id(c)}=?`));\n const cols = Object.keys(table.columns);\n const cached = this.db.statementCache.get(\n `SELECT ${cols.map(c => id(c)).join(',')} FROM ${id(\n table.name,\n )} WHERE ${conds.map(cond => cond.join(' AND ')).join(' OR ')}`,\n );\n cached.statement.safeIntegers(true);\n try {\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n return cached.statement.all<any>(\n keys.flatMap(key => key.map(column => row[column])),\n );\n } finally {\n this.db.statementCache.return(cached);\n }\n }\n\n resetToHead(): Snapshot {\n this.db.rollback();\n return new Snapshot(this.db, this.#appID);\n }\n}\n\nclass Diff implements SnapshotDiff {\n readonly #permissionsTable: string;\n readonly tables: Map<string, LiteAndZqlSpec>;\n readonly prev: Snapshot;\n readonly curr: Snapshot;\n readonly changes: number;\n\n constructor(\n appID: string,\n tables: Map<string, LiteAndZqlSpec>,\n prev: Snapshot,\n curr: Snapshot,\n ) {\n this.#permissionsTable = `${appID}.permissions`;\n this.tables = tables;\n this.prev = prev;\n this.curr = curr;\n this.changes = curr.numChangesSince(prev.version);\n }\n\n [Symbol.iterator](): Iterator<Change> {\n const {changes, cleanup: done} = this.curr.changesSince(this.prev.version);\n\n const cleanup = () => {\n try {\n // Allow open iterators to clean up their state.\n changes.return?.(undefined);\n } finally {\n done();\n }\n };\n\n return {\n next: () => {\n try {\n for (;;) {\n const {value, done} = changes.next();\n if (done) {\n cleanup();\n return {value, done: true};\n }\n\n const {table, rowKey, op, stateVersion} = v.parse(value, schema);\n if (op === RESET_OP) {\n // The current map of `TableSpec`s may not have the correct or complete information.\n throw new ResetPipelinesSignal(\n `schema for table ${table} has changed`,\n );\n }\n if (op === TRUNCATE_OP) {\n // Truncates are also processed by rehydrating pipelines at current.\n throw new ResetPipelinesSignal(\n `table ${table} has been truncated`,\n );\n }\n const {tableSpec, zqlSpec} = must(this.tables.get(table));\n\n assert(rowKey !== null, 'rowKey must be present for row changes');\n const nextValue =\n op === SET_OP ? this.curr.getRow(tableSpec, rowKey) : null;\n let prevValues;\n if (nextValue) {\n prevValues = this.prev.getRows(\n tableSpec,\n tableSpec.uniqueKeys,\n nextValue,\n );\n } else {\n const prevValue = this.prev.getRow(tableSpec, rowKey);\n prevValues = prevValue ? [prevValue] : [];\n }\n if (nextValue === undefined) {\n throw new Error(\n `Missing value for ${table} ${stringify(rowKey)}`,\n );\n }\n // Sanity check detects if the diff is being accessed after the Snapshots have advanced.\n this.checkThatDiffIsValid(stateVersion, op, prevValues, nextValue);\n\n if (prevValues.length === 0 && nextValue === null) {\n // Filter out no-op changes (e.g. a delete of a row that does not exist in prev).\n // TODO: Consider doing this for deep-equal values.\n continue;\n }\n\n if (\n table === this.#permissionsTable &&\n prevValues.find(\n prevValue => prevValue.permissions !== nextValue.permissions,\n )\n ) {\n throw new ResetPipelinesSignal(\n `Permissions have changed ${\n prevValues.find(\n prevValue =>\n prevValue.permissions !== nextValue.permissions,\n ).hash\n } => ${nextValue.hash}`,\n );\n }\n\n // Modify the values in place when converting to ZQL rows\n // This is safe since we're the first node in the iterator chain.\n // TODO Can we get rid of these RowValue casts?\n return {\n value: {\n table,\n prevValues: prevValues.map(prevValue =>\n fromSQLiteTypes(zqlSpec, prevValue, table),\n ),\n nextValue: nextValue\n ? fromSQLiteTypes(zqlSpec, nextValue, table)\n : null,\n rowKey,\n } satisfies Change,\n };\n }\n } catch (e) {\n // This control flow path is not covered by the return() method (i.e. `break`).\n cleanup();\n throw e;\n }\n },\n\n return: (value: unknown) => {\n cleanup();\n return {value, done: true};\n },\n };\n }\n\n checkThatDiffIsValid(\n stateVersion: string,\n op: string,\n prevValues: RowValue[],\n nextValue: RowValue,\n ) {\n // Sanity checks to detect that the diff is not being accessed after\n // the Snapshots have advanced.\n if (stateVersion > this.curr.version) {\n throw new InvalidDiffError(\n `Diff is no longer valid. curr db has advanced past ${this.curr.version}`,\n );\n }\n if (\n prevValues.findIndex(\n prevValue => (prevValue[ROW_VERSION] ?? '~') > this.prev.version,\n ) !== -1\n ) {\n throw new InvalidDiffError(\n `Diff is no longer valid. prev db has advanced past ${this.prev.version}.`,\n );\n }\n if (op === SET_OP && nextValue[ROW_VERSION] !== stateVersion) {\n throw new InvalidDiffError(\n 'Diff is no longer valid. curr db has advanced.',\n );\n }\n }\n}\n\nexport class InvalidDiffError extends Error {\n constructor(msg: string) {\n super(msg);\n }\n}\n"],"names":["done","v.parse","schema","ROW_VERSION"],"mappings":"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"}
@@ -29,11 +29,12 @@ export type SyncContext = {
29
29
  readonly protocolVersion: number;
30
30
  readonly tokenData: TokenData | undefined;
31
31
  readonly httpCookie: string | undefined;
32
+ readonly origin: string | undefined;
32
33
  };
33
34
  export interface ViewSyncer {
34
35
  initConnection(ctx: SyncContext, msg: InitConnectionMessage): Source<Downstream>;
35
36
  changeDesiredQueries(ctx: SyncContext, msg: ChangeDesiredQueriesMessage): Promise<void>;
36
- deleteClients(ctx: SyncContext, msg: DeleteClientsMessage): Promise<void>;
37
+ deleteClients(ctx: SyncContext, msg: DeleteClientsMessage): Promise<string[]>;
37
38
  inspect(context: SyncContext, msg: InspectUpMessage): Promise<void>;
38
39
  }
39
40
  type SetTimeout = (fn: (...args: unknown[]) => void, delay?: number) => ReturnType<typeof setTimeout>;
@@ -54,7 +55,8 @@ export declare class ViewSyncerService implements ViewSyncer, ActivityBasedServi
54
55
  #private;
55
56
  readonly id: string;
56
57
  userQueryURL?: string | undefined;
57
- constructor(config: NormalizedZeroConfig, lc: LogContext, shard: ShardID, taskID: string, clientGroupID: string, cvrDb: PostgresDB, upstreamDb: PostgresDB | undefined, pipelineDriver: PipelineDriver, versionChanges: Subscription<ReplicaState>, drainCoordinator: DrainCoordinator, slowHydrateThreshold: number, inspectorDelegate: InspectorDelegate, customQueryTransformer: CustomQueryTransformer | undefined, runPriorityOp: <T>(op: () => Promise<T>) => Promise<T>, keepaliveMs?: number, setTimeoutFn?: SetTimeout);
58
+ userQueryHeaders?: Record<string, string> | undefined;
59
+ constructor(config: NormalizedZeroConfig, lc: LogContext, shard: ShardID, taskID: string, clientGroupID: string, cvrDb: PostgresDB, pipelineDriver: PipelineDriver, versionChanges: Subscription<ReplicaState>, drainCoordinator: DrainCoordinator, slowHydrateThreshold: number, inspectorDelegate: InspectorDelegate, customQueryTransformer: CustomQueryTransformer | undefined, runPriorityOp: <T>(lc: LogContext, description: string, op: () => Promise<T>) => Promise<T>, keepaliveMs?: number, setTimeoutFn?: SetTimeout);
58
60
  readyState(): Promise<'initialized' | 'draining'>;
59
61
  run(): Promise<void>;
60
62
  /**
@@ -69,7 +71,7 @@ export declare class ViewSyncerService implements ViewSyncer, ActivityBasedServi
69
71
  keepalive(): boolean;
70
72
  initConnection(ctx: SyncContext, initConnectionMessage: InitConnectionMessage): Source<Downstream>;
71
73
  changeDesiredQueries(ctx: SyncContext, msg: ChangeDesiredQueriesMessage): Promise<void>;
72
- deleteClients(ctx: SyncContext, msg: DeleteClientsMessage): Promise<void>;
74
+ deleteClients(ctx: SyncContext, msg: DeleteClientsMessage): Promise<string[]>;
73
75
  inspect(context: SyncContext, msg: InspectUpMessage): Promise<void>;
74
76
  stop(): Promise<void>;
75
77
  /**
@@ -81,6 +83,7 @@ export declare class ViewSyncerService implements ViewSyncer, ActivityBasedServi
81
83
  export declare function pickToken(lc: LogContext, previousToken: TokenData | undefined, newToken: TokenData | undefined): TokenData | undefined;
82
84
  export declare class TimeSliceTimer {
83
85
  #private;
86
+ constructor(lc: LogContext);
84
87
  start(): Promise<this>;
85
88
  startWithoutYielding(): this;
86
89
  yieldProcess(_msgForTesting?: string): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"view-syncer.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/view-syncer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"view-syncer.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/view-syncer.ts"],"names":[],"mappings":"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"}