@rocicorp/zero 1.12.0-canary.0 → 1.12.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 (1387) hide show
  1. package/out/_virtual/{_@oxc-project_runtime@0.130.0/helpers → _@oxc-project_runtime@0.154.0/helpers/esm}/usingCtx.js +2 -2
  2. package/out/analyze-query/src/analyze-cli.d.ts.map +1 -1
  3. package/out/analyze-query/src/analyze-cli.js +4 -2
  4. package/out/analyze-query/src/analyze-cli.js.map +1 -1
  5. package/out/analyze-query/src/bin-transform.js +4 -3
  6. package/out/analyze-query/src/bin-transform.js.map +1 -1
  7. package/out/analyze-query/src/zql-chain.d.ts +23 -0
  8. package/out/analyze-query/src/zql-chain.d.ts.map +1 -0
  9. package/out/analyze-query/src/zql-chain.js +162 -0
  10. package/out/analyze-query/src/zql-chain.js.map +1 -0
  11. package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
  12. package/out/ast-to-zql/src/bin.js +4 -3
  13. package/out/ast-to-zql/src/bin.js.map +1 -1
  14. package/out/ast-to-zql/src/format.js.map +1 -1
  15. package/out/datadog/src/datadog-log-sink.js +5 -4
  16. package/out/datadog/src/datadog-log-sink.js.map +1 -1
  17. package/out/otel/src/log-options.js.map +1 -1
  18. package/out/replicache/src/btree/node.d.ts +0 -2
  19. package/out/replicache/src/btree/node.d.ts.map +1 -1
  20. package/out/replicache/src/btree/node.js +14 -12
  21. package/out/replicache/src/btree/node.js.map +1 -1
  22. package/out/replicache/src/btree/read.js +4 -2
  23. package/out/replicache/src/btree/read.js.map +1 -1
  24. package/out/replicache/src/btree/splice.js.map +1 -1
  25. package/out/replicache/src/btree/write.js +9 -5
  26. package/out/replicache/src/btree/write.js.map +1 -1
  27. package/out/replicache/src/config.js +1 -2
  28. package/out/replicache/src/connection-loop.d.ts.map +1 -1
  29. package/out/replicache/src/connection-loop.js +1 -1
  30. package/out/replicache/src/connection-loop.js.map +1 -1
  31. package/out/replicache/src/dag/chunk.js +1 -1
  32. package/out/replicache/src/dag/gc.js.map +1 -1
  33. package/out/replicache/src/dag/lazy-store.js +8 -7
  34. package/out/replicache/src/dag/lazy-store.js.map +1 -1
  35. package/out/replicache/src/dag/store-impl.js +1 -1
  36. package/out/replicache/src/dag/store-impl.js.map +1 -1
  37. package/out/replicache/src/dag/visitor.js.map +1 -1
  38. package/out/replicache/src/db/commit.d.ts +0 -1
  39. package/out/replicache/src/db/commit.d.ts.map +1 -1
  40. package/out/replicache/src/db/commit.js +5 -3
  41. package/out/replicache/src/db/commit.js.map +1 -1
  42. package/out/replicache/src/db/read.js +1 -1
  43. package/out/replicache/src/db/read.js.map +1 -1
  44. package/out/replicache/src/db/rebase.js.map +1 -1
  45. package/out/replicache/src/db/write.d.ts +0 -2
  46. package/out/replicache/src/db/write.d.ts.map +1 -1
  47. package/out/replicache/src/db/write.js +5 -2
  48. package/out/replicache/src/db/write.js.map +1 -1
  49. package/out/replicache/src/deleted-clients.d.ts +0 -2
  50. package/out/replicache/src/deleted-clients.d.ts.map +1 -1
  51. package/out/replicache/src/deleted-clients.js +5 -3
  52. package/out/replicache/src/deleted-clients.js.map +1 -1
  53. package/out/replicache/src/expo-sqlite.js +1 -2
  54. package/out/replicache/src/frozen-json.d.ts +0 -1
  55. package/out/replicache/src/frozen-json.d.ts.map +1 -1
  56. package/out/replicache/src/frozen-json.js +1 -1
  57. package/out/replicache/src/frozen-json.js.map +1 -1
  58. package/out/replicache/src/get-default-puller.js +1 -1
  59. package/out/replicache/src/hash.js +1 -1
  60. package/out/replicache/src/hash.js.map +1 -1
  61. package/out/replicache/src/impl.js +1 -2
  62. package/out/replicache/src/index-defs.d.ts +0 -2
  63. package/out/replicache/src/index-defs.d.ts.map +1 -1
  64. package/out/replicache/src/index-defs.js +5 -3
  65. package/out/replicache/src/index-defs.js.map +1 -1
  66. package/out/replicache/src/kv/expo-sqlite/store.d.ts +0 -1
  67. package/out/replicache/src/kv/expo-sqlite/store.d.ts.map +1 -1
  68. package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
  69. package/out/replicache/src/kv/idb-store.js +2 -2
  70. package/out/replicache/src/kv/idb-store.js.map +1 -1
  71. package/out/replicache/src/kv/mem-fallback-store.js +1 -1
  72. package/out/replicache/src/kv/mem-fallback-store.js.map +1 -1
  73. package/out/replicache/src/kv/mem-store.js.map +1 -1
  74. package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
  75. package/out/replicache/src/kv/op-sqlite/types.js.map +1 -1
  76. package/out/replicache/src/kv/read-impl.js.map +1 -1
  77. package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
  78. package/out/replicache/src/kv/sqlite-store.js +8 -4
  79. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  80. package/out/replicache/src/kv/throw-if-closed.d.ts +0 -3
  81. package/out/replicache/src/kv/throw-if-closed.d.ts.map +1 -1
  82. package/out/replicache/src/kv/throw-if-closed.js.map +1 -1
  83. package/out/replicache/src/kv/write-impl-base.js +4 -1
  84. package/out/replicache/src/kv/write-impl-base.js.map +1 -1
  85. package/out/replicache/src/kv/write-impl.js.map +1 -1
  86. package/out/replicache/src/log-options.js +2 -1
  87. package/out/replicache/src/log-options.js.map +1 -1
  88. package/out/replicache/src/make-idb-name.js +1 -1
  89. package/out/replicache/src/new-client-channel.js +9 -7
  90. package/out/replicache/src/new-client-channel.js.map +1 -1
  91. package/out/replicache/src/on-persist-channel.js +1 -1
  92. package/out/replicache/src/on-persist-channel.js.map +1 -1
  93. package/out/replicache/src/op-sqlite.js +1 -2
  94. package/out/replicache/src/pending-mutations.js +2 -1
  95. package/out/replicache/src/pending-mutations.js.map +1 -1
  96. package/out/replicache/src/persist/client-gc.js +2 -2
  97. package/out/replicache/src/persist/client-gc.js.map +1 -1
  98. package/out/replicache/src/persist/client-group-gc.d.ts +0 -7
  99. package/out/replicache/src/persist/client-group-gc.d.ts.map +1 -1
  100. package/out/replicache/src/persist/client-group-gc.js +1 -1
  101. package/out/replicache/src/persist/client-group-gc.js.map +1 -1
  102. package/out/replicache/src/persist/client-groups.js +1 -1
  103. package/out/replicache/src/persist/client-groups.js.map +1 -1
  104. package/out/replicache/src/persist/clients.js +1 -1
  105. package/out/replicache/src/persist/clients.js.map +1 -1
  106. package/out/replicache/src/persist/collect-idb-databases.js +5 -4
  107. package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
  108. package/out/replicache/src/persist/gather-mem-only-visitor.js.map +1 -1
  109. package/out/replicache/src/persist/gather-not-cached-visitor.js.map +1 -1
  110. package/out/replicache/src/persist/heartbeat.js +2 -2
  111. package/out/replicache/src/persist/heartbeat.js.map +1 -1
  112. package/out/replicache/src/persist/idb-databases-store.js +1 -1
  113. package/out/replicache/src/persist/idb-databases-store.js.map +1 -1
  114. package/out/replicache/src/persist/make-client-id.js +1 -1
  115. package/out/replicache/src/persist/make-client-id.js.map +1 -1
  116. package/out/replicache/src/persist/persist.js +3 -2
  117. package/out/replicache/src/persist/persist.js.map +1 -1
  118. package/out/replicache/src/persist/refresh.js +8 -5
  119. package/out/replicache/src/persist/refresh.js.map +1 -1
  120. package/out/replicache/src/process-scheduler.js +1 -1
  121. package/out/replicache/src/process-scheduler.js.map +1 -1
  122. package/out/replicache/src/replicache-impl.d.ts +2 -7
  123. package/out/replicache/src/replicache-impl.d.ts.map +1 -1
  124. package/out/replicache/src/replicache-impl.js +21 -18
  125. package/out/replicache/src/replicache-impl.js.map +1 -1
  126. package/out/replicache/src/scan-iterator.js.map +1 -1
  127. package/out/replicache/src/scan-options.js +7 -6
  128. package/out/replicache/src/scan-options.js.map +1 -1
  129. package/out/replicache/src/sqlite.js +1 -2
  130. package/out/replicache/src/subscriptions.js +1 -1
  131. package/out/replicache/src/subscriptions.js.map +1 -1
  132. package/out/replicache/src/sync/diff.js +3 -1
  133. package/out/replicache/src/sync/diff.js.map +1 -1
  134. package/out/replicache/src/sync/ids.js.map +1 -1
  135. package/out/replicache/src/sync/patch.d.ts +0 -5
  136. package/out/replicache/src/sync/patch.d.ts.map +1 -1
  137. package/out/replicache/src/sync/patch.js +2 -4
  138. package/out/replicache/src/sync/patch.js.map +1 -1
  139. package/out/replicache/src/sync/pull.d.ts +0 -1
  140. package/out/replicache/src/sync/pull.d.ts.map +1 -1
  141. package/out/replicache/src/sync/pull.js +3 -1
  142. package/out/replicache/src/sync/pull.js.map +1 -1
  143. package/out/replicache/src/sync/push.d.ts +2 -1
  144. package/out/replicache/src/sync/push.d.ts.map +1 -1
  145. package/out/replicache/src/sync/push.js.map +1 -1
  146. package/out/replicache/src/sync/request-id.js +1 -1
  147. package/out/replicache/src/sync/request-id.js.map +1 -1
  148. package/out/replicache/src/transactions.js +1 -1
  149. package/out/replicache/src/transactions.js.map +1 -1
  150. package/out/replicache/src/types.d.ts +2 -1
  151. package/out/replicache/src/types.d.ts.map +1 -1
  152. package/out/shared/src/arrays.js +4 -1
  153. package/out/shared/src/arrays.js.map +1 -1
  154. package/out/shared/src/asserts.d.ts +0 -5
  155. package/out/shared/src/asserts.d.ts.map +1 -1
  156. package/out/shared/src/asserts.js.map +1 -1
  157. package/out/shared/src/bigint-json.d.ts +9 -0
  158. package/out/shared/src/bigint-json.d.ts.map +1 -1
  159. package/out/shared/src/bigint-json.js +27 -2
  160. package/out/shared/src/bigint-json.js.map +1 -1
  161. package/out/shared/src/broadcast-channel.js +1 -1
  162. package/out/shared/src/browser-env.d.ts +0 -17
  163. package/out/shared/src/browser-env.d.ts.map +1 -1
  164. package/out/shared/src/browser-env.js.map +1 -1
  165. package/out/shared/src/btree-set.js +11 -8
  166. package/out/shared/src/btree-set.js.map +1 -1
  167. package/out/shared/src/cache.js.map +1 -1
  168. package/out/shared/src/config.js +1 -1
  169. package/out/shared/src/custom-key-map.js.map +1 -1
  170. package/out/shared/src/custom-key-set.js.map +1 -1
  171. package/out/shared/src/deep-clone.d.ts +0 -1
  172. package/out/shared/src/deep-clone.d.ts.map +1 -1
  173. package/out/shared/src/deep-clone.js.map +1 -1
  174. package/out/shared/src/document-visible.js +1 -1
  175. package/out/shared/src/document-visible.js.map +1 -1
  176. package/out/shared/src/dotenv.js.map +1 -1
  177. package/out/shared/src/error.js +1 -1
  178. package/out/shared/src/error.js.map +1 -1
  179. package/out/shared/src/expand.d.ts +0 -3
  180. package/out/shared/src/expand.d.ts.map +1 -1
  181. package/out/shared/src/hash.js +2 -2
  182. package/out/shared/src/hash.js.map +1 -1
  183. package/out/shared/src/immutable.d.ts +1 -1
  184. package/out/shared/src/immutable.d.ts.map +1 -1
  185. package/out/shared/src/iterables.d.ts +0 -1
  186. package/out/shared/src/iterables.d.ts.map +1 -1
  187. package/out/shared/src/iterables.js.map +1 -1
  188. package/out/shared/src/json-schema.js.map +1 -1
  189. package/out/shared/src/json.js.map +1 -1
  190. package/out/shared/src/logging-test-utils.js.map +1 -1
  191. package/out/shared/src/logging.js +1 -1
  192. package/out/shared/src/logging.js.map +1 -1
  193. package/out/shared/src/navigator.js +1 -1
  194. package/out/shared/src/options-types.d.ts +1 -1
  195. package/out/shared/src/options-types.d.ts.map +1 -1
  196. package/out/shared/src/options.d.ts +2 -3
  197. package/out/shared/src/options.d.ts.map +1 -1
  198. package/out/shared/src/options.js +8 -11
  199. package/out/shared/src/options.js.map +1 -1
  200. package/out/shared/src/promise-race.js +1 -1
  201. package/out/shared/src/promise-race.js.map +1 -1
  202. package/out/shared/src/queue.js +1 -1
  203. package/out/shared/src/queue.js.map +1 -1
  204. package/out/shared/src/record-proxy.js.map +1 -1
  205. package/out/shared/src/resolved-promises.d.ts +0 -4
  206. package/out/shared/src/resolved-promises.d.ts.map +1 -1
  207. package/out/shared/src/resolved-promises.js +0 -1
  208. package/out/shared/src/resolved-promises.js.map +1 -1
  209. package/out/shared/src/resolver.d.ts +9 -0
  210. package/out/shared/src/resolver.d.ts.map +1 -0
  211. package/out/shared/src/resolver.js +6 -0
  212. package/out/shared/src/resolver.js.map +1 -0
  213. package/out/shared/src/ring-buffer.js.map +1 -1
  214. package/out/shared/src/size-of-value.js +7 -10
  215. package/out/shared/src/size-of-value.js.map +1 -1
  216. package/out/shared/src/sleep.js +1 -1
  217. package/out/shared/src/sleep.js.map +1 -1
  218. package/out/shared/src/sorted-entries.js.map +1 -1
  219. package/out/shared/src/tdigest-schema.js.map +1 -1
  220. package/out/shared/src/tdigest.d.ts +0 -1
  221. package/out/shared/src/tdigest.d.ts.map +1 -1
  222. package/out/shared/src/tdigest.js.map +1 -1
  223. package/out/shared/src/valita.js +1 -1
  224. package/out/shared/src/valita.js.map +1 -1
  225. package/out/z2s/src/compiler.js +5 -7
  226. package/out/z2s/src/compiler.js.map +1 -1
  227. package/out/z2s/src/sql.d.ts +1 -5
  228. package/out/z2s/src/sql.d.ts.map +1 -1
  229. package/out/z2s/src/sql.js +1 -1
  230. package/out/z2s/src/sql.js.map +1 -1
  231. package/out/zero/package.js +210 -189
  232. package/out/zero/package.js.map +1 -1
  233. package/out/zero/src/adapters/drizzle.js +1 -2
  234. package/out/zero/src/adapters/kysely.js +1 -2
  235. package/out/zero/src/adapters/pg.js +1 -2
  236. package/out/zero/src/adapters/postgresjs.js +1 -2
  237. package/out/zero/src/adapters/prisma.js +1 -2
  238. package/out/zero/src/analyze.js +1 -2
  239. package/out/zero/src/change-protocol/v0.js +1 -2
  240. package/out/zero/src/expo-sqlite.js +1 -2
  241. package/out/zero/src/op-sqlite.js +1 -2
  242. package/out/zero/src/react-native.js.map +1 -1
  243. package/out/zero/src/sqlite.js +1 -2
  244. package/out/zero/src/zero-cache-dev.js +11 -10
  245. package/out/zero/src/zero-cache-dev.js.map +1 -1
  246. package/out/zero/src/zero-out.js +2 -1
  247. package/out/zero/src/zero-out.js.map +1 -1
  248. package/out/zero-cache/src/auth/auth.js +1 -1
  249. package/out/zero-cache/src/auth/auth.js.map +1 -1
  250. package/out/zero-cache/src/auth/jwt.js +4 -1
  251. package/out/zero-cache/src/auth/jwt.js.map +1 -1
  252. package/out/zero-cache/src/auth/load-permissions.js +1 -1
  253. package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
  254. package/out/zero-cache/src/auth/read-authorizer.js +3 -2
  255. package/out/zero-cache/src/auth/read-authorizer.js.map +1 -1
  256. package/out/zero-cache/src/auth/write-authorizer.js +15 -19
  257. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  258. package/out/zero-cache/src/config/network.js +1 -1
  259. package/out/zero-cache/src/config/network.js.map +1 -1
  260. package/out/zero-cache/src/config/server-context.js.map +1 -1
  261. package/out/zero-cache/src/config/zero-config.d.ts +19 -11
  262. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  263. package/out/zero-cache/src/config/zero-config.js +47 -10
  264. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  265. package/out/zero-cache/src/custom/fetch.js +1 -1
  266. package/out/zero-cache/src/custom/fetch.js.map +1 -1
  267. package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
  268. package/out/zero-cache/src/db/create.js.map +1 -1
  269. package/out/zero-cache/src/db/index-predicate.js.map +1 -1
  270. package/out/zero-cache/src/db/lite-tables.d.ts +2 -2
  271. package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
  272. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  273. package/out/zero-cache/src/db/migration-lite.js +1 -1
  274. package/out/zero-cache/src/db/migration-lite.js.map +1 -1
  275. package/out/zero-cache/src/db/migration.js +5 -3
  276. package/out/zero-cache/src/db/migration.js.map +1 -1
  277. package/out/zero-cache/src/db/pg-copy-binary.js +4 -3
  278. package/out/zero-cache/src/db/pg-copy-binary.js.map +1 -1
  279. package/out/zero-cache/src/db/pg-copy.js +0 -1
  280. package/out/zero-cache/src/db/pg-copy.js.map +1 -1
  281. package/out/zero-cache/src/db/pg-to-lite.js +1 -1
  282. package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
  283. package/out/zero-cache/src/db/pg-type-parser.js.map +1 -1
  284. package/out/zero-cache/src/db/run-transaction.js.map +1 -1
  285. package/out/zero-cache/src/db/specs.d.ts +3 -4
  286. package/out/zero-cache/src/db/specs.d.ts.map +1 -1
  287. package/out/zero-cache/src/db/specs.js +1 -1
  288. package/out/zero-cache/src/db/specs.js.map +1 -1
  289. package/out/zero-cache/src/db/sqlite-corruption.js.map +1 -1
  290. package/out/zero-cache/src/db/statements.d.ts.map +1 -1
  291. package/out/zero-cache/src/db/statements.js +9 -2
  292. package/out/zero-cache/src/db/statements.js.map +1 -1
  293. package/out/zero-cache/src/db/transaction-pool.js +6 -4
  294. package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
  295. package/out/zero-cache/src/db/warmup.js.map +1 -1
  296. package/out/zero-cache/src/observability/events.js +1 -1
  297. package/out/zero-cache/src/observability/events.js.map +1 -1
  298. package/out/zero-cache/src/observability/log-throttle.js.map +1 -1
  299. package/out/zero-cache/src/observability/metrics.js.map +1 -1
  300. package/out/zero-cache/src/scripts/decommission.js +1 -1
  301. package/out/zero-cache/src/scripts/deploy-permissions.js +5 -3
  302. package/out/zero-cache/src/scripts/deploy-permissions.js.map +1 -1
  303. package/out/zero-cache/src/scripts/permissions.js.map +1 -1
  304. package/out/zero-cache/src/server/anonymous-otel-start.js +2 -1
  305. package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
  306. package/out/zero-cache/src/server/change-streamer.js +49 -16
  307. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  308. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  309. package/out/zero-cache/src/server/logging.js +1 -1
  310. package/out/zero-cache/src/server/logging.js.map +1 -1
  311. package/out/zero-cache/src/server/main.js +2 -2
  312. package/out/zero-cache/src/server/main.js.map +1 -1
  313. package/out/zero-cache/src/server/otel-log-sink.js.map +1 -1
  314. package/out/zero-cache/src/server/otel-start.js.map +1 -1
  315. package/out/zero-cache/src/server/reaper.js +2 -2
  316. package/out/zero-cache/src/server/reaper.js.map +1 -1
  317. package/out/zero-cache/src/server/replicator.js +1 -1
  318. package/out/zero-cache/src/server/replicator.js.map +1 -1
  319. package/out/zero-cache/src/server/runner/run-worker.d.ts.map +1 -1
  320. package/out/zero-cache/src/server/runner/run-worker.js +4 -3
  321. package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
  322. package/out/zero-cache/src/server/runner/runtime.js +2 -1
  323. package/out/zero-cache/src/server/runner/runtime.js.map +1 -1
  324. package/out/zero-cache/src/server/runner/zero-dispatcher.d.ts +9 -1
  325. package/out/zero-cache/src/server/runner/zero-dispatcher.d.ts.map +1 -1
  326. package/out/zero-cache/src/server/runner/zero-dispatcher.js +29 -2
  327. package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
  328. package/out/zero-cache/src/server/shadow-syncer.js +1 -1
  329. package/out/zero-cache/src/server/shadow-syncer.js.map +1 -1
  330. package/out/zero-cache/src/server/slot-keeper.js.map +1 -1
  331. package/out/zero-cache/src/server/syncer-assigner.js.map +1 -1
  332. package/out/zero-cache/src/server/syncer.js +10 -5
  333. package/out/zero-cache/src/server/syncer.js.map +1 -1
  334. package/out/zero-cache/src/server/worker-dispatcher.js +1 -1
  335. package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
  336. package/out/zero-cache/src/server/worker-urls.js.map +1 -1
  337. package/out/zero-cache/src/services/analyze.js +2 -2
  338. package/out/zero-cache/src/services/analyze.js.map +1 -1
  339. package/out/zero-cache/src/services/change-source/common/backfill-manager.js +3 -3
  340. package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
  341. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js +1 -1
  342. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
  343. package/out/zero-cache/src/services/change-source/common/init-cleanup.js.map +1 -1
  344. package/out/zero-cache/src/services/change-source/common/replica-restore.js.map +1 -1
  345. package/out/zero-cache/src/services/change-source/common/replica-schema.js +4 -4
  346. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  347. package/out/zero-cache/src/services/change-source/custom/change-source.js +1 -1
  348. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  349. package/out/zero-cache/src/services/change-source/pg/backfill-metadata.js.map +1 -1
  350. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +4 -4
  351. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  352. package/out/zero-cache/src/services/change-source/pg/change-source-init.js +52 -27
  353. package/out/zero-cache/src/services/change-source/pg/change-source-init.js.map +1 -1
  354. package/out/zero-cache/src/services/change-source/pg/change-source.js +70 -15
  355. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  356. package/out/zero-cache/src/services/change-source/pg/decommission.js +2 -1
  357. package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
  358. package/out/zero-cache/src/services/change-source/pg/fork-request.js +54 -0
  359. package/out/zero-cache/src/services/change-source/pg/fork-request.js.map +1 -0
  360. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +9 -7
  361. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  362. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.js.map +1 -1
  363. package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput-parser.js.map +1 -1
  364. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +5 -4
  365. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
  366. package/out/zero-cache/src/services/change-source/pg/lsn.d.ts +0 -2
  367. package/out/zero-cache/src/services/change-source/pg/lsn.d.ts.map +1 -1
  368. package/out/zero-cache/src/services/change-source/pg/lsn.js.map +1 -1
  369. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js +2 -2
  370. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js.map +1 -1
  371. package/out/zero-cache/src/services/change-source/pg/replication-slot-health.js +1 -1
  372. package/out/zero-cache/src/services/change-source/pg/replication-slot-health.js.map +1 -1
  373. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +9 -3
  374. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  375. package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts +0 -12
  376. package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts.map +1 -1
  377. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +6 -4
  378. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
  379. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts +1 -1
  380. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts.map +1 -1
  381. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js +13 -12
  382. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js.map +1 -1
  383. package/out/zero-cache/src/services/change-source/pg/schema/init.js +1 -1
  384. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  385. package/out/zero-cache/src/services/change-source/pg/schema/published.js +2 -2
  386. package/out/zero-cache/src/services/change-source/pg/schema/published.js.map +1 -1
  387. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.js.map +1 -1
  388. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +10 -7
  389. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  390. package/out/zero-cache/src/services/change-source/pg/schema/validation.d.ts +0 -1
  391. package/out/zero-cache/src/services/change-source/pg/schema/validation.d.ts.map +1 -1
  392. package/out/zero-cache/src/services/change-source/pg/schema/validation.js +1 -1
  393. package/out/zero-cache/src/services/change-source/pg/schema/validation.js.map +1 -1
  394. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js +1 -1
  395. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js.map +1 -1
  396. package/out/zero-cache/src/services/change-source/protocol/current/control.js.map +1 -1
  397. package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
  398. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +1 -0
  399. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts.map +1 -1
  400. package/out/zero-cache/src/services/change-source/protocol/current/downstream.js.map +1 -1
  401. package/out/zero-cache/src/services/change-source/protocol/current/status.d.ts +3 -0
  402. package/out/zero-cache/src/services/change-source/protocol/current/status.d.ts.map +1 -1
  403. package/out/zero-cache/src/services/change-source/protocol/current/status.js +2 -1
  404. package/out/zero-cache/src/services/change-source/protocol/current/status.js.map +1 -1
  405. package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts +1 -0
  406. package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts.map +1 -1
  407. package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
  408. package/out/zero-cache/src/services/change-source/protocol/current.js +1 -1
  409. package/out/zero-cache/src/services/change-source/protocol/mod.js +1 -2
  410. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js.map +1 -1
  411. package/out/zero-cache/src/services/change-streamer/backfill-state.js +1 -1
  412. package/out/zero-cache/src/services/change-streamer/backfill-state.js.map +1 -1
  413. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js +6 -6
  414. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
  415. package/out/zero-cache/src/services/change-streamer/backup-monitor.js +56 -21
  416. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
  417. package/out/zero-cache/src/services/change-streamer/broadcast.js +2 -2
  418. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  419. package/out/zero-cache/src/services/change-streamer/change-log-codec.js +2 -3
  420. package/out/zero-cache/src/services/change-streamer/change-log-codec.js.map +1 -1
  421. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js +3 -3
  422. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js.map +1 -1
  423. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.js.map +1 -1
  424. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +8 -4
  425. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  426. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +85 -59
  427. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  428. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  429. package/out/zero-cache/src/services/change-streamer/drain-tracker.js +1 -1
  430. package/out/zero-cache/src/services/change-streamer/drain-tracker.js.map +1 -1
  431. package/out/zero-cache/src/services/change-streamer/forwarder.js +11 -10
  432. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  433. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js +81 -12
  434. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js.map +1 -1
  435. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js +4 -4
  436. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js.map +1 -1
  437. package/out/zero-cache/src/services/change-streamer/replica-poller.js +2 -2
  438. package/out/zero-cache/src/services/change-streamer/replica-poller.js.map +1 -1
  439. package/out/zero-cache/src/services/change-streamer/restore-admission.js +12 -1
  440. package/out/zero-cache/src/services/change-streamer/restore-admission.js.map +1 -1
  441. package/out/zero-cache/src/services/change-streamer/schema/init.js +1 -1
  442. package/out/zero-cache/src/services/change-streamer/schema/init.js.map +1 -1
  443. package/out/zero-cache/src/services/change-streamer/schema/tables.js +1 -1
  444. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  445. package/out/zero-cache/src/services/change-streamer/server-start-metrics.js +42 -0
  446. package/out/zero-cache/src/services/change-streamer/server-start-metrics.js.map +1 -0
  447. package/out/zero-cache/src/services/change-streamer/snapshot-message.js +1 -2
  448. package/out/zero-cache/src/services/change-streamer/snapshot-message.js.map +1 -1
  449. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -1
  450. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js +2 -2
  451. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js.map +1 -1
  452. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js +2 -2
  453. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js.map +1 -1
  454. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.js.map +1 -1
  455. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.js.map +1 -1
  456. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js +4 -6
  457. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js.map +1 -1
  458. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +3 -5
  459. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js.map +1 -1
  460. package/out/zero-cache/src/services/change-streamer/storer.js +4 -4
  461. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  462. package/out/zero-cache/src/services/change-streamer/subscribe.js.map +1 -1
  463. package/out/zero-cache/src/services/change-streamer/subscriber.js +8 -7
  464. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  465. package/out/zero-cache/src/services/change-streamer/upstream-acker.js +0 -1
  466. package/out/zero-cache/src/services/change-streamer/upstream-acker.js.map +1 -1
  467. package/out/zero-cache/src/services/heapz.js +2 -1
  468. package/out/zero-cache/src/services/heapz.js.map +1 -1
  469. package/out/zero-cache/src/services/http-service.d.ts +2 -2
  470. package/out/zero-cache/src/services/http-service.d.ts.map +1 -1
  471. package/out/zero-cache/src/services/http-service.js +6 -4
  472. package/out/zero-cache/src/services/http-service.js.map +1 -1
  473. package/out/zero-cache/src/services/life-cycle.js +3 -3
  474. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  475. package/out/zero-cache/src/services/limiter/sliding-window-limiter.js +4 -2
  476. package/out/zero-cache/src/services/limiter/sliding-window-limiter.js.map +1 -1
  477. package/out/zero-cache/src/services/litestream/commands.js +4 -4
  478. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  479. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js +2 -2
  480. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js.map +1 -1
  481. package/out/zero-cache/src/services/litestream/litestream-controller.js.map +1 -1
  482. package/out/zero-cache/src/services/litestream/metrics.js.map +1 -1
  483. package/out/zero-cache/src/services/litestream/restore-progress.js.map +1 -1
  484. package/out/zero-cache/src/services/mutagen/mutagen.js +3 -4
  485. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  486. package/out/zero-cache/src/services/mutagen/pusher.js +61 -17
  487. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  488. package/out/zero-cache/src/services/plannerz.d.ts +4 -12
  489. package/out/zero-cache/src/services/plannerz.d.ts.map +1 -1
  490. package/out/zero-cache/src/services/plannerz.js +6 -7
  491. package/out/zero-cache/src/services/plannerz.js.map +1 -1
  492. package/out/zero-cache/src/services/profz.js +5 -3
  493. package/out/zero-cache/src/services/profz.js.map +1 -1
  494. package/out/zero-cache/src/services/replicator/change-log-cookies.js.map +1 -1
  495. package/out/zero-cache/src/services/replicator/change-log-db.js +4 -3
  496. package/out/zero-cache/src/services/replicator/change-log-db.js.map +1 -1
  497. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js.map +1 -1
  498. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  499. package/out/zero-cache/src/services/replicator/drain-handler.js +1 -1
  500. package/out/zero-cache/src/services/replicator/drain-handler.js.map +1 -1
  501. package/out/zero-cache/src/services/replicator/incremental-sync.js +4 -4
  502. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  503. package/out/zero-cache/src/services/replicator/notifier.js.map +1 -1
  504. package/out/zero-cache/src/services/replicator/replication-status.js +4 -3
  505. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  506. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  507. package/out/zero-cache/src/services/replicator/reporter/recorder.js +2 -2
  508. package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
  509. package/out/zero-cache/src/services/replicator/reporter/report-schema.d.ts +2 -11
  510. package/out/zero-cache/src/services/replicator/reporter/report-schema.d.ts.map +1 -1
  511. package/out/zero-cache/src/services/replicator/reporter/report-schema.js.map +1 -1
  512. package/out/zero-cache/src/services/replicator/schema/backfilling.js +2 -2
  513. package/out/zero-cache/src/services/replicator/schema/backfilling.js.map +1 -1
  514. package/out/zero-cache/src/services/replicator/schema/change-log.d.ts +19 -17
  515. package/out/zero-cache/src/services/replicator/schema/change-log.d.ts.map +1 -1
  516. package/out/zero-cache/src/services/replicator/schema/change-log.js +31 -15
  517. package/out/zero-cache/src/services/replicator/schema/change-log.js.map +1 -1
  518. package/out/zero-cache/src/services/replicator/schema/column-metadata.d.ts +0 -7
  519. package/out/zero-cache/src/services/replicator/schema/column-metadata.d.ts.map +1 -1
  520. package/out/zero-cache/src/services/replicator/schema/column-metadata.js +1 -1
  521. package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
  522. package/out/zero-cache/src/services/replicator/schema/replication-state.js +10 -7
  523. package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
  524. package/out/zero-cache/src/services/replicator/schema/table-metadata.js.map +1 -1
  525. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +1 -1
  526. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -1
  527. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js +1 -1
  528. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js.map +1 -1
  529. package/out/zero-cache/src/services/replicator/write-worker-client.js +2 -2
  530. package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
  531. package/out/zero-cache/src/services/replicator/write-worker.js +3 -3
  532. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  533. package/out/zero-cache/src/services/run-ast.js +1 -1
  534. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  535. package/out/zero-cache/src/services/runner.js.map +1 -1
  536. package/out/zero-cache/src/services/running-state.js +1 -1
  537. package/out/zero-cache/src/services/running-state.js.map +1 -1
  538. package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js.map +1 -1
  539. package/out/zero-cache/src/services/statz.d.ts.map +1 -1
  540. package/out/zero-cache/src/services/statz.js +5 -4
  541. package/out/zero-cache/src/services/statz.js.map +1 -1
  542. package/out/zero-cache/src/services/view-syncer/active-users-gauge.js +2 -2
  543. package/out/zero-cache/src/services/view-syncer/active-users-gauge.js.map +1 -1
  544. package/out/zero-cache/src/services/view-syncer/advancement-metrics.js.map +1 -1
  545. package/out/zero-cache/src/services/view-syncer/batch-pacer.js.map +1 -1
  546. package/out/zero-cache/src/services/view-syncer/client-handler.js +71 -24
  547. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  548. package/out/zero-cache/src/services/view-syncer/client-schema.js +46 -6
  549. package/out/zero-cache/src/services/view-syncer/client-schema.js.map +1 -1
  550. package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
  551. package/out/zero-cache/src/services/view-syncer/cvr-purger.js +2 -2
  552. package/out/zero-cache/src/services/view-syncer/cvr-purger.js.map +1 -1
  553. package/out/zero-cache/src/services/view-syncer/cvr-store.js +12 -7
  554. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  555. package/out/zero-cache/src/services/view-syncer/cvr.js +12 -4
  556. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  557. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js +1 -1
  558. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js.map +1 -1
  559. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js +2 -2
  560. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
  561. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.js.map +1 -1
  562. package/out/zero-cache/src/services/view-syncer/hydration-budget.js.map +1 -1
  563. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js +2 -2
  564. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js.map +1 -1
  565. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +6 -4
  566. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  567. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +13 -10
  568. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  569. package/out/zero-cache/src/services/view-syncer/plan-warnings.js.map +1 -1
  570. package/out/zero-cache/src/services/view-syncer/push-cost.js.map +1 -1
  571. package/out/zero-cache/src/services/view-syncer/push-cutoff.js.map +1 -1
  572. package/out/zero-cache/src/services/view-syncer/query-covering.js.map +1 -1
  573. package/out/zero-cache/src/services/view-syncer/query-shape.js.map +1 -1
  574. package/out/zero-cache/src/services/view-syncer/query-stats.js +1 -1
  575. package/out/zero-cache/src/services/view-syncer/query-stats.js.map +1 -1
  576. package/out/zero-cache/src/services/view-syncer/row-record-cache.js +2 -2
  577. package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
  578. package/out/zero-cache/src/services/view-syncer/schema/cvr.js.map +1 -1
  579. package/out/zero-cache/src/services/view-syncer/schema/types.js +2 -16
  580. package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
  581. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js +7 -1
  582. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js.map +1 -1
  583. package/out/zero-cache/src/services/view-syncer/snapshotter.js +12 -8
  584. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  585. package/out/zero-cache/src/services/view-syncer/ttl-clock.js.map +1 -1
  586. package/out/zero-cache/src/services/view-syncer/view-syncer.js +52 -28
  587. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  588. package/out/zero-cache/src/types/error-with-level.js.map +1 -1
  589. package/out/zero-cache/src/types/lexi-version.js.map +1 -1
  590. package/out/zero-cache/src/types/lite.d.ts +0 -1
  591. package/out/zero-cache/src/types/lite.d.ts.map +1 -1
  592. package/out/zero-cache/src/types/lite.js +1 -1
  593. package/out/zero-cache/src/types/lite.js.map +1 -1
  594. package/out/zero-cache/src/types/pg-data-type.js +1 -1
  595. package/out/zero-cache/src/types/pg-data-type.js.map +1 -1
  596. package/out/zero-cache/src/types/pg-types.d.ts +0 -40
  597. package/out/zero-cache/src/types/pg-types.d.ts.map +1 -1
  598. package/out/zero-cache/src/types/pg-types.js.map +1 -1
  599. package/out/zero-cache/src/types/pg.js +6 -4
  600. package/out/zero-cache/src/types/pg.js.map +1 -1
  601. package/out/zero-cache/src/types/poke-chunk.d.ts +0 -1
  602. package/out/zero-cache/src/types/poke-chunk.d.ts.map +1 -1
  603. package/out/zero-cache/src/types/poke-chunk.js +1 -1
  604. package/out/zero-cache/src/types/poke-chunk.js.map +1 -1
  605. package/out/zero-cache/src/types/processes.js +1 -1
  606. package/out/zero-cache/src/types/processes.js.map +1 -1
  607. package/out/zero-cache/src/types/profiler.js +1 -1
  608. package/out/zero-cache/src/types/profiler.js.map +1 -1
  609. package/out/zero-cache/src/types/row-key.d.ts +0 -5
  610. package/out/zero-cache/src/types/row-key.d.ts.map +1 -1
  611. package/out/zero-cache/src/types/row-key.js.map +1 -1
  612. package/out/zero-cache/src/types/sql.js.map +1 -1
  613. package/out/zero-cache/src/types/streams.d.ts.map +1 -1
  614. package/out/zero-cache/src/types/streams.js +11 -10
  615. package/out/zero-cache/src/types/streams.js.map +1 -1
  616. package/out/zero-cache/src/types/strings.js.map +1 -1
  617. package/out/zero-cache/src/types/subscription.js +1 -1
  618. package/out/zero-cache/src/types/subscription.js.map +1 -1
  619. package/out/zero-cache/src/types/timeout.js +1 -1
  620. package/out/zero-cache/src/types/websocket-handoff.d.ts +4 -7
  621. package/out/zero-cache/src/types/websocket-handoff.d.ts.map +1 -1
  622. package/out/zero-cache/src/types/websocket-handoff.js +16 -2
  623. package/out/zero-cache/src/types/websocket-handoff.js.map +1 -1
  624. package/out/zero-cache/src/types/ws.d.ts +2 -1
  625. package/out/zero-cache/src/types/ws.d.ts.map +1 -1
  626. package/out/zero-cache/src/types/ws.js.map +1 -1
  627. package/out/zero-cache/src/workers/connection.d.ts +1 -2
  628. package/out/zero-cache/src/workers/connection.d.ts.map +1 -1
  629. package/out/zero-cache/src/workers/connection.js +5 -3
  630. package/out/zero-cache/src/workers/connection.js.map +1 -1
  631. package/out/zero-cache/src/workers/mutator.js +1 -1
  632. package/out/zero-cache/src/workers/mutator.js.map +1 -1
  633. package/out/zero-cache/src/workers/rehome.d.ts +15 -0
  634. package/out/zero-cache/src/workers/rehome.d.ts.map +1 -0
  635. package/out/zero-cache/src/workers/rehome.js +36 -0
  636. package/out/zero-cache/src/workers/rehome.js.map +1 -0
  637. package/out/zero-cache/src/workers/replicator.js +5 -4
  638. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  639. package/out/zero-cache/src/workers/syncer-ws-message-handler.js +9 -1
  640. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  641. package/out/zero-cache/src/workers/syncer.js +30 -5
  642. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  643. package/out/zero-client/src/client/active-clients-manager.d.ts.map +1 -1
  644. package/out/zero-client/src/client/active-clients-manager.js +2 -2
  645. package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
  646. package/out/zero-client/src/client/bindings.js +11 -11
  647. package/out/zero-client/src/client/connection-manager.js +1 -1
  648. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  649. package/out/zero-client/src/client/connection.js +1 -1
  650. package/out/zero-client/src/client/connection.js.map +1 -1
  651. package/out/zero-client/src/client/context.js.map +1 -1
  652. package/out/zero-client/src/client/crud-impl.js +1 -1
  653. package/out/zero-client/src/client/crud-impl.js.map +1 -1
  654. package/out/zero-client/src/client/crud.d.ts +2 -8
  655. package/out/zero-client/src/client/crud.d.ts.map +1 -1
  656. package/out/zero-client/src/client/crud.js.map +1 -1
  657. package/out/zero-client/src/client/custom.d.ts +0 -2
  658. package/out/zero-client/src/client/custom.d.ts.map +1 -1
  659. package/out/zero-client/src/client/custom.js.map +1 -1
  660. package/out/zero-client/src/client/delete-clients-manager.js.map +1 -1
  661. package/out/zero-client/src/client/enable-analytics.d.ts +0 -1
  662. package/out/zero-client/src/client/enable-analytics.d.ts.map +1 -1
  663. package/out/zero-client/src/client/enable-analytics.js +1 -0
  664. package/out/zero-client/src/client/enable-analytics.js.map +1 -1
  665. package/out/zero-client/src/client/error.d.ts +4 -5
  666. package/out/zero-client/src/client/error.d.ts.map +1 -1
  667. package/out/zero-client/src/client/error.js.map +1 -1
  668. package/out/zero-client/src/client/inspector/client-group.js.map +1 -1
  669. package/out/zero-client/src/client/inspector/client.js.map +1 -1
  670. package/out/zero-client/src/client/inspector/html-dialog-prompt.js +1 -1
  671. package/out/zero-client/src/client/inspector/html-dialog-prompt.js.map +1 -1
  672. package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
  673. package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
  674. package/out/zero-client/src/client/inspector/query.js.map +1 -1
  675. package/out/zero-client/src/client/ivm-branch.js +1 -3
  676. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  677. package/out/zero-client/src/client/keys.d.ts +0 -1
  678. package/out/zero-client/src/client/keys.d.ts.map +1 -1
  679. package/out/zero-client/src/client/keys.js +6 -3
  680. package/out/zero-client/src/client/keys.js.map +1 -1
  681. package/out/zero-client/src/client/log-options.js.map +1 -1
  682. package/out/zero-client/src/client/make-replicache-mutators.js +5 -3
  683. package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
  684. package/out/zero-client/src/client/metrics.js +3 -5
  685. package/out/zero-client/src/client/metrics.js.map +1 -1
  686. package/out/zero-client/src/client/mutation-tracker.d.ts.map +1 -1
  687. package/out/zero-client/src/client/mutation-tracker.js +1 -1
  688. package/out/zero-client/src/client/mutation-tracker.js.map +1 -1
  689. package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
  690. package/out/zero-client/src/client/options.d.ts +0 -4
  691. package/out/zero-client/src/client/options.d.ts.map +1 -1
  692. package/out/zero-client/src/client/options.js.map +1 -1
  693. package/out/zero-client/src/client/query-manager.js +2 -2
  694. package/out/zero-client/src/client/query-manager.js.map +1 -1
  695. package/out/zero-client/src/client/reload-error-handler.js +4 -3
  696. package/out/zero-client/src/client/reload-error-handler.js.map +1 -1
  697. package/out/zero-client/src/client/replicache-types.d.ts +3 -2
  698. package/out/zero-client/src/client/replicache-types.d.ts.map +1 -1
  699. package/out/zero-client/src/client/version.js +1 -1
  700. package/out/zero-client/src/client/zero-poke-handler.d.ts +1 -2
  701. package/out/zero-client/src/client/zero-poke-handler.d.ts.map +1 -1
  702. package/out/zero-client/src/client/zero-poke-handler.js +1 -1
  703. package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
  704. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  705. package/out/zero-client/src/client/zero.d.ts +1 -5
  706. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  707. package/out/zero-client/src/client/zero.js +20 -12
  708. package/out/zero-client/src/client/zero.js.map +1 -1
  709. package/out/zero-client/src/mod.js +23 -23
  710. package/out/zero-client/src/types/client-state.d.ts +0 -6
  711. package/out/zero-client/src/types/client-state.d.ts.map +1 -1
  712. package/out/zero-client/src/util/nanoid.js.map +1 -1
  713. package/out/zero-permissions/src/permissions.d.ts +1 -12
  714. package/out/zero-permissions/src/permissions.d.ts.map +1 -1
  715. package/out/zero-permissions/src/permissions.js +3 -3
  716. package/out/zero-permissions/src/permissions.js.map +1 -1
  717. package/out/zero-pg/src/mod.js +9 -9
  718. package/out/zero-protocol/src/analyze-query-result.js +1 -1
  719. package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
  720. package/out/zero-protocol/src/application-error.js.map +1 -1
  721. package/out/zero-protocol/src/ast.d.ts +0 -4
  722. package/out/zero-protocol/src/ast.d.ts.map +1 -1
  723. package/out/zero-protocol/src/ast.js +21 -11
  724. package/out/zero-protocol/src/ast.js.map +1 -1
  725. package/out/zero-protocol/src/change-desired-queries.d.ts +0 -20
  726. package/out/zero-protocol/src/change-desired-queries.d.ts.map +1 -1
  727. package/out/zero-protocol/src/change-desired-queries.js.map +1 -1
  728. package/out/zero-protocol/src/client-schema.d.ts +2 -5
  729. package/out/zero-protocol/src/client-schema.d.ts.map +1 -1
  730. package/out/zero-protocol/src/client-schema.js.map +1 -1
  731. package/out/zero-protocol/src/close-connection.d.ts +0 -8
  732. package/out/zero-protocol/src/close-connection.d.ts.map +1 -1
  733. package/out/zero-protocol/src/close-connection.js.map +1 -1
  734. package/out/zero-protocol/src/connect.d.ts +0 -12
  735. package/out/zero-protocol/src/connect.d.ts.map +1 -1
  736. package/out/zero-protocol/src/connect.js.map +1 -1
  737. package/out/zero-protocol/src/custom-queries.d.ts +3 -17
  738. package/out/zero-protocol/src/custom-queries.d.ts.map +1 -1
  739. package/out/zero-protocol/src/custom-queries.js.map +1 -1
  740. package/out/zero-protocol/src/data.d.ts +0 -1
  741. package/out/zero-protocol/src/data.d.ts.map +1 -1
  742. package/out/zero-protocol/src/data.js +3 -1
  743. package/out/zero-protocol/src/data.js.map +1 -1
  744. package/out/zero-protocol/src/delete-clients.js.map +1 -1
  745. package/out/zero-protocol/src/down.d.ts +2 -2
  746. package/out/zero-protocol/src/down.js.map +1 -1
  747. package/out/zero-protocol/src/error.js.map +1 -1
  748. package/out/zero-protocol/src/inspect-down.d.ts +82 -84
  749. package/out/zero-protocol/src/inspect-down.d.ts.map +1 -1
  750. package/out/zero-protocol/src/inspect-down.js.map +1 -1
  751. package/out/zero-protocol/src/inspect-up.d.ts +0 -28
  752. package/out/zero-protocol/src/inspect-up.d.ts.map +1 -1
  753. package/out/zero-protocol/src/inspect-up.js.map +1 -1
  754. package/out/zero-protocol/src/mutate-server.d.ts +0 -21
  755. package/out/zero-protocol/src/mutate-server.d.ts.map +1 -1
  756. package/out/zero-protocol/src/mutate-server.js.map +1 -1
  757. package/out/zero-protocol/src/mutation-id.js.map +1 -1
  758. package/out/zero-protocol/src/mutation-type-enum.d.ts +0 -1
  759. package/out/zero-protocol/src/mutation-type-enum.d.ts.map +1 -1
  760. package/out/zero-protocol/src/mutation-type-enum.js.map +1 -1
  761. package/out/zero-protocol/src/mutation.d.ts +2 -4
  762. package/out/zero-protocol/src/mutation.d.ts.map +1 -1
  763. package/out/zero-protocol/src/mutation.js.map +1 -1
  764. package/out/zero-protocol/src/mutations-patch.d.ts +0 -33
  765. package/out/zero-protocol/src/mutations-patch.d.ts.map +1 -1
  766. package/out/zero-protocol/src/mutations-patch.js.map +1 -1
  767. package/out/zero-protocol/src/ping.d.ts +0 -2
  768. package/out/zero-protocol/src/ping.d.ts.map +1 -1
  769. package/out/zero-protocol/src/ping.js.map +1 -1
  770. package/out/zero-protocol/src/poke.d.ts +3 -2
  771. package/out/zero-protocol/src/poke.d.ts.map +1 -1
  772. package/out/zero-protocol/src/poke.js +1 -1
  773. package/out/zero-protocol/src/poke.js.map +1 -1
  774. package/out/zero-protocol/src/pong.d.ts +0 -2
  775. package/out/zero-protocol/src/pong.d.ts.map +1 -1
  776. package/out/zero-protocol/src/pong.js.map +1 -1
  777. package/out/zero-protocol/src/primary-key.js.map +1 -1
  778. package/out/zero-protocol/src/protocol-version.js +1 -0
  779. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  780. package/out/zero-protocol/src/pull.d.ts +2 -7
  781. package/out/zero-protocol/src/pull.d.ts.map +1 -1
  782. package/out/zero-protocol/src/pull.js.map +1 -1
  783. package/out/zero-protocol/src/push.d.ts +1 -3
  784. package/out/zero-protocol/src/push.d.ts.map +1 -1
  785. package/out/zero-protocol/src/push.js.map +1 -1
  786. package/out/zero-protocol/src/queries-patch.d.ts +0 -16
  787. package/out/zero-protocol/src/queries-patch.d.ts.map +1 -1
  788. package/out/zero-protocol/src/queries-patch.js +1 -1
  789. package/out/zero-protocol/src/queries-patch.js.map +1 -1
  790. package/out/zero-protocol/src/query-hash-visitor.js.map +1 -1
  791. package/out/zero-protocol/src/query-hash.js +2 -2
  792. package/out/zero-protocol/src/query-server.d.ts +2 -41
  793. package/out/zero-protocol/src/query-server.d.ts.map +1 -1
  794. package/out/zero-protocol/src/query-server.js.map +1 -1
  795. package/out/zero-protocol/src/row-patch.js.map +1 -1
  796. package/out/zero-protocol/src/up.d.ts +60 -60
  797. package/out/zero-protocol/src/up.js +1 -1
  798. package/out/zero-protocol/src/up.js.map +1 -1
  799. package/out/zero-protocol/src/update-auth.js.map +1 -1
  800. package/out/zero-protocol/src/version.d.ts +0 -1
  801. package/out/zero-protocol/src/version.d.ts.map +1 -1
  802. package/out/zero-protocol/src/version.js.map +1 -1
  803. package/out/zero-react/src/bindings.js +11 -11
  804. package/out/zero-react/src/mod.js +5 -5
  805. package/out/zero-react/src/use-query.js +2 -2
  806. package/out/zero-react/src/use-query.js.map +1 -1
  807. package/out/zero-react/src/zero-provider.d.ts +1 -1
  808. package/out/zero-react/src/zero-provider.d.ts.map +1 -1
  809. package/out/zero-react/src/zero-provider.js +2 -1
  810. package/out/zero-react/src/zero-provider.js.map +1 -1
  811. package/out/zero-react/src/zero.js +23 -23
  812. package/out/zero-schema/src/builder/relationship-builder.d.ts +0 -2
  813. package/out/zero-schema/src/builder/relationship-builder.d.ts.map +1 -1
  814. package/out/zero-schema/src/builder/relationship-builder.js.map +1 -1
  815. package/out/zero-schema/src/builder/schema-builder.js +3 -2
  816. package/out/zero-schema/src/builder/schema-builder.js.map +1 -1
  817. package/out/zero-schema/src/builder/table-builder.d.ts +1 -1
  818. package/out/zero-schema/src/builder/table-builder.d.ts.map +1 -1
  819. package/out/zero-schema/src/builder/table-builder.js +1 -1
  820. package/out/zero-schema/src/builder/table-builder.js.map +1 -1
  821. package/out/zero-schema/src/compiled-permissions.js.map +1 -1
  822. package/out/zero-schema/src/name-mapper.js +7 -5
  823. package/out/zero-schema/src/name-mapper.js.map +1 -1
  824. package/out/zero-schema/src/schema-config.d.ts +0 -1
  825. package/out/zero-schema/src/schema-config.d.ts.map +1 -1
  826. package/out/zero-schema/src/schema-config.js +1 -1
  827. package/out/zero-schema/src/schema-config.js.map +1 -1
  828. package/out/zero-schema/src/table-schema.d.ts +3 -11
  829. package/out/zero-schema/src/table-schema.d.ts.map +1 -1
  830. package/out/zero-server/src/adapters/drizzle.js.map +1 -1
  831. package/out/zero-server/src/adapters/kysely.js.map +1 -1
  832. package/out/zero-server/src/adapters/pg.js.map +1 -1
  833. package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
  834. package/out/zero-server/src/adapters/prisma.js.map +1 -1
  835. package/out/zero-server/src/closable-db-transaction.js.map +1 -1
  836. package/out/zero-server/src/custom.js +1 -1
  837. package/out/zero-server/src/custom.js.map +1 -1
  838. package/out/zero-server/src/mod.js +8 -8
  839. package/out/zero-server/src/process-mutations.js.map +1 -1
  840. package/out/zero-server/src/push-processor.js.map +1 -1
  841. package/out/zero-server/src/queries/process-queries.d.ts +2 -4
  842. package/out/zero-server/src/queries/process-queries.d.ts.map +1 -1
  843. package/out/zero-server/src/queries/process-queries.js +2 -1
  844. package/out/zero-server/src/queries/process-queries.js.map +1 -1
  845. package/out/zero-server/src/schema.js +7 -7
  846. package/out/zero-server/src/schema.js.map +1 -1
  847. package/out/zero-server/src/zql-database.js.map +1 -1
  848. package/out/zero-solid/src/bindings.js +11 -11
  849. package/out/zero-solid/src/mod.js +5 -5
  850. package/out/zero-solid/src/solid-view.d.ts +0 -2
  851. package/out/zero-solid/src/solid-view.d.ts.map +1 -1
  852. package/out/zero-solid/src/solid-view.js +1 -1
  853. package/out/zero-solid/src/solid-view.js.map +1 -1
  854. package/out/zero-solid/src/use-connection-state.js +2 -1
  855. package/out/zero-solid/src/use-connection-state.js.map +1 -1
  856. package/out/zero-solid/src/use-query.js.map +1 -1
  857. package/out/zero-solid/src/use-zero-online.js +2 -1
  858. package/out/zero-solid/src/use-zero-online.js.map +1 -1
  859. package/out/zero-solid/src/use-zero.js +3 -2
  860. package/out/zero-solid/src/use-zero.js.map +1 -1
  861. package/out/zero-solid/src/zero.js +23 -23
  862. package/out/zero-types/src/name-mapper.d.ts +2 -1
  863. package/out/zero-types/src/name-mapper.d.ts.map +1 -1
  864. package/out/zero-types/src/name-mapper.js.map +1 -1
  865. package/out/zero-types/src/schema-value.d.ts +3 -2
  866. package/out/zero-types/src/schema-value.d.ts.map +1 -1
  867. package/out/zero-types/src/schema.d.ts +1 -1
  868. package/out/zero-types/src/schema.d.ts.map +1 -1
  869. package/out/zql/src/builder/builder.d.ts +1 -11
  870. package/out/zql/src/builder/builder.d.ts.map +1 -1
  871. package/out/zql/src/builder/builder.js +15 -13
  872. package/out/zql/src/builder/builder.js.map +1 -1
  873. package/out/zql/src/builder/correlated-predicate-pushdown.js.map +1 -1
  874. package/out/zql/src/builder/debug-delegate.js.map +1 -1
  875. package/out/zql/src/builder/filter.d.ts +0 -1
  876. package/out/zql/src/builder/filter.d.ts.map +1 -1
  877. package/out/zql/src/builder/filter.js.map +1 -1
  878. package/out/zql/src/builder/like.js +0 -1
  879. package/out/zql/src/builder/like.js.map +1 -1
  880. package/out/zql/src/error.d.ts +0 -5
  881. package/out/zql/src/error.d.ts.map +1 -1
  882. package/out/zql/src/error.js.map +1 -1
  883. package/out/zql/src/ivm/array-view.js.map +1 -1
  884. package/out/zql/src/ivm/cap.d.ts +1 -1
  885. package/out/zql/src/ivm/cap.d.ts.map +1 -1
  886. package/out/zql/src/ivm/cap.js +12 -4
  887. package/out/zql/src/ivm/cap.js.map +1 -1
  888. package/out/zql/src/ivm/constraint.d.ts +0 -7
  889. package/out/zql/src/ivm/constraint.d.ts.map +1 -1
  890. package/out/zql/src/ivm/constraint.js +1 -1
  891. package/out/zql/src/ivm/constraint.js.map +1 -1
  892. package/out/zql/src/ivm/data.d.ts +15 -2
  893. package/out/zql/src/ivm/data.d.ts.map +1 -1
  894. package/out/zql/src/ivm/data.js +15 -1
  895. package/out/zql/src/ivm/data.js.map +1 -1
  896. package/out/zql/src/ivm/deferred-input.js.map +1 -1
  897. package/out/zql/src/ivm/exists.d.ts.map +1 -1
  898. package/out/zql/src/ivm/exists.js +32 -22
  899. package/out/zql/src/ivm/exists.js.map +1 -1
  900. package/out/zql/src/ivm/fan-in.js.map +1 -1
  901. package/out/zql/src/ivm/fan-out.js.map +1 -1
  902. package/out/zql/src/ivm/filter-operators.d.ts +5 -0
  903. package/out/zql/src/ivm/filter-operators.d.ts.map +1 -1
  904. package/out/zql/src/ivm/filter-operators.js +1 -1
  905. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  906. package/out/zql/src/ivm/filter-push.js.map +1 -1
  907. package/out/zql/src/ivm/filter.js.map +1 -1
  908. package/out/zql/src/ivm/flipped-join.js +1 -3
  909. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  910. package/out/zql/src/ivm/join-index-tap.js.map +1 -1
  911. package/out/zql/src/ivm/join-index.js.map +1 -1
  912. package/out/zql/src/ivm/join-utils.js +11 -13
  913. package/out/zql/src/ivm/join-utils.js.map +1 -1
  914. package/out/zql/src/ivm/join.d.ts.map +1 -1
  915. package/out/zql/src/ivm/join.js +6 -3
  916. package/out/zql/src/ivm/join.js.map +1 -1
  917. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
  918. package/out/zql/src/ivm/memory-source.d.ts +1 -2
  919. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  920. package/out/zql/src/ivm/memory-source.js +27 -23
  921. package/out/zql/src/ivm/memory-source.js.map +1 -1
  922. package/out/zql/src/ivm/memory-storage.js.map +1 -1
  923. package/out/zql/src/ivm/operator.d.ts +6 -0
  924. package/out/zql/src/ivm/operator.d.ts.map +1 -1
  925. package/out/zql/src/ivm/operator.js +2 -2
  926. package/out/zql/src/ivm/operator.js.map +1 -1
  927. package/out/zql/src/ivm/push-accumulated.js +0 -1
  928. package/out/zql/src/ivm/push-accumulated.js.map +1 -1
  929. package/out/zql/src/ivm/skip-yields.js.map +1 -1
  930. package/out/zql/src/ivm/skip.js.map +1 -1
  931. package/out/zql/src/ivm/stream.d.ts +0 -1
  932. package/out/zql/src/ivm/stream.d.ts.map +1 -1
  933. package/out/zql/src/ivm/stream.js.map +1 -1
  934. package/out/zql/src/ivm/take-gate.js.map +1 -1
  935. package/out/zql/src/ivm/take.d.ts.map +1 -1
  936. package/out/zql/src/ivm/take.js +7 -8
  937. package/out/zql/src/ivm/take.js.map +1 -1
  938. package/out/zql/src/ivm/union-fan-in.d.ts +0 -1
  939. package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
  940. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  941. package/out/zql/src/ivm/union-fan-out.js.map +1 -1
  942. package/out/zql/src/ivm/view-apply-change.d.ts +5 -35
  943. package/out/zql/src/ivm/view-apply-change.d.ts.map +1 -1
  944. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  945. package/out/zql/src/mutate/crud.d.ts +1 -3
  946. package/out/zql/src/mutate/crud.d.ts.map +1 -1
  947. package/out/zql/src/mutate/crud.js +2 -1
  948. package/out/zql/src/mutate/crud.js.map +1 -1
  949. package/out/zql/src/mutate/mutator-registry.d.ts +1 -1
  950. package/out/zql/src/mutate/mutator-registry.d.ts.map +1 -1
  951. package/out/zql/src/mutate/mutator-registry.js.map +1 -1
  952. package/out/zql/src/mutate/mutator.d.ts +1 -1
  953. package/out/zql/src/mutate/mutator.d.ts.map +1 -1
  954. package/out/zql/src/mutate/mutator.js.map +1 -1
  955. package/out/zql/src/planner/planner-builder.js +1 -1
  956. package/out/zql/src/planner/planner-builder.js.map +1 -1
  957. package/out/zql/src/planner/planner-connection.js.map +1 -1
  958. package/out/zql/src/planner/planner-debug.d.ts +10 -9
  959. package/out/zql/src/planner/planner-debug.d.ts.map +1 -1
  960. package/out/zql/src/planner/planner-debug.js.map +1 -1
  961. package/out/zql/src/planner/planner-fan-in.js.map +1 -1
  962. package/out/zql/src/planner/planner-fan-out.js.map +1 -1
  963. package/out/zql/src/planner/planner-filter.js.map +1 -1
  964. package/out/zql/src/planner/planner-graph.js +1 -5
  965. package/out/zql/src/planner/planner-graph.js.map +1 -1
  966. package/out/zql/src/planner/planner-join.js +1 -1
  967. package/out/zql/src/planner/planner-join.js.map +1 -1
  968. package/out/zql/src/planner/planner-node.d.ts +0 -1
  969. package/out/zql/src/planner/planner-node.d.ts.map +1 -1
  970. package/out/zql/src/planner/planner-node.js.map +1 -1
  971. package/out/zql/src/planner/planner-source.js.map +1 -1
  972. package/out/zql/src/planner/planner-terminus.js.map +1 -1
  973. package/out/zql/src/planner/planner-warnings.d.ts +3 -3
  974. package/out/zql/src/planner/planner-warnings.d.ts.map +1 -1
  975. package/out/zql/src/planner/planner-warnings.js.map +1 -1
  976. package/out/zql/src/query/create-builder.js +2 -1
  977. package/out/zql/src/query/create-builder.js.map +1 -1
  978. package/out/zql/src/query/expression.d.ts +0 -22
  979. package/out/zql/src/query/expression.d.ts.map +1 -1
  980. package/out/zql/src/query/expression.js +1 -1
  981. package/out/zql/src/query/expression.js.map +1 -1
  982. package/out/zql/src/query/measure-push-operator.js.map +1 -1
  983. package/out/zql/src/query/named.js +2 -1
  984. package/out/zql/src/query/named.js.map +1 -1
  985. package/out/zql/src/query/query-delegate-base.d.ts +0 -7
  986. package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
  987. package/out/zql/src/query/query-delegate-base.js +1 -1
  988. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  989. package/out/zql/src/query/query-delegate.d.ts +1 -4
  990. package/out/zql/src/query/query-delegate.d.ts.map +1 -1
  991. package/out/zql/src/query/query-impl.js +3 -2
  992. package/out/zql/src/query/query-impl.js.map +1 -1
  993. package/out/zql/src/query/query-internals.d.ts +0 -3
  994. package/out/zql/src/query/query-internals.d.ts.map +1 -1
  995. package/out/zql/src/query/query-internals.js.map +1 -1
  996. package/out/zql/src/query/query-registry.d.ts +5 -5
  997. package/out/zql/src/query/query-registry.d.ts.map +1 -1
  998. package/out/zql/src/query/query-registry.js +1 -1
  999. package/out/zql/src/query/query-registry.js.map +1 -1
  1000. package/out/zql/src/query/query-transitions.js +1 -1
  1001. package/out/zql/src/query/query-transitions.js.map +1 -1
  1002. package/out/zql/src/query/query.d.ts +2 -13
  1003. package/out/zql/src/query/query.d.ts.map +1 -1
  1004. package/out/zql/src/query/runnable-query-impl.d.ts +1 -19
  1005. package/out/zql/src/query/runnable-query-impl.d.ts.map +1 -1
  1006. package/out/zql/src/query/runnable-query-impl.js +2 -1
  1007. package/out/zql/src/query/runnable-query-impl.js.map +1 -1
  1008. package/out/zql/src/query/static-query.js.map +1 -1
  1009. package/out/zql/src/query/ttl.d.ts +2 -3
  1010. package/out/zql/src/query/ttl.d.ts.map +1 -1
  1011. package/out/zql/src/query/ttl.js +8 -8
  1012. package/out/zql/src/query/ttl.js.map +1 -1
  1013. package/out/zql/src/query/validate-input.js.map +1 -1
  1014. package/out/zqlite/src/database-storage.js +195 -26
  1015. package/out/zqlite/src/database-storage.js.map +1 -1
  1016. package/out/zqlite/src/db.d.ts +47 -1
  1017. package/out/zqlite/src/db.d.ts.map +1 -1
  1018. package/out/zqlite/src/db.js +121 -21
  1019. package/out/zqlite/src/db.js.map +1 -1
  1020. package/out/zqlite/src/explain-queries.js.map +1 -1
  1021. package/out/zqlite/src/fetch-sql.d.ts +27 -0
  1022. package/out/zqlite/src/fetch-sql.d.ts.map +1 -0
  1023. package/out/zqlite/src/fetch-sql.js +112 -0
  1024. package/out/zqlite/src/fetch-sql.js.map +1 -0
  1025. package/out/zqlite/src/index-usage.js.map +1 -1
  1026. package/out/zqlite/src/internal/sql-inline.js.map +1 -1
  1027. package/out/zqlite/src/internal/sql.js.map +1 -1
  1028. package/out/zqlite/src/internal/statement-cache.d.ts +7 -0
  1029. package/out/zqlite/src/internal/statement-cache.d.ts.map +1 -1
  1030. package/out/zqlite/src/internal/statement-cache.js +11 -4
  1031. package/out/zqlite/src/internal/statement-cache.js.map +1 -1
  1032. package/out/zqlite/src/mod.js +5 -5
  1033. package/out/zqlite/src/pending-delta.js.map +1 -1
  1034. package/out/zqlite/src/query-builder.d.ts +11 -3
  1035. package/out/zqlite/src/query-builder.d.ts.map +1 -1
  1036. package/out/zqlite/src/query-builder.js +22 -7
  1037. package/out/zqlite/src/query-builder.js.map +1 -1
  1038. package/out/zqlite/src/query-delegate.js.map +1 -1
  1039. package/out/zqlite/src/resolve-scalar-subqueries.js +2 -5
  1040. package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
  1041. package/out/zqlite/src/sqlite-cost-model.js +3 -2
  1042. package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
  1043. package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
  1044. package/out/zqlite/src/table-source.d.ts +20 -3
  1045. package/out/zqlite/src/table-source.d.ts.map +1 -1
  1046. package/out/zqlite/src/table-source.js +74 -40
  1047. package/out/zqlite/src/table-source.js.map +1 -1
  1048. package/package.json +39 -39
  1049. package/out/analyze-query/src/bin-analyze.d.ts +0 -2
  1050. package/out/analyze-query/src/bin-analyze.d.ts.map +0 -1
  1051. package/out/analyze-query/src/bin-analyze.js +0 -283
  1052. package/out/analyze-query/src/bin-analyze.js.map +0 -1
  1053. package/out/otel/src/maybe-time.d.ts +0 -3
  1054. package/out/otel/src/maybe-time.d.ts.map +0 -1
  1055. package/out/otel/src/maybe-time.js +0 -15
  1056. package/out/otel/src/maybe-time.js.map +0 -1
  1057. package/out/shared/src/cache.d.ts +0 -22
  1058. package/out/shared/src/cache.d.ts.map +0 -1
  1059. package/out/shared/src/custom-key-map.d.ts +0 -26
  1060. package/out/shared/src/custom-key-map.d.ts.map +0 -1
  1061. package/out/shared/src/custom-key-set.d.ts +0 -25
  1062. package/out/shared/src/custom-key-set.d.ts.map +0 -1
  1063. package/out/shared/src/logging-test-utils.d.ts +0 -12
  1064. package/out/shared/src/logging-test-utils.d.ts.map +0 -1
  1065. package/out/shared/src/sorted-entries.d.ts +0 -2
  1066. package/out/shared/src/sorted-entries.d.ts.map +0 -1
  1067. package/out/zero/src/analyze-query.d.ts +0 -3
  1068. package/out/zero/src/analyze-query.d.ts.map +0 -1
  1069. package/out/zero/src/analyze-query.js +0 -3
  1070. package/out/zero/src/server/change-streamer.d.ts +0 -2
  1071. package/out/zero/src/server/change-streamer.d.ts.map +0 -1
  1072. package/out/zero/src/server/main.d.ts +0 -2
  1073. package/out/zero/src/server/main.d.ts.map +0 -1
  1074. package/out/zero/src/server/reaper.d.ts +0 -2
  1075. package/out/zero/src/server/reaper.d.ts.map +0 -1
  1076. package/out/zero/src/server/replicator.d.ts +0 -2
  1077. package/out/zero/src/server/replicator.d.ts.map +0 -1
  1078. package/out/zero/src/server/syncer.d.ts +0 -2
  1079. package/out/zero/src/server/syncer.d.ts.map +0 -1
  1080. package/out/zero-cache/src/auth/jwt.d.ts +0 -12
  1081. package/out/zero-cache/src/auth/jwt.d.ts.map +0 -1
  1082. package/out/zero-cache/src/auth/load-permissions.d.ts +0 -17
  1083. package/out/zero-cache/src/auth/load-permissions.d.ts.map +0 -1
  1084. package/out/zero-cache/src/auth/write-authorizer.d.ts +0 -28
  1085. package/out/zero-cache/src/auth/write-authorizer.d.ts.map +0 -1
  1086. package/out/zero-cache/src/config/server-context.d.ts +0 -16
  1087. package/out/zero-cache/src/config/server-context.d.ts.map +0 -1
  1088. package/out/zero-cache/src/custom/fetch.d.ts +0 -38
  1089. package/out/zero-cache/src/custom/fetch.d.ts.map +0 -1
  1090. package/out/zero-cache/src/custom/metrics.d.ts +0 -31
  1091. package/out/zero-cache/src/custom/metrics.d.ts.map +0 -1
  1092. package/out/zero-cache/src/custom-queries/transform-query.d.ts +0 -68
  1093. package/out/zero-cache/src/custom-queries/transform-query.d.ts.map +0 -1
  1094. package/out/zero-cache/src/db/create.d.ts +0 -12
  1095. package/out/zero-cache/src/db/create.d.ts.map +0 -1
  1096. package/out/zero-cache/src/db/delete-lite-db.d.ts +0 -2
  1097. package/out/zero-cache/src/db/delete-lite-db.d.ts.map +0 -1
  1098. package/out/zero-cache/src/db/index-predicate.d.ts +0 -4
  1099. package/out/zero-cache/src/db/index-predicate.d.ts.map +0 -1
  1100. package/out/zero-cache/src/db/migration-lite.d.ts +0 -78
  1101. package/out/zero-cache/src/db/migration-lite.d.ts.map +0 -1
  1102. package/out/zero-cache/src/db/migration.d.ts +0 -80
  1103. package/out/zero-cache/src/db/migration.d.ts.map +0 -1
  1104. package/out/zero-cache/src/db/pg-copy-binary.d.ts +0 -101
  1105. package/out/zero-cache/src/db/pg-copy-binary.d.ts.map +0 -1
  1106. package/out/zero-cache/src/db/pg-copy.d.ts +0 -30
  1107. package/out/zero-cache/src/db/pg-copy.d.ts.map +0 -1
  1108. package/out/zero-cache/src/db/pg-type-parser.d.ts +0 -15
  1109. package/out/zero-cache/src/db/pg-type-parser.d.ts.map +0 -1
  1110. package/out/zero-cache/src/db/transaction-pool.d.ts +0 -205
  1111. package/out/zero-cache/src/db/transaction-pool.d.ts.map +0 -1
  1112. package/out/zero-cache/src/db/warmup.d.ts +0 -4
  1113. package/out/zero-cache/src/db/warmup.d.ts.map +0 -1
  1114. package/out/zero-cache/src/observability/events.d.ts +0 -15
  1115. package/out/zero-cache/src/observability/events.d.ts.map +0 -1
  1116. package/out/zero-cache/src/observability/log-throttle.d.ts +0 -44
  1117. package/out/zero-cache/src/observability/log-throttle.d.ts.map +0 -1
  1118. package/out/zero-cache/src/server/anonymous-otel-start.d.ts +0 -21
  1119. package/out/zero-cache/src/server/anonymous-otel-start.d.ts.map +0 -1
  1120. package/out/zero-cache/src/server/change-streamer.d.ts +0 -3
  1121. package/out/zero-cache/src/server/change-streamer.d.ts.map +0 -1
  1122. package/out/zero-cache/src/server/inspector-delegate.d.ts +0 -71
  1123. package/out/zero-cache/src/server/inspector-delegate.d.ts.map +0 -1
  1124. package/out/zero-cache/src/server/main.d.ts +0 -3
  1125. package/out/zero-cache/src/server/main.d.ts.map +0 -1
  1126. package/out/zero-cache/src/server/otel-diag-logger.d.ts +0 -16
  1127. package/out/zero-cache/src/server/otel-diag-logger.d.ts.map +0 -1
  1128. package/out/zero-cache/src/server/otel-start.d.ts +0 -3
  1129. package/out/zero-cache/src/server/otel-start.d.ts.map +0 -1
  1130. package/out/zero-cache/src/server/priority-op.d.ts +0 -8
  1131. package/out/zero-cache/src/server/priority-op.d.ts.map +0 -1
  1132. package/out/zero-cache/src/server/reaper.d.ts +0 -3
  1133. package/out/zero-cache/src/server/reaper.d.ts.map +0 -1
  1134. package/out/zero-cache/src/server/replicator.d.ts +0 -10
  1135. package/out/zero-cache/src/server/replicator.d.ts.map +0 -1
  1136. package/out/zero-cache/src/server/slot-keeper.d.ts +0 -26
  1137. package/out/zero-cache/src/server/slot-keeper.d.ts.map +0 -1
  1138. package/out/zero-cache/src/server/syncer.d.ts +0 -3
  1139. package/out/zero-cache/src/server/syncer.d.ts.map +0 -1
  1140. package/out/zero-cache/src/services/analyze.d.ts +0 -9
  1141. package/out/zero-cache/src/services/analyze.d.ts.map +0 -1
  1142. package/out/zero-cache/src/services/change-source/change-source.d.ts +0 -32
  1143. package/out/zero-cache/src/services/change-source/change-source.d.ts.map +0 -1
  1144. package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts +0 -57
  1145. package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts.map +0 -1
  1146. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.d.ts +0 -55
  1147. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.d.ts.map +0 -1
  1148. package/out/zero-cache/src/services/change-source/common/init-cleanup.d.ts +0 -24
  1149. package/out/zero-cache/src/services/change-source/common/init-cleanup.d.ts.map +0 -1
  1150. package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts +0 -115
  1151. package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts.map +0 -1
  1152. package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts +0 -14
  1153. package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts.map +0 -1
  1154. package/out/zero-cache/src/services/change-source/custom/change-source.d.ts +0 -25
  1155. package/out/zero-cache/src/services/change-source/custom/change-source.d.ts.map +0 -1
  1156. package/out/zero-cache/src/services/change-source/pg/backfill-metadata.d.ts +0 -14
  1157. package/out/zero-cache/src/services/change-source/pg/backfill-metadata.d.ts.map +0 -1
  1158. package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts +0 -36
  1159. package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts.map +0 -1
  1160. package/out/zero-cache/src/services/change-source/pg/change-source-init.d.ts +0 -65
  1161. package/out/zero-cache/src/services/change-source/pg/change-source-init.d.ts.map +0 -1
  1162. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts +0 -115
  1163. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +0 -1
  1164. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +0 -155
  1165. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +0 -1
  1166. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.d.ts +0 -17
  1167. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.d.ts.map +0 -1
  1168. package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput-parser.d.ts +0 -28
  1169. package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput-parser.d.ts.map +0 -1
  1170. package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput.types.d.ts +0 -76
  1171. package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput.types.d.ts.map +0 -1
  1172. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts +0 -169
  1173. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts.map +0 -1
  1174. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.d.ts +0 -16
  1175. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.d.ts.map +0 -1
  1176. package/out/zero-cache/src/services/change-source/pg/replication-slot-health.d.ts +0 -15
  1177. package/out/zero-cache/src/services/change-source/pg/replication-slot-health.d.ts.map +0 -1
  1178. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +0 -127
  1179. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +0 -1
  1180. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts +0 -9
  1181. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +0 -1
  1182. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.d.ts +0 -68
  1183. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.d.ts.map +0 -1
  1184. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.d.ts +0 -72
  1185. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.d.ts.map +0 -1
  1186. package/out/zero-cache/src/services/change-streamer/backfill-state.d.ts +0 -50
  1187. package/out/zero-cache/src/services/change-streamer/backfill-state.d.ts.map +0 -1
  1188. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts +0 -14
  1189. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts.map +0 -1
  1190. package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts +0 -18
  1191. package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts.map +0 -1
  1192. package/out/zero-cache/src/services/change-streamer/broadcast.d.ts +0 -98
  1193. package/out/zero-cache/src/services/change-streamer/broadcast.d.ts.map +0 -1
  1194. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts +0 -34
  1195. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts.map +0 -1
  1196. package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.d.ts +0 -34
  1197. package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.d.ts.map +0 -1
  1198. package/out/zero-cache/src/services/change-streamer/change-log-initializer.d.ts +0 -158
  1199. package/out/zero-cache/src/services/change-streamer/change-log-initializer.d.ts.map +0 -1
  1200. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.d.ts +0 -19
  1201. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.d.ts.map +0 -1
  1202. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +0 -78
  1203. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +0 -1
  1204. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +0 -139
  1205. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +0 -1
  1206. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +0 -586
  1207. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +0 -1
  1208. package/out/zero-cache/src/services/change-streamer/drain-tracker.d.ts +0 -24
  1209. package/out/zero-cache/src/services/change-streamer/drain-tracker.d.ts.map +0 -1
  1210. package/out/zero-cache/src/services/change-streamer/error-type-enum.d.ts +0 -15
  1211. package/out/zero-cache/src/services/change-streamer/error-type-enum.d.ts.map +0 -1
  1212. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts +0 -49
  1213. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts.map +0 -1
  1214. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.d.ts +0 -53
  1215. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.d.ts.map +0 -1
  1216. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.d.ts +0 -88
  1217. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.d.ts.map +0 -1
  1218. package/out/zero-cache/src/services/change-streamer/replica-poller.d.ts +0 -15
  1219. package/out/zero-cache/src/services/change-streamer/replica-poller.d.ts.map +0 -1
  1220. package/out/zero-cache/src/services/change-streamer/restore-admission.d.ts +0 -243
  1221. package/out/zero-cache/src/services/change-streamer/restore-admission.d.ts.map +0 -1
  1222. package/out/zero-cache/src/services/change-streamer/schema/init.d.ts +0 -6
  1223. package/out/zero-cache/src/services/change-streamer/schema/init.d.ts.map +0 -1
  1224. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts +0 -152
  1225. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts.map +0 -1
  1226. package/out/zero-cache/src/services/change-streamer/shard-sampling.d.ts +0 -11
  1227. package/out/zero-cache/src/services/change-streamer/shard-sampling.d.ts.map +0 -1
  1228. package/out/zero-cache/src/services/change-streamer/snapshot-message.d.ts +0 -92
  1229. package/out/zero-cache/src/services/change-streamer/snapshot-message.d.ts.map +0 -1
  1230. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts +0 -36
  1231. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts.map +0 -1
  1232. package/out/zero-cache/src/services/change-streamer/snapshot.d.ts +0 -22
  1233. package/out/zero-cache/src/services/change-streamer/snapshot.d.ts.map +0 -1
  1234. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.d.ts +0 -99
  1235. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.d.ts.map +0 -1
  1236. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.d.ts +0 -83
  1237. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.d.ts.map +0 -1
  1238. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.d.ts +0 -121
  1239. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.d.ts.map +0 -1
  1240. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.d.ts +0 -73
  1241. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.d.ts.map +0 -1
  1242. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts +0 -62
  1243. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts.map +0 -1
  1244. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts +0 -114
  1245. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts.map +0 -1
  1246. package/out/zero-cache/src/services/change-streamer/storer.d.ts +0 -276
  1247. package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +0 -1
  1248. package/out/zero-cache/src/services/change-streamer/subscribe.d.ts +0 -485
  1249. package/out/zero-cache/src/services/change-streamer/subscribe.d.ts.map +0 -1
  1250. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +0 -194
  1251. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +0 -1
  1252. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts +0 -52
  1253. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts.map +0 -1
  1254. package/out/zero-cache/src/services/limiter/sliding-window-limiter.d.ts +0 -57
  1255. package/out/zero-cache/src/services/limiter/sliding-window-limiter.d.ts.map +0 -1
  1256. package/out/zero-cache/src/services/litestream/commands.d.ts +0 -77
  1257. package/out/zero-cache/src/services/litestream/commands.d.ts.map +0 -1
  1258. package/out/zero-cache/src/services/litestream/litestream-controller.d.ts +0 -78
  1259. package/out/zero-cache/src/services/litestream/litestream-controller.d.ts.map +0 -1
  1260. package/out/zero-cache/src/services/litestream/metrics.d.ts +0 -31
  1261. package/out/zero-cache/src/services/litestream/metrics.d.ts.map +0 -1
  1262. package/out/zero-cache/src/services/litestream/restore-progress.d.ts +0 -24
  1263. package/out/zero-cache/src/services/litestream/restore-progress.d.ts.map +0 -1
  1264. package/out/zero-cache/src/services/mutagen/mutagen.d.ts +0 -40
  1265. package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +0 -1
  1266. package/out/zero-cache/src/services/mutagen/pusher.d.ts +0 -63
  1267. package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +0 -1
  1268. package/out/zero-cache/src/services/replicator/change-log-db.d.ts +0 -368
  1269. package/out/zero-cache/src/services/replicator/change-log-db.d.ts.map +0 -1
  1270. package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts +0 -56
  1271. package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts.map +0 -1
  1272. package/out/zero-cache/src/services/replicator/change-processor.d.ts +0 -37
  1273. package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +0 -1
  1274. package/out/zero-cache/src/services/replicator/drain-handler.d.ts +0 -82
  1275. package/out/zero-cache/src/services/replicator/drain-handler.d.ts.map +0 -1
  1276. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts +0 -25
  1277. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +0 -1
  1278. package/out/zero-cache/src/services/replicator/notifier.d.ts +0 -9
  1279. package/out/zero-cache/src/services/replicator/notifier.d.ts.map +0 -1
  1280. package/out/zero-cache/src/services/replicator/replication-status.d.ts +0 -71
  1281. package/out/zero-cache/src/services/replicator/replication-status.d.ts.map +0 -1
  1282. package/out/zero-cache/src/services/replicator/replicator.d.ts +0 -86
  1283. package/out/zero-cache/src/services/replicator/replicator.d.ts.map +0 -1
  1284. package/out/zero-cache/src/services/replicator/reporter/recorder.d.ts +0 -55
  1285. package/out/zero-cache/src/services/replicator/reporter/recorder.d.ts.map +0 -1
  1286. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts +0 -132
  1287. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts.map +0 -1
  1288. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.d.ts +0 -120
  1289. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.d.ts.map +0 -1
  1290. package/out/zero-cache/src/services/replicator/write-worker-client.d.ts +0 -111
  1291. package/out/zero-cache/src/services/replicator/write-worker-client.d.ts.map +0 -1
  1292. package/out/zero-cache/src/services/run-ast.d.ts +0 -39
  1293. package/out/zero-cache/src/services/run-ast.d.ts.map +0 -1
  1294. package/out/zero-cache/src/services/runner.d.ts +0 -19
  1295. package/out/zero-cache/src/services/runner.d.ts.map +0 -1
  1296. package/out/zero-cache/src/services/view-syncer/active-users-gauge.d.ts +0 -17
  1297. package/out/zero-cache/src/services/view-syncer/active-users-gauge.d.ts.map +0 -1
  1298. package/out/zero-cache/src/services/view-syncer/advancement-metrics.d.ts +0 -26
  1299. package/out/zero-cache/src/services/view-syncer/advancement-metrics.d.ts.map +0 -1
  1300. package/out/zero-cache/src/services/view-syncer/batch-pacer.d.ts +0 -79
  1301. package/out/zero-cache/src/services/view-syncer/batch-pacer.d.ts.map +0 -1
  1302. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts +0 -79
  1303. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +0 -1
  1304. package/out/zero-cache/src/services/view-syncer/client-schema.d.ts +0 -5
  1305. package/out/zero-cache/src/services/view-syncer/client-schema.d.ts.map +0 -1
  1306. package/out/zero-cache/src/services/view-syncer/connection-context-manager.d.ts +0 -184
  1307. package/out/zero-cache/src/services/view-syncer/connection-context-manager.d.ts.map +0 -1
  1308. package/out/zero-cache/src/services/view-syncer/cvr-purger.d.ts +0 -22
  1309. package/out/zero-cache/src/services/view-syncer/cvr-purger.d.ts.map +0 -1
  1310. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts +0 -128
  1311. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +0 -1
  1312. package/out/zero-cache/src/services/view-syncer/cvr.d.ts +0 -294
  1313. package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +0 -1
  1314. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.d.ts +0 -81
  1315. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.d.ts.map +0 -1
  1316. package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts +0 -45
  1317. package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts.map +0 -1
  1318. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.d.ts +0 -53
  1319. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.d.ts.map +0 -1
  1320. package/out/zero-cache/src/services/view-syncer/hydration-budget.d.ts +0 -33
  1321. package/out/zero-cache/src/services/view-syncer/hydration-budget.d.ts.map +0 -1
  1322. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts +0 -51
  1323. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts.map +0 -1
  1324. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts +0 -34
  1325. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts.map +0 -1
  1326. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +0 -296
  1327. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +0 -1
  1328. package/out/zero-cache/src/services/view-syncer/plan-warnings.d.ts +0 -7
  1329. package/out/zero-cache/src/services/view-syncer/plan-warnings.d.ts.map +0 -1
  1330. package/out/zero-cache/src/services/view-syncer/push-cost.d.ts +0 -132
  1331. package/out/zero-cache/src/services/view-syncer/push-cost.d.ts.map +0 -1
  1332. package/out/zero-cache/src/services/view-syncer/push-cutoff.d.ts +0 -55
  1333. package/out/zero-cache/src/services/view-syncer/push-cutoff.d.ts.map +0 -1
  1334. package/out/zero-cache/src/services/view-syncer/query-covering.d.ts +0 -28
  1335. package/out/zero-cache/src/services/view-syncer/query-covering.d.ts.map +0 -1
  1336. package/out/zero-cache/src/services/view-syncer/query-shape.d.ts +0 -25
  1337. package/out/zero-cache/src/services/view-syncer/query-shape.d.ts.map +0 -1
  1338. package/out/zero-cache/src/services/view-syncer/query-stats.d.ts +0 -89
  1339. package/out/zero-cache/src/services/view-syncer/query-stats.d.ts.map +0 -1
  1340. package/out/zero-cache/src/services/view-syncer/row-record-cache.d.ts +0 -90
  1341. package/out/zero-cache/src/services/view-syncer/row-record-cache.d.ts.map +0 -1
  1342. package/out/zero-cache/src/services/view-syncer/row-set-signature.d.ts +0 -17
  1343. package/out/zero-cache/src/services/view-syncer/row-set-signature.d.ts.map +0 -1
  1344. package/out/zero-cache/src/services/view-syncer/schema/cvr.d.ts +0 -86
  1345. package/out/zero-cache/src/services/view-syncer/schema/cvr.d.ts.map +0 -1
  1346. package/out/zero-cache/src/services/view-syncer/schema/init.d.ts +0 -5
  1347. package/out/zero-cache/src/services/view-syncer/schema/init.d.ts.map +0 -1
  1348. package/out/zero-cache/src/services/view-syncer/schema/types.d.ts +0 -563
  1349. package/out/zero-cache/src/services/view-syncer/schema/types.d.ts.map +0 -1
  1350. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.d.ts +0 -95
  1351. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.d.ts.map +0 -1
  1352. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +0 -412
  1353. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +0 -1
  1354. package/out/zero-cache/src/services/view-syncer/tracer.d.ts +0 -2
  1355. package/out/zero-cache/src/services/view-syncer/tracer.d.ts.map +0 -1
  1356. package/out/zero-cache/src/services/view-syncer/ttl-clock.d.ts +0 -10
  1357. package/out/zero-cache/src/services/view-syncer/ttl-clock.d.ts.map +0 -1
  1358. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +0 -133
  1359. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +0 -1
  1360. package/out/zero-cache/src/types/pg-patch.d.ts +0 -21
  1361. package/out/zero-cache/src/types/pg-patch.d.ts.map +0 -1
  1362. package/out/zero-cache/src/types/pg-versions.d.ts +0 -3
  1363. package/out/zero-cache/src/types/pg-versions.d.ts.map +0 -1
  1364. package/out/zero-cache/src/workers/replicator.d.ts +0 -57
  1365. package/out/zero-cache/src/workers/replicator.d.ts.map +0 -1
  1366. package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts +0 -14
  1367. package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +0 -1
  1368. package/out/zero-cache/src/workers/syncer.d.ts +0 -67
  1369. package/out/zero-cache/src/workers/syncer.d.ts.map +0 -1
  1370. package/out/zero-events/src/index.d.ts +0 -27
  1371. package/out/zero-events/src/index.d.ts.map +0 -1
  1372. package/out/zero-events/src/json.d.ts +0 -20
  1373. package/out/zero-events/src/json.d.ts.map +0 -1
  1374. package/out/zero-events/src/status.d.ts +0 -144
  1375. package/out/zero-events/src/status.d.ts.map +0 -1
  1376. package/out/zqlite/src/database-storage.d.ts +0 -24
  1377. package/out/zqlite/src/database-storage.d.ts.map +0 -1
  1378. package/out/zqlite/src/explain-queries.d.ts +0 -4
  1379. package/out/zqlite/src/explain-queries.d.ts.map +0 -1
  1380. package/out/zqlite/src/internal/sql-inline.d.ts +0 -13
  1381. package/out/zqlite/src/internal/sql-inline.d.ts.map +0 -1
  1382. package/out/zqlite/src/resolve-scalar-subqueries.d.ts +0 -72
  1383. package/out/zqlite/src/resolve-scalar-subqueries.d.ts.map +0 -1
  1384. package/out/zqlite/src/sqlite-cost-model.d.ts +0 -17
  1385. package/out/zqlite/src/sqlite-cost-model.d.ts.map +0 -1
  1386. package/out/zqlite/src/sqlite-stat-fanout.d.ts +0 -121
  1387. package/out/zqlite/src/sqlite-stat-fanout.d.ts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"plan-warnings.js","names":[],"sources":["../../../../../../zero-cache/src/services/view-syncer/plan-warnings.ts"],"sourcesContent":["import type {PlanWarning} from '../../../../zql/src/planner/planner-warnings.ts';\n\n/**\n * Explains a {@link PlanWarning} for a zero-cache operator, in terms of the\n * SQLite replica, whose indexes come from the upstream database.\n */\nexport function planWarningMessage(warning: PlanWarning): string {\n switch (warning.type) {\n case 'missing-index': {\n const {columns, rows, perRow} = warning;\n return (\n `${perRow ? 'Each lookup' : 'Each read'} of ${location(warning)} ` +\n `by ${list(columns)} scans all ~${count(rows)} rows because no ` +\n `index covers ${list(columns)}` +\n `${perRow ? ', and it runs once per parent row' : ''}. ` +\n suggestion(warning)\n );\n }\n case 'full-sort': {\n const {orderBy, rows, perRow} = warning;\n const ordering = orderBy\n .map(([column, direction]) => `${column} ${direction}`)\n .join(', ');\n return (\n `Each read of ${location(warning)} sorts ~${count(rows)} rows for ` +\n `ORDER BY ${ordering}, reading every matching row before returning ` +\n `the first, so a limit does not reduce the rows read` +\n `${perRow ? ', and it runs once per parent row' : ''}. ` +\n suggestion(warning)\n );\n }\n case 'high-cost':\n return (\n `The best plan found for the query is estimated to process ` +\n `~${count(warning.cost)} rows.`\n );\n }\n}\n\nfunction location({table, path}: {table: string; path: readonly string[]}) {\n return path.length === 0\n ? table\n : `${table} (related ${path.map(alias => `'${alias}'`).join(' > ')})`;\n}\n\nfunction suggestion({\n table,\n suggestedIndex,\n}: {\n table: string;\n suggestedIndex: readonly string[];\n}) {\n return `Consider adding an index on ${table} (${suggestedIndex.join(', ')}) upstream.`;\n}\n\nfunction list(columns: readonly string[]) {\n return columns.join(', ');\n}\n\nfunction count(n: number) {\n return Math.round(n).toLocaleString('en-US');\n}\n"],"mappings":";;;;;AAMA,SAAgB,mBAAmB,SAA8B;CAC/D,QAAQ,QAAQ,MAAhB;EACE,KAAK,iBAAiB;GACpB,MAAM,EAAC,SAAS,MAAM,WAAU;GAChC,OACE,GAAG,SAAS,gBAAgB,YAAY,MAAM,SAAS,OAAO,EAAE,MAC1D,KAAK,OAAO,EAAE,cAAc,MAAM,IAAI,EAAE,gCAC9B,KAAK,OAAO,IACzB,SAAS,sCAAsC,GAAG,MACrD,WAAW,OAAO;EAEtB;EACA,KAAK,aAAa;GAChB,MAAM,EAAC,SAAS,MAAM,WAAU;GAChC,MAAM,WAAW,QACd,KAAK,CAAC,QAAQ,eAAe,GAAG,OAAO,GAAG,WAAW,EACrD,KAAK,IAAI;GACZ,OACE,gBAAgB,SAAS,OAAO,EAAE,UAAU,MAAM,IAAI,EAAE,qBAC5C,SAAS,mGAElB,SAAS,sCAAsC,GAAG,MACrD,WAAW,OAAO;EAEtB;EACA,KAAK,aACH,OACE,8DACI,MAAM,QAAQ,IAAI,EAAE;CAE9B;AACF;AAEA,SAAS,SAAS,EAAC,OAAO,QAAiD;CACzE,OAAO,KAAK,WAAW,IACnB,QACA,GAAG,MAAM,YAAY,KAAK,KAAI,UAAS,IAAI,MAAM,EAAE,EAAE,KAAK,KAAK,EAAE;AACvE;AAEA,SAAS,WAAW,EAClB,OACA,kBAIC;CACD,OAAO,+BAA+B,MAAM,IAAI,eAAe,KAAK,IAAI,EAAE;AAC5E;AAEA,SAAS,KAAK,SAA4B;CACxC,OAAO,QAAQ,KAAK,IAAI;AAC1B;AAEA,SAAS,MAAM,GAAW;CACxB,OAAO,KAAK,MAAM,CAAC,EAAE,eAAe,OAAO;AAC7C"}
1
+ {"version":3,"file":"plan-warnings.js","names":[],"sources":["../../../../../../zero-cache/src/services/view-syncer/plan-warnings.ts"],"sourcesContent":["import type {PlanWarning} from '../../../../zql/src/planner/planner-warnings.ts';\n\n/**\n * Explains a {@link PlanWarning} for a zero-cache operator, in terms of the\n * SQLite replica, whose indexes come from the upstream database.\n */\nexport function planWarningMessage(warning: PlanWarning): string {\n switch (warning.type) {\n case 'missing-index': {\n const {columns, rows, perRow} = warning;\n return (\n `${perRow ? 'Each lookup' : 'Each read'} of ${location(warning)} ` +\n `by ${list(columns)} scans all ~${count(rows)} rows because no ` +\n `index covers ${list(columns)}` +\n `${perRow ? ', and it runs once per parent row' : ''}. ` +\n suggestion(warning)\n );\n }\n case 'full-sort': {\n const {orderBy, rows, perRow} = warning;\n const ordering = orderBy\n .map(([column, direction]) => `${column} ${direction}`)\n .join(', ');\n return (\n `Each read of ${location(warning)} sorts ~${count(rows)} rows for ` +\n `ORDER BY ${ordering}, reading every matching row before returning ` +\n `the first, so a limit does not reduce the rows read` +\n `${perRow ? ', and it runs once per parent row' : ''}. ` +\n suggestion(warning)\n );\n }\n case 'high-cost':\n return (\n `The best plan found for the query is estimated to process ` +\n `~${count(warning.cost)} rows.`\n );\n }\n}\n\nfunction location({table, path}: {table: string; path: readonly string[]}) {\n return path.length === 0\n ? table\n : `${table} (related ${path.map(alias => `'${alias}'`).join(' > ')})`;\n}\n\nfunction suggestion({\n table,\n suggestedIndex,\n}: {\n table: string;\n suggestedIndex: readonly string[];\n}) {\n return `Consider adding an index on ${table} (${suggestedIndex.join(', ')}) upstream.`;\n}\n\nfunction list(columns: readonly string[]) {\n return columns.join(', ');\n}\n\nfunction count(n: number) {\n return Math.round(n).toLocaleString('en-US');\n}\n"],"mappings":";;;;;AAMA,SAAgB,mBAAmB,SAA8B;CAC/D,QAAQ,QAAQ,MAAhB;EACE,KAAK,iBAAiB;GACpB,MAAM,EAAC,SAAS,MAAM,WAAU;GAChC,OACE,GAAG,SAAS,gBAAgB,YAAY,MAAM,SAAS,OAAO,EAAE,MAC1D,KAAK,OAAO,EAAE,cAAc,MAAM,IAAI,EAAE,gCAC9B,KAAK,OAAO,IACzB,SAAS,sCAAsC,GAAG,MACrD,WAAW,OAAO;EAEtB;EACA,KAAK,aAAa;GAChB,MAAM,EAAC,SAAS,MAAM,WAAU;GAChC,MAAM,WAAW,QACd,KAAK,CAAC,QAAQ,eAAe,GAAG,OAAO,GAAG,WAAW,CAAC,CACtD,KAAK,IAAI;GACZ,OACE,gBAAgB,SAAS,OAAO,EAAE,UAAU,MAAM,IAAI,EAAE,qBAC5C,SAAS,mGAElB,SAAS,sCAAsC,GAAG,MACrD,WAAW,OAAO;EAEtB;EACA,KAAK,aACH,OACE,8DACI,MAAM,QAAQ,IAAI,EAAE;CAE9B;AACF;AAEA,SAAS,SAAS,EAAC,OAAO,QAAiD;CACzE,OAAO,KAAK,WAAW,IACnB,QACA,GAAG,MAAM,YAAY,KAAK,KAAI,UAAS,IAAI,MAAM,EAAE,CAAC,CAAC,KAAK,KAAK,EAAE;AACvE;AAEA,SAAS,WAAW,EAClB,OACA,kBAIC;CACD,OAAO,+BAA+B,MAAM,IAAI,eAAe,KAAK,IAAI,EAAE;AAC5E;AAEA,SAAS,KAAK,SAA4B;CACxC,OAAO,QAAQ,KAAK,IAAI;AAC1B;AAEA,SAAS,MAAM,GAAW;CACxB,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,eAAe,OAAO;AAC7C"}
@@ -1 +1 @@
1
- {"version":3,"file":"push-cost.js","names":["#costs","#walkMsPerEntry","#parseMsPerEntry","#lookupMsPerEntry"],"sources":["../../../../../../zero-cache/src/services/view-syncer/push-cost.ts"],"sourcesContent":["import {MIN_PUSH_CUTOFF_MS} from './push-cutoff.ts';\n\n/**\n * The weight of the latest advancement in a query's cost per change. The\n * cost of a change depends on the row (one joined to many others costs more\n * than one that is filtered out), so it varies from one advancement to the\n * next, and workloads shift.\n */\nconst LATEST_WEIGHT = 0.3;\n\n/**\n * Learns what it costs to push a change of each table through each query of\n * a client group, and estimates from it what pushing a batch of changes\n * would cost.\n *\n * The estimates only decide whether a query skips pushing and starts an\n * advancement on rebuild (see {@link shouldStartOnRebuild}). Nothing depends\n * on their accuracy: a query that starts on push when it should not have is\n * cut off (see `PushCutoff`), and one that starts on rebuild costs its\n * hydration time, as it would have at the cutoff.\n */\nexport class PushCostEstimator {\n /** Milliseconds per change, by query ID and table. */\n readonly #costs = new Map<string, Map<string, number>>();\n\n /**\n * Records what an advancement cost a query.\n *\n * @param tables The tables the query reads.\n * @param timeMsByTable The time spent pushing the changes of each table\n * to the query. Absent for a table whose changes cost it nothing,\n * e.g. because the rows did not match its filters.\n * @param changesByTable The entries of each table that the advancement\n * read from the change log, whether or not they reached the query,\n * or changed anything at all (a row that is inserted and deleted\n * between two snapshots is an entry that changes nothing). These\n * are the entries that {@link estimate} is given the count of.\n */\n record(\n queryID: string,\n tables: Iterable<string>,\n timeMsByTable: ReadonlyMap<string, number> | undefined,\n changesByTable: ReadonlyMap<string, number>,\n ): void {\n let costs = this.#costs.get(queryID);\n for (const table of tables) {\n const changes = changesByTable.get(table);\n if (!changes) {\n continue;\n }\n if (costs === undefined) {\n costs = new Map();\n this.#costs.set(queryID, costs);\n }\n const latest = (timeMsByTable?.get(table) ?? 0) / changes;\n const previous = costs.get(table);\n const cost =\n previous === undefined\n ? latest\n : previous + LATEST_WEIGHT * (latest - previous);\n costs.set(table, cost);\n }\n }\n\n /**\n * The estimated time to push `changes` to the query, or `undefined` if it\n * has never been pushed any change. A table that the query has never been\n * pushed a change of is estimated to cost nothing.\n *\n * @param tables The tables the query reads.\n * @param changes The number of changes to push, by table.\n */\n estimate(\n queryID: string,\n tables: Iterable<string>,\n changes: ReadonlyMap<string, number>,\n ): number | undefined {\n const costs = this.#costs.get(queryID);\n if (costs === undefined) {\n return undefined;\n }\n let total = 0;\n for (const table of tables) {\n total += (changes.get(table) ?? 0) * (costs.get(table) ?? 0);\n }\n return total;\n }\n\n /** Forgets what was learned about a query. */\n forget(queryID: string): void {\n this.#costs.delete(queryID);\n }\n}\n\n/**\n * Learns what it costs a client group to read a batch of changes, apart from\n * pushing them through its queries, and estimates from it what reading the\n * next one would cost.\n *\n * Reading has three parts, which do not cost the same entries (see\n * {@link ReadParts}): walking the change log, parsing the entries that\n * the walk returns, and looking up the rows of the entries of the tables\n * that are read. What each costs is learned per entry that it was done for.\n * A cost learned per entry of some other count is only right for a batch\n * with the same mix of entries: charged to the entries that are pushed, for\n * instance, the parsing of a batch that is mostly of tables that no query\n * reads makes the next batch that is not look many times as expensive as it\n * is.\n *\n * No query can be cut off for what this costs, as it is not a cost of any\n * one query. It is what pushing costs all of them together, on top of what\n * it costs each (see {@link shouldRebuildInsteadOfReading}).\n */\nexport class ReadCostEstimator {\n #walkMsPerEntry: number | undefined;\n #parseMsPerEntry: number | undefined;\n #lookupMsPerEntry: number | undefined;\n\n /**\n * Records what reading a batch cost. Only for a batch that was read to\n * its end.\n *\n * @param timeMs The time spent on each part of reading the batch.\n * @param entries The entries that each part was done for.\n */\n record(timeMs: ReadParts, entries: ReadParts): void {\n this.#walkMsPerEntry = learn(\n this.#walkMsPerEntry,\n timeMs.walk,\n entries.walk,\n );\n this.#parseMsPerEntry = learn(\n this.#parseMsPerEntry,\n timeMs.parse,\n entries.parse,\n );\n this.#lookupMsPerEntry = learn(\n this.#lookupMsPerEntry,\n timeMs.lookup,\n entries.lookup,\n );\n }\n\n /**\n * The estimated time to read a batch, or `undefined` if none has been\n * read yet.\n *\n * @param entries The entries that each part of reading the batch would be\n * done for.\n */\n estimate(entries: ReadParts): number | undefined {\n if (this.#walkMsPerEntry === undefined) {\n return undefined;\n }\n return (\n entries.walk * this.#walkMsPerEntry +\n entries.parse * (this.#parseMsPerEntry ?? 0) +\n entries.lookup * (this.#lookupMsPerEntry ?? 0)\n );\n }\n}\n\n/**\n * A number for each part of reading a batch of changes: the entries of the\n * change log that the part is done for, or the time that it takes.\n */\nexport type ReadParts = {\n /**\n * Walking the change log, which is done for every entry since the\n * previous snapshot, whether or not the entry is of a table that is read:\n * the entries are in the order they were committed in, not grouped by\n * table.\n */\n readonly walk: number;\n /**\n * Parsing, which is done for every entry that the walk returns. That is\n * all of them, unless the walk is told which tables are read.\n */\n readonly parse: number;\n /**\n * Looking up the row in both snapshots, which is done for every entry of\n * a table that is read. This includes the entries that then turn out to\n * change nothing (e.g. a row inserted and deleted since the previous\n * snapshot).\n */\n readonly lookup: number;\n};\n\nfunction learn(\n msPerEntry: number | undefined,\n timeMs: number,\n entries: number,\n): number | undefined {\n return entries > 0 ? decay(msPerEntry, timeMs / entries) : msPerEntry;\n}\n\nfunction decay(previous: number | undefined, latest: number): number {\n return previous === undefined\n ? latest\n : previous + LATEST_WEIGHT * (latest - previous);\n}\n\n/**\n * Whether the queries that are left to push a batch to should all be\n * hydrated again instead, without the batch being read: when reading it and\n * pushing it to them is estimated to cost at least `margin` times what\n * hydrating them does.\n *\n * This is {@link shouldStartOnRebuild} for the queries together, with what\n * reading the batch costs added to what pushing it costs each of them. A\n * query that few of the batch's changes reach is cheap to push to, but the\n * whole batch still has to be walked to find those changes.\n *\n * @param estimatedReadMs `undefined` if there is no estimate, in which case\n * the batch is read.\n * @param queries What pushing the batch is estimated to cost each query\n * (nothing, if it has no estimate), and what hydrating it took.\n */\nexport function shouldRebuildInsteadOfReading(\n estimatedReadMs: number | undefined,\n queries: Iterable<{\n readonly estimatedPushMs: number | undefined;\n readonly hydrationTimeMs: number;\n }>,\n margin: number,\n): boolean {\n if (estimatedReadMs === undefined) {\n return false;\n }\n let pushMs = estimatedReadMs;\n let hydrationMs = 0;\n for (const query of queries) {\n pushMs += query.estimatedPushMs ?? 0;\n hydrationMs += Math.max(query.hydrationTimeMs, MIN_PUSH_CUTOFF_MS);\n }\n return hydrationMs > 0 && pushMs >= margin * hydrationMs;\n}\n\n/**\n * Whether a query should start an advancement on rebuild, i.e. be hydrated\n * again without being pushed any of the advancement's changes: when pushing\n * them is estimated to cost at least `margin` times what hydrating it does.\n *\n * The margin is for the imprecision of the estimate. A query without one\n * starts on push.\n */\nexport function shouldStartOnRebuild(\n estimatedPushMs: number | undefined,\n hydrationTimeMs: number,\n margin: number,\n): boolean {\n return (\n estimatedPushMs !== undefined &&\n estimatedPushMs >= margin * Math.max(hydrationTimeMs, MIN_PUSH_CUTOFF_MS)\n );\n}\n"],"mappings":";;;;;;;AAQA,IAAM,gBAAgB;;;;;;;;;;;;AAatB,IAAa,oBAAb,MAA+B;;CAE7B,yBAAkB,IAAI,IAAiC;;;;;;;;;;;;;;CAevD,OACE,SACA,QACA,eACA,gBACM;EACN,IAAI,QAAQ,KAAKA,OAAO,IAAI,OAAO;EACnC,KAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,UAAU,eAAe,IAAI,KAAK;GACxC,IAAI,CAAC,SACH;GAEF,IAAI,UAAU,KAAA,GAAW;IACvB,wBAAQ,IAAI,IAAI;IAChB,KAAKA,OAAO,IAAI,SAAS,KAAK;GAChC;GACA,MAAM,UAAU,eAAe,IAAI,KAAK,KAAK,KAAK;GAClD,MAAM,WAAW,MAAM,IAAI,KAAK;GAChC,MAAM,OACJ,aAAa,KAAA,IACT,SACA,WAAW,iBAAiB,SAAS;GAC3C,MAAM,IAAI,OAAO,IAAI;EACvB;CACF;;;;;;;;;CAUA,SACE,SACA,QACA,SACoB;EACpB,MAAM,QAAQ,KAAKA,OAAO,IAAI,OAAO;EACrC,IAAI,UAAU,KAAA,GACZ;EAEF,IAAI,QAAQ;EACZ,KAAK,MAAM,SAAS,QAClB,UAAU,QAAQ,IAAI,KAAK,KAAK,MAAM,MAAM,IAAI,KAAK,KAAK;EAE5D,OAAO;CACT;;CAGA,OAAO,SAAuB;EAC5B,KAAKA,OAAO,OAAO,OAAO;CAC5B;AACF;;;;;;;;;;;;;;;;;;;;AAqBA,IAAa,oBAAb,MAA+B;CAC7B;CACA;CACA;;;;;;;;CASA,OAAO,QAAmB,SAA0B;EAClD,KAAKC,kBAAkB,MACrB,KAAKA,iBACL,OAAO,MACP,QAAQ,IACV;EACA,KAAKC,mBAAmB,MACtB,KAAKA,kBACL,OAAO,OACP,QAAQ,KACV;EACA,KAAKC,oBAAoB,MACvB,KAAKA,mBACL,OAAO,QACP,QAAQ,MACV;CACF;;;;;;;;CASA,SAAS,SAAwC;EAC/C,IAAI,KAAKF,oBAAoB,KAAA,GAC3B;EAEF,OACE,QAAQ,OAAO,KAAKA,kBACpB,QAAQ,SAAS,KAAKC,oBAAoB,KAC1C,QAAQ,UAAU,KAAKC,qBAAqB;CAEhD;AACF;AA4BA,SAAS,MACP,YACA,QACA,SACoB;CACpB,OAAO,UAAU,IAAI,MAAM,YAAY,SAAS,OAAO,IAAI;AAC7D;AAEA,SAAS,MAAM,UAA8B,QAAwB;CACnE,OAAO,aAAa,KAAA,IAChB,SACA,WAAW,iBAAiB,SAAS;AAC3C;;;;;;;;;;;;;;;;;AAkBA,SAAgB,8BACd,iBACA,SAIA,QACS;CACT,IAAI,oBAAoB,KAAA,GACtB,OAAO;CAET,IAAI,SAAS;CACb,IAAI,cAAc;CAClB,KAAK,MAAM,SAAS,SAAS;EAC3B,UAAU,MAAM,mBAAmB;EACnC,eAAe,KAAK,IAAI,MAAM,iBAAA,CAAmC;CACnE;CACA,OAAO,cAAc,KAAK,UAAU,SAAS;AAC/C;;;;;;;;;AAUA,SAAgB,qBACd,iBACA,iBACA,QACS;CACT,OACE,oBAAoB,KAAA,KACpB,mBAAmB,SAAS,KAAK,IAAI,iBAAA,CAAmC;AAE5E"}
1
+ {"version":3,"file":"push-cost.js","names":[],"sources":["../../../../../../zero-cache/src/services/view-syncer/push-cost.ts"],"sourcesContent":["import {MIN_PUSH_CUTOFF_MS} from './push-cutoff.ts';\n\n/**\n * The weight of the latest advancement in a query's cost per change. The\n * cost of a change depends on the row (one joined to many others costs more\n * than one that is filtered out), so it varies from one advancement to the\n * next, and workloads shift.\n */\nconst LATEST_WEIGHT = 0.3;\n\n/**\n * Learns what it costs to push a change of each table through each query of\n * a client group, and estimates from it what pushing a batch of changes\n * would cost.\n *\n * The estimates only decide whether a query skips pushing and starts an\n * advancement on rebuild (see {@link shouldStartOnRebuild}). Nothing depends\n * on their accuracy: a query that starts on push when it should not have is\n * cut off (see `PushCutoff`), and one that starts on rebuild costs its\n * hydration time, as it would have at the cutoff.\n */\nexport class PushCostEstimator {\n /** Milliseconds per change, by query ID and table. */\n readonly #costs = new Map<string, Map<string, number>>();\n\n /**\n * Records what an advancement cost a query.\n *\n * @param tables The tables the query reads.\n * @param timeMsByTable The time spent pushing the changes of each table\n * to the query. Absent for a table whose changes cost it nothing,\n * e.g. because the rows did not match its filters.\n * @param changesByTable The entries of each table that the advancement\n * read from the change log, whether or not they reached the query,\n * or changed anything at all (a row that is inserted and deleted\n * between two snapshots is an entry that changes nothing). These\n * are the entries that {@link estimate} is given the count of.\n */\n record(\n queryID: string,\n tables: Iterable<string>,\n timeMsByTable: ReadonlyMap<string, number> | undefined,\n changesByTable: ReadonlyMap<string, number>,\n ): void {\n let costs = this.#costs.get(queryID);\n for (const table of tables) {\n const changes = changesByTable.get(table);\n if (!changes) {\n continue;\n }\n if (costs === undefined) {\n costs = new Map();\n this.#costs.set(queryID, costs);\n }\n const latest = (timeMsByTable?.get(table) ?? 0) / changes;\n const previous = costs.get(table);\n const cost =\n previous === undefined\n ? latest\n : previous + LATEST_WEIGHT * (latest - previous);\n costs.set(table, cost);\n }\n }\n\n /**\n * The estimated time to push `changes` to the query, or `undefined` if it\n * has never been pushed any change. A table that the query has never been\n * pushed a change of is estimated to cost nothing.\n *\n * @param tables The tables the query reads.\n * @param changes The number of changes to push, by table.\n */\n estimate(\n queryID: string,\n tables: Iterable<string>,\n changes: ReadonlyMap<string, number>,\n ): number | undefined {\n const costs = this.#costs.get(queryID);\n if (costs === undefined) {\n return undefined;\n }\n let total = 0;\n for (const table of tables) {\n total += (changes.get(table) ?? 0) * (costs.get(table) ?? 0);\n }\n return total;\n }\n\n /** Forgets what was learned about a query. */\n forget(queryID: string): void {\n this.#costs.delete(queryID);\n }\n}\n\n/**\n * Learns what it costs a client group to read a batch of changes, apart from\n * pushing them through its queries, and estimates from it what reading the\n * next one would cost.\n *\n * Reading has three parts, which do not cost the same entries (see\n * {@link ReadParts}): walking the change log, parsing the entries that\n * the walk returns, and looking up the rows of the entries of the tables\n * that are read. What each costs is learned per entry that it was done for.\n * A cost learned per entry of some other count is only right for a batch\n * with the same mix of entries: charged to the entries that are pushed, for\n * instance, the parsing of a batch that is mostly of tables that no query\n * reads makes the next batch that is not look many times as expensive as it\n * is.\n *\n * No query can be cut off for what this costs, as it is not a cost of any\n * one query. It is what pushing costs all of them together, on top of what\n * it costs each (see {@link shouldRebuildInsteadOfReading}).\n */\nexport class ReadCostEstimator {\n #walkMsPerEntry: number | undefined;\n #parseMsPerEntry: number | undefined;\n #lookupMsPerEntry: number | undefined;\n\n /**\n * Records what reading a batch cost. Only for a batch that was read to\n * its end.\n *\n * @param timeMs The time spent on each part of reading the batch.\n * @param entries The entries that each part was done for.\n */\n record(timeMs: ReadParts, entries: ReadParts): void {\n this.#walkMsPerEntry = learn(\n this.#walkMsPerEntry,\n timeMs.walk,\n entries.walk,\n );\n this.#parseMsPerEntry = learn(\n this.#parseMsPerEntry,\n timeMs.parse,\n entries.parse,\n );\n this.#lookupMsPerEntry = learn(\n this.#lookupMsPerEntry,\n timeMs.lookup,\n entries.lookup,\n );\n }\n\n /**\n * The estimated time to read a batch, or `undefined` if none has been\n * read yet.\n *\n * @param entries The entries that each part of reading the batch would be\n * done for.\n */\n estimate(entries: ReadParts): number | undefined {\n if (this.#walkMsPerEntry === undefined) {\n return undefined;\n }\n return (\n entries.walk * this.#walkMsPerEntry +\n entries.parse * (this.#parseMsPerEntry ?? 0) +\n entries.lookup * (this.#lookupMsPerEntry ?? 0)\n );\n }\n}\n\n/**\n * A number for each part of reading a batch of changes: the entries of the\n * change log that the part is done for, or the time that it takes.\n */\nexport type ReadParts = {\n /**\n * Walking the change log, which is done for every entry since the\n * previous snapshot, whether or not the entry is of a table that is read:\n * the entries are in the order they were committed in, not grouped by\n * table.\n */\n readonly walk: number;\n /**\n * Parsing, which is done for every entry that the walk returns. That is\n * all of them, unless the walk is told which tables are read.\n */\n readonly parse: number;\n /**\n * Looking up the row in both snapshots, which is done for every entry of\n * a table that is read. This includes the entries that then turn out to\n * change nothing (e.g. a row inserted and deleted since the previous\n * snapshot).\n */\n readonly lookup: number;\n};\n\nfunction learn(\n msPerEntry: number | undefined,\n timeMs: number,\n entries: number,\n): number | undefined {\n return entries > 0 ? decay(msPerEntry, timeMs / entries) : msPerEntry;\n}\n\nfunction decay(previous: number | undefined, latest: number): number {\n return previous === undefined\n ? latest\n : previous + LATEST_WEIGHT * (latest - previous);\n}\n\n/**\n * Whether the queries that are left to push a batch to should all be\n * hydrated again instead, without the batch being read: when reading it and\n * pushing it to them is estimated to cost at least `margin` times what\n * hydrating them does.\n *\n * This is {@link shouldStartOnRebuild} for the queries together, with what\n * reading the batch costs added to what pushing it costs each of them. A\n * query that few of the batch's changes reach is cheap to push to, but the\n * whole batch still has to be walked to find those changes.\n *\n * @param estimatedReadMs `undefined` if there is no estimate, in which case\n * the batch is read.\n * @param queries What pushing the batch is estimated to cost each query\n * (nothing, if it has no estimate), and what hydrating it took.\n */\nexport function shouldRebuildInsteadOfReading(\n estimatedReadMs: number | undefined,\n queries: Iterable<{\n readonly estimatedPushMs: number | undefined;\n readonly hydrationTimeMs: number;\n }>,\n margin: number,\n): boolean {\n if (estimatedReadMs === undefined) {\n return false;\n }\n let pushMs = estimatedReadMs;\n let hydrationMs = 0;\n for (const query of queries) {\n pushMs += query.estimatedPushMs ?? 0;\n hydrationMs += Math.max(query.hydrationTimeMs, MIN_PUSH_CUTOFF_MS);\n }\n return hydrationMs > 0 && pushMs >= margin * hydrationMs;\n}\n\n/**\n * Whether a query should start an advancement on rebuild, i.e. be hydrated\n * again without being pushed any of the advancement's changes: when pushing\n * them is estimated to cost at least `margin` times what hydrating it does.\n *\n * The margin is for the imprecision of the estimate. A query without one\n * starts on push.\n */\nexport function shouldStartOnRebuild(\n estimatedPushMs: number | undefined,\n hydrationTimeMs: number,\n margin: number,\n): boolean {\n return (\n estimatedPushMs !== undefined &&\n estimatedPushMs >= margin * Math.max(hydrationTimeMs, MIN_PUSH_CUTOFF_MS)\n );\n}\n"],"mappings":";;;;;;;AAQA,IAAM,gBAAgB;;;;;;;;;;;;AAatB,IAAa,oBAAb,MAA+B;;CAE7B,yBAAkB,IAAI,IAAiC;;;;;;;;;;;;;;CAevD,OACE,SACA,QACA,eACA,gBACM;EACN,IAAI,QAAQ,KAAK,OAAO,IAAI,OAAO;EACnC,KAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,UAAU,eAAe,IAAI,KAAK;GACxC,IAAI,CAAC,SACH;GAEF,IAAI,UAAU,KAAA,GAAW;IACvB,wBAAQ,IAAI,IAAI;IAChB,KAAK,OAAO,IAAI,SAAS,KAAK;GAChC;GACA,MAAM,UAAU,eAAe,IAAI,KAAK,KAAK,KAAK;GAClD,MAAM,WAAW,MAAM,IAAI,KAAK;GAChC,MAAM,OACJ,aAAa,KAAA,IACT,SACA,WAAW,iBAAiB,SAAS;GAC3C,MAAM,IAAI,OAAO,IAAI;EACvB;CACF;;;;;;;;;CAUA,SACE,SACA,QACA,SACoB;EACpB,MAAM,QAAQ,KAAK,OAAO,IAAI,OAAO;EACrC,IAAI,UAAU,KAAA,GACZ;EAEF,IAAI,QAAQ;EACZ,KAAK,MAAM,SAAS,QAClB,UAAU,QAAQ,IAAI,KAAK,KAAK,MAAM,MAAM,IAAI,KAAK,KAAK;EAE5D,OAAO;CACT;;CAGA,OAAO,SAAuB;EAC5B,KAAK,OAAO,OAAO,OAAO;CAC5B;AACF;;;;;;;;;;;;;;;;;;;;AAqBA,IAAa,oBAAb,MAA+B;CAC7B;CACA;CACA;;;;;;;;CASA,OAAO,QAAmB,SAA0B;EAClD,KAAK,kBAAkB,MACrB,KAAK,iBACL,OAAO,MACP,QAAQ,IACV;EACA,KAAK,mBAAmB,MACtB,KAAK,kBACL,OAAO,OACP,QAAQ,KACV;EACA,KAAK,oBAAoB,MACvB,KAAK,mBACL,OAAO,QACP,QAAQ,MACV;CACF;;;;;;;;CASA,SAAS,SAAwC;EAC/C,IAAI,KAAK,oBAAoB,KAAA,GAC3B;EAEF,OACE,QAAQ,OAAO,KAAK,kBACpB,QAAQ,SAAS,KAAK,oBAAoB,KAC1C,QAAQ,UAAU,KAAK,qBAAqB;CAEhD;AACF;AA4BA,SAAS,MACP,YACA,QACA,SACoB;CACpB,OAAO,UAAU,IAAI,MAAM,YAAY,SAAS,OAAO,IAAI;AAC7D;AAEA,SAAS,MAAM,UAA8B,QAAwB;CACnE,OAAO,aAAa,KAAA,IAChB,SACA,WAAW,iBAAiB,SAAS;AAC3C;;;;;;;;;;;;;;;;;AAkBA,SAAgB,8BACd,iBACA,SAIA,QACS;CACT,IAAI,oBAAoB,KAAA,GACtB,OAAO;CAET,IAAI,SAAS;CACb,IAAI,cAAc;CAClB,KAAK,MAAM,SAAS,SAAS;EAC3B,UAAU,MAAM,mBAAmB;EACnC,eAAe,KAAK,IAAI,MAAM,iBAAA,CAAmC;CACnE;CACA,OAAO,cAAc,KAAK,UAAU,SAAS;AAC/C;;;;;;;;;AAUA,SAAgB,qBACd,iBACA,iBACA,QACS;CACT,OACE,oBAAoB,KAAA,KACpB,mBAAmB,SAAS,KAAK,IAAI,iBAAA,CAAmC;AAE5E"}
@@ -1 +1 @@
1
- {"version":3,"file":"push-cutoff.js","names":["#shouldCutOff","#pushed"],"sources":["../../../../../../zero-cache/src/services/view-syncer/push-cutoff.ts"],"sourcesContent":["/** What an advancement has pushed to a query so far. */\nexport type QueryPushProgress = {\n readonly queryID: string;\n /** The time spent processing pushes to the query in this advancement. */\n readonly pushTimeMs: number;\n /** The number of changes pushed to the query in this advancement. */\n readonly changes: number;\n /** The query's hydration time, i.e. what rebuilding it is expected to cost. */\n readonly hydrationTimeMs: number;\n};\n\n/** Whether to stop pushing an advancement's changes to a query. */\nexport type ShouldCutOffPush = (progress: QueryPushProgress) => boolean;\n\n/**\n * The least a query's hydration is taken to cost when deciding whether to\n * stop pushing to it. Small queries hydrate in well under a millisecond,\n * where one push can take longer than the hydration did on timer noise alone,\n * and hydrating again saves too little to be worth its fixed costs.\n */\nexport const MIN_PUSH_CUTOFF_MS = 2;\n\n/**\n * Whether to stop pushing an advancement's changes to a query and hydrate it\n * again instead: once the pushes have taken as long as the hydration is\n * expected to.\n *\n * This is the ski-rental strategy (rent until the rent paid equals the\n * purchase price, then buy). Whatever the actual costs turn out to be, the\n * query costs at most about twice the cheaper of pushing every change and\n * hydrating from the start, plus the one change that crossed the line. It\n * needs no estimate of what the remaining changes will cost.\n */\nexport function shouldCutOffPush({\n pushTimeMs,\n hydrationTimeMs,\n}: Pick<QueryPushProgress, 'pushTimeMs' | 'hydrationTimeMs'>): boolean {\n return pushTimeMs >= Math.max(hydrationTimeMs, MIN_PUSH_CUTOFF_MS);\n}\n\n/**\n * Bounds the cost of one advancement query by query: each query is pushed\n * changes until {@link ShouldCutOffPush} says to stop, and is then hydrated\n * again once the advancement is done.\n *\n * An advancement with a cutoff is not subject to the client group's\n * advancement time limit, which resets every pipeline of the client group.\n * The two are alternatives and are never applied together.\n */\nexport class PushCutoff {\n readonly #shouldCutOff: ShouldCutOffPush;\n /** The queries pushed to since the last {@link takeCutOff}. */\n readonly #pushed = new Set<string>();\n\n constructor(shouldCutOff: ShouldCutOffPush = shouldCutOffPush) {\n this.#shouldCutOff = shouldCutOff;\n }\n\n /** Called for each push to a query. */\n recordPush(queryID: string): void {\n this.#pushed.add(queryID);\n }\n\n /**\n * Called between changes, when no push is in progress. Returns the queries\n * to stop pushing to, of those pushed to since the last call.\n *\n * @param progressOf The progress of a query, or `undefined` if it is no\n * longer being pushed to.\n */\n takeCutOff(\n progressOf: (queryID: string) => QueryPushProgress | undefined,\n ): QueryPushProgress[] {\n if (this.#pushed.size === 0) {\n return [];\n }\n const cutOff: QueryPushProgress[] = [];\n for (const queryID of this.#pushed) {\n const progress = progressOf(queryID);\n if (progress !== undefined && this.#shouldCutOff(progress)) {\n cutOff.push(progress);\n }\n }\n this.#pushed.clear();\n return cutOff;\n }\n}\n"],"mappings":";;;;;;;;;;;AAiCA,SAAgB,iBAAiB,EAC/B,YACA,mBACqE;CACrE,OAAO,cAAc,KAAK,IAAI,iBAAA,CAAmC;AACnE;;;;;;;;;;AAWA,IAAa,aAAb,MAAwB;CACtB;;CAEA,0BAAmB,IAAI,IAAY;CAEnC,YAAY,eAAiC,kBAAkB;EAC7D,KAAKA,gBAAgB;CACvB;;CAGA,WAAW,SAAuB;EAChC,KAAKC,QAAQ,IAAI,OAAO;CAC1B;;;;;;;;CASA,WACE,YACqB;EACrB,IAAI,KAAKA,QAAQ,SAAS,GACxB,OAAO,CAAC;EAEV,MAAM,SAA8B,CAAC;EACrC,KAAK,MAAM,WAAW,KAAKA,SAAS;GAClC,MAAM,WAAW,WAAW,OAAO;GACnC,IAAI,aAAa,KAAA,KAAa,KAAKD,cAAc,QAAQ,GACvD,OAAO,KAAK,QAAQ;EAExB;EACA,KAAKC,QAAQ,MAAM;EACnB,OAAO;CACT;AACF"}
1
+ {"version":3,"file":"push-cutoff.js","names":[],"sources":["../../../../../../zero-cache/src/services/view-syncer/push-cutoff.ts"],"sourcesContent":["/** What an advancement has pushed to a query so far. */\nexport type QueryPushProgress = {\n readonly queryID: string;\n /** The time spent processing pushes to the query in this advancement. */\n readonly pushTimeMs: number;\n /** The number of changes pushed to the query in this advancement. */\n readonly changes: number;\n /** The query's hydration time, i.e. what rebuilding it is expected to cost. */\n readonly hydrationTimeMs: number;\n};\n\n/** Whether to stop pushing an advancement's changes to a query. */\nexport type ShouldCutOffPush = (progress: QueryPushProgress) => boolean;\n\n/**\n * The least a query's hydration is taken to cost when deciding whether to\n * stop pushing to it. Small queries hydrate in well under a millisecond,\n * where one push can take longer than the hydration did on timer noise alone,\n * and hydrating again saves too little to be worth its fixed costs.\n */\nexport const MIN_PUSH_CUTOFF_MS = 2;\n\n/**\n * Whether to stop pushing an advancement's changes to a query and hydrate it\n * again instead: once the pushes have taken as long as the hydration is\n * expected to.\n *\n * This is the ski-rental strategy (rent until the rent paid equals the\n * purchase price, then buy). Whatever the actual costs turn out to be, the\n * query costs at most about twice the cheaper of pushing every change and\n * hydrating from the start, plus the one change that crossed the line. It\n * needs no estimate of what the remaining changes will cost.\n */\nexport function shouldCutOffPush({\n pushTimeMs,\n hydrationTimeMs,\n}: Pick<QueryPushProgress, 'pushTimeMs' | 'hydrationTimeMs'>): boolean {\n return pushTimeMs >= Math.max(hydrationTimeMs, MIN_PUSH_CUTOFF_MS);\n}\n\n/**\n * Bounds the cost of one advancement query by query: each query is pushed\n * changes until {@link ShouldCutOffPush} says to stop, and is then hydrated\n * again once the advancement is done.\n *\n * An advancement with a cutoff is not subject to the client group's\n * advancement time limit, which resets every pipeline of the client group.\n * The two are alternatives and are never applied together.\n */\nexport class PushCutoff {\n readonly #shouldCutOff: ShouldCutOffPush;\n /** The queries pushed to since the last {@link takeCutOff}. */\n readonly #pushed = new Set<string>();\n\n constructor(shouldCutOff: ShouldCutOffPush = shouldCutOffPush) {\n this.#shouldCutOff = shouldCutOff;\n }\n\n /** Called for each push to a query. */\n recordPush(queryID: string): void {\n this.#pushed.add(queryID);\n }\n\n /**\n * Called between changes, when no push is in progress. Returns the queries\n * to stop pushing to, of those pushed to since the last call.\n *\n * @param progressOf The progress of a query, or `undefined` if it is no\n * longer being pushed to.\n */\n takeCutOff(\n progressOf: (queryID: string) => QueryPushProgress | undefined,\n ): QueryPushProgress[] {\n if (this.#pushed.size === 0) {\n return [];\n }\n const cutOff: QueryPushProgress[] = [];\n for (const queryID of this.#pushed) {\n const progress = progressOf(queryID);\n if (progress !== undefined && this.#shouldCutOff(progress)) {\n cutOff.push(progress);\n }\n }\n this.#pushed.clear();\n return cutOff;\n }\n}\n"],"mappings":";;;;;;;;;;;AAiCA,SAAgB,iBAAiB,EAC/B,YACA,mBACqE;CACrE,OAAO,cAAc,KAAK,IAAI,iBAAA,CAAmC;AACnE;;;;;;;;;;AAWA,IAAa,aAAb,MAAwB;CACtB;;CAEA,0BAAmB,IAAI,IAAY;CAEnC,YAAY,eAAiC,kBAAkB;EAC7D,KAAK,gBAAgB;CACvB;;CAGA,WAAW,SAAuB;EAChC,KAAK,QAAQ,IAAI,OAAO;CAC1B;;;;;;;;CASA,WACE,YACqB;EACrB,IAAI,KAAK,QAAQ,SAAS,GACxB,OAAO,CAAC;EAEV,MAAM,SAA8B,CAAC;EACrC,KAAK,MAAM,WAAW,KAAK,SAAS;GAClC,MAAM,WAAW,WAAW,OAAO;GACnC,IAAI,aAAa,KAAA,KAAa,KAAK,cAAc,QAAQ,GACvD,OAAO,KAAK,QAAQ;EAExB;EACA,KAAK,QAAQ,MAAM;EACnB,OAAO;CACT;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"query-covering.js","names":["#byRoot","#queryIDToRoot"],"sources":["../../../../../../zero-cache/src/services/view-syncer/query-covering.ts"],"sourcesContent":["import {\n deepEqual,\n type ReadonlyJSONValue,\n} from '../../../../shared/src/json.ts';\nimport {getOrInsertComputed, newMap} from '../../../../shared/src/map.ts';\nimport {\n normalizeAST,\n type AST,\n type Condition,\n type CorrelatedSubquery,\n type CorrelatedSubqueryCondition,\n type LiteralValue,\n type NormalizedAST,\n type SimpleCondition,\n} from '../../../../zero-protocol/src/ast.ts';\n\nexport type RunningQuery = {\n readonly transformedAst: AST;\n readonly transformationHash: string;\n readonly queryName?: string | undefined;\n};\n\nexport type CoveringQuery = {\n readonly queryID: string;\n readonly transformationHash: string;\n readonly queryName?: string | undefined;\n};\n\ntype NonNullScalarLiteralValue = string | number | boolean;\ntype IndexedRunningQuery = RunningQuery & {\n readonly normalizedAst: NormalizedAST;\n};\n\n/**\n * Returns true when every row that can be produced by `covered` is also\n * produced by `covering`.\n *\n * This is intentionally conservative: unsupported cases return false rather\n * than guessing. It is used for shadow logging only.\n */\nexport function isQueryCoveredBy(covered: AST, covering: AST): boolean {\n return astCoveredBy(normalizeAST(covered), normalizeAST(covering));\n}\n\nexport function findCoveringQuery(\n coveredQueryID: string,\n coveredAst: AST,\n runningQueries: ReadonlyMap<string, RunningQuery>,\n): CoveringQuery | undefined {\n return new QueryCoveringIndex(runningQueries).findCoveringQuery(\n coveredQueryID,\n coveredAst,\n );\n}\n\nexport class QueryCoveringIndex {\n readonly #byRoot = new Map<string, Map<string, IndexedRunningQuery>>();\n readonly #queryIDToRoot = new Map<string, string>();\n\n constructor(runningQueries?: ReadonlyMap<string, RunningQuery>) {\n if (runningQueries) {\n for (const [queryID, query] of runningQueries) {\n this.add(queryID, query);\n }\n }\n }\n\n add(queryID: string, query: RunningQuery): void {\n this.remove(queryID);\n\n const normalizedAst = normalizeAST(query.transformedAst);\n const root = rootKey(normalizedAst);\n getOrInsertComputed(this.#byRoot, root, newMap).set(queryID, {\n ...query,\n normalizedAst,\n });\n this.#queryIDToRoot.set(queryID, root);\n }\n\n remove(queryID: string): void {\n const root = this.#queryIDToRoot.get(queryID);\n if (root === undefined) {\n return;\n }\n\n this.#queryIDToRoot.delete(queryID);\n const queries = this.#byRoot.get(root);\n if (!queries) {\n return;\n }\n\n queries.delete(queryID);\n if (queries.size === 0) {\n this.#byRoot.delete(root);\n }\n }\n\n findCoveringQuery(\n coveredQueryID: string,\n coveredAst: AST,\n ): CoveringQuery | undefined {\n const normalizedCoveredAst = normalizeAST(coveredAst);\n const queries = this.#byRoot.get(rootKey(normalizedCoveredAst));\n if (!queries) {\n return undefined;\n }\n\n for (const [queryID, query] of queries) {\n if (queryID === coveredQueryID) {\n continue;\n }\n if (astCoveredBy(normalizedCoveredAst, query.normalizedAst)) {\n return {\n queryID,\n transformationHash: query.transformationHash,\n ...(query.queryName !== undefined && {queryName: query.queryName}),\n };\n }\n }\n return undefined;\n }\n}\n\nfunction rootKey(ast: NormalizedAST): string {\n return JSON.stringify([ast.schema, ast.table, ast.alias]);\n}\n\nfunction astCoveredBy(\n covered: NormalizedAST,\n covering: NormalizedAST,\n): boolean {\n return (\n covered.schema === covering.schema &&\n covered.table === covering.table &&\n covered.alias === covering.alias &&\n conditionImplies(covered.where, covering.where) &&\n relatedCoveredBy(covered.related, covering.related) &&\n boundsCoveredBy(covered, covering)\n );\n}\n\nfunction boundsCoveredBy(\n covered: NormalizedAST,\n covering: NormalizedAST,\n): boolean {\n if (covering.limit === undefined) {\n if (covering.start === undefined) {\n return true;\n }\n return (\n jsonEqual(covered.start, covering.start) &&\n jsonEqual(covered.orderBy, covering.orderBy)\n );\n }\n\n if (covered.limit === undefined || covering.limit < covered.limit) {\n return false;\n }\n\n // A limited broader query does not necessarily contain a limited narrower\n // query. The ordered input to the limit must be equivalent.\n return (\n conditionEquivalent(covered.where, covering.where) &&\n jsonEqual(covered.start, covering.start) &&\n jsonEqual(covered.orderBy, covering.orderBy)\n );\n}\n\nfunction relatedCoveredBy(\n covered: readonly CorrelatedSubquery[] | undefined,\n covering: readonly CorrelatedSubquery[] | undefined,\n): boolean {\n if (covered === undefined || covered.length === 0) {\n return true;\n }\n if (covering === undefined) {\n return false;\n }\n return covered.every(coveredRelated =>\n covering.some(\n coveringRelated =>\n sameRelatedEdge(coveredRelated, coveringRelated) &&\n astCoveredBy(\n coveredRelated.subquery as NormalizedAST,\n coveringRelated.subquery as NormalizedAST,\n ),\n ),\n );\n}\n\nfunction conditionEquivalent(\n a: Condition | undefined,\n b: Condition | undefined,\n): boolean {\n return conditionImplies(a, b) && conditionImplies(b, a);\n}\n\nfunction conditionImplies(\n covered: Condition | undefined,\n covering: Condition | undefined,\n): boolean {\n if (covering === undefined) {\n return true;\n }\n if (covered === undefined) {\n return false;\n }\n if (jsonEqual(covered, covering)) {\n return true;\n }\n\n if (covered.type === 'or') {\n return covered.conditions.every(c => conditionImplies(c, covering));\n }\n if (covering.type === 'or') {\n return covering.conditions.some(c => conditionImplies(covered, c));\n }\n if (covering.type === 'and') {\n return covering.conditions.every(c => conditionImplies(covered, c));\n }\n if (covered.type === 'and') {\n return covered.conditions.some(c => conditionImplies(c, covering));\n }\n if (covered.type === 'simple' && covering.type === 'simple') {\n return simpleConditionImplies(covered, covering);\n }\n if (\n covered.type === 'correlatedSubquery' &&\n covering.type === 'correlatedSubquery'\n ) {\n return correlatedConditionImplies(covered, covering);\n }\n return false;\n}\n\nfunction correlatedConditionImplies(\n covered: CorrelatedSubqueryCondition,\n covering: CorrelatedSubqueryCondition,\n): boolean {\n if (\n covered.op !== covering.op ||\n covered.scalar !== covering.scalar ||\n !sameRelatedEdge(covered.related, covering.related)\n ) {\n return false;\n }\n\n if (covered.op === 'EXISTS') {\n return astCoveredBy(\n covered.related.subquery as NormalizedAST,\n covering.related.subquery as NormalizedAST,\n );\n }\n\n return astCoveredBy(\n covering.related.subquery as NormalizedAST,\n covered.related.subquery as NormalizedAST,\n );\n}\n\nfunction sameRelatedEdge(\n a: CorrelatedSubquery,\n b: CorrelatedSubquery,\n): boolean {\n return (\n jsonEqual(a.correlation, b.correlation) &&\n a.hidden === b.hidden &&\n a.system === b.system &&\n a.subquery.alias === b.subquery.alias\n );\n}\n\nfunction simpleConditionImplies(\n covered: SimpleCondition,\n covering: SimpleCondition,\n): boolean {\n const coveredParts = columnLiteralParts(covered);\n const coveringParts = columnLiteralParts(covering);\n if (!coveredParts || !coveringParts) {\n return false;\n }\n if (coveredParts.column !== coveringParts.column) {\n return false;\n }\n\n const {op: coveredOp, value: coveredValue} = coveredParts;\n const {op: coveringOp, value: coveringValue} = coveringParts;\n\n if (isEqualityOp(coveredOp) && isNonNullScalarLiteralValue(coveredValue)) {\n return equalityImplies(coveredValue, coveringOp, coveringValue);\n }\n\n if (\n coveredOp === 'IN' &&\n coveringOp === 'IN' &&\n Array.isArray(coveredValue) &&\n Array.isArray(coveringValue)\n ) {\n return coveredValue.every(v => literalArrayIncludes(coveringValue, v));\n }\n\n if (isNumericOrderOp(coveredOp) && isNumericOrderOp(coveringOp)) {\n return orderConditionImplies(\n coveredOp,\n coveredValue,\n coveringOp,\n coveringValue,\n );\n }\n\n return false;\n}\n\nfunction equalityImplies(\n value: NonNullScalarLiteralValue,\n coveringOp: SimpleCondition['op'],\n coveringValue: LiteralValue,\n): boolean {\n switch (coveringOp) {\n case '=':\n case 'IS':\n return jsonEqual(value, coveringValue);\n case '!=':\n case 'IS NOT':\n return !jsonEqual(value, coveringValue);\n case 'IN':\n return (\n Array.isArray(coveringValue) &&\n literalArrayIncludes(coveringValue, value)\n );\n case '<':\n return (\n typeof value === 'number' &&\n typeof coveringValue === 'number' &&\n value < coveringValue\n );\n case '<=':\n return (\n typeof value === 'number' &&\n typeof coveringValue === 'number' &&\n value <= coveringValue\n );\n case '>':\n return (\n typeof value === 'number' &&\n typeof coveringValue === 'number' &&\n value > coveringValue\n );\n case '>=':\n return (\n typeof value === 'number' &&\n typeof coveringValue === 'number' &&\n value >= coveringValue\n );\n case 'NOT IN':\n case 'LIKE':\n case 'NOT LIKE':\n case 'ILIKE':\n case 'NOT ILIKE':\n return false;\n }\n}\n\nfunction orderConditionImplies(\n coveredOp: '<' | '>' | '<=' | '>=',\n coveredValue: LiteralValue,\n coveringOp: '<' | '>' | '<=' | '>=',\n coveringValue: LiteralValue,\n): boolean {\n if (typeof coveredValue !== 'number' || typeof coveringValue !== 'number') {\n return false;\n }\n\n switch (coveredOp) {\n case '>':\n return (\n (coveringOp === '>' && coveredValue >= coveringValue) ||\n (coveringOp === '>=' && coveredValue >= coveringValue)\n );\n case '>=':\n return (\n (coveringOp === '>' && coveredValue > coveringValue) ||\n (coveringOp === '>=' && coveredValue >= coveringValue)\n );\n case '<':\n return (\n (coveringOp === '<' && coveredValue <= coveringValue) ||\n (coveringOp === '<=' && coveredValue <= coveringValue)\n );\n case '<=':\n return (\n (coveringOp === '<' && coveredValue < coveringValue) ||\n (coveringOp === '<=' && coveredValue <= coveringValue)\n );\n }\n}\n\nfunction columnLiteralParts(condition: SimpleCondition):\n | {\n readonly column: string;\n readonly op: SimpleCondition['op'];\n readonly value: LiteralValue;\n }\n | undefined {\n if (condition.left.type !== 'column' || condition.right.type !== 'literal') {\n return undefined;\n }\n return {\n column: condition.left.name,\n op: condition.op,\n value: condition.right.value,\n };\n}\n\nfunction isEqualityOp(op: SimpleCondition['op']): op is '=' | 'IS' {\n return op === '=' || op === 'IS';\n}\n\nfunction isNumericOrderOp(\n op: SimpleCondition['op'],\n): op is '<' | '>' | '<=' | '>=' {\n return op === '<' || op === '>' || op === '<=' || op === '>=';\n}\n\nfunction isNonNullScalarLiteralValue(\n value: LiteralValue,\n): value is NonNullScalarLiteralValue {\n return value !== null && !Array.isArray(value);\n}\n\nfunction literalArrayIncludes(\n values: ReadonlyArray<string | number | boolean>,\n value: string | number | boolean | null,\n): boolean {\n return values.some(v => jsonEqual(v, value));\n}\n\nfunction jsonEqual(a: unknown, b: unknown): boolean {\n return deepEqual(\n a as ReadonlyJSONValue | undefined,\n b as ReadonlyJSONValue | undefined,\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"query-covering.js","names":[],"sources":["../../../../../../zero-cache/src/services/view-syncer/query-covering.ts"],"sourcesContent":["import {\n deepEqual,\n type ReadonlyJSONValue,\n} from '../../../../shared/src/json.ts';\nimport {getOrInsertComputed, newMap} from '../../../../shared/src/map.ts';\nimport {\n normalizeAST,\n type AST,\n type Condition,\n type CorrelatedSubquery,\n type CorrelatedSubqueryCondition,\n type LiteralValue,\n type NormalizedAST,\n type SimpleCondition,\n} from '../../../../zero-protocol/src/ast.ts';\n\nexport type RunningQuery = {\n readonly transformedAst: AST;\n readonly transformationHash: string;\n readonly queryName?: string | undefined;\n};\n\nexport type CoveringQuery = {\n readonly queryID: string;\n readonly transformationHash: string;\n readonly queryName?: string | undefined;\n};\n\ntype NonNullScalarLiteralValue = string | number | boolean;\ntype IndexedRunningQuery = RunningQuery & {\n readonly normalizedAst: NormalizedAST;\n};\n\n/**\n * Returns true when every row that can be produced by `covered` is also\n * produced by `covering`.\n *\n * This is intentionally conservative: unsupported cases return false rather\n * than guessing. It is used for shadow logging only.\n */\nexport function isQueryCoveredBy(covered: AST, covering: AST): boolean {\n return astCoveredBy(normalizeAST(covered), normalizeAST(covering));\n}\n\nexport function findCoveringQuery(\n coveredQueryID: string,\n coveredAst: AST,\n runningQueries: ReadonlyMap<string, RunningQuery>,\n): CoveringQuery | undefined {\n return new QueryCoveringIndex(runningQueries).findCoveringQuery(\n coveredQueryID,\n coveredAst,\n );\n}\n\nexport class QueryCoveringIndex {\n readonly #byRoot = new Map<string, Map<string, IndexedRunningQuery>>();\n readonly #queryIDToRoot = new Map<string, string>();\n\n constructor(runningQueries?: ReadonlyMap<string, RunningQuery>) {\n if (runningQueries) {\n for (const [queryID, query] of runningQueries) {\n this.add(queryID, query);\n }\n }\n }\n\n add(queryID: string, query: RunningQuery): void {\n this.remove(queryID);\n\n const normalizedAst = normalizeAST(query.transformedAst);\n const root = rootKey(normalizedAst);\n getOrInsertComputed(this.#byRoot, root, newMap).set(queryID, {\n ...query,\n normalizedAst,\n });\n this.#queryIDToRoot.set(queryID, root);\n }\n\n remove(queryID: string): void {\n const root = this.#queryIDToRoot.get(queryID);\n if (root === undefined) {\n return;\n }\n\n this.#queryIDToRoot.delete(queryID);\n const queries = this.#byRoot.get(root);\n if (!queries) {\n return;\n }\n\n queries.delete(queryID);\n if (queries.size === 0) {\n this.#byRoot.delete(root);\n }\n }\n\n findCoveringQuery(\n coveredQueryID: string,\n coveredAst: AST,\n ): CoveringQuery | undefined {\n const normalizedCoveredAst = normalizeAST(coveredAst);\n const queries = this.#byRoot.get(rootKey(normalizedCoveredAst));\n if (!queries) {\n return undefined;\n }\n\n for (const [queryID, query] of queries) {\n if (queryID === coveredQueryID) {\n continue;\n }\n if (astCoveredBy(normalizedCoveredAst, query.normalizedAst)) {\n return {\n queryID,\n transformationHash: query.transformationHash,\n ...(query.queryName !== undefined && {queryName: query.queryName}),\n };\n }\n }\n return undefined;\n }\n}\n\nfunction rootKey(ast: NormalizedAST): string {\n return JSON.stringify([ast.schema, ast.table, ast.alias]);\n}\n\nfunction astCoveredBy(\n covered: NormalizedAST,\n covering: NormalizedAST,\n): boolean {\n return (\n covered.schema === covering.schema &&\n covered.table === covering.table &&\n covered.alias === covering.alias &&\n conditionImplies(covered.where, covering.where) &&\n relatedCoveredBy(covered.related, covering.related) &&\n boundsCoveredBy(covered, covering)\n );\n}\n\nfunction boundsCoveredBy(\n covered: NormalizedAST,\n covering: NormalizedAST,\n): boolean {\n if (covering.limit === undefined) {\n if (covering.start === undefined) {\n return true;\n }\n return (\n jsonEqual(covered.start, covering.start) &&\n jsonEqual(covered.orderBy, covering.orderBy)\n );\n }\n\n if (covered.limit === undefined || covering.limit < covered.limit) {\n return false;\n }\n\n // A limited broader query does not necessarily contain a limited narrower\n // query. The ordered input to the limit must be equivalent.\n return (\n conditionEquivalent(covered.where, covering.where) &&\n jsonEqual(covered.start, covering.start) &&\n jsonEqual(covered.orderBy, covering.orderBy)\n );\n}\n\nfunction relatedCoveredBy(\n covered: readonly CorrelatedSubquery[] | undefined,\n covering: readonly CorrelatedSubquery[] | undefined,\n): boolean {\n if (covered === undefined || covered.length === 0) {\n return true;\n }\n if (covering === undefined) {\n return false;\n }\n return covered.every(coveredRelated =>\n covering.some(\n coveringRelated =>\n sameRelatedEdge(coveredRelated, coveringRelated) &&\n astCoveredBy(\n coveredRelated.subquery as NormalizedAST,\n coveringRelated.subquery as NormalizedAST,\n ),\n ),\n );\n}\n\nfunction conditionEquivalent(\n a: Condition | undefined,\n b: Condition | undefined,\n): boolean {\n return conditionImplies(a, b) && conditionImplies(b, a);\n}\n\nfunction conditionImplies(\n covered: Condition | undefined,\n covering: Condition | undefined,\n): boolean {\n if (covering === undefined) {\n return true;\n }\n if (covered === undefined) {\n return false;\n }\n if (jsonEqual(covered, covering)) {\n return true;\n }\n\n if (covered.type === 'or') {\n return covered.conditions.every(c => conditionImplies(c, covering));\n }\n if (covering.type === 'or') {\n return covering.conditions.some(c => conditionImplies(covered, c));\n }\n if (covering.type === 'and') {\n return covering.conditions.every(c => conditionImplies(covered, c));\n }\n if (covered.type === 'and') {\n return covered.conditions.some(c => conditionImplies(c, covering));\n }\n if (covered.type === 'simple' && covering.type === 'simple') {\n return simpleConditionImplies(covered, covering);\n }\n if (\n covered.type === 'correlatedSubquery' &&\n covering.type === 'correlatedSubquery'\n ) {\n return correlatedConditionImplies(covered, covering);\n }\n return false;\n}\n\nfunction correlatedConditionImplies(\n covered: CorrelatedSubqueryCondition,\n covering: CorrelatedSubqueryCondition,\n): boolean {\n if (\n covered.op !== covering.op ||\n covered.scalar !== covering.scalar ||\n !sameRelatedEdge(covered.related, covering.related)\n ) {\n return false;\n }\n\n if (covered.op === 'EXISTS') {\n return astCoveredBy(\n covered.related.subquery as NormalizedAST,\n covering.related.subquery as NormalizedAST,\n );\n }\n\n return astCoveredBy(\n covering.related.subquery as NormalizedAST,\n covered.related.subquery as NormalizedAST,\n );\n}\n\nfunction sameRelatedEdge(\n a: CorrelatedSubquery,\n b: CorrelatedSubquery,\n): boolean {\n return (\n jsonEqual(a.correlation, b.correlation) &&\n a.hidden === b.hidden &&\n a.system === b.system &&\n a.subquery.alias === b.subquery.alias\n );\n}\n\nfunction simpleConditionImplies(\n covered: SimpleCondition,\n covering: SimpleCondition,\n): boolean {\n const coveredParts = columnLiteralParts(covered);\n const coveringParts = columnLiteralParts(covering);\n if (!coveredParts || !coveringParts) {\n return false;\n }\n if (coveredParts.column !== coveringParts.column) {\n return false;\n }\n\n const {op: coveredOp, value: coveredValue} = coveredParts;\n const {op: coveringOp, value: coveringValue} = coveringParts;\n\n if (isEqualityOp(coveredOp) && isNonNullScalarLiteralValue(coveredValue)) {\n return equalityImplies(coveredValue, coveringOp, coveringValue);\n }\n\n if (\n coveredOp === 'IN' &&\n coveringOp === 'IN' &&\n Array.isArray(coveredValue) &&\n Array.isArray(coveringValue)\n ) {\n return coveredValue.every(v => literalArrayIncludes(coveringValue, v));\n }\n\n if (isNumericOrderOp(coveredOp) && isNumericOrderOp(coveringOp)) {\n return orderConditionImplies(\n coveredOp,\n coveredValue,\n coveringOp,\n coveringValue,\n );\n }\n\n return false;\n}\n\nfunction equalityImplies(\n value: NonNullScalarLiteralValue,\n coveringOp: SimpleCondition['op'],\n coveringValue: LiteralValue,\n): boolean {\n switch (coveringOp) {\n case '=':\n case 'IS':\n return jsonEqual(value, coveringValue);\n case '!=':\n case 'IS NOT':\n return !jsonEqual(value, coveringValue);\n case 'IN':\n return (\n Array.isArray(coveringValue) &&\n literalArrayIncludes(coveringValue, value)\n );\n case '<':\n return (\n typeof value === 'number' &&\n typeof coveringValue === 'number' &&\n value < coveringValue\n );\n case '<=':\n return (\n typeof value === 'number' &&\n typeof coveringValue === 'number' &&\n value <= coveringValue\n );\n case '>':\n return (\n typeof value === 'number' &&\n typeof coveringValue === 'number' &&\n value > coveringValue\n );\n case '>=':\n return (\n typeof value === 'number' &&\n typeof coveringValue === 'number' &&\n value >= coveringValue\n );\n case 'NOT IN':\n case 'LIKE':\n case 'NOT LIKE':\n case 'ILIKE':\n case 'NOT ILIKE':\n return false;\n }\n}\n\nfunction orderConditionImplies(\n coveredOp: '<' | '>' | '<=' | '>=',\n coveredValue: LiteralValue,\n coveringOp: '<' | '>' | '<=' | '>=',\n coveringValue: LiteralValue,\n): boolean {\n if (typeof coveredValue !== 'number' || typeof coveringValue !== 'number') {\n return false;\n }\n\n switch (coveredOp) {\n case '>':\n return (\n (coveringOp === '>' && coveredValue >= coveringValue) ||\n (coveringOp === '>=' && coveredValue >= coveringValue)\n );\n case '>=':\n return (\n (coveringOp === '>' && coveredValue > coveringValue) ||\n (coveringOp === '>=' && coveredValue >= coveringValue)\n );\n case '<':\n return (\n (coveringOp === '<' && coveredValue <= coveringValue) ||\n (coveringOp === '<=' && coveredValue <= coveringValue)\n );\n case '<=':\n return (\n (coveringOp === '<' && coveredValue < coveringValue) ||\n (coveringOp === '<=' && coveredValue <= coveringValue)\n );\n }\n}\n\nfunction columnLiteralParts(condition: SimpleCondition):\n | {\n readonly column: string;\n readonly op: SimpleCondition['op'];\n readonly value: LiteralValue;\n }\n | undefined {\n if (condition.left.type !== 'column' || condition.right.type !== 'literal') {\n return undefined;\n }\n return {\n column: condition.left.name,\n op: condition.op,\n value: condition.right.value,\n };\n}\n\nfunction isEqualityOp(op: SimpleCondition['op']): op is '=' | 'IS' {\n return op === '=' || op === 'IS';\n}\n\nfunction isNumericOrderOp(\n op: SimpleCondition['op'],\n): op is '<' | '>' | '<=' | '>=' {\n return op === '<' || op === '>' || op === '<=' || op === '>=';\n}\n\nfunction isNonNullScalarLiteralValue(\n value: LiteralValue,\n): value is NonNullScalarLiteralValue {\n return value !== null && !Array.isArray(value);\n}\n\nfunction literalArrayIncludes(\n values: ReadonlyArray<string | number | boolean>,\n value: string | number | boolean | null,\n): boolean {\n return values.some(v => jsonEqual(v, value));\n}\n\nfunction jsonEqual(a: unknown, b: unknown): boolean {\n return deepEqual(\n a as ReadonlyJSONValue | undefined,\n b as ReadonlyJSONValue | undefined,\n );\n}\n"],"mappings":";;;;AAuDA,IAAa,qBAAb,MAAgC;CAC9B,0BAAmB,IAAI,IAA8C;CACrE,iCAA0B,IAAI,IAAoB;CAElD,YAAY,gBAAoD;EAC9D,IAAI,gBACF,KAAK,MAAM,CAAC,SAAS,UAAU,gBAC7B,KAAK,IAAI,SAAS,KAAK;CAG7B;CAEA,IAAI,SAAiB,OAA2B;EAC9C,KAAK,OAAO,OAAO;EAEnB,MAAM,gBAAgB,aAAa,MAAM,cAAc;EACvD,MAAM,OAAO,QAAQ,aAAa;EAClC,oBAAoB,KAAK,SAAS,MAAM,MAAM,CAAC,CAAC,IAAI,SAAS;GAC3D,GAAG;GACH;EACF,CAAC;EACD,KAAK,eAAe,IAAI,SAAS,IAAI;CACvC;CAEA,OAAO,SAAuB;EAC5B,MAAM,OAAO,KAAK,eAAe,IAAI,OAAO;EAC5C,IAAI,SAAS,KAAA,GACX;EAGF,KAAK,eAAe,OAAO,OAAO;EAClC,MAAM,UAAU,KAAK,QAAQ,IAAI,IAAI;EACrC,IAAI,CAAC,SACH;EAGF,QAAQ,OAAO,OAAO;EACtB,IAAI,QAAQ,SAAS,GACnB,KAAK,QAAQ,OAAO,IAAI;CAE5B;CAEA,kBACE,gBACA,YAC2B;EAC3B,MAAM,uBAAuB,aAAa,UAAU;EACpD,MAAM,UAAU,KAAK,QAAQ,IAAI,QAAQ,oBAAoB,CAAC;EAC9D,IAAI,CAAC,SACH;EAGF,KAAK,MAAM,CAAC,SAAS,UAAU,SAAS;GACtC,IAAI,YAAY,gBACd;GAEF,IAAI,aAAa,sBAAsB,MAAM,aAAa,GACxD,OAAO;IACL;IACA,oBAAoB,MAAM;IAC1B,GAAI,MAAM,cAAc,KAAA,KAAa,EAAC,WAAW,MAAM,UAAS;GAClE;EAEJ;CAEF;AACF;AAEA,SAAS,QAAQ,KAA4B;CAC3C,OAAO,KAAK,UAAU;EAAC,IAAI;EAAQ,IAAI;EAAO,IAAI;CAAK,CAAC;AAC1D;AAEA,SAAS,aACP,SACA,UACS;CACT,OACE,QAAQ,WAAW,SAAS,UAC5B,QAAQ,UAAU,SAAS,SAC3B,QAAQ,UAAU,SAAS,SAC3B,iBAAiB,QAAQ,OAAO,SAAS,KAAK,KAC9C,iBAAiB,QAAQ,SAAS,SAAS,OAAO,KAClD,gBAAgB,SAAS,QAAQ;AAErC;AAEA,SAAS,gBACP,SACA,UACS;CACT,IAAI,SAAS,UAAU,KAAA,GAAW;EAChC,IAAI,SAAS,UAAU,KAAA,GACrB,OAAO;EAET,OACE,UAAU,QAAQ,OAAO,SAAS,KAAK,KACvC,UAAU,QAAQ,SAAS,SAAS,OAAO;CAE/C;CAEA,IAAI,QAAQ,UAAU,KAAA,KAAa,SAAS,QAAQ,QAAQ,OAC1D,OAAO;CAKT,OACE,oBAAoB,QAAQ,OAAO,SAAS,KAAK,KACjD,UAAU,QAAQ,OAAO,SAAS,KAAK,KACvC,UAAU,QAAQ,SAAS,SAAS,OAAO;AAE/C;AAEA,SAAS,iBACP,SACA,UACS;CACT,IAAI,YAAY,KAAA,KAAa,QAAQ,WAAW,GAC9C,OAAO;CAET,IAAI,aAAa,KAAA,GACf,OAAO;CAET,OAAO,QAAQ,OAAM,mBACnB,SAAS,MACP,oBACE,gBAAgB,gBAAgB,eAAe,KAC/C,aACE,eAAe,UACf,gBAAgB,QAClB,CACJ,CACF;AACF;AAEA,SAAS,oBACP,GACA,GACS;CACT,OAAO,iBAAiB,GAAG,CAAC,KAAK,iBAAiB,GAAG,CAAC;AACxD;AAEA,SAAS,iBACP,SACA,UACS;CACT,IAAI,aAAa,KAAA,GACf,OAAO;CAET,IAAI,YAAY,KAAA,GACd,OAAO;CAET,IAAI,UAAU,SAAS,QAAQ,GAC7B,OAAO;CAGT,IAAI,QAAQ,SAAS,MACnB,OAAO,QAAQ,WAAW,OAAM,MAAK,iBAAiB,GAAG,QAAQ,CAAC;CAEpE,IAAI,SAAS,SAAS,MACpB,OAAO,SAAS,WAAW,MAAK,MAAK,iBAAiB,SAAS,CAAC,CAAC;CAEnE,IAAI,SAAS,SAAS,OACpB,OAAO,SAAS,WAAW,OAAM,MAAK,iBAAiB,SAAS,CAAC,CAAC;CAEpE,IAAI,QAAQ,SAAS,OACnB,OAAO,QAAQ,WAAW,MAAK,MAAK,iBAAiB,GAAG,QAAQ,CAAC;CAEnE,IAAI,QAAQ,SAAS,YAAY,SAAS,SAAS,UACjD,OAAO,uBAAuB,SAAS,QAAQ;CAEjD,IACE,QAAQ,SAAS,wBACjB,SAAS,SAAS,sBAElB,OAAO,2BAA2B,SAAS,QAAQ;CAErD,OAAO;AACT;AAEA,SAAS,2BACP,SACA,UACS;CACT,IACE,QAAQ,OAAO,SAAS,MACxB,QAAQ,WAAW,SAAS,UAC5B,CAAC,gBAAgB,QAAQ,SAAS,SAAS,OAAO,GAElD,OAAO;CAGT,IAAI,QAAQ,OAAO,UACjB,OAAO,aACL,QAAQ,QAAQ,UAChB,SAAS,QAAQ,QACnB;CAGF,OAAO,aACL,SAAS,QAAQ,UACjB,QAAQ,QAAQ,QAClB;AACF;AAEA,SAAS,gBACP,GACA,GACS;CACT,OACE,UAAU,EAAE,aAAa,EAAE,WAAW,KACtC,EAAE,WAAW,EAAE,UACf,EAAE,WAAW,EAAE,UACf,EAAE,SAAS,UAAU,EAAE,SAAS;AAEpC;AAEA,SAAS,uBACP,SACA,UACS;CACT,MAAM,eAAe,mBAAmB,OAAO;CAC/C,MAAM,gBAAgB,mBAAmB,QAAQ;CACjD,IAAI,CAAC,gBAAgB,CAAC,eACpB,OAAO;CAET,IAAI,aAAa,WAAW,cAAc,QACxC,OAAO;CAGT,MAAM,EAAC,IAAI,WAAW,OAAO,iBAAgB;CAC7C,MAAM,EAAC,IAAI,YAAY,OAAO,kBAAiB;CAE/C,IAAI,aAAa,SAAS,KAAK,4BAA4B,YAAY,GACrE,OAAO,gBAAgB,cAAc,YAAY,aAAa;CAGhE,IACE,cAAc,QACd,eAAe,QACf,MAAM,QAAQ,YAAY,KAC1B,MAAM,QAAQ,aAAa,GAE3B,OAAO,aAAa,OAAM,MAAK,qBAAqB,eAAe,CAAC,CAAC;CAGvE,IAAI,iBAAiB,SAAS,KAAK,iBAAiB,UAAU,GAC5D,OAAO,sBACL,WACA,cACA,YACA,aACF;CAGF,OAAO;AACT;AAEA,SAAS,gBACP,OACA,YACA,eACS;CACT,QAAQ,YAAR;EACE,KAAK;EACL,KAAK,MACH,OAAO,UAAU,OAAO,aAAa;EACvC,KAAK;EACL,KAAK,UACH,OAAO,CAAC,UAAU,OAAO,aAAa;EACxC,KAAK,MACH,OACE,MAAM,QAAQ,aAAa,KAC3B,qBAAqB,eAAe,KAAK;EAE7C,KAAK,KACH,OACE,OAAO,UAAU,YACjB,OAAO,kBAAkB,YACzB,QAAQ;EAEZ,KAAK,MACH,OACE,OAAO,UAAU,YACjB,OAAO,kBAAkB,YACzB,SAAS;EAEb,KAAK,KACH,OACE,OAAO,UAAU,YACjB,OAAO,kBAAkB,YACzB,QAAQ;EAEZ,KAAK,MACH,OACE,OAAO,UAAU,YACjB,OAAO,kBAAkB,YACzB,SAAS;EAEb,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,aACH,OAAO;CACX;AACF;AAEA,SAAS,sBACP,WACA,cACA,YACA,eACS;CACT,IAAI,OAAO,iBAAiB,YAAY,OAAO,kBAAkB,UAC/D,OAAO;CAGT,QAAQ,WAAR;EACE,KAAK,KACH,OACG,eAAe,OAAO,gBAAgB,iBACtC,eAAe,QAAQ,gBAAgB;EAE5C,KAAK,MACH,OACG,eAAe,OAAO,eAAe,iBACrC,eAAe,QAAQ,gBAAgB;EAE5C,KAAK,KACH,OACG,eAAe,OAAO,gBAAgB,iBACtC,eAAe,QAAQ,gBAAgB;EAE5C,KAAK,MACH,OACG,eAAe,OAAO,eAAe,iBACrC,eAAe,QAAQ,gBAAgB;CAE9C;AACF;AAEA,SAAS,mBAAmB,WAMd;CACZ,IAAI,UAAU,KAAK,SAAS,YAAY,UAAU,MAAM,SAAS,WAC/D;CAEF,OAAO;EACL,QAAQ,UAAU,KAAK;EACvB,IAAI,UAAU;EACd,OAAO,UAAU,MAAM;CACzB;AACF;AAEA,SAAS,aAAa,IAA6C;CACjE,OAAO,OAAO,OAAO,OAAO;AAC9B;AAEA,SAAS,iBACP,IAC+B;CAC/B,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,QAAQ,OAAO;AAC3D;AAEA,SAAS,4BACP,OACoC;CACpC,OAAO,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC/C;AAEA,SAAS,qBACP,QACA,OACS;CACT,OAAO,OAAO,MAAK,MAAK,UAAU,GAAG,KAAK,CAAC;AAC7C;AAEA,SAAS,UAAU,GAAY,GAAqB;CAClD,OAAO,UACL,GACA,CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"query-shape.js","names":[],"sources":["../../../../../../zero-cache/src/services/view-syncer/query-shape.ts"],"sourcesContent":["// @circular-dep-ignore\nimport {astToZQL} from '../../../../ast-to-zql/src/ast-to-zql.ts';\nimport {h64} from '../../../../shared/src/hash.ts';\nimport type {AST} from '../../../../zero-protocol/src/ast.ts';\n\n/**\n * A query with its literal values redacted, for logging.\n */\nexport type QueryShape = {\n /**\n * The query as ZQL with every literal (and the `start` row) rendered as\n * `?`, so it can be logged without leaking row data, query arguments, or\n * auth data substituted in by permissions.\n */\n readonly zql: string;\n\n /**\n * A hash of {@link zql}. Queries that differ only in their literal values,\n * e.g. the same named query called with different arguments, share a hash,\n * which makes it a key for grouping and throttling logs.\n */\n readonly hash: string;\n};\n\n/**\n * Never throws: it is computed for every hydration, and a query that cannot be\n * rendered as ZQL (e.g. a subquery without an alias) must still hydrate. Such\n * a query is shaped by its AST with the literal values redacted instead.\n */\nexport function queryShape(ast: AST): QueryShape {\n let zql: string;\n try {\n zql = ast.table + astToZQL(ast, {redactLiterals: true});\n } catch {\n zql = JSON.stringify(ast, redactLiterals);\n }\n return {zql, hash: h64(zql).toString(36)};\n}\n\nfunction redactLiterals(this: unknown, key: string, value: unknown) {\n if (\n (key === 'value' && (this as {type?: unknown}).type === 'literal') ||\n key === 'start'\n ) {\n return '?';\n }\n return value;\n}\n"],"mappings":";;;;;;;;AA6BA,SAAgB,WAAW,KAAsB;CAC/C,IAAI;CACJ,IAAI;EACF,MAAM,IAAI,QAAQ,SAAS,KAAK,EAAC,gBAAgB,KAAI,CAAC;CACxD,QAAQ;EACN,MAAM,KAAK,UAAU,KAAK,cAAc;CAC1C;CACA,OAAO;EAAC;EAAK,MAAM,IAAI,GAAG,EAAE,SAAS,EAAE;CAAC;AAC1C;AAEA,SAAS,eAA8B,KAAa,OAAgB;CAClE,IACG,QAAQ,WAAY,KAA0B,SAAS,aACxD,QAAQ,SAER,OAAO;CAET,OAAO;AACT"}
1
+ {"version":3,"file":"query-shape.js","names":[],"sources":["../../../../../../zero-cache/src/services/view-syncer/query-shape.ts"],"sourcesContent":["// @circular-dep-ignore\nimport {astToZQL} from '../../../../ast-to-zql/src/ast-to-zql.ts';\nimport {h64} from '../../../../shared/src/hash.ts';\nimport type {AST} from '../../../../zero-protocol/src/ast.ts';\n\n/**\n * A query with its literal values redacted, for logging.\n */\nexport type QueryShape = {\n /**\n * The query as ZQL with every literal (and the `start` row) rendered as\n * `?`, so it can be logged without leaking row data, query arguments, or\n * auth data substituted in by permissions.\n */\n readonly zql: string;\n\n /**\n * A hash of {@link zql}. Queries that differ only in their literal values,\n * e.g. the same named query called with different arguments, share a hash,\n * which makes it a key for grouping and throttling logs.\n */\n readonly hash: string;\n};\n\n/**\n * Never throws: it is computed for every hydration, and a query that cannot be\n * rendered as ZQL (e.g. a subquery without an alias) must still hydrate. Such\n * a query is shaped by its AST with the literal values redacted instead.\n */\nexport function queryShape(ast: AST): QueryShape {\n let zql: string;\n try {\n zql = ast.table + astToZQL(ast, {redactLiterals: true});\n } catch {\n zql = JSON.stringify(ast, redactLiterals);\n }\n return {zql, hash: h64(zql).toString(36)};\n}\n\nfunction redactLiterals(this: unknown, key: string, value: unknown) {\n if (\n (key === 'value' && (this as {type?: unknown}).type === 'literal') ||\n key === 'start'\n ) {\n return '?';\n }\n return value;\n}\n"],"mappings":";;;;;;;;AA6BA,SAAgB,WAAW,KAAsB;CAC/C,IAAI;CACJ,IAAI;EACF,MAAM,IAAI,QAAQ,SAAS,KAAK,EAAC,gBAAgB,KAAI,CAAC;CACxD,QAAQ;EACN,MAAM,KAAK,UAAU,KAAK,cAAc;CAC1C;CACA,OAAO;EAAC;EAAK,MAAM,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE;CAAC;AAC1C;AAEA,SAAS,eAA8B,KAAa,OAAgB;CAClE,IACG,QAAQ,WAAY,KAA0B,SAAS,aACxD,QAAQ,SAER,OAAO;CAET,OAAO;AACT"}
@@ -79,7 +79,7 @@ var ShapeStats = class extends Totals {
79
79
  };
80
80
  /** A fixed-size linear-counting sketch for shapes beyond the tracking limit. */
81
81
  var OverflowShapeCounter = class {
82
- #buckets = new Uint8Array(4096);
82
+ #buckets = /* @__PURE__ */ new Uint8Array(4096);
83
83
  #occupied = 0;
84
84
  add(key) {
85
85
  let hash = 2166136261;
@@ -1 +1 @@
1
- {"version":3,"file":"query-stats.js","names":["#buckets","#occupied","#maxShapes","#maxReported","#now","#takeIndexUsage","#intervalStart","#totals","#stats","#shapes","#overflowShapes"],"sources":["../../../../../../zero-cache/src/services/view-syncer/query-stats.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../../shared/src/asserts.ts';\nimport type {PlanWarning} from '../../../../zql/src/planner/planner-warnings.ts';\nimport {takeIndexUsage} from '../../../../zqlite/src/index-usage.ts';\nimport type {QueryShape} from './query-shape.ts';\n\n/** The query that work is accounted to. */\nexport type QueryIdentity = {\n readonly queryName?: string | undefined;\n readonly shape: QueryShape;\n};\n\nexport type HydrationOutcome = 'finished' | 'aborted' | 'failed';\n\nexport type HydrationRecord = {\n readonly outcome: HydrationOutcome;\n readonly timeMs: number;\n /** Only for a finished hydration. */\n readonly rowCount?: number | undefined;\n /** Only for a finished hydration. */\n readonly rowsRead?: number | undefined;\n readonly planWarnings?: readonly PlanWarning[] | undefined;\n};\n\nexport type AdvanceRecord = {\n /** The time spent processing the pushes to the query. */\n readonly timeMs: number;\n /** The number of changes pushed to the query. */\n readonly changes: number;\n /** Whether the advancement timed out and reset the pipelines. */\n readonly timedOut: boolean;\n};\n\n/** Why an advancement stopped pushing to a query and hydrated it again. */\nexport type AbandonReason = 'estimate' | 'read' | 'cutoff';\n\nexport type QueryStatsOptions = {\n /**\n * The most query shapes tracked per interval. Work on shapes beyond this is\n * accounted to the interval's totals only, which bounds memory when the\n * shape space is unbounded (e.g. dynamically built queries).\n */\n readonly maxShapes?: number | undefined;\n\n /**\n * The most query shapes logged per interval, those that took the most\n * time. The rest are included in the interval's totals.\n */\n readonly maxReported?: number | undefined;\n\n readonly now?: (() => number) | undefined;\n\n /** Returns the index uses since the last call. See `recordIndexUsage()`. */\n readonly takeIndexUsage?: (() => Map<string, number>) | undefined;\n};\n\nclass Distribution {\n count = 0;\n sumMs = 0;\n minMs = Infinity;\n maxMs = 0;\n\n add(ms: number) {\n this.count++;\n this.sumMs += ms;\n this.minMs = Math.min(this.minMs, ms);\n this.maxMs = Math.max(this.maxMs, ms);\n }\n\n toJSON() {\n return {\n count: this.count,\n sumMs: round(this.sumMs),\n minMs: round(this.count === 0 ? 0 : this.minMs),\n maxMs: round(this.maxMs),\n };\n }\n}\n\nclass Totals {\n readonly hydrations = new Distribution();\n hydrationsAborted = 0;\n hydrationsFailed = 0;\n hydrationRowCount = 0;\n hydrationRowsRead = 0;\n readonly advances = new Distribution();\n advanceChanges = 0;\n advanceTimeouts = 0;\n /**\n * The advancements that hydrated the query again instead of pushing (all\n * of) their changes to it, by why. The hydrations are in `hydrations`.\n */\n readonly advancesAbandoned: Record<AbandonReason, number> = {\n estimate: 0,\n read: 0,\n cutoff: 0,\n };\n\n get timeMs() {\n return this.hydrations.sumMs + this.advances.sumMs;\n }\n\n addHydration({outcome, timeMs, rowCount, rowsRead}: HydrationRecord) {\n this.hydrations.add(timeMs);\n if (outcome === 'aborted') {\n this.hydrationsAborted++;\n } else if (outcome === 'failed') {\n this.hydrationsFailed++;\n }\n this.hydrationRowCount += rowCount ?? 0;\n this.hydrationRowsRead += rowsRead ?? 0;\n }\n\n addAdvance({timeMs, changes, timedOut}: AdvanceRecord) {\n this.advances.add(timeMs);\n this.advanceChanges += changes;\n if (timedOut) {\n this.advanceTimeouts++;\n }\n }\n\n toJSON() {\n return {\n timeMs: round(this.timeMs),\n hydrations: this.hydrations.toJSON(),\n hydrationsAborted: this.hydrationsAborted,\n hydrationsFailed: this.hydrationsFailed,\n hydrationRowCount: this.hydrationRowCount,\n hydrationRowsRead: this.hydrationRowsRead,\n advances: this.advances.toJSON(),\n advanceChanges: this.advanceChanges,\n advanceTimeouts: this.advanceTimeouts,\n // Only with cost-based sync, without which there are none.\n ...(Object.values(this.advancesAbandoned).some(n => n > 0) && {\n advancesAbandoned: {...this.advancesAbandoned},\n }),\n };\n }\n}\n\nclass ShapeStats extends Totals {\n readonly query: QueryIdentity;\n planWarnings: readonly PlanWarning[] | undefined;\n\n constructor(query: QueryIdentity) {\n super();\n this.query = query;\n }\n}\n\n/** A fixed-size linear-counting sketch for shapes beyond the tracking limit. */\nclass OverflowShapeCounter {\n readonly #buckets = new Uint8Array(4096);\n #occupied = 0;\n\n add(key: string): void {\n // FNV-1a gives a stable bucket without retaining the key.\n let hash = 0x811c9dc5;\n for (let i = 0; i < key.length; i++) {\n hash = Math.imul(hash ^ key.charCodeAt(i), 0x01000193);\n }\n const bucket = (hash >>> 0) % this.#buckets.length;\n if (this.#buckets[bucket] === 0) {\n this.#buckets[bucket] = 1;\n this.#occupied++;\n }\n }\n\n get estimatedCount(): number {\n const empty = this.#buckets.length - this.#occupied;\n return empty === 0\n ? this.#buckets.length\n : Math.round(\n -this.#buckets.length * Math.log(empty / this.#buckets.length),\n );\n }\n}\n\n/**\n * Aggregates the work done for each query shape (the query with its values\n * redacted, see {@link QueryShape}) on a worker, and periodically logs it,\n * like `pg_stat_statements` does for Postgres.\n *\n * Unlike the slow query logs, which are throttled and only written above a\n * threshold, every hydration and advancement is counted, so the logs can be\n * summed over any time range and across workers to rank queries by the time\n * they take.\n *\n * Each {@link flush} logs the interval since the previous one:\n *\n * - a `query-stats` event per query shape, for the {@link\n * QueryStatsOptions.maxReported} shapes that took the most time, and\n * - a `query-stats-summary` event with the totals of all shapes, including\n * those not reported individually, and the `indexUses`: how often the\n * process read each index of the replica, like `pg_stat_user_indexes`. An\n * index that is never read is absent. The workers of a single-process\n * zero-cache share these counts, which the first of them to flush reports.\n *\n * The events carry counts, sums, minimums and maximums, which compose across\n * intervals and workers.\n */\nexport class QueryStats {\n readonly #maxShapes: number;\n readonly #maxReported: number;\n readonly #now: () => number;\n readonly #takeIndexUsage: () => Map<string, number>;\n\n #shapes = new Map<string, ShapeStats>();\n #totals = new Totals();\n #overflowShapes = new OverflowShapeCounter();\n #intervalStart: number;\n\n constructor({\n maxShapes = 1000,\n maxReported = 100,\n now = Date.now,\n takeIndexUsage: takeIndexUsageFn = takeIndexUsage,\n }: QueryStatsOptions = {}) {\n assert(maxShapes > 0, 'maxShapes must be positive');\n assert(maxReported > 0, 'maxReported must be positive');\n this.#maxShapes = maxShapes;\n this.#maxReported = maxReported;\n this.#now = now;\n this.#takeIndexUsage = takeIndexUsageFn;\n this.#intervalStart = now();\n }\n\n recordHydration(query: QueryIdentity, record: HydrationRecord): void {\n this.#totals.addHydration(record);\n const stats = this.#stats(query);\n if (stats) {\n stats.addHydration(record);\n if (record.planWarnings !== undefined) {\n stats.planWarnings = record.planWarnings;\n }\n }\n }\n\n recordAdvance(query: QueryIdentity, record: AdvanceRecord): void {\n this.#totals.addAdvance(record);\n this.#stats(query)?.addAdvance(record);\n }\n\n /**\n * Records that an advancement abandoned the query's pipeline, to hydrate\n * the query again.\n */\n recordAbandoned(query: QueryIdentity, reason: AbandonReason): void {\n this.#totals.advancesAbandoned[reason]++;\n const stats = this.#stats(query);\n if (stats) {\n stats.advancesAbandoned[reason]++;\n }\n }\n\n #stats(query: QueryIdentity): ShapeStats | undefined {\n const key = `${query.queryName ?? ''}:${query.shape.hash}`;\n let stats = this.#shapes.get(key);\n if (stats === undefined) {\n if (this.#shapes.size >= this.#maxShapes) {\n this.#overflowShapes.add(key);\n return undefined;\n }\n stats = new ShapeStats({queryName: query.queryName, shape: query.shape});\n this.#shapes.set(key, stats);\n }\n return stats;\n }\n\n /**\n * Logs the stats of the interval since the previous flush, and starts a\n * new interval. Nothing is logged for an interval without any work.\n */\n flush(lc: LogContext): void {\n const now = this.#now();\n const intervalMs = now - this.#intervalStart;\n const shapes = this.#shapes;\n const totals = this.#totals;\n const overflowShapes = this.#overflowShapes.estimatedCount;\n const indexUses = this.#takeIndexUsage();\n this.#shapes = new Map();\n this.#totals = new Totals();\n this.#overflowShapes = new OverflowShapeCounter();\n this.#intervalStart = now;\n\n if (shapes.size === 0 && overflowShapes === 0 && indexUses.size === 0) {\n return;\n }\n const reported = [...shapes.values()]\n .toSorted((a, b) => b.timeMs - a.timeMs)\n .slice(0, this.#maxReported);\n for (const stats of reported) {\n const {queryName, shape} = stats.query;\n lc.info?.('query stats', {\n zeroEvent: 'query-stats',\n intervalMs,\n ...(queryName !== undefined && {queryName}),\n queryShape: shape.hash,\n ...stats.toJSON(),\n ...(stats.planWarnings !== undefined &&\n stats.planWarnings.length > 0 && {planWarnings: stats.planWarnings}),\n zql: shape.zql,\n });\n }\n lc.info?.('query stats summary', {\n zeroEvent: 'query-stats-summary',\n intervalMs,\n shapes: shapes.size + overflowShapes,\n shapesReported: reported.length,\n ...totals.toJSON(),\n ...(indexUses.size > 0 && {indexUses: Object.fromEntries(indexUses)}),\n });\n }\n\n /**\n * Flushes every `intervalMs` until the returned function is called, which\n * flushes one last time.\n */\n start(lc: LogContext, intervalMs: number): () => void {\n const timer = setInterval(() => this.flush(lc), intervalMs);\n timer.unref?.();\n return () => {\n clearInterval(timer);\n this.flush(lc);\n };\n }\n}\n\nfunction round(ms: number) {\n return Math.round(ms * 100) / 100;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"query-stats.js","names":[],"sources":["../../../../../../zero-cache/src/services/view-syncer/query-stats.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../../shared/src/asserts.ts';\nimport type {PlanWarning} from '../../../../zql/src/planner/planner-warnings.ts';\nimport {takeIndexUsage} from '../../../../zqlite/src/index-usage.ts';\nimport type {QueryShape} from './query-shape.ts';\n\n/** The query that work is accounted to. */\nexport type QueryIdentity = {\n readonly queryName?: string | undefined;\n readonly shape: QueryShape;\n};\n\ntype HydrationOutcome = 'finished' | 'aborted' | 'failed';\n\nexport type HydrationRecord = {\n readonly outcome: HydrationOutcome;\n readonly timeMs: number;\n /** Only for a finished hydration. */\n readonly rowCount?: number | undefined;\n /** Only for a finished hydration. */\n readonly rowsRead?: number | undefined;\n readonly planWarnings?: readonly PlanWarning[] | undefined;\n};\n\nexport type AdvanceRecord = {\n /** The time spent processing the pushes to the query. */\n readonly timeMs: number;\n /** The number of changes pushed to the query. */\n readonly changes: number;\n /** Whether the advancement timed out and reset the pipelines. */\n readonly timedOut: boolean;\n};\n\n/** Why an advancement stopped pushing to a query and hydrated it again. */\nexport type AbandonReason = 'estimate' | 'read' | 'cutoff';\n\nexport type QueryStatsOptions = {\n /**\n * The most query shapes tracked per interval. Work on shapes beyond this is\n * accounted to the interval's totals only, which bounds memory when the\n * shape space is unbounded (e.g. dynamically built queries).\n */\n readonly maxShapes?: number | undefined;\n\n /**\n * The most query shapes logged per interval, those that took the most\n * time. The rest are included in the interval's totals.\n */\n readonly maxReported?: number | undefined;\n\n readonly now?: (() => number) | undefined;\n\n /** Returns the index uses since the last call. See `recordIndexUsage()`. */\n readonly takeIndexUsage?: (() => Map<string, number>) | undefined;\n};\n\nclass Distribution {\n count = 0;\n sumMs = 0;\n minMs = Infinity;\n maxMs = 0;\n\n add(ms: number) {\n this.count++;\n this.sumMs += ms;\n this.minMs = Math.min(this.minMs, ms);\n this.maxMs = Math.max(this.maxMs, ms);\n }\n\n toJSON() {\n return {\n count: this.count,\n sumMs: round(this.sumMs),\n minMs: round(this.count === 0 ? 0 : this.minMs),\n maxMs: round(this.maxMs),\n };\n }\n}\n\nclass Totals {\n readonly hydrations = new Distribution();\n hydrationsAborted = 0;\n hydrationsFailed = 0;\n hydrationRowCount = 0;\n hydrationRowsRead = 0;\n readonly advances = new Distribution();\n advanceChanges = 0;\n advanceTimeouts = 0;\n /**\n * The advancements that hydrated the query again instead of pushing (all\n * of) their changes to it, by why. The hydrations are in `hydrations`.\n */\n readonly advancesAbandoned: Record<AbandonReason, number> = {\n estimate: 0,\n read: 0,\n cutoff: 0,\n };\n\n get timeMs() {\n return this.hydrations.sumMs + this.advances.sumMs;\n }\n\n addHydration({outcome, timeMs, rowCount, rowsRead}: HydrationRecord) {\n this.hydrations.add(timeMs);\n if (outcome === 'aborted') {\n this.hydrationsAborted++;\n } else if (outcome === 'failed') {\n this.hydrationsFailed++;\n }\n this.hydrationRowCount += rowCount ?? 0;\n this.hydrationRowsRead += rowsRead ?? 0;\n }\n\n addAdvance({timeMs, changes, timedOut}: AdvanceRecord) {\n this.advances.add(timeMs);\n this.advanceChanges += changes;\n if (timedOut) {\n this.advanceTimeouts++;\n }\n }\n\n toJSON() {\n return {\n timeMs: round(this.timeMs),\n hydrations: this.hydrations.toJSON(),\n hydrationsAborted: this.hydrationsAborted,\n hydrationsFailed: this.hydrationsFailed,\n hydrationRowCount: this.hydrationRowCount,\n hydrationRowsRead: this.hydrationRowsRead,\n advances: this.advances.toJSON(),\n advanceChanges: this.advanceChanges,\n advanceTimeouts: this.advanceTimeouts,\n // Only with cost-based sync, without which there are none.\n ...(Object.values(this.advancesAbandoned).some(n => n > 0) && {\n advancesAbandoned: {...this.advancesAbandoned},\n }),\n };\n }\n}\n\nclass ShapeStats extends Totals {\n readonly query: QueryIdentity;\n planWarnings: readonly PlanWarning[] | undefined;\n\n constructor(query: QueryIdentity) {\n super();\n this.query = query;\n }\n}\n\n/** A fixed-size linear-counting sketch for shapes beyond the tracking limit. */\nclass OverflowShapeCounter {\n readonly #buckets = new Uint8Array(4096);\n #occupied = 0;\n\n add(key: string): void {\n // FNV-1a gives a stable bucket without retaining the key.\n let hash = 0x811c9dc5;\n for (let i = 0; i < key.length; i++) {\n hash = Math.imul(hash ^ key.charCodeAt(i), 0x01000193);\n }\n const bucket = (hash >>> 0) % this.#buckets.length;\n if (this.#buckets[bucket] === 0) {\n this.#buckets[bucket] = 1;\n this.#occupied++;\n }\n }\n\n get estimatedCount(): number {\n const empty = this.#buckets.length - this.#occupied;\n return empty === 0\n ? this.#buckets.length\n : Math.round(\n -this.#buckets.length * Math.log(empty / this.#buckets.length),\n );\n }\n}\n\n/**\n * Aggregates the work done for each query shape (the query with its values\n * redacted, see {@link QueryShape}) on a worker, and periodically logs it,\n * like `pg_stat_statements` does for Postgres.\n *\n * Unlike the slow query logs, which are throttled and only written above a\n * threshold, every hydration and advancement is counted, so the logs can be\n * summed over any time range and across workers to rank queries by the time\n * they take.\n *\n * Each {@link flush} logs the interval since the previous one:\n *\n * - a `query-stats` event per query shape, for the {@link\n * QueryStatsOptions.maxReported} shapes that took the most time, and\n * - a `query-stats-summary` event with the totals of all shapes, including\n * those not reported individually, and the `indexUses`: how often the\n * process read each index of the replica, like `pg_stat_user_indexes`. An\n * index that is never read is absent. The workers of a single-process\n * zero-cache share these counts, which the first of them to flush reports.\n *\n * The events carry counts, sums, minimums and maximums, which compose across\n * intervals and workers.\n */\nexport class QueryStats {\n readonly #maxShapes: number;\n readonly #maxReported: number;\n readonly #now: () => number;\n readonly #takeIndexUsage: () => Map<string, number>;\n\n #shapes = new Map<string, ShapeStats>();\n #totals = new Totals();\n #overflowShapes = new OverflowShapeCounter();\n #intervalStart: number;\n\n constructor({\n maxShapes = 1000,\n maxReported = 100,\n now = Date.now,\n takeIndexUsage: takeIndexUsageFn = takeIndexUsage,\n }: QueryStatsOptions = {}) {\n assert(maxShapes > 0, 'maxShapes must be positive');\n assert(maxReported > 0, 'maxReported must be positive');\n this.#maxShapes = maxShapes;\n this.#maxReported = maxReported;\n this.#now = now;\n this.#takeIndexUsage = takeIndexUsageFn;\n this.#intervalStart = now();\n }\n\n recordHydration(query: QueryIdentity, record: HydrationRecord): void {\n this.#totals.addHydration(record);\n const stats = this.#stats(query);\n if (stats) {\n stats.addHydration(record);\n if (record.planWarnings !== undefined) {\n stats.planWarnings = record.planWarnings;\n }\n }\n }\n\n recordAdvance(query: QueryIdentity, record: AdvanceRecord): void {\n this.#totals.addAdvance(record);\n this.#stats(query)?.addAdvance(record);\n }\n\n /**\n * Records that an advancement abandoned the query's pipeline, to hydrate\n * the query again.\n */\n recordAbandoned(query: QueryIdentity, reason: AbandonReason): void {\n this.#totals.advancesAbandoned[reason]++;\n const stats = this.#stats(query);\n if (stats) {\n stats.advancesAbandoned[reason]++;\n }\n }\n\n #stats(query: QueryIdentity): ShapeStats | undefined {\n const key = `${query.queryName ?? ''}:${query.shape.hash}`;\n let stats = this.#shapes.get(key);\n if (stats === undefined) {\n if (this.#shapes.size >= this.#maxShapes) {\n this.#overflowShapes.add(key);\n return undefined;\n }\n stats = new ShapeStats({queryName: query.queryName, shape: query.shape});\n this.#shapes.set(key, stats);\n }\n return stats;\n }\n\n /**\n * Logs the stats of the interval since the previous flush, and starts a\n * new interval. Nothing is logged for an interval without any work.\n */\n flush(lc: LogContext): void {\n const now = this.#now();\n const intervalMs = now - this.#intervalStart;\n const shapes = this.#shapes;\n const totals = this.#totals;\n const overflowShapes = this.#overflowShapes.estimatedCount;\n const indexUses = this.#takeIndexUsage();\n this.#shapes = new Map();\n this.#totals = new Totals();\n this.#overflowShapes = new OverflowShapeCounter();\n this.#intervalStart = now;\n\n if (shapes.size === 0 && overflowShapes === 0 && indexUses.size === 0) {\n return;\n }\n const reported = [...shapes.values()]\n .toSorted((a, b) => b.timeMs - a.timeMs)\n .slice(0, this.#maxReported);\n for (const stats of reported) {\n const {queryName, shape} = stats.query;\n lc.info?.('query stats', {\n zeroEvent: 'query-stats',\n intervalMs,\n ...(queryName !== undefined && {queryName}),\n queryShape: shape.hash,\n ...stats.toJSON(),\n ...(stats.planWarnings !== undefined &&\n stats.planWarnings.length > 0 && {planWarnings: stats.planWarnings}),\n zql: shape.zql,\n });\n }\n lc.info?.('query stats summary', {\n zeroEvent: 'query-stats-summary',\n intervalMs,\n shapes: shapes.size + overflowShapes,\n shapesReported: reported.length,\n ...totals.toJSON(),\n ...(indexUses.size > 0 && {indexUses: Object.fromEntries(indexUses)}),\n });\n }\n\n /**\n * Flushes every `intervalMs` until the returned function is called, which\n * flushes one last time.\n */\n start(lc: LogContext, intervalMs: number): () => void {\n const timer = setInterval(() => this.flush(lc), intervalMs);\n timer.unref?.();\n return () => {\n clearInterval(timer);\n this.flush(lc);\n };\n }\n}\n\nfunction round(ms: number) {\n return Math.round(ms * 100) / 100;\n}\n"],"mappings":"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"}
@@ -1,10 +1,11 @@
1
1
  import { promiseVoid } from "../../../../shared/src/resolved-promises.js";
2
+ import { resolver } from "../../../../shared/src/resolver.js";
2
3
  import { must } from "../../../../shared/src/must.js";
3
4
  import { cvrSchema } from "../../types/shards.js";
4
5
  import { startAsyncSpan } from "../../../../otel/src/span.js";
6
+ import "../../types/pg.js";
5
7
  import { getOrCreateCounter, getOrCreateLatencyHistogram } from "../../observability/metrics.js";
6
8
  import { rowIDString } from "../../types/row-key.js";
7
- import "../../types/pg.js";
8
9
  import { READONLY, READ_COMMITTED } from "../../db/mode-enum.js";
9
10
  import { runTx } from "../../db/run-transaction.js";
10
11
  import { CustomKeyMap } from "../../../../shared/src/custom-key-map.js";
@@ -13,7 +14,6 @@ import { cmpVersions, versionString, versionToNullableCookie } from "./schema/ty
13
14
  import { rowRecordToRowsRow, rowsRowToRowRecord } from "./schema/cvr.js";
14
15
  import { tracer } from "./tracer.js";
15
16
  import { checkVersion } from "./cvr-store.js";
16
- import { resolver } from "@rocicorp/resolver";
17
17
  //#region ../zero-cache/src/services/view-syncer/row-record-cache.ts
18
18
  var FLUSH_TYPE_ATTRIBUTE = "flush.type";
19
19
  var RESULT_ATTRIBUTE = "result";
@@ -1 +1 @@
1
- {"version":3,"file":"row-record-cache.js","names":["#lc","#db","#schema","#cvrID","#failService","#deferredRowFlushThreshold","#setTimeout","#pending","#cvrFlushTime","#cvrRowsFlushed","#cvrFlushes","#ensureLoaded","#cache","#cvr","#pendingRowsVersion","#flushing","#flush","#flushedRowsVersion","#recordAsyncFlushStats"],"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 {startAsyncSpan} from '../../../../otel/src/span.ts';\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 {runTx} from '../../db/run-transaction.ts';\nimport {TransactionPool} from '../../db/transaction-pool.ts';\nimport {\n getOrCreateCounter,\n getOrCreateLatencyHistogram,\n} from '../../observability/metrics.ts';\nimport {type PostgresDB, type PostgresTransaction} 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';\nimport {tracer} from './tracer.ts';\n\nconst FLUSH_TYPE_ATTRIBUTE = 'flush.type';\nconst RESULT_ATTRIBUTE = 'result';\nconst ERROR_KIND_ATTRIBUTE = 'error.kind';\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 = getOrCreateLatencyHistogram(\n 'sync',\n 'cvr.flush-time',\n 'Time to flush a CVR transaction. This includes both synchronous ' +\n 'and asynchronous flushes, distinguished by the flush.type attribute.',\n );\n readonly #cvrRowsFlushed = getOrCreateCounter(\n 'sync',\n 'cvr.rows-flushed',\n 'Number of (changed) rows flushed to a CVR',\n );\n readonly #cvrFlushes = getOrCreateCounter(\n 'sync',\n 'cvr.flush_attempts',\n 'CVR flush attempts, labeled by result and flush.type.',\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.recordMs(elapsedMs, {\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.recordMs(elapsedMs, {\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 r.promise.catch(() => {});\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 = await startAsyncSpan(\n tracer,\n 'RowRecordCache.load',\n async span => {\n const cache: CustomKeyMap<RowID, RowRecord> = new CustomKeyMap(\n rowIDString,\n );\n for await (const rows of this.#db<RowsRow[]>`\n SELECT\n \"clientGroupID\",\n \"schema\",\n \"table\",\n \"rowKey\",\n \"rowVersion\",\n \"patchVersion\",\n \"refCounts\"\n 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 span.setAttribute('rows', cache.size);\n return cache;\n },\n );\n this.#lc.info?.(\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 // The #flush() method handles propagating errors to #failService.\n // Attach a rejection handler to this promise to avoid unhandled\n // rejections.\n this.#flushing.promise.catch(() => {});\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 runTx(\n this.#db,\n tx => {\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 {mode: Mode.READ_COMMITTED},\n );\n const elapsed = performance.now() - start;\n this.#lc.info?.(\n `flushed ${rows} rows@${versionString(rowsVersion)} (${elapsed} ms)`,\n );\n this.#recordAsyncFlushStats(rows, elapsed);\n this.#cvrFlushes.add(1, {\n [RESULT_ATTRIBUTE]: 'success',\n [FLUSH_TYPE_ATTRIBUTE]: 'async',\n });\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.info?.(\n `up to date rows@${versionToNullableCookie(this.#flushedRowsVersion)}`,\n );\n flushing.resolve();\n this.#flushing = null;\n } catch (e) {\n this.#lc.info?.(`row record flush failed`, e);\n this.#cvrFlushes.add(1, {\n [RESULT_ATTRIBUTE]: 'error',\n [FLUSH_TYPE_ATTRIBUTE]: 'async',\n [ERROR_KIND_ATTRIBUTE]: 'error',\n });\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: 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\n \"clientGroupID\",\n \"schema\",\n \"table\",\n \"rowKey\",\n \"rowVersion\",\n \"patchVersion\",\n \"refCounts\"\n 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\n \"clientGroupID\",\n \"schema\",\n \"table\",\n \"rowKey\",\n \"rowVersion\",\n \"patchVersion\",\n \"refCounts\"\n 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.info?.(\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)}`,\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 `,\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 `,\n );\n lc.info?.(\n `flushing ${rowUpdates.size} rows (${rowRecordRows.length} inserts, ${\n rowUpdates.size - rowRecordRows.length\n } deletes)`,\n );\n }\n return pending;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"row-record-cache.js","names":["Mode.READ_COMMITTED","Mode.READONLY"],"sources":["../../../../../../zero-cache/src/services/view-syncer/row-record-cache.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {PendingQuery, Row} from 'postgres';\nimport {startAsyncSpan} from '../../../../otel/src/span.ts';\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 {type Resolver, resolver} from '../../../../shared/src/resolver.ts';\nimport * as Mode from '../../db/mode-enum.ts';\nimport {runTx} from '../../db/run-transaction.ts';\nimport {TransactionPool} from '../../db/transaction-pool.ts';\nimport {\n getOrCreateCounter,\n getOrCreateLatencyHistogram,\n} from '../../observability/metrics.ts';\nimport {type PostgresDB, type PostgresTransaction} 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';\nimport {tracer} from './tracer.ts';\n\nconst FLUSH_TYPE_ATTRIBUTE = 'flush.type';\nconst RESULT_ATTRIBUTE = 'result';\nconst ERROR_KIND_ATTRIBUTE = 'error.kind';\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 = getOrCreateLatencyHistogram(\n 'sync',\n 'cvr.flush-time',\n 'Time to flush a CVR transaction. This includes both synchronous ' +\n 'and asynchronous flushes, distinguished by the flush.type attribute.',\n );\n readonly #cvrRowsFlushed = getOrCreateCounter(\n 'sync',\n 'cvr.rows-flushed',\n 'Number of (changed) rows flushed to a CVR',\n );\n readonly #cvrFlushes = getOrCreateCounter(\n 'sync',\n 'cvr.flush_attempts',\n 'CVR flush attempts, labeled by result and flush.type.',\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.recordMs(elapsedMs, {\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.recordMs(elapsedMs, {\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 r.promise.catch(() => {});\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 = await startAsyncSpan(\n tracer,\n 'RowRecordCache.load',\n async span => {\n const cache: CustomKeyMap<RowID, RowRecord> = new CustomKeyMap(\n rowIDString,\n );\n for await (const rows of this.#db<RowsRow[]>`\n SELECT\n \"clientGroupID\",\n \"schema\",\n \"table\",\n \"rowKey\",\n \"rowVersion\",\n \"patchVersion\",\n \"refCounts\"\n 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 span.setAttribute('rows', cache.size);\n return cache;\n },\n );\n this.#lc.info?.(\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 // The #flush() method handles propagating errors to #failService.\n // Attach a rejection handler to this promise to avoid unhandled\n // rejections.\n this.#flushing.promise.catch(() => {});\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 runTx(\n this.#db,\n tx => {\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 {mode: Mode.READ_COMMITTED},\n );\n const elapsed = performance.now() - start;\n this.#lc.info?.(\n `flushed ${rows} rows@${versionString(rowsVersion)} (${elapsed} ms)`,\n );\n this.#recordAsyncFlushStats(rows, elapsed);\n this.#cvrFlushes.add(1, {\n [RESULT_ATTRIBUTE]: 'success',\n [FLUSH_TYPE_ATTRIBUTE]: 'async',\n });\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.info?.(\n `up to date rows@${versionToNullableCookie(this.#flushedRowsVersion)}`,\n );\n flushing.resolve();\n this.#flushing = null;\n } catch (e) {\n this.#lc.info?.(`row record flush failed`, e);\n this.#cvrFlushes.add(1, {\n [RESULT_ATTRIBUTE]: 'error',\n [FLUSH_TYPE_ATTRIBUTE]: 'async',\n [ERROR_KIND_ATTRIBUTE]: 'error',\n });\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: 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\n \"clientGroupID\",\n \"schema\",\n \"table\",\n \"rowKey\",\n \"rowVersion\",\n \"patchVersion\",\n \"refCounts\"\n 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\n \"clientGroupID\",\n \"schema\",\n \"table\",\n \"rowKey\",\n \"rowVersion\",\n \"patchVersion\",\n \"refCounts\"\n 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.info?.(\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)}`,\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 `,\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 `,\n );\n lc.info?.(\n `flushing ${rowUpdates.size} rows (${rowRecordRows.length} inserts, ${\n rowUpdates.size - rowRecordRows.length\n } deletes)`,\n );\n }\n return pending;\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"cvr.js","names":[],"sources":["../../../../../../../zero-cache/src/services/view-syncer/schema/cvr.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {ident} from 'pg-format';\nimport type postgres from 'postgres';\nimport {\n type JSONObject,\n type JSONValue,\n stringify,\n} from '../../../../../shared/src/bigint-json.ts';\nimport type {ReadonlyJSONValue} from '../../../../../shared/src/json.ts';\nimport {stringCompare} from '../../../../../shared/src/string-compare.ts';\nimport type {ClientSchema} from '../../../../../zero-protocol/src/client-schema.ts';\nimport {normalizedKeyOrder, type RowKey} from '../../../types/row-key.ts';\nimport {cvrSchema, type ShardID} from '../../../types/shards.ts';\nimport type {TTLClock} from '../ttl-clock.ts';\nimport {\n type RowID,\n type RowRecord,\n versionFromString,\n versionString,\n} from './types.ts';\n\n// For readability in the sql statements.\nfunction schema(shard: ShardID) {\n return ident(cvrSchema(shard));\n}\n\nfunction createSchema(shard: ShardID) {\n return `CREATE SCHEMA IF NOT EXISTS ${schema(shard)};`;\n}\n\nexport type InstancesRow = {\n clientGroupID: string;\n version: string;\n lastActive: number;\n ttlClock: TTLClock;\n replicaVersion: string | null;\n owner: string | null;\n grantedAt: number | null;\n clientSchema: ClientSchema | null;\n profileID: string | null;\n};\n\nfunction createInstancesTable(shard: ShardID) {\n return /*sql*/ `\nCREATE TABLE ${schema(shard)}.instances (\n \"clientGroupID\" TEXT PRIMARY KEY,\n \"version\" TEXT NOT NULL, -- Sortable representation of CVRVersion, e.g. \"5nbqa2w:09\"\n \"lastActive\" TIMESTAMPTZ NOT NULL, -- For garbage collection\n \"ttlClock\" DOUBLE PRECISION NOT NULL DEFAULT 0, -- The ttl clock gets \"paused\" when disconnected.\n \"replicaVersion\" TEXT, -- Identifies the replica (i.e. initial-sync point) from which the CVR data comes.\n \"owner\" TEXT, -- The ID of the task / server that has been granted ownership of the CVR.\n \"grantedAt\" TIMESTAMPTZ, -- The time at which the current owner was last granted ownership (most recent connection time).\n \"clientSchema\" JSONB, -- ClientSchema of the client group\n \"profileID\" TEXT, -- Stable profile id (\"p...\"), falling back to the clientGroupID (\"cg{clientGroupID}\") for old clients\n \"deleted\" BOOL DEFAULT FALSE -- Tombstone column for deleted CVRs; instances rows are kept longer for usage stats\n);\n\n-- For garbage collection.\nCREATE INDEX instances_last_active\n ON ${schema(shard)}.instances (\"lastActive\") WHERE NOT \"deleted\";\nCREATE INDEX tombstones_last_active\n ON ${schema(shard)}.instances (\"lastActive\") WHERE \"deleted\";\n\n-- For usage stats; the composite index allows a \n-- SELECT COUNT(DISTINCT(\"profileID\")) query to be answered by\n-- an index scan without additional table lookups.\nCREATE INDEX profile_ids_last_active ON ${schema(shard)}.instances (\"lastActive\", \"profileID\")\n WHERE \"profileID\" IS NOT NULL;\n`;\n}\n\nexport function compareInstancesRows(a: InstancesRow, b: InstancesRow) {\n return stringCompare(a.clientGroupID, b.clientGroupID);\n}\n\nexport type ClientsRow = {\n clientGroupID: string;\n clientID: string;\n};\n\nfunction createClientsTable(shard: ShardID) {\n return `\nCREATE TABLE ${schema(shard)}.clients (\n \"clientGroupID\" TEXT,\n \"clientID\" TEXT,\n\n PRIMARY KEY (\"clientGroupID\", \"clientID\"),\n\n CONSTRAINT fk_clients_client_group\n FOREIGN KEY(\"clientGroupID\")\n REFERENCES ${schema(shard)}.instances(\"clientGroupID\")\n ON DELETE CASCADE\n);\n\n`;\n}\nexport function compareClientsRows(a: ClientsRow, b: ClientsRow) {\n const clientGroupIDComp = stringCompare(a.clientGroupID, b.clientGroupID);\n if (clientGroupIDComp !== 0) {\n return clientGroupIDComp;\n }\n return stringCompare(a.clientID, b.clientID);\n}\n\nexport type QueriesRow = {\n clientGroupID: string;\n queryHash: string;\n // This is the client AST _AFTER_ applying server name transformations.\n clientAST: JSONValue | null;\n queryName: string | null;\n queryArgs: readonly ReadonlyJSONValue[] | null;\n patchVersion: string | null;\n transformationHash: string | null;\n transformationVersion: string | null;\n internal: boolean | null;\n deleted: boolean | null;\n // Optional because (a) old DBs migrated to v17 will start with NULL, and\n // (b) test fixtures predate this column.\n rowSetSignature?: string | null;\n};\n\nfunction createQueriesTable(shard: ShardID) {\n return `\nCREATE TABLE ${schema(shard)}.queries (\n \"clientGroupID\" TEXT,\n \"queryHash\" TEXT, -- this is the hash of the client query AST\n \"clientAST\" JSONB, -- this is nullable as custom queries will not persist an AST\n \"queryName\" TEXT, -- the name of the query if it is a custom query\n \"queryArgs\" JSON, -- the arguments of the query if it is a custom query\n \"patchVersion\" TEXT, -- NULL if only desired but not yet \"got\"\n \"transformationHash\" TEXT,\n \"transformationVersion\" TEXT,\n \"internal\" BOOL, -- If true, no need to track / send patches\n \"deleted\" BOOL, -- put vs del \"got\" query\n \"rowSetSignature\" TEXT, -- Hex XOR of h64([schema,table,rowKey]) for rows in this query (drift detection for Cap)\n\n PRIMARY KEY (\"clientGroupID\", \"queryHash\"),\n\n CONSTRAINT fk_queries_client_group\n FOREIGN KEY(\"clientGroupID\")\n REFERENCES ${schema(shard)}.instances(\"clientGroupID\")\n ON DELETE CASCADE\n);\n\n-- For catchup patches.\nCREATE INDEX queries_patch_version \n ON ${schema(shard)}.queries (\"patchVersion\" NULLS FIRST);\n`;\n}\n\nexport function compareQueriesRows(a: QueriesRow, b: QueriesRow) {\n const clientGroupIDComp = stringCompare(a.clientGroupID, b.clientGroupID);\n if (clientGroupIDComp !== 0) {\n return clientGroupIDComp;\n }\n return stringCompare(a.queryHash, b.queryHash);\n}\n\nexport type DesiresRow = {\n clientGroupID: string;\n clientID: string;\n queryHash: string;\n patchVersion: string;\n deleted: boolean | null;\n ttl: number | null;\n inactivatedAt: TTLClock | null;\n};\n\nfunction createDesiresTable(shard: ShardID) {\n return `\nCREATE TABLE ${schema(shard)}.desires (\n \"clientGroupID\" TEXT,\n \"clientID\" TEXT,\n \"queryHash\" TEXT,\n \"patchVersion\" TEXT NOT NULL,\n \"deleted\" BOOL, -- put vs del \"desired\" query\n \"ttl\" INTERVAL, -- DEPRECATED: Use ttlMs instead. Time to live for this client\n \"ttlMs\" DOUBLE PRECISION, -- Time to live in milliseconds\n \"inactivatedAt\" TIMESTAMPTZ, -- DEPRECATED: Use inactivatedAtMs instead. Time at which this row was inactivated\n \"inactivatedAtMs\" DOUBLE PRECISION, -- Time at which this row was inactivated (milliseconds since client group start)\n\n PRIMARY KEY (\"clientGroupID\", \"clientID\", \"queryHash\"),\n\n CONSTRAINT fk_desires_query\n FOREIGN KEY(\"clientGroupID\", \"queryHash\")\n REFERENCES ${ident(cvrSchema(shard))}.queries(\"clientGroupID\", \"queryHash\")\n ON DELETE CASCADE\n);\n\n-- For catchup patches.\nCREATE INDEX desires_patch_version\n ON ${schema(shard)}.desires (\"patchVersion\");\n\nCREATE INDEX desires_inactivated_at\n ON ${schema(shard)}.desires (\"inactivatedAt\");\n`;\n}\n\nexport function compareDesiresRows(a: DesiresRow, b: DesiresRow) {\n const clientGroupIDComp = stringCompare(a.clientGroupID, b.clientGroupID);\n if (clientGroupIDComp !== 0) {\n return clientGroupIDComp;\n }\n const clientIDComp = stringCompare(a.clientID, b.clientID);\n if (clientIDComp !== 0) {\n return clientIDComp;\n }\n return stringCompare(a.queryHash, b.queryHash);\n}\n\nexport type RowsRow = {\n clientGroupID: string;\n schema: string;\n table: string;\n rowKey: JSONObject;\n rowVersion: string;\n patchVersion: string;\n refCounts: {[queryHash: string]: number} | null;\n};\n\nexport function rowsRowToRowID(rowsRow: RowsRow): RowID {\n return {\n schema: rowsRow.schema,\n table: rowsRow.table,\n rowKey: rowsRow.rowKey as Record<string, JSONValue>,\n };\n}\n\nexport function rowsRowToRowRecord(rowsRow: RowsRow): RowRecord {\n return {\n id: rowsRowToRowID(rowsRow),\n rowVersion: rowsRow.rowVersion,\n patchVersion: versionFromString(rowsRow.patchVersion),\n refCounts: rowsRow.refCounts,\n };\n}\n\nexport function rowRecordToRowsRow(\n clientGroupID: string,\n rowRecord: RowRecord,\n): RowsRow {\n return {\n clientGroupID,\n schema: rowRecord.id.schema,\n table: rowRecord.id.table,\n rowKey: rowRecord.id.rowKey as Record<string, JSONValue>,\n rowVersion: rowRecord.rowVersion,\n patchVersion: versionString(rowRecord.patchVersion),\n refCounts: rowRecord.refCounts,\n };\n}\n\nexport function compareRowsRows(a: RowsRow, b: RowsRow) {\n const clientGroupIDComp = stringCompare(a.clientGroupID, b.clientGroupID);\n if (clientGroupIDComp !== 0) {\n return clientGroupIDComp;\n }\n const schemaComp = stringCompare(a.schema, b.schema);\n if (schemaComp !== 0) {\n return schemaComp;\n }\n const tableComp = stringCompare(b.table, b.table);\n if (tableComp !== 0) {\n return tableComp;\n }\n return stringCompare(\n stringifySorted(a.rowKey as RowKey),\n stringifySorted(b.rowKey as RowKey),\n );\n}\n\n/**\n * The version of the data in the `cvr.rows` table. This may lag\n * `version` in `cvr.instances` but eventually catches up, modulo\n * exceptional circumstances like a server crash.\n *\n * The `rowsVersion` is tracked in a separate table (as opposed to\n * a column in the `cvr.instances` table) so that general `cvr` updates\n * and `row` updates can be executed independently without serialization\n * conflicts.\n *\n * Note: Although `clientGroupID` logically references the same column in\n * `cvr.instances`, a FOREIGN KEY constraint must not be declared as the\n * `cvr.rows` TABLE needs to be updated without affecting the\n * `SELECT ... FOR UPDATE` lock when `cvr.instances` is updated.\n */\nexport function createRowsVersionTable(shard: ShardID) {\n return `\nCREATE TABLE ${schema(shard)}.\"rowsVersion\" (\n \"clientGroupID\" TEXT PRIMARY KEY,\n \"version\" TEXT NOT NULL\n);\n`;\n}\n\n/**\n * CVR `rows` are updated asynchronously from the CVR metadata\n * (i.e. `instances`). The `rowsVersion` table is updated atomically with\n * updates to the `rows` data.\n */\nfunction createRowsTable(shard: ShardID) {\n return `\nCREATE TABLE ${schema(shard)}.rows (\n \"clientGroupID\" TEXT,\n \"schema\" TEXT,\n \"table\" TEXT,\n \"rowKey\" JSONB,\n \"rowVersion\" TEXT NOT NULL,\n \"patchVersion\" TEXT NOT NULL,\n \"refCounts\" JSONB, -- {[queryHash: string]: number}, NULL for tombstone\n\n PRIMARY KEY (\"clientGroupID\", \"schema\", \"table\", \"rowKey\"),\n\n CONSTRAINT fk_rows_client_group\n FOREIGN KEY(\"clientGroupID\")\n REFERENCES ${schema(shard)}.\"rowsVersion\" (\"clientGroupID\")\n ON DELETE CASCADE\n);\n\n-- For catchup patches.\nCREATE INDEX row_patch_version \n ON ${schema(shard)}.rows (\"patchVersion\");\n\n-- For listing rows returned by one or more query hashes. e.g.\n-- SELECT * FROM cvr_shard.rows WHERE \"refCounts\" ?| array[...queryHashes...];\nCREATE INDEX row_ref_counts ON ${schema(shard)}.rows \n USING GIN (\"refCounts\");\n`;\n}\n\nexport type RowsVersionRow = {\n clientGroupID: string;\n version: string;\n};\n\nfunction createTables(shard: ShardID) {\n return (\n createSchema(shard) +\n createInstancesTable(shard) +\n createClientsTable(shard) +\n createQueriesTable(shard) +\n createDesiresTable(shard) +\n createRowsVersionTable(shard) +\n createRowsTable(shard)\n );\n}\n\nexport async function setupCVRTables(\n lc: LogContext,\n db: postgres.TransactionSql,\n shard: ShardID,\n) {\n lc.info?.(`Setting up CVR tables`);\n await db.unsafe(createTables(shard));\n}\n\nfunction stringifySorted(r: RowKey) {\n return stringify(normalizedKeyOrder(r));\n}\n"],"mappings":";;;;;;AAsBA,SAAS,OAAO,OAAgB;CAC9B,OAAO,MAAM,UAAU,KAAK,CAAC;AAC/B;AAEA,SAAS,aAAa,OAAgB;CACpC,OAAO,+BAA+B,OAAO,KAAK,EAAE;AACtD;AAcA,SAAS,qBAAqB,OAAgB;CAC5C,OAAe;eACF,OAAO,KAAK,EAAE;;;;;;;;;;;;;;;OAetB,OAAO,KAAK,EAAE;;OAEd,OAAO,KAAK,EAAE;;;;;0CAKqB,OAAO,KAAK,EAAE;;;AAGxD;AAWA,SAAS,mBAAmB,OAAgB;CAC1C,OAAO;eACM,OAAO,KAAK,EAAE;;;;;;;;iBAQZ,OAAO,KAAK,EAAE;;;;;AAK/B;AA0BA,SAAS,mBAAmB,OAAgB;CAC1C,OAAO;eACM,OAAO,KAAK,EAAE;;;;;;;;;;;;;;;;;iBAiBZ,OAAO,KAAK,EAAE;;;;;;OAMxB,OAAO,KAAK,EAAE;;AAErB;AAoBA,SAAS,mBAAmB,OAAgB;CAC1C,OAAO;eACM,OAAO,KAAK,EAAE;;;;;;;;;;;;;;;iBAeZ,MAAM,UAAU,KAAK,CAAC,EAAE;;;;;;OAMlC,OAAO,KAAK,EAAE;;;OAGd,OAAO,KAAK,EAAE;;AAErB;AAwBA,SAAgB,eAAe,SAAyB;CACtD,OAAO;EACL,QAAQ,QAAQ;EAChB,OAAO,QAAQ;EACf,QAAQ,QAAQ;CAClB;AACF;AAEA,SAAgB,mBAAmB,SAA6B;CAC9D,OAAO;EACL,IAAI,eAAe,OAAO;EAC1B,YAAY,QAAQ;EACpB,cAAc,kBAAkB,QAAQ,YAAY;EACpD,WAAW,QAAQ;CACrB;AACF;AAEA,SAAgB,mBACd,eACA,WACS;CACT,OAAO;EACL;EACA,QAAQ,UAAU,GAAG;EACrB,OAAO,UAAU,GAAG;EACpB,QAAQ,UAAU,GAAG;EACrB,YAAY,UAAU;EACtB,cAAc,cAAc,UAAU,YAAY;EAClD,WAAW,UAAU;CACvB;AACF;;;;;;;;;;;;;;;;AAoCA,SAAgB,uBAAuB,OAAgB;CACrD,OAAO;eACM,OAAO,KAAK,EAAE;;;;;AAK7B;;;;;;AAOA,SAAS,gBAAgB,OAAgB;CACvC,OAAO;eACM,OAAO,KAAK,EAAE;;;;;;;;;;;;;iBAaZ,OAAO,KAAK,EAAE;;;;;;OAMxB,OAAO,KAAK,EAAE;;;;iCAIY,OAAO,KAAK,EAAE;;;AAG/C;AAOA,SAAS,aAAa,OAAgB;CACpC,OACE,aAAa,KAAK,IAClB,qBAAqB,KAAK,IAC1B,mBAAmB,KAAK,IACxB,mBAAmB,KAAK,IACxB,mBAAmB,KAAK,IACxB,uBAAuB,KAAK,IAC5B,gBAAgB,KAAK;AAEzB;AAEA,eAAsB,eACpB,IACA,IACA,OACA;CACA,GAAG,OAAO,uBAAuB;CACjC,MAAM,GAAG,OAAO,aAAa,KAAK,CAAC;AACrC"}
1
+ {"version":3,"file":"cvr.js","names":[],"sources":["../../../../../../../zero-cache/src/services/view-syncer/schema/cvr.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {ident} from 'pg-format';\nimport type postgres from 'postgres';\nimport {\n type JSONObject,\n type JSONValue,\n stringify,\n} from '../../../../../shared/src/bigint-json.ts';\nimport type {ReadonlyJSONValue} from '../../../../../shared/src/json.ts';\nimport {stringCompare} from '../../../../../shared/src/string-compare.ts';\nimport type {ClientSchema} from '../../../../../zero-protocol/src/client-schema.ts';\nimport {normalizedKeyOrder, type RowKey} from '../../../types/row-key.ts';\nimport {cvrSchema, type ShardID} from '../../../types/shards.ts';\nimport type {TTLClock} from '../ttl-clock.ts';\nimport {\n type RowID,\n type RowRecord,\n versionFromString,\n versionString,\n} from './types.ts';\n\n// For readability in the sql statements.\nfunction schema(shard: ShardID) {\n return ident(cvrSchema(shard));\n}\n\nfunction createSchema(shard: ShardID) {\n return `CREATE SCHEMA IF NOT EXISTS ${schema(shard)};`;\n}\n\nexport type InstancesRow = {\n clientGroupID: string;\n version: string;\n lastActive: number;\n ttlClock: TTLClock;\n replicaVersion: string | null;\n owner: string | null;\n grantedAt: number | null;\n clientSchema: ClientSchema | null;\n profileID: string | null;\n};\n\nfunction createInstancesTable(shard: ShardID) {\n return /*sql*/ `\nCREATE TABLE ${schema(shard)}.instances (\n \"clientGroupID\" TEXT PRIMARY KEY,\n \"version\" TEXT NOT NULL, -- Sortable representation of CVRVersion, e.g. \"5nbqa2w:09\"\n \"lastActive\" TIMESTAMPTZ NOT NULL, -- For garbage collection\n \"ttlClock\" DOUBLE PRECISION NOT NULL DEFAULT 0, -- The ttl clock gets \"paused\" when disconnected.\n \"replicaVersion\" TEXT, -- Identifies the replica (i.e. initial-sync point) from which the CVR data comes.\n \"owner\" TEXT, -- The ID of the task / server that has been granted ownership of the CVR.\n \"grantedAt\" TIMESTAMPTZ, -- The time at which the current owner was last granted ownership (most recent connection time).\n \"clientSchema\" JSONB, -- ClientSchema of the client group\n \"profileID\" TEXT, -- Stable profile id (\"p...\"), falling back to the clientGroupID (\"cg{clientGroupID}\") for old clients\n \"deleted\" BOOL DEFAULT FALSE -- Tombstone column for deleted CVRs; instances rows are kept longer for usage stats\n);\n\n-- For garbage collection.\nCREATE INDEX instances_last_active\n ON ${schema(shard)}.instances (\"lastActive\") WHERE NOT \"deleted\";\nCREATE INDEX tombstones_last_active\n ON ${schema(shard)}.instances (\"lastActive\") WHERE \"deleted\";\n\n-- For usage stats; the composite index allows a \n-- SELECT COUNT(DISTINCT(\"profileID\")) query to be answered by\n-- an index scan without additional table lookups.\nCREATE INDEX profile_ids_last_active ON ${schema(shard)}.instances (\"lastActive\", \"profileID\")\n WHERE \"profileID\" IS NOT NULL;\n`;\n}\n\nexport function compareInstancesRows(a: InstancesRow, b: InstancesRow) {\n return stringCompare(a.clientGroupID, b.clientGroupID);\n}\n\nexport type ClientsRow = {\n clientGroupID: string;\n clientID: string;\n};\n\nfunction createClientsTable(shard: ShardID) {\n return `\nCREATE TABLE ${schema(shard)}.clients (\n \"clientGroupID\" TEXT,\n \"clientID\" TEXT,\n\n PRIMARY KEY (\"clientGroupID\", \"clientID\"),\n\n CONSTRAINT fk_clients_client_group\n FOREIGN KEY(\"clientGroupID\")\n REFERENCES ${schema(shard)}.instances(\"clientGroupID\")\n ON DELETE CASCADE\n);\n\n`;\n}\nexport function compareClientsRows(a: ClientsRow, b: ClientsRow) {\n const clientGroupIDComp = stringCompare(a.clientGroupID, b.clientGroupID);\n if (clientGroupIDComp !== 0) {\n return clientGroupIDComp;\n }\n return stringCompare(a.clientID, b.clientID);\n}\n\nexport type QueriesRow = {\n clientGroupID: string;\n queryHash: string;\n // This is the client AST _AFTER_ applying server name transformations.\n clientAST: JSONValue | null;\n queryName: string | null;\n queryArgs: readonly ReadonlyJSONValue[] | null;\n patchVersion: string | null;\n transformationHash: string | null;\n transformationVersion: string | null;\n internal: boolean | null;\n deleted: boolean | null;\n // Optional because (a) old DBs migrated to v17 will start with NULL, and\n // (b) test fixtures predate this column.\n rowSetSignature?: string | null;\n};\n\nfunction createQueriesTable(shard: ShardID) {\n return `\nCREATE TABLE ${schema(shard)}.queries (\n \"clientGroupID\" TEXT,\n \"queryHash\" TEXT, -- this is the hash of the client query AST\n \"clientAST\" JSONB, -- this is nullable as custom queries will not persist an AST\n \"queryName\" TEXT, -- the name of the query if it is a custom query\n \"queryArgs\" JSON, -- the arguments of the query if it is a custom query\n \"patchVersion\" TEXT, -- NULL if only desired but not yet \"got\"\n \"transformationHash\" TEXT,\n \"transformationVersion\" TEXT,\n \"internal\" BOOL, -- If true, no need to track / send patches\n \"deleted\" BOOL, -- put vs del \"got\" query\n \"rowSetSignature\" TEXT, -- Hex XOR of h64([schema,table,rowKey]) for rows in this query (drift detection for Cap)\n\n PRIMARY KEY (\"clientGroupID\", \"queryHash\"),\n\n CONSTRAINT fk_queries_client_group\n FOREIGN KEY(\"clientGroupID\")\n REFERENCES ${schema(shard)}.instances(\"clientGroupID\")\n ON DELETE CASCADE\n);\n\n-- For catchup patches.\nCREATE INDEX queries_patch_version \n ON ${schema(shard)}.queries (\"patchVersion\" NULLS FIRST);\n`;\n}\n\nexport function compareQueriesRows(a: QueriesRow, b: QueriesRow) {\n const clientGroupIDComp = stringCompare(a.clientGroupID, b.clientGroupID);\n if (clientGroupIDComp !== 0) {\n return clientGroupIDComp;\n }\n return stringCompare(a.queryHash, b.queryHash);\n}\n\nexport type DesiresRow = {\n clientGroupID: string;\n clientID: string;\n queryHash: string;\n patchVersion: string;\n deleted: boolean | null;\n ttl: number | null;\n inactivatedAt: TTLClock | null;\n};\n\nfunction createDesiresTable(shard: ShardID) {\n return `\nCREATE TABLE ${schema(shard)}.desires (\n \"clientGroupID\" TEXT,\n \"clientID\" TEXT,\n \"queryHash\" TEXT,\n \"patchVersion\" TEXT NOT NULL,\n \"deleted\" BOOL, -- put vs del \"desired\" query\n \"ttl\" INTERVAL, -- DEPRECATED: Use ttlMs instead. Time to live for this client\n \"ttlMs\" DOUBLE PRECISION, -- Time to live in milliseconds\n \"inactivatedAt\" TIMESTAMPTZ, -- DEPRECATED: Use inactivatedAtMs instead. Time at which this row was inactivated\n \"inactivatedAtMs\" DOUBLE PRECISION, -- Time at which this row was inactivated (milliseconds since client group start)\n\n PRIMARY KEY (\"clientGroupID\", \"clientID\", \"queryHash\"),\n\n CONSTRAINT fk_desires_query\n FOREIGN KEY(\"clientGroupID\", \"queryHash\")\n REFERENCES ${ident(cvrSchema(shard))}.queries(\"clientGroupID\", \"queryHash\")\n ON DELETE CASCADE\n);\n\n-- For catchup patches.\nCREATE INDEX desires_patch_version\n ON ${schema(shard)}.desires (\"patchVersion\");\n\nCREATE INDEX desires_inactivated_at\n ON ${schema(shard)}.desires (\"inactivatedAt\");\n`;\n}\n\nexport function compareDesiresRows(a: DesiresRow, b: DesiresRow) {\n const clientGroupIDComp = stringCompare(a.clientGroupID, b.clientGroupID);\n if (clientGroupIDComp !== 0) {\n return clientGroupIDComp;\n }\n const clientIDComp = stringCompare(a.clientID, b.clientID);\n if (clientIDComp !== 0) {\n return clientIDComp;\n }\n return stringCompare(a.queryHash, b.queryHash);\n}\n\nexport type RowsRow = {\n clientGroupID: string;\n schema: string;\n table: string;\n rowKey: JSONObject;\n rowVersion: string;\n patchVersion: string;\n refCounts: {[queryHash: string]: number} | null;\n};\n\nfunction rowsRowToRowID(rowsRow: RowsRow): RowID {\n return {\n schema: rowsRow.schema,\n table: rowsRow.table,\n rowKey: rowsRow.rowKey as Record<string, JSONValue>,\n };\n}\n\nexport function rowsRowToRowRecord(rowsRow: RowsRow): RowRecord {\n return {\n id: rowsRowToRowID(rowsRow),\n rowVersion: rowsRow.rowVersion,\n patchVersion: versionFromString(rowsRow.patchVersion),\n refCounts: rowsRow.refCounts,\n };\n}\n\nexport function rowRecordToRowsRow(\n clientGroupID: string,\n rowRecord: RowRecord,\n): RowsRow {\n return {\n clientGroupID,\n schema: rowRecord.id.schema,\n table: rowRecord.id.table,\n rowKey: rowRecord.id.rowKey as Record<string, JSONValue>,\n rowVersion: rowRecord.rowVersion,\n patchVersion: versionString(rowRecord.patchVersion),\n refCounts: rowRecord.refCounts,\n };\n}\n\nexport function compareRowsRows(a: RowsRow, b: RowsRow) {\n const clientGroupIDComp = stringCompare(a.clientGroupID, b.clientGroupID);\n if (clientGroupIDComp !== 0) {\n return clientGroupIDComp;\n }\n const schemaComp = stringCompare(a.schema, b.schema);\n if (schemaComp !== 0) {\n return schemaComp;\n }\n const tableComp = stringCompare(b.table, b.table);\n if (tableComp !== 0) {\n return tableComp;\n }\n return stringCompare(\n stringifySorted(a.rowKey as RowKey),\n stringifySorted(b.rowKey as RowKey),\n );\n}\n\n/**\n * The version of the data in the `cvr.rows` table. This may lag\n * `version` in `cvr.instances` but eventually catches up, modulo\n * exceptional circumstances like a server crash.\n *\n * The `rowsVersion` is tracked in a separate table (as opposed to\n * a column in the `cvr.instances` table) so that general `cvr` updates\n * and `row` updates can be executed independently without serialization\n * conflicts.\n *\n * Note: Although `clientGroupID` logically references the same column in\n * `cvr.instances`, a FOREIGN KEY constraint must not be declared as the\n * `cvr.rows` TABLE needs to be updated without affecting the\n * `SELECT ... FOR UPDATE` lock when `cvr.instances` is updated.\n */\nexport function createRowsVersionTable(shard: ShardID) {\n return `\nCREATE TABLE ${schema(shard)}.\"rowsVersion\" (\n \"clientGroupID\" TEXT PRIMARY KEY,\n \"version\" TEXT NOT NULL\n);\n`;\n}\n\n/**\n * CVR `rows` are updated asynchronously from the CVR metadata\n * (i.e. `instances`). The `rowsVersion` table is updated atomically with\n * updates to the `rows` data.\n */\nfunction createRowsTable(shard: ShardID) {\n return `\nCREATE TABLE ${schema(shard)}.rows (\n \"clientGroupID\" TEXT,\n \"schema\" TEXT,\n \"table\" TEXT,\n \"rowKey\" JSONB,\n \"rowVersion\" TEXT NOT NULL,\n \"patchVersion\" TEXT NOT NULL,\n \"refCounts\" JSONB, -- {[queryHash: string]: number}, NULL for tombstone\n\n PRIMARY KEY (\"clientGroupID\", \"schema\", \"table\", \"rowKey\"),\n\n CONSTRAINT fk_rows_client_group\n FOREIGN KEY(\"clientGroupID\")\n REFERENCES ${schema(shard)}.\"rowsVersion\" (\"clientGroupID\")\n ON DELETE CASCADE\n);\n\n-- For catchup patches.\nCREATE INDEX row_patch_version \n ON ${schema(shard)}.rows (\"patchVersion\");\n\n-- For listing rows returned by one or more query hashes. e.g.\n-- SELECT * FROM cvr_shard.rows WHERE \"refCounts\" ?| array[...queryHashes...];\nCREATE INDEX row_ref_counts ON ${schema(shard)}.rows \n USING GIN (\"refCounts\");\n`;\n}\n\nexport type RowsVersionRow = {\n clientGroupID: string;\n version: string;\n};\n\nfunction createTables(shard: ShardID) {\n return (\n createSchema(shard) +\n createInstancesTable(shard) +\n createClientsTable(shard) +\n createQueriesTable(shard) +\n createDesiresTable(shard) +\n createRowsVersionTable(shard) +\n createRowsTable(shard)\n );\n}\n\nexport async function setupCVRTables(\n lc: LogContext,\n db: postgres.TransactionSql,\n shard: ShardID,\n) {\n lc.info?.(`Setting up CVR tables`);\n await db.unsafe(createTables(shard));\n}\n\nfunction stringifySorted(r: RowKey) {\n return stringify(normalizedKeyOrder(r));\n}\n"],"mappings":";;;;;;AAsBA,SAAS,OAAO,OAAgB;CAC9B,OAAO,MAAM,UAAU,KAAK,CAAC;AAC/B;AAEA,SAAS,aAAa,OAAgB;CACpC,OAAO,+BAA+B,OAAO,KAAK,EAAE;AACtD;AAcA,SAAS,qBAAqB,OAAgB;CAC5C,OAAe;eACF,OAAO,KAAK,EAAE;;;;;;;;;;;;;;;OAetB,OAAO,KAAK,EAAE;;OAEd,OAAO,KAAK,EAAE;;;;;0CAKqB,OAAO,KAAK,EAAE;;;AAGxD;AAWA,SAAS,mBAAmB,OAAgB;CAC1C,OAAO;eACM,OAAO,KAAK,EAAE;;;;;;;;iBAQZ,OAAO,KAAK,EAAE;;;;;AAK/B;AA0BA,SAAS,mBAAmB,OAAgB;CAC1C,OAAO;eACM,OAAO,KAAK,EAAE;;;;;;;;;;;;;;;;;iBAiBZ,OAAO,KAAK,EAAE;;;;;;OAMxB,OAAO,KAAK,EAAE;;AAErB;AAoBA,SAAS,mBAAmB,OAAgB;CAC1C,OAAO;eACM,OAAO,KAAK,EAAE;;;;;;;;;;;;;;;iBAeZ,MAAM,UAAU,KAAK,CAAC,EAAE;;;;;;OAMlC,OAAO,KAAK,EAAE;;;OAGd,OAAO,KAAK,EAAE;;AAErB;AAwBA,SAAS,eAAe,SAAyB;CAC/C,OAAO;EACL,QAAQ,QAAQ;EAChB,OAAO,QAAQ;EACf,QAAQ,QAAQ;CAClB;AACF;AAEA,SAAgB,mBAAmB,SAA6B;CAC9D,OAAO;EACL,IAAI,eAAe,OAAO;EAC1B,YAAY,QAAQ;EACpB,cAAc,kBAAkB,QAAQ,YAAY;EACpD,WAAW,QAAQ;CACrB;AACF;AAEA,SAAgB,mBACd,eACA,WACS;CACT,OAAO;EACL;EACA,QAAQ,UAAU,GAAG;EACrB,OAAO,UAAU,GAAG;EACpB,QAAQ,UAAU,GAAG;EACrB,YAAY,UAAU;EACtB,cAAc,cAAc,UAAU,YAAY;EAClD,WAAW,UAAU;CACvB;AACF;;;;;;;;;;;;;;;;AAoCA,SAAgB,uBAAuB,OAAgB;CACrD,OAAO;eACM,OAAO,KAAK,EAAE;;;;;AAK7B;;;;;;AAOA,SAAS,gBAAgB,OAAgB;CACvC,OAAO;eACM,OAAO,KAAK,EAAE;;;;;;;;;;;;;iBAaZ,OAAO,KAAK,EAAE;;;;;;OAMxB,OAAO,KAAK,EAAE;;;;iCAIY,OAAO,KAAK,EAAE;;;AAG/C;AAOA,SAAS,aAAa,OAAgB;CACpC,OACE,aAAa,KAAK,IAClB,qBAAqB,KAAK,IAC1B,mBAAmB,KAAK,IACxB,mBAAmB,KAAK,IACxB,mBAAmB,KAAK,IACxB,uBAAuB,KAAK,IAC5B,gBAAgB,KAAK;AAEzB;AAEA,eAAsB,eACpB,IACA,IACA,OACA;CACA,GAAG,OAAO,uBAAuB;CACjC,MAAM,GAAG,OAAO,aAAa,KAAK,CAAC;AACrC"}
@@ -52,7 +52,6 @@ function cookieToVersion(cookie) {
52
52
  if (cookie === null) return null;
53
53
  return versionFromString(cookie);
54
54
  }
55
- valita_exports.object({ id: valita_exports.string() });
56
55
  var cvrRecordSchema = valita_exports.object({
57
56
  /**
58
57
  * CVR records store the CVRVersion at which the record was last patched into
@@ -171,23 +170,10 @@ cvrRecordSchema.extend({
171
170
  rowVersion: valita_exports.string(),
172
171
  refCounts: valita_exports.record(valita_exports.number()).nullable()
173
172
  });
174
- var patchSchema = valita_exports.object({
173
+ valita_exports.object({
175
174
  type: literalUnion("row", "query"),
176
175
  op: literalUnion("put", "del")
177
- });
178
- var putRowPatchSchema = patchSchema.extend({
179
- type: valita_exports.literal("row"),
180
- op: valita_exports.literal("put"),
181
- id: rowIDSchema,
182
- rowVersion: valita_exports.string()
183
- });
184
- var delRowPatchSchema = patchSchema.extend({
185
- type: valita_exports.literal("row"),
186
- op: valita_exports.literal("del"),
187
- id: rowIDSchema
188
- });
189
- valita_exports.union(putRowPatchSchema, delRowPatchSchema);
190
- patchSchema.extend({
176
+ }).extend({
191
177
  type: valita_exports.literal("query"),
192
178
  id: valita_exports.string(),
193
179
  clientID: valita_exports.string().optional()