@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":"restore-admission.js","names":["#lc","#maxConcurrentRestores","#pendingGrantTTLMs","#readinessGracePeriodMs","#setTimeout","#clearTimeout","#replicaVersion","#replacing","#catchingUp","#unclassified","#grants","#queue","#inFlight","#replacementOpened","#startGrantTTL","#processQueue","#deleteGrant","#release"],"sources":["../../../../../../zero-cache/src/services/change-streamer/restore-admission.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {getOrCreateGauge} from '../../observability/metrics.ts';\nimport type {LocalReplica} from './subscribe.ts';\n\n/**\n * The default time to wait for a subscriber that was granted permission to\n * restore to reconnect (i.e. to open a replacement reservation) after its\n * held connection closes, or after its replacement reservation closes\n * without subscribing (e.g. to retry a failed restore). This covers the\n * subscriber's process exit and restart (e.g. a container restart, the\n * backoff of which starts at 10 seconds in Kubernetes), but not the restore\n * itself, which is tracked by the liveness of its snapshot reservation.\n */\nexport const DEFAULT_PENDING_GRANT_TTL_MS = 90_000;\n\n/**\n * The default time for which a replacement continues to be counted after its\n * subscriber has caught up, which covers the time for the subscriber to start\n * serving (i.e. become ready) after catching up. See\n * {@link RestoreAdmissionOptions.readinessGracePeriodMs}.\n */\nexport const DEFAULT_READINESS_GRACE_PERIOD_MS = 5_000;\n\nexport type RestoreAdmissionOptions = {\n /** The replica version of the replication-manager. */\n replicaVersion: string;\n /**\n * The maximum number of subscribers that may be restoring (or granted\n * permission to restore) at the same time.\n */\n maxConcurrentRestores: number;\n pendingGrantTTLMs?: number | undefined;\n /**\n * The time for which a replacement continues to be counted after its\n * subscriber has caught up, so that the next subscriber is not granted\n * permission to restore (and stop serving) before the restored one has\n * started serving. Catching up is followed by the subscriber's readiness\n * checks (e.g. waiting for the next replication lag report), and by the\n * container runner's detection of its readiness.\n */\n readinessGracePeriodMs?: number | undefined;\n // Injectable timers, for deterministic testing.\n setTimeoutFn?: typeof setTimeout | undefined;\n clearTimeoutFn?: typeof clearTimeout | undefined;\n};\n\nexport type AdmissionRequest = {\n /**\n * Called when the request is queued, and again whenever its (1-based)\n * position in the queue changes.\n */\n onQueued: (position: number) => void;\n\n /** Called (once) when the request is granted. */\n onGranted: () => void;\n};\n\n/** A subscribed replacement, returned by {@link RestoreAdmission.reservationSubscribed}. */\nexport type Replacement = {\n /**\n * Called when the subscriber has caught up. The replacement is released\n * after the readiness grace period.\n */\n caughtUp: () => void;\n\n /** Called when the subscription closes. The replacement is released. */\n closed: () => void;\n};\n\nexport type AdmissionTicket = {\n /**\n * Must be called when the requesting connection closes. A queued request\n * is removed from the queue. For a granted request, this starts the\n * timer within which the subscriber is expected to reconnect and open a\n * replacement reservation.\n */\n release(): void;\n};\n\ntype Entry = {\n readonly request: AdmissionRequest;\n state: 'queued' | 'granted' | 'released';\n position: number;\n grant?: Grant | undefined;\n};\n\ntype Grant = {\n /** Whether the granted subscriber's connection has closed. */\n released: boolean;\n timer: ReturnType<typeof setTimeout> | undefined;\n};\n\n/**\n * Limits the number of view-syncers that replace their replica at the same\n * time, so that view-syncers switching over from an incompatible\n * replication-manager (e.g. after a resync) do not all stop serving at once.\n *\n * Capacity is consumed by:\n * * Replacement restores: snapshot reservations of subscribers that are\n * replacing an (incompatible or outdated) local replica, i.e. subscribers\n * that were serving. Restores onto an empty disk (e.g. a new pod) do not\n * take a serving subscriber out of service, and are not counted. The\n * reservation's connection is held open for the duration of the restore,\n * and the subscription that follows it is held open until the restored\n * subscriber has caught up, so their liveness tracks that of the\n * replacement without a timeout.\n * * Outstanding grants: permission to restore that has not yet been\n * followed by a replacement reservation (e.g. while the subscriber drains\n * and restarts). Grants are not matched to reservations by identity:\n * any replacement reservation consumes an outstanding grant, and a grant\n * that is not consumed expires a short time after the granted connection\n * closes (e.g. if the subscriber reconnected to a different\n * replication-manager or was deleted).\n *\n * ## Life cycle of a switchover restore\n *\n * The following traces a view-syncer (VS) with a replica at version `R1`,\n * served by an old replication-manager (RM) at `R1`, while a new RM (e.g.\n * after a resync) is at `R2 > R1`. The RestoreAdmission is that of the new\n * RM. Terms in `code` are those used in the code.\n *\n * 1. **Drain.** The old RM receives a SIGTERM, and its `DrainTracker` sends\n * `status {draining: true}` to its `drainable` (i.e. serving) subscribers.\n * It continues to stream changes to them, and exits once they have all\n * disconnected.\n *\n * 2. **Switchover.** The VS's `DrainHandler` opens a second `/subscribe`\n * with `switchover: true` at the watermark of its last committed\n * transaction, which is routed to some RM. The VS continues to consume\n * the old RM's stream. If the RM is compatible (i.e. another old RM),\n * the subscription is accepted and the VS simply moves to it. If the RM\n * is the new one, the subscriber's replica version is older, and the\n * subscription proceeds to step 3.\n *\n * 3. **Queued.** `ChangeStreamerImpl.subscribe()` holds the subscription\n * (it is not added to the forwarder) and calls {@link request}. The\n * request is `queued` and `onQueued(position)` sends\n * `status {restore: {state: 'queued', position}}`, again whenever the\n * position changes. If the held connection closes while queued (e.g.\n * the VS's old stream ended and it gave up on the switchover), the\n * ticket is `release()`d and the request leaves the queue.\n *\n * 4. **Granted.** When the number in flight (`replacing` + `granted`) is\n * below `maxConcurrentRestores`, the head of the queue is `granted`: a\n * {@link Grant} is added (counting toward capacity), and `onGranted()`\n * sends `status {restore: {state: 'granted'}}`. The VS keeps the granted\n * connection open, and initiates a self-drain (`drainRequested`): it\n * fails its health checks, drains its clients, and keeps replicating\n * from the old RM in the meantime.\n *\n * 5. **Restart.** The VS process exits, closing the granted connection.\n * The ticket is `release()`d, marking the grant as `released` and\n * starting the pending grant TTL ({@link DEFAULT_PENDING_GRANT_TTL_MS}),\n * within which a replacement reservation is expected. The drain itself\n * does not count against the TTL.\n *\n * 6. **Reservation opened.** The restarted VS sends\n * `reserve-snapshot {taskID, localReplica}`, and\n * `ChangeStreamerImpl.startSnapshotReservation()` calls\n * {@link reservationOpened}:\n * * A `localReplica` of a different replica version (e.g. `R1`) is\n * `replacing`: the reservation is counted, and consumes an outstanding\n * grant (preferring a `released` one), so the number in flight is\n * unchanged.\n * * A `localReplica` of the same replica version is `unclassified` until\n * the reservation is confirmed ({@link reservationConfirmed}) with the\n * backup's minimum watermark. It is `replacing` if its watermark is\n * older (i.e. it cannot catch up from the change log).\n * * No `localReplica` (an empty disk) is not counted.\n *\n * If the VS reached a different RM instead, the grant here expires after\n * the TTL and frees its capacity, while the replacement is counted (as\n * an ungranted one) by the RM that it reached.\n *\n * 7. **Restoring.** The VS restores the backup while holding the\n * reservation's connection open. A retried reservation from the same\n * task that arrives while the previous one is still open replaces it\n * (`SnapshotReservations` `onClose`) but does not end it (`onEnd`), so it\n * remains `replacing`. If the reservation instead closes without\n * subscribing (e.g. a failed or disconnected restore, after which the VS\n * retries), {@link reservationClosed} converts its capacity into a\n * `released` grant, which the retry's replacement reservation consumes\n * (step 6), or which expires after the pending grant TTL (e.g. if the VS\n * was deleted).\n *\n * 8. **Subscribed.** Once restored, the VS subscribes on the same\n * connection at `R2`, which is accepted. `ChangeStreamerImpl.subscribe()`\n * calls {@link reservationSubscribed}, which moves the replacement from\n * its reservation (the key leaves `replacing`) to the subscription\n * (`catchingUp`), and then closes the task's reservation. Its `onEnd`\n * calls {@link reservationClosed}, which is a no-op for the moved key.\n *\n * 9. **Caught up.** The VS catches up on the change stream from the\n * backup's watermark. Once it has acked all changes up to the head of\n * the stream (`Subscriber.caughtUp`), the replacement is released after\n * the readiness grace period, which covers the time for the VS to start\n * serving, and the next queued request is granted. If the VS disconnects\n * first, the replacement is released immediately.\n *\n * A VS that restarts outside of a switchover with an incompatible replica\n * (e.g. after a crash) enters at step 6 without a grant, and its restore\n * consumes capacity all the same, delaying queued requests.\n *\n * ## States\n *\n * | Term | Where | Meaning |\n * | ------------- | ------------------------ | -------------------------------- |\n * | queued | `Entry.state` | Held, waiting for capacity |\n * | granted | `Entry.state`, `#grants` | Permitted to restore, pending a |\n * | | | replacement reservation |\n * | released | `Entry.state` | The held connection closed |\n * | released | `Grant.released` | The granted connection, or an |\n * | | | unsubscribed replacement |\n * | | | reservation, closed; the TTL is |\n * | | | running |\n * | unclassified | `#unclassified` | Same-version reservation, not |\n * | | | yet confirmed |\n * | replacing | `#replacing` | Open replacement reservation |\n * | catching up | `#catchingUp` | Subscribed replacement, not yet |\n * | | | caught up (or within the |\n * | | | readiness grace period) |\n * | in flight | `#inFlight` | `replacing` + `catchingUp` + |\n * | | | `granted`, bounded by |\n * | | | `maxConcurrentRestores` |\n */\nexport class RestoreAdmission {\n readonly #lc: LogContext;\n readonly #maxConcurrentRestores: number;\n readonly #pendingGrantTTLMs: number;\n readonly #readinessGracePeriodMs: number;\n readonly #setTimeout: typeof setTimeout;\n readonly #clearTimeout: typeof clearTimeout;\n\n readonly #replicaVersion: string;\n /** Keys (task IDs) of open replacement reservations. */\n readonly #replacing = new Set<string>();\n /**\n * Replacements whose subscribers have subscribed, but have not caught up\n * (or are within the readiness grace period).\n */\n readonly #catchingUp = new Set<object>();\n /**\n * The local replicas of open reservations with this replica version, which\n * are classified upon confirmation of the reservation's minimum watermark.\n */\n readonly #unclassified = new Map<string, LocalReplica>();\n readonly #grants = new Set<Grant>();\n readonly #queue: Entry[] = [];\n\n constructor(lc: LogContext, opts: RestoreAdmissionOptions) {\n this.#lc = lc.withContext('component', 'restore-admission');\n this.#replicaVersion = opts.replicaVersion;\n this.#maxConcurrentRestores = Math.max(1, opts.maxConcurrentRestores);\n this.#pendingGrantTTLMs =\n opts.pendingGrantTTLMs ?? DEFAULT_PENDING_GRANT_TTL_MS;\n this.#readinessGracePeriodMs =\n opts.readinessGracePeriodMs ?? DEFAULT_READINESS_GRACE_PERIOD_MS;\n this.#setTimeout = opts.setTimeoutFn ?? setTimeout;\n this.#clearTimeout = opts.clearTimeoutFn ?? clearTimeout;\n\n getOrCreateGauge(\n 'replication',\n 'restore_admission.queued',\n 'Switchover subscribers waiting for permission to restore a new replica.',\n ).addCallback(result => result.observe(this.#queue.length));\n getOrCreateGauge(\n 'replication',\n 'restore_admission.in_flight',\n 'Subscribers replacing, or granted permission to replace, their replica.',\n ).addCallback(result => result.observe(this.#inFlight));\n }\n\n get #inFlight() {\n return this.#replacing.size + this.#catchingUp.size + this.#grants.size;\n }\n\n /** For testing and logging. */\n get stats() {\n return {\n replacing: this.#replacing.size + this.#catchingUp.size,\n granted: this.#grants.size,\n queued: this.#queue.length,\n };\n }\n\n /**\n * Called when a snapshot reservation is opened by a subscriber with the\n * given `localReplica` (if any). A subscriber with a local replica of a\n * different replica version is replacing it. One with the same replica\n * version is replacing it if it is outdated, which is determined by\n * {@link reservationConfirmed}. Idempotent for the same `key` (e.g. a\n * retried reservation).\n */\n reservationOpened(key: string, localReplica: LocalReplica | undefined) {\n this.#unclassified.delete(key);\n if (!localReplica) {\n return; // Restoring onto an empty disk.\n }\n if (localReplica.replicaVersion !== this.#replicaVersion) {\n this.#replacementOpened(key);\n } else {\n this.#unclassified.set(key, localReplica);\n }\n }\n\n /**\n * Called when a reservation is confirmed with the minimum watermark of the\n * replica to restore.\n */\n reservationConfirmed(key: string, minWatermark: string) {\n const localReplica = this.#unclassified.get(key);\n this.#unclassified.delete(key);\n if (localReplica && localReplica.watermark < minWatermark) {\n this.#replacementOpened(key); // The local replica is outdated.\n }\n }\n\n /**\n * Called when a reservation ends. A replacement reservation normally ends\n * after its task subscribes ({@link reservationSubscribed}), in which case\n * this is a no-op. A replacement reservation that ends without subscribing\n * (e.g. a failed or disconnected restore) is most likely followed by a\n * retry, so its capacity is retained as a released grant (which the retry\n * consumes) rather than released immediately.\n */\n reservationClosed(key: string) {\n this.#unclassified.delete(key);\n if (this.#replacing.delete(key)) {\n const grant: Grant = {released: true, timer: undefined};\n this.#grants.add(grant);\n this.#startGrantTTL(grant);\n }\n }\n\n /**\n * Called when the task of a reservation subscribes (before the reservation\n * is closed). If the reservation is a replacement, it continues to be\n * counted until the readiness grace period after the subscriber has caught\n * up, or until the subscription closes.\n */\n reservationSubscribed(key: string): Replacement | undefined {\n this.#unclassified.delete(key);\n if (!this.#replacing.delete(key)) {\n return undefined;\n }\n const replacement = {};\n this.#catchingUp.add(replacement);\n let timer: ReturnType<typeof setTimeout> | undefined;\n const release = () => {\n if (timer) {\n this.#clearTimeout(timer);\n }\n if (this.#catchingUp.delete(replacement)) {\n this.#processQueue();\n }\n };\n return {\n caughtUp: () => {\n if (this.#catchingUp.has(replacement) && !timer) {\n timer = this.#setTimeout(release, this.#readinessGracePeriodMs);\n }\n },\n closed: release,\n };\n }\n\n #replacementOpened(key: string) {\n if (this.#replacing.has(key)) {\n return;\n }\n this.#replacing.add(key);\n // The restore is now tracked by its reservation. Prefer consuming a grant\n // whose subscriber has already disconnected, as it is the most likely\n // origin of the reservation.\n const grants = [...this.#grants];\n const grant = grants.find(g => g.released) ?? grants[0];\n if (grant) {\n this.#deleteGrant(grant);\n }\n }\n\n request(request: AdmissionRequest): AdmissionTicket {\n const entry: Entry = {request, state: 'queued', position: 0};\n this.#queue.push(entry);\n this.#processQueue();\n return {release: () => this.#release(entry)};\n }\n\n #release(entry: Entry) {\n const {state, grant} = entry;\n entry.state = 'released';\n if (state === 'queued') {\n const i = this.#queue.indexOf(entry);\n if (i >= 0) {\n this.#queue.splice(i, 1);\n }\n this.#processQueue();\n } else if (state === 'granted' && grant && this.#grants.has(grant)) {\n // Wait for the subscriber to reconnect with a replacement reservation.\n grant.released = true;\n this.#startGrantTTL(grant);\n }\n }\n\n #startGrantTTL(grant: Grant) {\n grant.timer = this.#setTimeout(() => {\n if (this.#grants.has(grant)) {\n this.#lc.info?.(`grant expired without a replacement reservation`);\n this.#deleteGrant(grant);\n this.#processQueue();\n }\n }, this.#pendingGrantTTLMs);\n }\n\n #deleteGrant(grant: Grant) {\n if (grant.timer) {\n this.#clearTimeout(grant.timer);\n }\n this.#grants.delete(grant);\n }\n\n #processQueue() {\n while (this.#inFlight < this.#maxConcurrentRestores && this.#queue.length) {\n const entry = this.#queue.shift()!;\n entry.state = 'granted';\n entry.grant = {released: false, timer: undefined};\n this.#grants.add(entry.grant);\n this.#lc.info?.(`granting permission to restore`, {...this.stats});\n entry.request.onGranted();\n }\n this.#queue.forEach((entry, i) => {\n const position = i + 1;\n if (entry.position !== position) {\n entry.position = position;\n entry.request.onQueued(position);\n }\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"restore-admission.js","names":[],"sources":["../../../../../../zero-cache/src/services/change-streamer/restore-admission.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {getOrCreateGauge} from '../../observability/metrics.ts';\nimport type {LocalReplica} from './subscribe.ts';\n\n/**\n * The default time to wait for a subscriber that was granted permission to\n * restore to reconnect (i.e. to open a replacement reservation) after its\n * held connection closes, or after its replacement reservation closes\n * without subscribing (e.g. to retry a failed restore). This covers the\n * subscriber's process exit and restart (e.g. a container restart, the\n * backoff of which starts at 10 seconds in Kubernetes), but not the restore\n * itself, which is tracked by the liveness of its snapshot reservation.\n */\nconst DEFAULT_PENDING_GRANT_TTL_MS = 90_000;\n\n/**\n * The default time for which a replacement continues to be counted after its\n * subscriber has caught up, which covers the time for the subscriber to start\n * serving (i.e. become ready) after catching up. See\n * {@link RestoreAdmissionOptions.readinessGracePeriodMs}.\n */\nexport const DEFAULT_READINESS_GRACE_PERIOD_MS = 5_000;\n\nexport type RestoreAdmissionOptions = {\n /** The replica version of the replication-manager. */\n replicaVersion: string;\n /**\n * The maximum number of subscribers that may be restoring (or granted\n * permission to restore) at the same time.\n */\n maxConcurrentRestores: number;\n pendingGrantTTLMs?: number | undefined;\n /**\n * The time for which a replacement continues to be counted after its\n * subscriber has caught up, so that the next subscriber is not granted\n * permission to restore (and stop serving) before the restored one has\n * started serving. Catching up is followed by the subscriber's readiness\n * checks (e.g. waiting for the next replication lag report), and by the\n * container runner's detection of its readiness.\n */\n readinessGracePeriodMs?: number | undefined;\n // Injectable timers, for deterministic testing.\n setTimeoutFn?: typeof setTimeout | undefined;\n clearTimeoutFn?: typeof clearTimeout | undefined;\n};\n\nexport type AdmissionRequest = {\n /**\n * Called when the request is queued, and again whenever its (1-based)\n * position in the queue changes.\n */\n onQueued: (position: number) => void;\n\n /** Called (once) when the request is granted. */\n onGranted: () => void;\n};\n\n/** A subscribed replacement, returned by {@link RestoreAdmission.reservationSubscribed}. */\nexport type Replacement = {\n /**\n * Called when the subscriber has caught up. The replacement is released\n * after the readiness grace period.\n */\n caughtUp: () => void;\n\n /** Called when the subscription closes. The replacement is released. */\n closed: () => void;\n};\n\nexport type AdmissionTicket = {\n /**\n * Must be called when the requesting connection closes. A queued request\n * is removed from the queue. For a granted request, this starts the\n * timer within which the subscriber is expected to reconnect and open a\n * replacement reservation.\n */\n release(): void;\n};\n\ntype Entry = {\n readonly request: AdmissionRequest;\n state: 'queued' | 'granted' | 'released';\n position: number;\n grant?: Grant | undefined;\n};\n\ntype Grant = {\n /** Whether the granted subscriber's connection has closed. */\n released: boolean;\n timer: ReturnType<typeof setTimeout> | undefined;\n};\n\n/**\n * Limits the number of view-syncers that replace their replica at the same\n * time, so that view-syncers switching over from an incompatible\n * replication-manager (e.g. after a resync) do not all stop serving at once.\n *\n * Capacity is consumed by:\n * * Replacement restores: snapshot reservations of subscribers that are\n * replacing an (incompatible or outdated) local replica, i.e. subscribers\n * that were serving. Restores onto an empty disk (e.g. a new pod) do not\n * take a serving subscriber out of service, and are not counted. The\n * reservation's connection is held open for the duration of the restore,\n * and the subscription that follows it is held open until the restored\n * subscriber has caught up, so their liveness tracks that of the\n * replacement without a timeout.\n * * Outstanding grants: permission to restore that has not yet been\n * followed by a replacement reservation (e.g. while the subscriber drains\n * and restarts). Grants are not matched to reservations by identity:\n * any replacement reservation consumes an outstanding grant, and a grant\n * that is not consumed expires a short time after the granted connection\n * closes (e.g. if the subscriber reconnected to a different\n * replication-manager or was deleted).\n *\n * ## Life cycle of a switchover restore\n *\n * The following traces a view-syncer (VS) with a replica at version `R1`,\n * served by an old replication-manager (RM) at `R1`, while a new RM (e.g.\n * after a resync) is at `R2 > R1`. The RestoreAdmission is that of the new\n * RM. Terms in `code` are those used in the code.\n *\n * 1. **Drain.** The old RM receives a SIGTERM, and its `DrainTracker` sends\n * `status {draining: true}` to its `drainable` (i.e. serving) subscribers.\n * It continues to stream changes to them, and exits once they have all\n * disconnected.\n *\n * 2. **Switchover.** The VS's `DrainHandler` opens a second `/subscribe`\n * with `switchover: true` at the watermark of its last committed\n * transaction, which is routed to some RM. The VS continues to consume\n * the old RM's stream. If the RM is compatible (i.e. another old RM),\n * the subscription is accepted and the VS simply moves to it. If the RM\n * is the new one, the subscriber's replica version is older, and the\n * subscription proceeds to step 3.\n *\n * 3. **Queued.** `ChangeStreamerImpl.subscribe()` holds the subscription\n * (it is not added to the forwarder) and calls {@link request}. The\n * request is `queued` and `onQueued(position)` sends\n * `status {restore: {state: 'queued', position}}`, again whenever the\n * position changes. If the held connection closes while queued (e.g.\n * the VS's old stream ended and it gave up on the switchover), the\n * ticket is `release()`d and the request leaves the queue.\n *\n * 4. **Granted.** When the number in flight (`replacing` + `granted`) is\n * below `maxConcurrentRestores`, the head of the queue is `granted`: a\n * {@link Grant} is added (counting toward capacity), and `onGranted()`\n * sends `status {restore: {state: 'granted'}}`. The VS keeps the granted\n * connection open, and initiates a self-drain (`drainRequested`): it\n * fails its health checks, drains its clients, and keeps replicating\n * from the old RM in the meantime.\n *\n * 5. **Restart.** The VS process exits, closing the granted connection.\n * The ticket is `release()`d, marking the grant as `released` and\n * starting the pending grant TTL ({@link DEFAULT_PENDING_GRANT_TTL_MS}),\n * within which a replacement reservation is expected. The drain itself\n * does not count against the TTL.\n *\n * 6. **Reservation opened.** The restarted VS sends\n * `reserve-snapshot {taskID, localReplica}`, and\n * `ChangeStreamerImpl.startSnapshotReservation()` calls\n * {@link reservationOpened}:\n * * A `localReplica` of a different replica version (e.g. `R1`) is\n * `replacing`: the reservation is counted, and consumes an outstanding\n * grant (preferring a `released` one), so the number in flight is\n * unchanged.\n * * A `localReplica` of the same replica version is `unclassified` until\n * the reservation is confirmed ({@link reservationConfirmed}) with the\n * backup's minimum watermark. It is `replacing` if its watermark is\n * older (i.e. it cannot catch up from the change log).\n * * No `localReplica` (an empty disk) is not counted.\n *\n * If the VS reached a different RM instead, the grant here expires after\n * the TTL and frees its capacity, while the replacement is counted (as\n * an ungranted one) by the RM that it reached.\n *\n * 7. **Restoring.** The VS restores the backup while holding the\n * reservation's connection open. A retried reservation from the same\n * task that arrives while the previous one is still open replaces it\n * (`SnapshotReservations` `onClose`) but does not end it (`onEnd`), so it\n * remains `replacing`. If the reservation instead closes without\n * subscribing (e.g. a failed or disconnected restore, after which the VS\n * retries), {@link reservationClosed} converts its capacity into a\n * `released` grant, which the retry's replacement reservation consumes\n * (step 6), or which expires after the pending grant TTL (e.g. if the VS\n * was deleted).\n *\n * 8. **Subscribed.** Once restored, the VS subscribes on the same\n * connection at `R2`, which is accepted. `ChangeStreamerImpl.subscribe()`\n * calls {@link reservationSubscribed}, which moves the replacement from\n * its reservation (the key leaves `replacing`) to the subscription\n * (`catchingUp`), and then closes the task's reservation. Its `onEnd`\n * calls {@link reservationClosed}, which is a no-op for the moved key.\n *\n * 9. **Caught up.** The VS catches up on the change stream from the\n * backup's watermark. Once it has acked all changes up to the head of\n * the stream (`Subscriber.caughtUp`), the replacement is released after\n * the readiness grace period, which covers the time for the VS to start\n * serving, and the next queued request is granted. If the VS disconnects\n * first, the replacement is released immediately.\n *\n * A VS that restarts outside of a switchover with an incompatible replica\n * (e.g. after a crash) enters at step 6 without a grant, and its restore\n * consumes capacity all the same, delaying queued requests.\n *\n * ## States\n *\n * | Term | Where | Meaning |\n * | ------------- | ------------------------ | -------------------------------- |\n * | queued | `Entry.state` | Held, waiting for capacity |\n * | granted | `Entry.state`, `#grants` | Permitted to restore, pending a |\n * | | | replacement reservation |\n * | released | `Entry.state` | The held connection closed |\n * | released | `Grant.released` | The granted connection, or an |\n * | | | unsubscribed replacement |\n * | | | reservation, closed; the TTL is |\n * | | | running |\n * | unclassified | `#unclassified` | Same-version reservation, not |\n * | | | yet confirmed |\n * | replacing | `#replacing` | Open replacement reservation |\n * | catching up | `#catchingUp` | Subscribed replacement, not yet |\n * | | | caught up (or within the |\n * | | | readiness grace period) |\n * | in flight | `#inFlight` | `replacing` + `catchingUp` + |\n * | | | `granted`, bounded by |\n * | | | `maxConcurrentRestores` |\n */\nexport class RestoreAdmission {\n readonly #lc: LogContext;\n readonly #maxConcurrentRestores: number;\n readonly #pendingGrantTTLMs: number;\n readonly #readinessGracePeriodMs: number;\n readonly #setTimeout: typeof setTimeout;\n readonly #clearTimeout: typeof clearTimeout;\n\n readonly #replicaVersion: string;\n /** Keys (task IDs) of open replacement reservations. */\n readonly #replacing = new Set<string>();\n /**\n * Replacements whose subscribers have subscribed, but have not caught up\n * (or are within the readiness grace period).\n */\n readonly #catchingUp = new Set<object>();\n /**\n * The local replicas of open reservations with this replica version, which\n * are classified upon confirmation of the reservation's minimum watermark.\n */\n readonly #unclassified = new Map<string, LocalReplica>();\n readonly #grants = new Set<Grant>();\n readonly #queue: Entry[] = [];\n\n constructor(lc: LogContext, opts: RestoreAdmissionOptions) {\n this.#lc = lc.withContext('component', 'restore-admission');\n this.#replicaVersion = opts.replicaVersion;\n this.#maxConcurrentRestores = Math.max(1, opts.maxConcurrentRestores);\n this.#pendingGrantTTLMs =\n opts.pendingGrantTTLMs ?? DEFAULT_PENDING_GRANT_TTL_MS;\n this.#readinessGracePeriodMs =\n opts.readinessGracePeriodMs ?? DEFAULT_READINESS_GRACE_PERIOD_MS;\n this.#setTimeout = opts.setTimeoutFn ?? setTimeout;\n this.#clearTimeout = opts.clearTimeoutFn ?? clearTimeout;\n\n getOrCreateGauge(\n 'replication',\n 'restore_admission.queued',\n 'Switchover subscribers waiting for permission to restore a new replica.',\n ).addCallback(result => result.observe(this.#queue.length));\n getOrCreateGauge(\n 'replication',\n 'restore_admission.in_flight',\n 'Subscribers replacing, or granted permission to replace, their replica.',\n ).addCallback(result => result.observe(this.#inFlight));\n }\n\n get #inFlight() {\n return this.#replacing.size + this.#catchingUp.size + this.#grants.size;\n }\n\n /** For testing and logging. */\n get stats() {\n return {\n replacing: this.#replacing.size + this.#catchingUp.size,\n granted: this.#grants.size,\n queued: this.#queue.length,\n };\n }\n\n /**\n * Called when a snapshot reservation is opened by a subscriber with the\n * given `localReplica` (if any). A subscriber with a local replica of a\n * different replica version is replacing it. One with the same replica\n * version is replacing it if it is outdated, which is determined by\n * {@link reservationConfirmed}. Idempotent for the same `key` (e.g. a\n * retried reservation).\n */\n reservationOpened(key: string, localReplica: LocalReplica | undefined) {\n this.#unclassified.delete(key);\n if (!localReplica) {\n return; // Restoring onto an empty disk.\n }\n if (localReplica.replicaVersion !== this.#replicaVersion) {\n this.#replacementOpened(key);\n } else {\n this.#unclassified.set(key, localReplica);\n }\n }\n\n /**\n * Called when a reservation is confirmed with the minimum watermark of the\n * replica to restore.\n */\n reservationConfirmed(key: string, minWatermark: string) {\n const localReplica = this.#unclassified.get(key);\n this.#unclassified.delete(key);\n if (localReplica && localReplica.watermark < minWatermark) {\n this.#replacementOpened(key); // The local replica is outdated.\n }\n }\n\n /**\n * Called when a reservation ends. A replacement reservation normally ends\n * after its task subscribes ({@link reservationSubscribed}), in which case\n * this is a no-op. A replacement reservation that ends without subscribing\n * (e.g. a failed or disconnected restore) is most likely followed by a\n * retry, so its capacity is retained as a released grant (which the retry\n * consumes) rather than released immediately.\n */\n reservationClosed(key: string) {\n this.#unclassified.delete(key);\n if (this.#replacing.delete(key)) {\n const grant: Grant = {released: true, timer: undefined};\n this.#grants.add(grant);\n this.#startGrantTTL(grant);\n }\n }\n\n /**\n * Called when the task of a reservation subscribes (before the reservation\n * is closed). If the reservation is a replacement, it continues to be\n * counted until the readiness grace period after the subscriber has caught\n * up, or until the subscription closes.\n */\n reservationSubscribed(key: string): Replacement | undefined {\n this.#unclassified.delete(key);\n if (!this.#replacing.delete(key)) {\n return undefined;\n }\n const replacement = {};\n this.#catchingUp.add(replacement);\n let timer: ReturnType<typeof setTimeout> | undefined;\n const release = () => {\n if (timer) {\n this.#clearTimeout(timer);\n }\n if (this.#catchingUp.delete(replacement)) {\n this.#processQueue();\n }\n };\n return {\n caughtUp: () => {\n if (this.#catchingUp.has(replacement) && !timer) {\n timer = this.#setTimeout(release, this.#readinessGracePeriodMs);\n }\n },\n closed: release,\n };\n }\n\n #replacementOpened(key: string) {\n if (this.#replacing.has(key)) {\n return;\n }\n this.#replacing.add(key);\n // The restore is now tracked by its reservation. Prefer consuming a grant\n // whose subscriber has already disconnected, as it is the most likely\n // origin of the reservation.\n const grants = [...this.#grants];\n const grant = grants.find(g => g.released) ?? grants[0];\n if (grant) {\n this.#deleteGrant(grant);\n }\n }\n\n request(request: AdmissionRequest): AdmissionTicket {\n const entry: Entry = {request, state: 'queued', position: 0};\n this.#queue.push(entry);\n this.#processQueue();\n return {release: () => this.#release(entry)};\n }\n\n #release(entry: Entry) {\n const {state, grant} = entry;\n entry.state = 'released';\n if (state === 'queued') {\n const i = this.#queue.indexOf(entry);\n if (i >= 0) {\n this.#queue.splice(i, 1);\n }\n this.#processQueue();\n } else if (state === 'granted' && grant && this.#grants.has(grant)) {\n // Wait for the subscriber to reconnect with a replacement reservation.\n grant.released = true;\n this.#startGrantTTL(grant);\n }\n }\n\n #startGrantTTL(grant: Grant) {\n grant.timer = this.#setTimeout(() => {\n if (this.#grants.has(grant)) {\n this.#lc.info?.(`grant expired without a replacement reservation`);\n this.#deleteGrant(grant);\n this.#processQueue();\n }\n }, this.#pendingGrantTTLMs);\n }\n\n #deleteGrant(grant: Grant) {\n if (grant.timer) {\n this.#clearTimeout(grant.timer);\n }\n this.#grants.delete(grant);\n }\n\n #processQueue() {\n while (this.#inFlight < this.#maxConcurrentRestores && this.#queue.length) {\n const entry = this.#queue.shift()!;\n entry.state = 'granted';\n entry.grant = {released: false, timer: undefined};\n this.#grants.add(entry.grant);\n this.#lc.info?.(`granting permission to restore`, {...this.stats});\n entry.request.onGranted();\n }\n this.#queue.forEach((entry, i) => {\n const position = i + 1;\n if (entry.position !== position) {\n entry.position = position;\n entry.request.onQueued(position);\n }\n });\n }\n}\n"],"mappings":"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"}
@@ -65,6 +65,6 @@ async function getLastWatermarkV2(db, shard) {
65
65
  return replicaVersion;
66
66
  }
67
67
  //#endregion
68
- export { initChangeStreamerSchema };
68
+ export { getLastWatermarkV2, initChangeStreamerSchema };
69
69
 
70
70
  //# sourceMappingURL=init.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","names":[],"sources":["../../../../../../../zero-cache/src/services/change-streamer/schema/init.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {\n runSchemaMigrations,\n type IncrementalMigrationMap,\n type Migration,\n} from '../../../db/migration.ts';\nimport type {PostgresDB, PostgresTransaction} from '../../../types/pg.ts';\nimport {cdcSchema, type ShardID} from '../../../types/shards.ts';\nimport {\n createBackfillTables,\n createReplicationStateTable,\n setupCDCTables,\n type ReplicationState,\n} from './tables.ts';\n\nasync function migrateFromLegacySchemas(\n lc: LogContext,\n db: PostgresDB,\n newSchema: string,\n ...legacy: string[]\n) {\n const rows = await db<{nspname: string}[]>`\n SELECT nspname FROM pg_namespace \n WHERE nspname IN ${db([newSchema, ...legacy])}`.values();\n // oxlint-disable-next-line unicorn/prefer-set-has -- Small collection, array is more appropriate\n const names = rows.flat();\n if (names.includes(newSchema)) {\n return; // already migrated\n }\n for (const schema of legacy) {\n if (names.includes(schema)) {\n lc.info?.(`Migrating ${schema} to ${newSchema}`);\n await db`ALTER SCHEMA ${db(schema)} RENAME TO ${db(newSchema)}`;\n break;\n }\n }\n}\n\nexport async function initChangeStreamerSchema(\n log: LogContext,\n db: PostgresDB,\n shard: ShardID,\n): Promise<void> {\n const schema = cdcSchema(shard);\n const {appID} = shard;\n await migrateFromLegacySchemas(\n log,\n db,\n schema,\n appID === 'zero' ? `cdc_0` : `cdc_${appID}`,\n 'cdc',\n );\n\n const setupMigration: Migration = {\n migrateSchema: (lc, tx) => setupCDCTables(lc, tx, shard),\n minSafeVersion: 1,\n };\n\n async function migrateV1toV2(_: LogContext, db: PostgresTransaction) {\n await db`\n ALTER TABLE ${db(schema)}.\"replicationConfig\" ADD \"resetRequired\" BOOL`;\n }\n\n const migrateV2ToV3 = {\n migrateSchema: async (_: LogContext, db: PostgresTransaction) => {\n await db.unsafe(createReplicationStateTable(shard));\n },\n\n migrateData: async (_: LogContext, db: PostgresTransaction) => {\n const lastWatermark = await getLastWatermarkV2(db, shard);\n const replicationState: Partial<ReplicationState> = {lastWatermark};\n await db`\n TRUNCATE TABLE ${db(schema)}.\"replicationState\"`;\n await db`\n INSERT INTO ${db(schema)}.\"replicationState\" ${db(replicationState)}`;\n },\n };\n\n const migrateV3ToV4 = {\n migrateSchema: async (_: LogContext, db: PostgresTransaction) => {\n await db`\n ALTER TABLE ${db(schema)}.\"changeLog\" ALTER \"change\" TYPE JSON;\n `;\n },\n };\n\n const migrateV4ToV5 = {\n migrateSchema: async (_: LogContext, db: PostgresTransaction) => {\n await db`\n ALTER TABLE ${db(schema)}.\"replicationState\" ADD \"ownerAddress\" TEXT;\n `;\n },\n };\n\n const migrateV5ToV6 = {\n migrateSchema: async (_: LogContext, db: PostgresTransaction) => {\n await db.unsafe(createBackfillTables(shard));\n },\n };\n\n const schemaVersionMigrationMap: IncrementalMigrationMap = {\n 2: {migrateSchema: migrateV1toV2},\n 3: migrateV2ToV3,\n 4: migrateV3ToV4,\n 5: migrateV4ToV5,\n 6: migrateV5ToV6,\n };\n\n await runSchemaMigrations(\n log,\n 'change-streamer',\n schema,\n db,\n setupMigration,\n schemaVersionMigrationMap,\n );\n}\n\nexport async function getLastWatermarkV2(\n db: PostgresDB,\n shard: ShardID,\n): Promise<string> {\n const schema = cdcSchema(shard);\n const [{max}] = await db<{max: string | null}[]>`\n SELECT MAX(watermark) as max FROM ${db(schema)}.\"changeLog\"`;\n if (max !== null) {\n return max;\n }\n // The changeLog is only empty if nothing has been synced since initial-sync.\n // In this case, the last watermark is the replicaVersion.\n const [{replicaVersion}] = await db<{replicaVersion: string}[]>`\n SELECT \"replicaVersion\" FROM ${db(schema)}.\"replicationConfig\"\n `;\n return replicaVersion;\n}\n"],"mappings":";;;;AAeA,eAAe,yBACb,IACA,IACA,WACA,GAAG,QACH;CAKA,MAAM,SAAQ,MAJK,EAAuB;;yBAEnB,GAAG,CAAC,WAAW,GAAG,MAAM,CAAC,IAAI,OAAO,GAExC,KAAK;CACxB,IAAI,MAAM,SAAS,SAAS,GAC1B;CAEF,KAAK,MAAM,UAAU,QACnB,IAAI,MAAM,SAAS,MAAM,GAAG;EAC1B,GAAG,OAAO,aAAa,OAAO,MAAM,WAAW;EAC/C,MAAM,EAAE,gBAAgB,GAAG,MAAM,EAAE,aAAa,GAAG,SAAS;EAC5D;CACF;AAEJ;AAEA,eAAsB,yBACpB,KACA,IACA,OACe;CACf,MAAM,SAAS,UAAU,KAAK;CAC9B,MAAM,EAAC,UAAS;CAChB,MAAM,yBACJ,KACA,IACA,QACA,UAAU,SAAS,UAAU,OAAO,SACpC,KACF;CAEA,MAAM,iBAA4B;EAChC,gBAAgB,IAAI,OAAO,eAAe,IAAI,IAAI,KAAK;EACvD,gBAAgB;CAClB;CAEA,eAAe,cAAc,GAAe,IAAyB;EACnE,MAAM,EAAE;kBACM,GAAG,MAAM,EAAE;CAC3B;CA+CA,MAAM,oBACJ,KACA,mBACA,QACA,IACA,gBACA;EAbA,GAAG,EAAC,eAAe,cAAa;EAChC,GAAG;GAtCH,eAAe,OAAO,GAAe,OAA4B;IAC/D,MAAM,GAAG,OAAO,4BAA4B,KAAK,CAAC;GACpD;GAEA,aAAa,OAAO,GAAe,OAA4B;IAE7D,MAAM,mBAA8C,EAAC,eAAA,MADzB,mBAAmB,IAAI,KAAK,EACU;IAClE,MAAM,EAAE;uBACS,GAAG,MAAM,EAAE;IAC5B,MAAM,EAAE;oBACM,GAAG,MAAM,EAAE,sBAAsB,GAAG,gBAAgB;GACpE;EA2BG;EACH,GAAG,EAxBH,eAAe,OAAO,GAAe,OAA4B;GAC/D,MAAM,EAAE;oBACM,GAAG,MAAM,EAAE;;EAE3B,EAoBG;EACH,GAAG,EAjBH,eAAe,OAAO,GAAe,OAA4B;GAC/D,MAAM,EAAE;oBACM,GAAG,MAAM,EAAE;;EAE3B,EAaG;EACH,GAAG,EAVH,eAAe,OAAO,GAAe,OAA4B;GAC/D,MAAM,GAAG,OAAO,qBAAqB,KAAK,CAAC;EAC7C,EAQG;CASH,CACF;AACF;AAEA,eAAsB,mBACpB,IACA,OACiB;CACjB,MAAM,SAAS,UAAU,KAAK;CAC9B,MAAM,CAAC,EAAC,SAAQ,MAAM,EAA0B;wCACV,GAAG,MAAM,EAAE;CACjD,IAAI,QAAQ,MACV,OAAO;CAIT,MAAM,CAAC,EAAC,oBAAmB,MAAM,EAA8B;mCAC9B,GAAG,MAAM,EAAE;;CAE5C,OAAO;AACT"}
1
+ {"version":3,"file":"init.js","names":[],"sources":["../../../../../../../zero-cache/src/services/change-streamer/schema/init.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {\n runSchemaMigrations,\n type IncrementalMigrationMap,\n type Migration,\n} from '../../../db/migration.ts';\nimport type {PostgresDB, PostgresTransaction} from '../../../types/pg.ts';\nimport {cdcSchema, type ShardID} from '../../../types/shards.ts';\nimport {\n createBackfillTables,\n createReplicationStateTable,\n setupCDCTables,\n type ReplicationState,\n} from './tables.ts';\n\nasync function migrateFromLegacySchemas(\n lc: LogContext,\n db: PostgresDB,\n newSchema: string,\n ...legacy: string[]\n) {\n const rows = await db<{nspname: string}[]>`\n SELECT nspname FROM pg_namespace \n WHERE nspname IN ${db([newSchema, ...legacy])}`.values();\n // oxlint-disable-next-line unicorn/prefer-set-has -- Small collection, array is more appropriate\n const names = rows.flat();\n if (names.includes(newSchema)) {\n return; // already migrated\n }\n for (const schema of legacy) {\n if (names.includes(schema)) {\n lc.info?.(`Migrating ${schema} to ${newSchema}`);\n await db`ALTER SCHEMA ${db(schema)} RENAME TO ${db(newSchema)}`;\n break;\n }\n }\n}\n\nexport async function initChangeStreamerSchema(\n log: LogContext,\n db: PostgresDB,\n shard: ShardID,\n): Promise<void> {\n const schema = cdcSchema(shard);\n const {appID} = shard;\n await migrateFromLegacySchemas(\n log,\n db,\n schema,\n appID === 'zero' ? `cdc_0` : `cdc_${appID}`,\n 'cdc',\n );\n\n const setupMigration: Migration = {\n migrateSchema: (lc, tx) => setupCDCTables(lc, tx, shard),\n minSafeVersion: 1,\n };\n\n async function migrateV1toV2(_: LogContext, db: PostgresTransaction) {\n await db`\n ALTER TABLE ${db(schema)}.\"replicationConfig\" ADD \"resetRequired\" BOOL`;\n }\n\n const migrateV2ToV3 = {\n migrateSchema: async (_: LogContext, db: PostgresTransaction) => {\n await db.unsafe(createReplicationStateTable(shard));\n },\n\n migrateData: async (_: LogContext, db: PostgresTransaction) => {\n const lastWatermark = await getLastWatermarkV2(db, shard);\n const replicationState: Partial<ReplicationState> = {lastWatermark};\n await db`\n TRUNCATE TABLE ${db(schema)}.\"replicationState\"`;\n await db`\n INSERT INTO ${db(schema)}.\"replicationState\" ${db(replicationState)}`;\n },\n };\n\n const migrateV3ToV4 = {\n migrateSchema: async (_: LogContext, db: PostgresTransaction) => {\n await db`\n ALTER TABLE ${db(schema)}.\"changeLog\" ALTER \"change\" TYPE JSON;\n `;\n },\n };\n\n const migrateV4ToV5 = {\n migrateSchema: async (_: LogContext, db: PostgresTransaction) => {\n await db`\n ALTER TABLE ${db(schema)}.\"replicationState\" ADD \"ownerAddress\" TEXT;\n `;\n },\n };\n\n const migrateV5ToV6 = {\n migrateSchema: async (_: LogContext, db: PostgresTransaction) => {\n await db.unsafe(createBackfillTables(shard));\n },\n };\n\n const schemaVersionMigrationMap: IncrementalMigrationMap = {\n 2: {migrateSchema: migrateV1toV2},\n 3: migrateV2ToV3,\n 4: migrateV3ToV4,\n 5: migrateV4ToV5,\n 6: migrateV5ToV6,\n };\n\n await runSchemaMigrations(\n log,\n 'change-streamer',\n schema,\n db,\n setupMigration,\n schemaVersionMigrationMap,\n );\n}\n\nexport async function getLastWatermarkV2(\n db: PostgresDB,\n shard: ShardID,\n): Promise<string> {\n const schema = cdcSchema(shard);\n const [{max}] = await db<{max: string | null}[]>`\n SELECT MAX(watermark) as max FROM ${db(schema)}.\"changeLog\"`;\n if (max !== null) {\n return max;\n }\n // The changeLog is only empty if nothing has been synced since initial-sync.\n // In this case, the last watermark is the replicaVersion.\n const [{replicaVersion}] = await db<{replicaVersion: string}[]>`\n SELECT \"replicaVersion\" FROM ${db(schema)}.\"replicationConfig\"\n `;\n return replicaVersion;\n}\n"],"mappings":";;;;AAeA,eAAe,yBACb,IACA,IACA,WACA,GAAG,QACH;CAKA,MAAM,SAAQ,MAJK,EAAuB;;yBAEnB,GAAG,CAAC,WAAW,GAAG,MAAM,CAAC,IAAI,OAAO,EAAA,CAExC,KAAK;CACxB,IAAI,MAAM,SAAS,SAAS,GAC1B;CAEF,KAAK,MAAM,UAAU,QACnB,IAAI,MAAM,SAAS,MAAM,GAAG;EAC1B,GAAG,OAAO,aAAa,OAAO,MAAM,WAAW;EAC/C,MAAM,EAAE,gBAAgB,GAAG,MAAM,EAAE,aAAa,GAAG,SAAS;EAC5D;CACF;AAEJ;AAEA,eAAsB,yBACpB,KACA,IACA,OACe;CACf,MAAM,SAAS,UAAU,KAAK;CAC9B,MAAM,EAAC,UAAS;CAChB,MAAM,yBACJ,KACA,IACA,QACA,UAAU,SAAS,UAAU,OAAO,SACpC,KACF;CAEA,MAAM,iBAA4B;EAChC,gBAAgB,IAAI,OAAO,eAAe,IAAI,IAAI,KAAK;EACvD,gBAAgB;CAClB;CAEA,eAAe,cAAc,GAAe,IAAyB;EACnE,MAAM,EAAE;kBACM,GAAG,MAAM,EAAE;CAC3B;CA+CA,MAAM,oBACJ,KACA,mBACA,QACA,IACA,gBACA;EAbA,GAAG,EAAC,eAAe,cAAa;EAChC,GAAG;GAtCH,eAAe,OAAO,GAAe,OAA4B;IAC/D,MAAM,GAAG,OAAO,4BAA4B,KAAK,CAAC;GACpD;GAEA,aAAa,OAAO,GAAe,OAA4B;IAE7D,MAAM,mBAA8C,EAAC,eAAA,MADzB,mBAAmB,IAAI,KAAK,EACU;IAClE,MAAM,EAAE;uBACS,GAAG,MAAM,EAAE;IAC5B,MAAM,EAAE;oBACM,GAAG,MAAM,EAAE,sBAAsB,GAAG,gBAAgB;GACpE;EA2BG;EACH,GAAG,EAxBH,eAAe,OAAO,GAAe,OAA4B;GAC/D,MAAM,EAAE;oBACM,GAAG,MAAM,EAAE;;EAE3B,EAoBG;EACH,GAAG,EAjBH,eAAe,OAAO,GAAe,OAA4B;GAC/D,MAAM,EAAE;oBACM,GAAG,MAAM,EAAE;;EAE3B,EAaG;EACH,GAAG,EAVH,eAAe,OAAO,GAAe,OAA4B;GAC/D,MAAM,GAAG,OAAO,qBAAqB,KAAK,CAAC;EAC7C,EAQG;CASH,CACF;AACF;AAEA,eAAsB,mBACpB,IACA,OACiB;CACjB,MAAM,SAAS,UAAU,KAAK;CAC9B,MAAM,CAAC,EAAC,SAAQ,MAAM,EAA0B;wCACV,GAAG,MAAM,EAAE;CACjD,IAAI,QAAQ,MACV,OAAO;CAIT,MAAM,CAAC,EAAC,oBAAmB,MAAM,EAA8B;mCAC9B,GAAG,MAAM,EAAE;;CAE5C,OAAO;AACT"}
@@ -313,6 +313,6 @@ async function terminateChangeDBLockHolders(lc, db, shard) {
313
313
  else for (const { pid, applicationName, query, terminated: ok } of terminated) lc.info?.(`terminated blocking backend pid=${pid} app=${applicationName} ok=${ok} query=${query.slice(0, 200)}`);
314
314
  }
315
315
  //#endregion
316
- export { AutoResetSignal, checkReplicationConfig, createBackfillTables, createReplicationStateTable, discoverChangeStreamerAddress, ensureReplicationConfig, markResetRequired, reinitializeChangeLog, setupCDCTables };
316
+ export { AutoResetSignal, CHANGE_STREAMER_APP_NAME, checkReplicationConfig, createBackfillTables, createReplicationStateTable, discoverChangeStreamerAddress, ensureReplicationConfig, markResetRequired, reinitializeChangeLog, setupCDCTables };
317
317
 
318
318
  //# sourceMappingURL=tables.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tables.js","names":[],"sources":["../../../../../../../zero-cache/src/services/change-streamer/schema/tables.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {ident} from 'pg-format';\nimport type postgres from 'postgres';\nimport {type PendingQuery, type Row} from 'postgres';\nimport {AbortError} from '../../../../../shared/src/abort-error.ts';\nimport {equals} from '../../../../../shared/src/set-utils.ts';\nimport {runTx} from '../../../db/run-transaction.ts';\nimport {type PostgresDB, type PostgresTransaction} from '../../../types/pg.ts';\nimport {cdcSchema, type ShardID} from '../../../types/shards.ts';\nimport type {\n BackfillID,\n Change,\n TableMetadata,\n} from '../../change-source/protocol/current/data.ts';\nimport type {SubscriptionState} from '../../replicator/schema/replication-state.ts';\n\n// For readability in the sql statements.\nfunction schema(shard: ShardID) {\n return ident(cdcSchema(shard));\n}\n\nexport const PG_SCHEMA = 'cdc';\n\nfunction createSchema(shard: ShardID) {\n return /*sql*/ `CREATE SCHEMA IF NOT EXISTS ${schema(shard)};`;\n}\n\nexport type ChangeLogEntry = {\n // A strictly monotonically increasing, lexicographically sortable\n // value that uniquely identifies a position in the change stream.\n watermark: string;\n change: Change;\n};\n\ntype FullChangeLogEntry = ChangeLogEntry & {pos: number};\n\nfunction createChangeLogTable(shard: ShardID) {\n // Note: The \"change\" column used to be JSONB, but that was problematic in that\n // it does not handle the NULL unicode character.\n // https://vladimir.varank.in/notes/2021/01/you-dont-insert-unicode-null-character-as-postgres-jsonb/\n return /*sql*/ `\n CREATE TABLE ${schema(shard)}.\"changeLog\" (\n watermark TEXT,\n pos INT8,\n change JSON NOT NULL,\n precommit TEXT, -- Only exists on commit entries. Purely for debugging.\n PRIMARY KEY (watermark, pos)\n );\n`;\n}\n\n/**\n * Tracks the watermark from which to resume the change stream and the\n * current owner (task ID) acting as the single writer to the changeLog.\n */\nexport type ReplicationState = {\n lastWatermark: string;\n owner: string | null;\n ownerAddress: string | null;\n};\n\nexport function createReplicationStateTable(shard: ShardID) {\n return /*sql*/ `\n CREATE TABLE ${schema(shard)}.\"replicationState\" (\n \"lastWatermark\" TEXT NOT NULL,\n \"owner\" TEXT,\n \"ownerAddress\" TEXT,\n \"lock\" INTEGER PRIMARY KEY DEFAULT 1 CHECK (lock=1)\n );\n`;\n}\n\nexport async function discoverChangeStreamerAddress(\n shard: ShardID,\n sql: PostgresDB,\n): Promise<string | null> {\n const result = await sql<{ownerAddress: string | null}[]> /*sql*/ `\n SELECT \"ownerAddress\" FROM ${sql(cdcSchema(shard))}.\"replicationState\"`;\n return result[0].ownerAddress;\n}\n\n/**\n * This mirrors the analogously named table in the SQLite replica\n * (`services/replicator/schema/replication-state.ts`), and is used\n * to detect when the replica has been reset and is no longer compatible\n * with the current ChangeLog.\n */\nexport type ReplicationConfig = {\n replicaVersion: string;\n publications: readonly string[];\n};\n\nfunction createReplicationConfigTable(shard: ShardID) {\n return /*sql*/ `\n CREATE TABLE ${schema(shard)}.\"replicationConfig\" (\n \"replicaVersion\" TEXT NOT NULL,\n \"publications\" TEXT[] NOT NULL,\n \"resetRequired\" BOOL,\n \"lock\" INTEGER PRIMARY KEY DEFAULT 1 CHECK (lock=1)\n );\n`;\n}\n\nexport function createBackfillTables(shard: ShardID) {\n return /*sql*/ `\n CREATE TABLE ${schema(shard)}.\"tableMetadata\" (\n \"schema\" TEXT NOT NULL,\n \"table\" TEXT NOT NULL,\n \"metadata\" JSONB NOT NULL,\n PRIMARY KEY(\"schema\", \"table\")\n );\n\n CREATE TABLE ${schema(shard)}.\"backfilling\" (\n \"schema\" TEXT NOT NULL,\n \"table\" TEXT NOT NULL,\n \"column\" TEXT NOT NULL,\n \"backfill\" JSONB NOT NULL,\n PRIMARY KEY(\"schema\", \"table\", \"column\")\n );\n `;\n}\n\nexport type TableMetadataRow = {\n schema: string;\n table: string;\n metadata: TableMetadata;\n};\n\nexport type BackfillingColumn = {\n schema: string;\n table: string;\n column: string;\n backfill: BackfillID;\n};\n\nfunction createTables(shard: ShardID) {\n return (\n createSchema(shard) +\n createChangeLogTable(shard) +\n createReplicationStateTable(shard) +\n createReplicationConfigTable(shard) +\n createBackfillTables(shard)\n );\n}\n\ninterface PurgeLock {\n release(): Promise<void>;\n}\n\nexport async function setupCDCTables(\n lc: LogContext,\n db: postgres.TransactionSql,\n shard: ShardID,\n) {\n lc.info?.(`Setting up CDC tables`);\n await db.unsafe(createTables(shard));\n}\n\nexport async function markResetRequired(sql: PostgresDB, shard: ShardID) {\n const schema = cdcSchema(shard);\n await sql`\n UPDATE ${sql(schema)}.\"replicationConfig\"\n SET \"resetRequired\" = true`;\n}\n\ntype ReplicationConfigRow = ReplicationConfig & {\n resetRequired: boolean | null;\n};\n\nexport type ReplicaSubscriptionState = Pick<\n SubscriptionState,\n 'publications' | 'replicaVersion' | 'watermark'\n>;\n\n/**\n * Validates the `replicationConfig` of the change db against the replica,\n * throwing an {@link AutoResetSignal} if the replica cannot be served from it.\n *\n * @param behindSlot Whether the change log is behind the replication slot,\n * in which case it is re-initialized from the replica (see\n * {@link reinitializeChangeLog}) and can thus be reset for a replica\n * at any watermark.\n * @returns `'init'` if the tables are uninitialized, `'reset'` if they are\n * for a different replica, or `'compatible'`.\n */\nfunction validateReplicationConfig(\n lc: LogContext,\n current: ReplicationConfigRow | undefined,\n {publications, replicaVersion, watermark}: ReplicaSubscriptionState,\n autoReset: boolean,\n behindSlot: boolean,\n): 'init' | 'reset' | 'compatible' {\n if (!current) {\n return 'init';\n }\n if (\n current.replicaVersion !== replicaVersion ||\n !equals(new Set(current.publications), new Set(publications))\n ) {\n if (replicaVersion !== watermark && !behindSlot) {\n throw new AutoResetSignal(\n `Cannot reset change db@${current.replicaVersion} to ` +\n `service replica@${replicaVersion} from watermark ${watermark}`,\n );\n }\n return 'reset';\n }\n if (current.resetRequired) {\n if (autoReset) {\n throw new AutoResetSignal('reset required by replication stream');\n }\n lc.error?.(\n '\\n\\n\\n' +\n 'Reset required but --auto-reset is not enabled.\\n' +\n 'This can happen for upstream databases that do not support event triggers.\\n' +\n 'To correct this, see https://zero.rocicorp.dev/docs/connecting-to-postgres#schema-changes' +\n '\\n\\n\\n',\n );\n }\n return 'compatible';\n}\n\nfunction selectReplicationConfig(sql: PostgresTransaction, shard: ShardID) {\n return sql<ReplicationConfigRow[]> /*sql*/ `\n SELECT \"replicaVersion\", \"publications\", \"resetRequired\" \n FROM ${sql(cdcSchema(shard))}.\"replicationConfig\"`;\n}\n\n/**\n * Checks, without modifying the change db, that the replica can be served\n * by the change log once {@link ensureReplicationConfig} is called upon\n * taking over the change log. Throws an {@link AutoResetSignal} otherwise.\n *\n * This allows a replication-manager to detect an incompatible replica at\n * startup while leaving the change log to its current owner until then.\n */\nexport async function checkReplicationConfig(\n lc: LogContext,\n db: PostgresDB,\n subscriptionState: ReplicaSubscriptionState,\n shard: ShardID,\n autoReset: boolean,\n behindSlot = false,\n): Promise<void> {\n const [current] = await runTx(db, sql => selectReplicationConfig(sql, shard));\n const status = validateReplicationConfig(\n lc,\n current,\n subscriptionState,\n autoReset,\n behindSlot,\n );\n if (status === 'reset') {\n lc.info?.(\n `Data in cdc tables @${current?.replicaVersion} is incompatible ` +\n `with replica @${subscriptionState.replicaVersion}. ` +\n `Tables will be reset when taking over the change log.`,\n );\n }\n}\n\n/**\n * Initializes the cdc tables for the replica, resetting them if they were\n * written for a different replica (version or publications). This disrupts\n * a replication-manager that is serving the change log, and is thus only\n * done when taking over the change log (see `Storer.assumeOwnershipAtHead`).\n *\n * @param purgeLock A purge lock held by the caller, if any, which is released\n * before the tables are reset.\n * @param behindSlot See {@link checkReplicationConfig}.\n */\nexport async function ensureReplicationConfig(\n lc: LogContext,\n db: PostgresDB,\n subscriptionState: ReplicaSubscriptionState,\n shard: ShardID,\n autoReset: boolean,\n purgeLock?: PurgeLock,\n setTimeoutFn: typeof setTimeout = setTimeout,\n behindSlot = false,\n) {\n const {publications, replicaVersion} = subscriptionState;\n const replicaConfig = {publications, replicaVersion};\n const replicationState: ReplicationState = {\n lastWatermark: replicaVersion,\n owner: null,\n ownerAddress: null,\n };\n const schema = cdcSchema(shard);\n\n await runTx(db, async sql => {\n const [current] = await selectReplicationConfig(sql, shard);\n const status = validateReplicationConfig(\n lc,\n current,\n subscriptionState,\n autoReset,\n behindSlot,\n );\n if (status === 'compatible') {\n return [];\n }\n const stmts: PendingQuery<Row[]>[] = [];\n if (status === 'reset') {\n lc.info?.(\n `Data in cdc tables @${current?.replicaVersion} is incompatible ` +\n `with replica @${replicaVersion}. Clearing tables.`,\n );\n // Release any purge lock held by the caller; the changeLog is\n // incompatible with the replica and needs to be truncated.\n void purgeLock?.release();\n\n // Note: The order of TRUNCATE matters. The replicationState table is\n // truncated before the changeLog table, in order to acquire\n // (exclusive) locks on the tables in the same order that the\n // storer.ts acquires locks when writing the changeLog, namely:\n //\n // 1. SELECT ... FROM replicationState FOR UPDATE;\n // 2. INSERT INTO changeLog ...;\n // ...\n // n. UPDATE replicationState ...;\n stmts.push(\n sql`TRUNCATE TABLE ${sql(schema)}.\"replicationState\"`,\n sql`TRUNCATE TABLE ${sql(schema)}.\"changeLog\"`,\n sql`TRUNCATE TABLE ${sql(schema)}.\"replicationConfig\"`,\n sql`TRUNCATE TABLE ${sql(schema)}.\"tableMetadata\"`,\n sql`TRUNCATE TABLE ${sql(schema)}.\"backfilling\"`,\n );\n }\n // Initialize (or re-initialize TRUNCATED) tables.\n //\n // The PG storer uses the earliest changeLog entry as the safe watermark\n // from which subscribers can be resumed. These initial entries ensure\n // that subscribers can start from a freshly synced replica, even if new\n // changes have been replicated and not purged.\n //\n // TODO: Replace this with an explicit `firstWatermark` column in the\n // change db.\n const initialTx: FullChangeLogEntry[] = [\n {watermark: replicaVersion, pos: 0, change: {tag: 'begin'}},\n {watermark: replicaVersion, pos: 1, change: {tag: 'commit'}},\n ];\n stmts.push(\n sql`INSERT INTO ${sql(schema)}.\"replicationConfig\" ${sql(replicaConfig)}`,\n sql`INSERT INTO ${sql(schema)}.\"replicationState\" ${sql(replicationState)} \n ON CONFLICT (lock) DO UPDATE SET ${sql(replicationState)}`,\n ...initialTx.map(\n change => sql`INSERT INTO ${sql(schema)}.\"changeLog\" ${sql(change)}`,\n ),\n );\n\n if (status === 'reset') {\n return runStatementsAndTerminateLockHolders(\n lc,\n db,\n shard,\n 'ensureReplicationConfig',\n stmts,\n setTimeoutFn,\n );\n }\n return Promise.all(stmts);\n });\n}\n\n// The time to wait for a TRUNCATE in ensureReplicationConfig before\n// terminating blocking backends via terminateChangeDBLockHolders.\nconst LOCK_HOLDER_TERMINATE_TIMEOUT_MS = 5_000;\n\n/**\n * Executes `stmts` that require ACCESS EXCLUSIVE locks (e.g. `TRUNCATE`),\n * which may be blocked by old storer catchup reads. Races against a\n * timeout that terminates the blocking backends if the `stmts` take too\n * long. The check is repeated until the statements complete, since the\n * `stmts` may not yet be waiting on a lock when the timer fires.\n */\nasync function runStatementsAndTerminateLockHolders(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardID,\n name: string,\n stmts: PendingQuery<Row[]>[],\n setTimeoutFn: typeof setTimeout,\n) {\n let done = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const scheduleTerminate = () => {\n timer = setTimeoutFn(async () => {\n lc.info?.(`${name} blocked, terminating lock holders`);\n try {\n await terminateChangeDBLockHolders(lc, db, shard);\n } catch (e) {\n lc.warn?.('error terminating lock holders', e);\n }\n if (!done) {\n scheduleTerminate();\n }\n }, LOCK_HOLDER_TERMINATE_TIMEOUT_MS);\n };\n scheduleTerminate();\n try {\n return await Promise.all(stmts);\n } finally {\n done = true;\n clearTimeout(timer);\n }\n}\n\nexport type ChangeLogReinitialization = {\n /**\n * The watermark of the replica from which the change log is re-initialized,\n * i.e. the watermark from which the change stream will resume.\n */\n watermark: string;\n cookies: {\n tableMetadata: readonly {\n schema: string;\n table: string;\n metadata: TableMetadata;\n }[];\n backfilling: readonly {\n schema: string;\n table: string;\n column: string;\n backfill: BackfillID;\n }[];\n };\n owner: string;\n ownerAddress: string;\n};\n\n/**\n * Re-initializes the change log from the state of the replica if it does not\n * contain all of the replica's changes, i.e. if its `lastWatermark` is before\n * the replica's watermark. This is the case if the change log is no longer\n * maintained (e.g. after running in high-availability mode), or if its last\n * owner stopped before persisting changes that it had already forwarded\n * (and backed up). Ownership of the change log is taken in the same\n * transaction, in which:\n *\n * * The `replicationState`, `changeLog`, `tableMetadata`, and `backfilling`\n * tables are truncated. (The `replicationConfig` is kept;\n * {@link ensureReplicationConfig} handles changes of the replica version\n * or publications.)\n * * The change log is seeded with an empty transaction at the replica's\n * watermark, so that subscribers at that watermark can be caught up, while\n * subscribers at earlier watermarks are rejected as being too old.\n * * The `tableMetadata` and `backfilling` cookies are set from the replica.\n *\n * Returns `'maintained'` without modifying anything if the change log's\n * `lastWatermark` is at or past the replica's watermark, i.e. the change log\n * contains all of the replica's changes. Because a replication slot is only\n * acked past a transaction once it has been backed up, this means that the\n * change log contains every transaction up to the position of the replica's\n * replication slot, and it can be taken over (and resumed from its head).\n *\n * This is called when the change log's head is behind the position of the\n * replication slot (see `PgChangeLogPurgeLocker`). That position cannot be\n * compared with the watermarks of transactions to determine whether the\n * change log is maintained, as it is also advanced past positions of changes\n * outside of the publication.\n *\n * @param purgeLock A purge lock held by the caller, if any, which is released\n * before the tables are truncated.\n */\nexport function reinitializeChangeLog(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardID,\n {watermark, cookies, owner, ownerAddress}: ChangeLogReinitialization,\n purgeLock?: PurgeLock,\n setTimeoutFn: typeof setTimeout = setTimeout,\n): Promise<'reinitialized' | 'maintained'> {\n const schema = cdcSchema(shard);\n return runTx(db, async sql => {\n // Lock the replicationState first, which is the order in which the\n // storer acquires locks when writing the changeLog.\n const state = await sql<{lastWatermark: string}[]>`\n SELECT \"lastWatermark\" FROM ${sql(schema)}.\"replicationState\" FOR UPDATE`;\n const lastWatermark = state[0]?.lastWatermark;\n if (lastWatermark !== undefined && lastWatermark >= watermark) {\n lc.info?.(\n `changeLog@${lastWatermark} contains the replica's changes (@${watermark})`,\n );\n return 'maintained';\n }\n lc.info?.(\n `re-initializing changeLog@${lastWatermark} from the replica @${watermark}`,\n );\n const replicationState: ReplicationState = {\n lastWatermark: watermark,\n owner,\n ownerAddress,\n };\n const initialTx: FullChangeLogEntry[] = [\n {watermark, pos: 0, change: {tag: 'begin'}},\n {watermark, pos: 1, change: {tag: 'commit'}},\n ];\n // The caller's purge lock would block the TRUNCATE. Releasing it is safe:\n // a concurrent purge checks the ownership after deleting, which blocks\n // on the replicationState lock held by this transaction.\n await purgeLock?.release();\n await runStatementsAndTerminateLockHolders(\n lc,\n db,\n shard,\n 'reinitializeChangeLog',\n [\n sql`TRUNCATE TABLE ${sql(schema)}.\"replicationState\"`,\n sql`TRUNCATE TABLE ${sql(schema)}.\"changeLog\"`,\n sql`TRUNCATE TABLE ${sql(schema)}.\"tableMetadata\"`,\n sql`TRUNCATE TABLE ${sql(schema)}.\"backfilling\"`,\n ],\n setTimeoutFn,\n );\n await sql`INSERT INTO ${sql(schema)}.\"replicationState\" ${sql(replicationState)}`;\n for (const entry of initialTx) {\n await sql`INSERT INTO ${sql(schema)}.\"changeLog\" ${sql(entry)}`;\n }\n for (const row of cookies.tableMetadata) {\n await sql`INSERT INTO ${sql(schema)}.\"tableMetadata\" ${sql({...row})}`;\n }\n for (const row of cookies.backfilling) {\n await sql`INSERT INTO ${sql(schema)}.\"backfilling\" ${sql({...row})}`;\n }\n return 'reinitialized';\n });\n}\n\nexport const CHANGE_STREAMER_APP_NAME = 'zero-change-streamer';\n\nexport class AutoResetSignal extends AbortError {\n readonly name = 'AutoResetSignal';\n}\n\n/**\n * Terminates zero-cache backends that are blocking the current backend\n * from acquiring locks on CDC tables (e.g., during TRUNCATE).\n *\n * This is used during change-DB takeover when the new replication-manager's\n * `ensureReplicationConfig` needs to TRUNCATE tables, but the old\n * replication-manager's storer is still reading from them (e.g., large\n * catchup cursors).\n *\n * The function:\n * 1. Finds backends waiting for a lock on a TRUNCATE in {schema}\n * 2. Uses `pg_blocking_pids()` to identify which backends are blocking them\n * 3. Terminates blocking backends that have `application_name = 'zero-change-streamer'`\n *\n * Must be called on a **separate connection** from the one that is blocked,\n * since the blocked connection is inside a pending transaction.\n */\nexport async function terminateChangeDBLockHolders(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardID,\n) {\n const schema = cdcSchema(shard);\n\n // Step 1: Find backends that are blocked waiting for a lock,\n // whose query involves a TRUNCATE on this shard's CDC schema.\n const blocked = await db<{pid: number}[]>`\n SELECT pid FROM pg_stat_activity\n WHERE wait_event_type = 'Lock'\n AND application_name = ${CHANGE_STREAMER_APP_NAME}\n AND query LIKE ${'%TRUNCATE%' + schema + '%'}`;\n\n if (blocked.length === 0) {\n lc.info?.('no blocked TRUNCATE backends found');\n return;\n }\n\n const blockedPids = blocked.map(r => r.pid);\n lc.info?.(`found blocked TRUNCATE backends: ${JSON.stringify(blockedPids)}`);\n\n // Step 2: For each blocked backend, find and terminate its blockers\n // that are zero-change-streamer connections.\n const terminated = await db<\n {pid: number; applicationName: string; query: string; terminated: boolean}[]\n >`\n SELECT pid, application_name as \"applicationName\", query,\n pg_terminate_backend(pid) as terminated\n FROM pg_stat_activity\n WHERE pid = ANY(\n SELECT unnest(pg_blocking_pids(blocked.pid))\n FROM unnest(${blockedPids}::int[]) AS blocked(pid)\n )\n AND application_name = ${CHANGE_STREAMER_APP_NAME}\n AND pid != ALL(${blockedPids}::int[])`;\n\n if (terminated.length === 0) {\n lc.info?.(`no ${CHANGE_STREAMER_APP_NAME} blockers found to terminate`);\n } else {\n for (const {pid, applicationName, query, terminated: ok} of terminated) {\n lc.info?.(\n `terminated blocking backend pid=${pid} app=${applicationName} ok=${ok} query=${query.slice(0, 200)}`,\n );\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tables.js","names":[],"sources":["../../../../../../../zero-cache/src/services/change-streamer/schema/tables.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {ident} from 'pg-format';\nimport type postgres from 'postgres';\nimport {type PendingQuery, type Row} from 'postgres';\nimport {AbortError} from '../../../../../shared/src/abort-error.ts';\nimport {equals} from '../../../../../shared/src/set-utils.ts';\nimport {runTx} from '../../../db/run-transaction.ts';\nimport {type PostgresDB, type PostgresTransaction} from '../../../types/pg.ts';\nimport {cdcSchema, type ShardID} from '../../../types/shards.ts';\nimport type {\n BackfillID,\n Change,\n TableMetadata,\n} from '../../change-source/protocol/current/data.ts';\nimport type {SubscriptionState} from '../../replicator/schema/replication-state.ts';\n\n// For readability in the sql statements.\nfunction schema(shard: ShardID) {\n return ident(cdcSchema(shard));\n}\n\nfunction createSchema(shard: ShardID) {\n return /*sql*/ `CREATE SCHEMA IF NOT EXISTS ${schema(shard)};`;\n}\n\ntype ChangeLogEntry = {\n // A strictly monotonically increasing, lexicographically sortable\n // value that uniquely identifies a position in the change stream.\n watermark: string;\n change: Change;\n};\n\ntype FullChangeLogEntry = ChangeLogEntry & {pos: number};\n\nfunction createChangeLogTable(shard: ShardID) {\n // Note: The \"change\" column used to be JSONB, but that was problematic in that\n // it does not handle the NULL unicode character.\n // https://vladimir.varank.in/notes/2021/01/you-dont-insert-unicode-null-character-as-postgres-jsonb/\n return /*sql*/ `\n CREATE TABLE ${schema(shard)}.\"changeLog\" (\n watermark TEXT,\n pos INT8,\n change JSON NOT NULL,\n precommit TEXT, -- Only exists on commit entries. Purely for debugging.\n PRIMARY KEY (watermark, pos)\n );\n`;\n}\n\n/**\n * Tracks the watermark from which to resume the change stream and the\n * current owner (task ID) acting as the single writer to the changeLog.\n */\nexport type ReplicationState = {\n lastWatermark: string;\n owner: string | null;\n ownerAddress: string | null;\n};\n\nexport function createReplicationStateTable(shard: ShardID) {\n return /*sql*/ `\n CREATE TABLE ${schema(shard)}.\"replicationState\" (\n \"lastWatermark\" TEXT NOT NULL,\n \"owner\" TEXT,\n \"ownerAddress\" TEXT,\n \"lock\" INTEGER PRIMARY KEY DEFAULT 1 CHECK (lock=1)\n );\n`;\n}\n\nexport async function discoverChangeStreamerAddress(\n shard: ShardID,\n sql: PostgresDB,\n): Promise<string | null> {\n const result = await sql<{ownerAddress: string | null}[]> /*sql*/ `\n SELECT \"ownerAddress\" FROM ${sql(cdcSchema(shard))}.\"replicationState\"`;\n return result[0].ownerAddress;\n}\n\n/**\n * This mirrors the analogously named table in the SQLite replica\n * (`services/replicator/schema/replication-state.ts`), and is used\n * to detect when the replica has been reset and is no longer compatible\n * with the current ChangeLog.\n */\nexport type ReplicationConfig = {\n replicaVersion: string;\n publications: readonly string[];\n};\n\nfunction createReplicationConfigTable(shard: ShardID) {\n return /*sql*/ `\n CREATE TABLE ${schema(shard)}.\"replicationConfig\" (\n \"replicaVersion\" TEXT NOT NULL,\n \"publications\" TEXT[] NOT NULL,\n \"resetRequired\" BOOL,\n \"lock\" INTEGER PRIMARY KEY DEFAULT 1 CHECK (lock=1)\n );\n`;\n}\n\nexport function createBackfillTables(shard: ShardID) {\n return /*sql*/ `\n CREATE TABLE ${schema(shard)}.\"tableMetadata\" (\n \"schema\" TEXT NOT NULL,\n \"table\" TEXT NOT NULL,\n \"metadata\" JSONB NOT NULL,\n PRIMARY KEY(\"schema\", \"table\")\n );\n\n CREATE TABLE ${schema(shard)}.\"backfilling\" (\n \"schema\" TEXT NOT NULL,\n \"table\" TEXT NOT NULL,\n \"column\" TEXT NOT NULL,\n \"backfill\" JSONB NOT NULL,\n PRIMARY KEY(\"schema\", \"table\", \"column\")\n );\n `;\n}\n\nexport type TableMetadataRow = {\n schema: string;\n table: string;\n metadata: TableMetadata;\n};\n\nexport type BackfillingColumn = {\n schema: string;\n table: string;\n column: string;\n backfill: BackfillID;\n};\n\nfunction createTables(shard: ShardID) {\n return (\n createSchema(shard) +\n createChangeLogTable(shard) +\n createReplicationStateTable(shard) +\n createReplicationConfigTable(shard) +\n createBackfillTables(shard)\n );\n}\n\ninterface PurgeLock {\n release(): Promise<void>;\n}\n\nexport async function setupCDCTables(\n lc: LogContext,\n db: postgres.TransactionSql,\n shard: ShardID,\n) {\n lc.info?.(`Setting up CDC tables`);\n await db.unsafe(createTables(shard));\n}\n\nexport async function markResetRequired(sql: PostgresDB, shard: ShardID) {\n const schema = cdcSchema(shard);\n await sql`\n UPDATE ${sql(schema)}.\"replicationConfig\"\n SET \"resetRequired\" = true`;\n}\n\ntype ReplicationConfigRow = ReplicationConfig & {\n resetRequired: boolean | null;\n};\n\nexport type ReplicaSubscriptionState = Pick<\n SubscriptionState,\n 'publications' | 'replicaVersion' | 'watermark'\n>;\n\n/**\n * Validates the `replicationConfig` of the change db against the replica,\n * throwing an {@link AutoResetSignal} if the replica cannot be served from it.\n *\n * @param behindSlot Whether the change log is behind the replication slot,\n * in which case it is re-initialized from the replica (see\n * {@link reinitializeChangeLog}) and can thus be reset for a replica\n * at any watermark.\n * @returns `'init'` if the tables are uninitialized, `'reset'` if they are\n * for a different replica, or `'compatible'`.\n */\nfunction validateReplicationConfig(\n lc: LogContext,\n current: ReplicationConfigRow | undefined,\n {publications, replicaVersion, watermark}: ReplicaSubscriptionState,\n autoReset: boolean,\n behindSlot: boolean,\n): 'init' | 'reset' | 'compatible' {\n if (!current) {\n return 'init';\n }\n if (\n current.replicaVersion !== replicaVersion ||\n !equals(new Set(current.publications), new Set(publications))\n ) {\n if (replicaVersion !== watermark && !behindSlot) {\n throw new AutoResetSignal(\n `Cannot reset change db@${current.replicaVersion} to ` +\n `service replica@${replicaVersion} from watermark ${watermark}`,\n );\n }\n return 'reset';\n }\n if (current.resetRequired) {\n if (autoReset) {\n throw new AutoResetSignal('reset required by replication stream');\n }\n lc.error?.(\n '\\n\\n\\n' +\n 'Reset required but --auto-reset is not enabled.\\n' +\n 'This can happen for upstream databases that do not support event triggers.\\n' +\n 'To correct this, see https://zero.rocicorp.dev/docs/connecting-to-postgres#schema-changes' +\n '\\n\\n\\n',\n );\n }\n return 'compatible';\n}\n\nfunction selectReplicationConfig(sql: PostgresTransaction, shard: ShardID) {\n return sql<ReplicationConfigRow[]> /*sql*/ `\n SELECT \"replicaVersion\", \"publications\", \"resetRequired\" \n FROM ${sql(cdcSchema(shard))}.\"replicationConfig\"`;\n}\n\n/**\n * Checks, without modifying the change db, that the replica can be served\n * by the change log once {@link ensureReplicationConfig} is called upon\n * taking over the change log. Throws an {@link AutoResetSignal} otherwise.\n *\n * This allows a replication-manager to detect an incompatible replica at\n * startup while leaving the change log to its current owner until then.\n */\nexport async function checkReplicationConfig(\n lc: LogContext,\n db: PostgresDB,\n subscriptionState: ReplicaSubscriptionState,\n shard: ShardID,\n autoReset: boolean,\n behindSlot = false,\n): Promise<void> {\n const [current] = await runTx(db, sql => selectReplicationConfig(sql, shard));\n const status = validateReplicationConfig(\n lc,\n current,\n subscriptionState,\n autoReset,\n behindSlot,\n );\n if (status === 'reset') {\n lc.info?.(\n `Data in cdc tables @${current?.replicaVersion} is incompatible ` +\n `with replica @${subscriptionState.replicaVersion}. ` +\n `Tables will be reset when taking over the change log.`,\n );\n }\n}\n\n/**\n * Initializes the cdc tables for the replica, resetting them if they were\n * written for a different replica (version or publications). This disrupts\n * a replication-manager that is serving the change log, and is thus only\n * done when taking over the change log (see `Storer.assumeOwnershipAtHead`).\n *\n * @param purgeLock A purge lock held by the caller, if any, which is released\n * before the tables are reset.\n * @param behindSlot See {@link checkReplicationConfig}.\n */\nexport async function ensureReplicationConfig(\n lc: LogContext,\n db: PostgresDB,\n subscriptionState: ReplicaSubscriptionState,\n shard: ShardID,\n autoReset: boolean,\n purgeLock?: PurgeLock,\n setTimeoutFn: typeof setTimeout = setTimeout,\n behindSlot = false,\n) {\n const {publications, replicaVersion} = subscriptionState;\n const replicaConfig = {publications, replicaVersion};\n const replicationState: ReplicationState = {\n lastWatermark: replicaVersion,\n owner: null,\n ownerAddress: null,\n };\n const schema = cdcSchema(shard);\n\n await runTx(db, async sql => {\n const [current] = await selectReplicationConfig(sql, shard);\n const status = validateReplicationConfig(\n lc,\n current,\n subscriptionState,\n autoReset,\n behindSlot,\n );\n if (status === 'compatible') {\n return [];\n }\n const stmts: PendingQuery<Row[]>[] = [];\n if (status === 'reset') {\n lc.info?.(\n `Data in cdc tables @${current?.replicaVersion} is incompatible ` +\n `with replica @${replicaVersion}. Clearing tables.`,\n );\n // Release any purge lock held by the caller; the changeLog is\n // incompatible with the replica and needs to be truncated.\n void purgeLock?.release();\n\n // Note: The order of TRUNCATE matters. The replicationState table is\n // truncated before the changeLog table, in order to acquire\n // (exclusive) locks on the tables in the same order that the\n // storer.ts acquires locks when writing the changeLog, namely:\n //\n // 1. SELECT ... FROM replicationState FOR UPDATE;\n // 2. INSERT INTO changeLog ...;\n // ...\n // n. UPDATE replicationState ...;\n stmts.push(\n sql`TRUNCATE TABLE ${sql(schema)}.\"replicationState\"`,\n sql`TRUNCATE TABLE ${sql(schema)}.\"changeLog\"`,\n sql`TRUNCATE TABLE ${sql(schema)}.\"replicationConfig\"`,\n sql`TRUNCATE TABLE ${sql(schema)}.\"tableMetadata\"`,\n sql`TRUNCATE TABLE ${sql(schema)}.\"backfilling\"`,\n );\n }\n // Initialize (or re-initialize TRUNCATED) tables.\n //\n // The PG storer uses the earliest changeLog entry as the safe watermark\n // from which subscribers can be resumed. These initial entries ensure\n // that subscribers can start from a freshly synced replica, even if new\n // changes have been replicated and not purged.\n //\n // TODO: Replace this with an explicit `firstWatermark` column in the\n // change db.\n const initialTx: FullChangeLogEntry[] = [\n {watermark: replicaVersion, pos: 0, change: {tag: 'begin'}},\n {watermark: replicaVersion, pos: 1, change: {tag: 'commit'}},\n ];\n stmts.push(\n sql`INSERT INTO ${sql(schema)}.\"replicationConfig\" ${sql(replicaConfig)}`,\n sql`INSERT INTO ${sql(schema)}.\"replicationState\" ${sql(replicationState)} \n ON CONFLICT (lock) DO UPDATE SET ${sql(replicationState)}`,\n ...initialTx.map(\n change => sql`INSERT INTO ${sql(schema)}.\"changeLog\" ${sql(change)}`,\n ),\n );\n\n if (status === 'reset') {\n return runStatementsAndTerminateLockHolders(\n lc,\n db,\n shard,\n 'ensureReplicationConfig',\n stmts,\n setTimeoutFn,\n );\n }\n return Promise.all(stmts);\n });\n}\n\n// The time to wait for a TRUNCATE in ensureReplicationConfig before\n// terminating blocking backends via terminateChangeDBLockHolders.\nconst LOCK_HOLDER_TERMINATE_TIMEOUT_MS = 5_000;\n\n/**\n * Executes `stmts` that require ACCESS EXCLUSIVE locks (e.g. `TRUNCATE`),\n * which may be blocked by old storer catchup reads. Races against a\n * timeout that terminates the blocking backends if the `stmts` take too\n * long. The check is repeated until the statements complete, since the\n * `stmts` may not yet be waiting on a lock when the timer fires.\n */\nasync function runStatementsAndTerminateLockHolders(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardID,\n name: string,\n stmts: PendingQuery<Row[]>[],\n setTimeoutFn: typeof setTimeout,\n) {\n let done = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const scheduleTerminate = () => {\n timer = setTimeoutFn(async () => {\n lc.info?.(`${name} blocked, terminating lock holders`);\n try {\n await terminateChangeDBLockHolders(lc, db, shard);\n } catch (e) {\n lc.warn?.('error terminating lock holders', e);\n }\n if (!done) {\n scheduleTerminate();\n }\n }, LOCK_HOLDER_TERMINATE_TIMEOUT_MS);\n };\n scheduleTerminate();\n try {\n return await Promise.all(stmts);\n } finally {\n done = true;\n clearTimeout(timer);\n }\n}\n\nexport type ChangeLogReinitialization = {\n /**\n * The watermark of the replica from which the change log is re-initialized,\n * i.e. the watermark from which the change stream will resume.\n */\n watermark: string;\n cookies: {\n tableMetadata: readonly {\n schema: string;\n table: string;\n metadata: TableMetadata;\n }[];\n backfilling: readonly {\n schema: string;\n table: string;\n column: string;\n backfill: BackfillID;\n }[];\n };\n owner: string;\n ownerAddress: string;\n};\n\n/**\n * Re-initializes the change log from the state of the replica if it does not\n * contain all of the replica's changes, i.e. if its `lastWatermark` is before\n * the replica's watermark. This is the case if the change log is no longer\n * maintained (e.g. after running in high-availability mode), or if its last\n * owner stopped before persisting changes that it had already forwarded\n * (and backed up). Ownership of the change log is taken in the same\n * transaction, in which:\n *\n * * The `replicationState`, `changeLog`, `tableMetadata`, and `backfilling`\n * tables are truncated. (The `replicationConfig` is kept;\n * {@link ensureReplicationConfig} handles changes of the replica version\n * or publications.)\n * * The change log is seeded with an empty transaction at the replica's\n * watermark, so that subscribers at that watermark can be caught up, while\n * subscribers at earlier watermarks are rejected as being too old.\n * * The `tableMetadata` and `backfilling` cookies are set from the replica.\n *\n * Returns `'maintained'` without modifying anything if the change log's\n * `lastWatermark` is at or past the replica's watermark, i.e. the change log\n * contains all of the replica's changes. Because a replication slot is only\n * acked past a transaction once it has been backed up, this means that the\n * change log contains every transaction up to the position of the replica's\n * replication slot, and it can be taken over (and resumed from its head).\n *\n * This is called when the change log's head is behind the position of the\n * replication slot (see `PgChangeLogPurgeLocker`). That position cannot be\n * compared with the watermarks of transactions to determine whether the\n * change log is maintained, as it is also advanced past positions of changes\n * outside of the publication.\n *\n * @param purgeLock A purge lock held by the caller, if any, which is released\n * before the tables are truncated.\n */\nexport function reinitializeChangeLog(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardID,\n {watermark, cookies, owner, ownerAddress}: ChangeLogReinitialization,\n purgeLock?: PurgeLock,\n setTimeoutFn: typeof setTimeout = setTimeout,\n): Promise<'reinitialized' | 'maintained'> {\n const schema = cdcSchema(shard);\n return runTx(db, async sql => {\n // Lock the replicationState first, which is the order in which the\n // storer acquires locks when writing the changeLog.\n const state = await sql<{lastWatermark: string}[]>`\n SELECT \"lastWatermark\" FROM ${sql(schema)}.\"replicationState\" FOR UPDATE`;\n const lastWatermark = state[0]?.lastWatermark;\n if (lastWatermark !== undefined && lastWatermark >= watermark) {\n lc.info?.(\n `changeLog@${lastWatermark} contains the replica's changes (@${watermark})`,\n );\n return 'maintained';\n }\n lc.info?.(\n `re-initializing changeLog@${lastWatermark} from the replica @${watermark}`,\n );\n const replicationState: ReplicationState = {\n lastWatermark: watermark,\n owner,\n ownerAddress,\n };\n const initialTx: FullChangeLogEntry[] = [\n {watermark, pos: 0, change: {tag: 'begin'}},\n {watermark, pos: 1, change: {tag: 'commit'}},\n ];\n // The caller's purge lock would block the TRUNCATE. Releasing it is safe:\n // a concurrent purge checks the ownership after deleting, which blocks\n // on the replicationState lock held by this transaction.\n await purgeLock?.release();\n await runStatementsAndTerminateLockHolders(\n lc,\n db,\n shard,\n 'reinitializeChangeLog',\n [\n sql`TRUNCATE TABLE ${sql(schema)}.\"replicationState\"`,\n sql`TRUNCATE TABLE ${sql(schema)}.\"changeLog\"`,\n sql`TRUNCATE TABLE ${sql(schema)}.\"tableMetadata\"`,\n sql`TRUNCATE TABLE ${sql(schema)}.\"backfilling\"`,\n ],\n setTimeoutFn,\n );\n await sql`INSERT INTO ${sql(schema)}.\"replicationState\" ${sql(replicationState)}`;\n for (const entry of initialTx) {\n await sql`INSERT INTO ${sql(schema)}.\"changeLog\" ${sql(entry)}`;\n }\n for (const row of cookies.tableMetadata) {\n await sql`INSERT INTO ${sql(schema)}.\"tableMetadata\" ${sql({...row})}`;\n }\n for (const row of cookies.backfilling) {\n await sql`INSERT INTO ${sql(schema)}.\"backfilling\" ${sql({...row})}`;\n }\n return 'reinitialized';\n });\n}\n\nexport const CHANGE_STREAMER_APP_NAME = 'zero-change-streamer';\n\nexport class AutoResetSignal extends AbortError {\n readonly name = 'AutoResetSignal';\n}\n\n/**\n * Terminates zero-cache backends that are blocking the current backend\n * from acquiring locks on CDC tables (e.g., during TRUNCATE).\n *\n * This is used during change-DB takeover when the new replication-manager's\n * `ensureReplicationConfig` needs to TRUNCATE tables, but the old\n * replication-manager's storer is still reading from them (e.g., large\n * catchup cursors).\n *\n * The function:\n * 1. Finds backends waiting for a lock on a TRUNCATE in {schema}\n * 2. Uses `pg_blocking_pids()` to identify which backends are blocking them\n * 3. Terminates blocking backends that have `application_name = 'zero-change-streamer'`\n *\n * Must be called on a **separate connection** from the one that is blocked,\n * since the blocked connection is inside a pending transaction.\n */\nasync function terminateChangeDBLockHolders(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardID,\n) {\n const schema = cdcSchema(shard);\n\n // Step 1: Find backends that are blocked waiting for a lock,\n // whose query involves a TRUNCATE on this shard's CDC schema.\n const blocked = await db<{pid: number}[]>`\n SELECT pid FROM pg_stat_activity\n WHERE wait_event_type = 'Lock'\n AND application_name = ${CHANGE_STREAMER_APP_NAME}\n AND query LIKE ${'%TRUNCATE%' + schema + '%'}`;\n\n if (blocked.length === 0) {\n lc.info?.('no blocked TRUNCATE backends found');\n return;\n }\n\n const blockedPids = blocked.map(r => r.pid);\n lc.info?.(`found blocked TRUNCATE backends: ${JSON.stringify(blockedPids)}`);\n\n // Step 2: For each blocked backend, find and terminate its blockers\n // that are zero-change-streamer connections.\n const terminated = await db<\n {pid: number; applicationName: string; query: string; terminated: boolean}[]\n >`\n SELECT pid, application_name as \"applicationName\", query,\n pg_terminate_backend(pid) as terminated\n FROM pg_stat_activity\n WHERE pid = ANY(\n SELECT unnest(pg_blocking_pids(blocked.pid))\n FROM unnest(${blockedPids}::int[]) AS blocked(pid)\n )\n AND application_name = ${CHANGE_STREAMER_APP_NAME}\n AND pid != ALL(${blockedPids}::int[])`;\n\n if (terminated.length === 0) {\n lc.info?.(`no ${CHANGE_STREAMER_APP_NAME} blockers found to terminate`);\n } else {\n for (const {pid, applicationName, query, terminated: ok} of terminated) {\n lc.info?.(\n `terminated blocking backend pid=${pid} app=${applicationName} ok=${ok} query=${query.slice(0, 200)}`,\n );\n }\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import { getOrCreateGauge } from "../../observability/metrics.js";
2
+ //#region ../zero-cache/src/services/change-streamer/server-start-metrics.ts
3
+ /**
4
+ * Records where a replication-manager's start goes, reported once it is
5
+ * ready by the `zero.replication.server_start` gauge, in seconds, labeled
6
+ * by `phase` and `kind` (see {@link InitializeKind}). This is for diagnosis,
7
+ * e.g. of why a start is slow, as the process only starts once.
8
+ */
9
+ var ServerStartMetrics = class {
10
+ #phasesMs = /* @__PURE__ */ new Map();
11
+ #kind;
12
+ #ready = false;
13
+ setKind(kind) {
14
+ this.#kind = kind;
15
+ }
16
+ record(phase, ms) {
17
+ if (ms !== void 0 && !this.#ready) this.#phasesMs.set(phase, ms);
18
+ }
19
+ /**
20
+ * Records the `total` (by default, the time since the process started) and
21
+ * starts reporting the gauge. Phases recorded afterwards are ignored.
22
+ */
23
+ ready(totalMs = performance.now()) {
24
+ if (this.#ready) return;
25
+ this.record("total", totalMs);
26
+ this.#ready = true;
27
+ const kind = this.#kind ?? "unknown";
28
+ getOrCreateGauge("replication", "server_start", {
29
+ description: "The duration of each phase of the replication-manager start (slot_create, fork_wait, restore, catch_up, backup_confirm, and the total from process start to readiness), by the kind of initialization (fork, resume, rmv1, initial_sync).",
30
+ unit: "s"
31
+ }).addCallback((o) => {
32
+ for (const [phase, ms] of this.#phasesMs) o.observe(ms / 1e3, {
33
+ phase,
34
+ kind
35
+ });
36
+ });
37
+ }
38
+ };
39
+ //#endregion
40
+ export { ServerStartMetrics };
41
+
42
+ //# sourceMappingURL=server-start-metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-start-metrics.js","names":[],"sources":["../../../../../../zero-cache/src/services/change-streamer/server-start-metrics.ts"],"sourcesContent":["import {getOrCreateGauge} from '../../observability/metrics.ts';\nimport type {InitializeKind} from '../change-source/common/replica-restore.ts';\n\n/**\n * The phases of a replication-manager's start:\n * - `slot_create`: creating the new replica's slot, including retries while\n * it is blocked by upstream transactions. This is separate from `fork_wait`\n * because the upstream (e.g. a long-running transaction), not zero-cache or\n * its capacity, determines how long it takes.\n * - `fork_wait`: selecting the replica to restore from (and claiming its\n * slot), including the wait for a forked replica to pass the new slot's\n * position, excluding `slot_create`\n * - `restore`: restoring the replica from its backup\n * - `catch_up`: from initialization to the backup-replicator being caught up\n * (i.e. its replication lag readiness)\n * - `backup_confirm`: waiting for the first backup to cover the replica\n * - `total`: from the start of the (change-streamer) process to readiness\n *\n * The phases are not exhaustive (e.g. connecting to and preparing the\n * upstream), and `catch_up` and `backup_confirm` overlap.\n */\nexport type ServerStartPhase =\n | 'slot_create'\n | 'fork_wait'\n | 'restore'\n | 'catch_up'\n | 'backup_confirm'\n | 'total';\n\n/**\n * Records where a replication-manager's start goes, reported once it is\n * ready by the `zero.replication.server_start` gauge, in seconds, labeled\n * by `phase` and `kind` (see {@link InitializeKind}). This is for diagnosis,\n * e.g. of why a start is slow, as the process only starts once.\n */\nexport class ServerStartMetrics {\n readonly #phasesMs = new Map<ServerStartPhase, number>();\n #kind: InitializeKind | undefined;\n #ready = false;\n\n setKind(kind: InitializeKind | undefined) {\n this.#kind = kind;\n }\n\n record(phase: ServerStartPhase, ms: number | undefined) {\n if (ms !== undefined && !this.#ready) {\n this.#phasesMs.set(phase, ms);\n }\n }\n\n /**\n * Records the `total` (by default, the time since the process started) and\n * starts reporting the gauge. Phases recorded afterwards are ignored.\n */\n ready(totalMs = performance.now()) {\n if (this.#ready) {\n return;\n }\n this.record('total', totalMs);\n this.#ready = true;\n const kind = this.#kind ?? 'unknown';\n getOrCreateGauge('replication', 'server_start', {\n description:\n 'The duration of each phase of the replication-manager start ' +\n '(slot_create, fork_wait, restore, catch_up, backup_confirm, and the total ' +\n 'from process start to readiness), by the kind of initialization ' +\n '(fork, resume, rmv1, initial_sync).',\n unit: 's',\n }).addCallback(o => {\n for (const [phase, ms] of this.#phasesMs) {\n o.observe(ms / 1000, {phase, kind});\n }\n });\n }\n}\n"],"mappings":";;;;;;;;AAmCA,IAAa,qBAAb,MAAgC;CAC9B,4BAAqB,IAAI,IAA8B;CACvD;CACA,SAAS;CAET,QAAQ,MAAkC;EACxC,KAAK,QAAQ;CACf;CAEA,OAAO,OAAyB,IAAwB;EACtD,IAAI,OAAO,KAAA,KAAa,CAAC,KAAK,QAC5B,KAAK,UAAU,IAAI,OAAO,EAAE;CAEhC;;;;;CAMA,MAAM,UAAU,YAAY,IAAI,GAAG;EACjC,IAAI,KAAK,QACP;EAEF,KAAK,OAAO,SAAS,OAAO;EAC5B,KAAK,SAAS;EACd,MAAM,OAAO,KAAK,SAAS;EAC3B,iBAAiB,eAAe,gBAAgB;GAC9C,aACE;GAIF,MAAM;EACR,CAAC,CAAC,CAAC,aAAY,MAAK;GAClB,KAAK,MAAM,CAAC,OAAO,OAAO,KAAK,WAC7B,EAAE,QAAQ,KAAK,KAAM;IAAC;IAAO;GAAI,CAAC;EAEtC,CAAC;CACH;AACF"}
@@ -34,8 +34,7 @@ var statusSchema = valita_exports.object({
34
34
  */
35
35
  replicaSize: valita_exports.number().optional()
36
36
  });
37
- var statusMessageSchema = valita_exports.tuple([valita_exports.literal("status"), statusSchema]);
38
- valita_exports.union(statusMessageSchema);
37
+ valita_exports.tuple([valita_exports.literal("status"), statusSchema]);
39
38
  //#endregion
40
39
  export { statusSchema };
41
40
 
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot-message.js","names":[],"sources":["../../../../../../zero-cache/src/services/change-streamer/snapshot-message.ts"],"sourcesContent":["/**\n * Pure schema definitions for the `/snapshot` reservation status message.\n *\n * These live in their own module (depending only on valita) so that both\n * {@link snapshot.ts} (which also pulls in the change-streamer HTTP client) and\n * {@link subscribe.ts} can import them without forming an import cycle.\n */\n\nimport * as v from '../../../../shared/src/valita.ts';\n\nexport const statusSchema = v.object({\n tag: v.literal('status'),\n\n /**\n * The location from which litestream should perform the restore.\n */\n backupURL: v.string(),\n\n /**\n * The `replicaVersion` of the backup. If a subscriber's restored or\n * existing replica is of a different version, it should delete it and\n * retry the restore from litestream (i.e. equivalent to a\n * `WrongReplicaVersion` response from a `/changes` subscription).\n */\n replicaVersion: v.string(),\n\n /**\n * The earliest watermark from which catchup is possible. If the\n * subscriber's replica is older that this watermark, it should delete it\n * and (retry the) restore from litestream (i.e. equivalent to a\n * `WatermarkTooOld` response from a `/changes` subscription).\n */\n minWatermark: v.string(),\n\n /**\n * The size in bytes of the replication-manager's replica when the snapshot\n * was reserved, used as an estimate of the size of the restored replica\n * when reporting restore progress. Absent from older replication-managers.\n */\n replicaSize: v.number().optional(),\n});\n\nexport type SnapshotStatus = v.Infer<typeof statusSchema>;\n\nexport const statusMessageSchema = v.tuple([v.literal('status'), statusSchema]);\n\nexport const snapshotMessageSchema = v.union(statusMessageSchema);\n\nexport type SnapshotMessage = v.Infer<typeof statusMessageSchema>;\n"],"mappings":";;;;;;;;;AAUA,IAAa,eAAe,eAAE,OAAO;CACnC,KAAK,eAAE,QAAQ,QAAQ;;;;CAKvB,WAAW,eAAE,OAAO;;;;;;;CAQpB,gBAAgB,eAAE,OAAO;;;;;;;CAQzB,cAAc,eAAE,OAAO;;;;;;CAOvB,aAAa,eAAE,OAAO,EAAE,SAAS;AACnC,CAAC;AAID,IAAa,sBAAsB,eAAE,MAAM,CAAC,eAAE,QAAQ,QAAQ,GAAG,YAAY,CAAC;AAEzC,eAAE,MAAM,mBAAmB"}
1
+ {"version":3,"file":"snapshot-message.js","names":["v"],"sources":["../../../../../../zero-cache/src/services/change-streamer/snapshot-message.ts"],"sourcesContent":["/**\n * Pure schema definitions for the `/snapshot` reservation status message.\n *\n * These live in their own module (depending only on valita) so that both\n * {@link snapshot.ts} (which also pulls in the change-streamer HTTP client) and\n * {@link subscribe.ts} can import them without forming an import cycle.\n */\n\nimport * as v from '../../../../shared/src/valita.ts';\n\nexport const statusSchema = v.object({\n tag: v.literal('status'),\n\n /**\n * The location from which litestream should perform the restore.\n */\n backupURL: v.string(),\n\n /**\n * The `replicaVersion` of the backup. If a subscriber's restored or\n * existing replica is of a different version, it should delete it and\n * retry the restore from litestream (i.e. equivalent to a\n * `WrongReplicaVersion` response from a `/changes` subscription).\n */\n replicaVersion: v.string(),\n\n /**\n * The earliest watermark from which catchup is possible. If the\n * subscriber's replica is older that this watermark, it should delete it\n * and (retry the) restore from litestream (i.e. equivalent to a\n * `WatermarkTooOld` response from a `/changes` subscription).\n */\n minWatermark: v.string(),\n\n /**\n * The size in bytes of the replication-manager's replica when the snapshot\n * was reserved, used as an estimate of the size of the restored replica\n * when reporting restore progress. Absent from older replication-managers.\n */\n replicaSize: v.number().optional(),\n});\n\nconst statusMessageSchema = v.tuple([v.literal('status'), statusSchema]);\n\nexport type SnapshotMessage = v.Infer<typeof statusMessageSchema>;\n"],"mappings":";;;;;;;;;AAUA,IAAa,eAAeA,eAAE,OAAO;CACnC,KAAKA,eAAE,QAAQ,QAAQ;;;;CAKvB,WAAWA,eAAE,OAAO;;;;;;;CAQpB,gBAAgBA,eAAE,OAAO;;;;;;;CAQzB,cAAcA,eAAE,OAAO;;;;;;CAOvB,aAAaA,eAAE,OAAO,CAAC,CAAC,SAAS;AACnC,CAAC;AAE2BA,eAAE,MAAM,CAACA,eAAE,QAAQ,QAAQ,GAAG,YAAY,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot-reservations.js","names":["#lc","#backupConfig","#callbacks","#reservations","#close","#metricAttrs","#replicaSize","#downstream","#watermark","#delayNoted"],"sources":["../../../../../../zero-cache/src/services/change-streamer/snapshot-reservations.ts"],"sourcesContent":["import {statSync} from 'node:fs';\nimport type {LogContext} from '@rocicorp/logger';\nimport type {Source} from '../../types/streams.ts';\nimport {Subscription} from '../../types/subscription.ts';\nimport {\n litestreamMonitorMetricAttrs,\n litestreamSnapshotReservationConfirmDuration,\n litestreamSnapshotReservationDuration,\n} from '../litestream/metrics.ts';\nimport type {BackupConfig} from './change-streamer-service.ts';\nimport type {SnapshotMessage} from './snapshot-message.ts';\nimport type {ChangeLogReadSource} from './sqlite-change-log-read-router.ts';\n\nexport type ReservationCallbacks = {\n /**\n * Called whenever a reservation is closed, including when it is replaced\n * by a new reservation (e.g. a retry) for the same task.\n */\n onClose?: ((taskID: string) => void) | undefined;\n\n /**\n * Called when a task's reservation is closed without being replaced,\n * i.e. when the task no longer holds a reservation.\n */\n onEnd?: ((taskID: string) => void) | undefined;\n};\n\nexport class SnapshotReservations {\n readonly #lc: LogContext;\n readonly #backupConfig: BackupConfig;\n readonly #callbacks: ReservationCallbacks;\n readonly #reservations = new Map<string, Reservation>();\n\n constructor(\n lc: LogContext,\n backupConfig: BackupConfig,\n callbacks: ReservationCallbacks = {},\n ) {\n this.#lc = lc.withContext('component', 'snapshot-reserver');\n this.#backupConfig = backupConfig;\n this.#callbacks = callbacks;\n }\n\n open(taskID: string): Source<SnapshotMessage> {\n this.#close(taskID, undefined, true);\n\n const instanceID = {};\n const downstream = Subscription.create<SnapshotMessage>({\n cleanup: () => this.#close(taskID, instanceID),\n });\n this.#reservations.set(taskID, new Reservation(instanceID, downstream));\n this.#lc.info?.(`created snapshot reservation for ${taskID}`);\n return downstream;\n }\n\n close(taskID: string) {\n this.#close(taskID, undefined);\n }\n\n isCurrent(taskID: string, source: Source<SnapshotMessage>): boolean {\n return this.#reservations.get(taskID)?.owns(source) ?? false;\n }\n\n #metricAttrs() {\n return litestreamMonitorMetricAttrs(\n this.#backupConfig.backupURL,\n this.#backupConfig.litestreamVersion,\n 'view_syncer',\n );\n }\n\n #close(\n taskID: string,\n cancelledInstanceID: InstanceID | undefined,\n replaced = false,\n ) {\n const res = this.#reservations.get(taskID);\n if (\n res &&\n (!cancelledInstanceID || res.instanceID === cancelledInstanceID)\n ) {\n // Note: delete first, so that the reservation is gone when close() is called.\n this.#reservations.delete(taskID);\n this.#callbacks.onClose?.(taskID);\n if (!replaced) {\n this.#callbacks.onEnd?.(taskID);\n }\n res.close();\n\n const duration = Date.now() - res.startTime.getTime();\n this.#lc.info?.(\n `ended snapshot reservation for ${taskID} (${duration} ms)`,\n );\n // `result` reports how the reservation ended and `confirmed` whether it\n // ever received its bounds. They are separate dimensions: a follower\n // that gives up while waiting for a confirmation is\n // `result=cancelled, confirmed=false`, which a single collapsed\n // attribute cannot distinguish from a client that simply went away.\n litestreamSnapshotReservationDuration().recordMs(duration, {\n ...this.#metricAttrs(),\n result: cancelledInstanceID ? 'cancelled' : 'closed',\n confirmed: res.confirmed(),\n });\n }\n }\n\n confirmationsRequired() {\n for (const res of this.#reservations.values()) {\n if (!res.confirmed()) {\n return true;\n }\n }\n return false;\n }\n\n unconfirmedTaskIDs(): string[] {\n return [...this.#reservations.entries()]\n .filter(([, reservation]) => !reservation.confirmed())\n .map(([taskID]) => taskID);\n }\n\n /** Confirms one reservation with the bounds of its pinned read source. */\n confirmFor(\n taskID: string,\n replicaVersion: string,\n minWatermark: string,\n source: ChangeLogReadSource,\n ): void {\n const res = this.#reservations.get(taskID);\n if (res && !res.confirmed()) {\n this.#lc.info?.(\n `reserving change-log entries since ${minWatermark} for ${taskID}`,\n );\n res.confirm(\n this.#backupConfig.backupURL,\n replicaVersion,\n minWatermark,\n this.#replicaSize(),\n );\n // Measured from `open()`, not from the first confirmation attempt: what\n // matters is how long the follower waited before it could restore.\n litestreamSnapshotReservationConfirmDuration().recordMs(\n Date.now() - res.startTime.getTime(),\n {...this.#metricAttrs(), source},\n );\n }\n }\n\n #replicaSize(): number | undefined {\n const {replicaFile} = this.#backupConfig;\n if (!replicaFile) {\n return undefined;\n }\n try {\n return statSync(replicaFile).size;\n } catch (e) {\n this.#lc.warn?.(`Unable to get the size of ${replicaFile}`, e);\n return undefined;\n }\n }\n\n /**\n * Notes that a confirmation was deferred, returning true only the first time\n * for this reservation. Confirmation is retried on every backup, so an\n * undeduplicated count would measure backups rather than delayed followers.\n */\n noteConfirmationDelayed(taskID: string): boolean {\n return this.#reservations.get(taskID)?.noteDelayed() ?? false;\n }\n\n getReservedWatermarks() {\n return Array.from(\n this.#reservations.values(),\n ({reservedWatermark}) => reservedWatermark,\n ).filter(watermark => watermark !== null);\n }\n}\n\ntype InstanceID = {};\n\nclass Reservation {\n readonly instanceID: InstanceID;\n readonly startTime: Date = new Date();\n readonly #downstream: Subscription<SnapshotMessage>;\n #watermark: string | null = null;\n #delayNoted = false;\n\n constructor(\n instanceID: InstanceID,\n downstream: Subscription<SnapshotMessage>,\n ) {\n this.instanceID = instanceID;\n this.#downstream = downstream;\n }\n\n get reservedWatermark() {\n return this.#watermark;\n }\n\n confirmed() {\n return this.#watermark !== null;\n }\n\n owns(source: Source<SnapshotMessage>): boolean {\n return this.#downstream === source;\n }\n\n noteDelayed(): boolean {\n if (this.#delayNoted) {\n return false;\n }\n this.#delayNoted = true;\n return true;\n }\n\n confirm(\n backupURL: string,\n replicaVersion: string,\n minWatermark: string,\n replicaSize: number | undefined,\n ) {\n if (this.#watermark === null) {\n if (this.#downstream.active) {\n this.#downstream.push([\n 'status',\n {tag: 'status', backupURL, replicaVersion, minWatermark, replicaSize},\n ]);\n }\n this.#watermark = minWatermark;\n }\n }\n\n close() {\n this.#downstream.cancel();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"snapshot-reservations.js","names":[],"sources":["../../../../../../zero-cache/src/services/change-streamer/snapshot-reservations.ts"],"sourcesContent":["import {statSync} from 'node:fs';\nimport type {LogContext} from '@rocicorp/logger';\nimport type {Source} from '../../types/streams.ts';\nimport {Subscription} from '../../types/subscription.ts';\nimport {\n litestreamMonitorMetricAttrs,\n litestreamSnapshotReservationConfirmDuration,\n litestreamSnapshotReservationDuration,\n} from '../litestream/metrics.ts';\nimport type {BackupConfig} from './change-streamer-service.ts';\nimport type {SnapshotMessage} from './snapshot-message.ts';\nimport type {ChangeLogReadSource} from './sqlite-change-log-read-router.ts';\n\nexport type ReservationCallbacks = {\n /**\n * Called whenever a reservation is closed, including when it is replaced\n * by a new reservation (e.g. a retry) for the same task.\n */\n onClose?: ((taskID: string) => void) | undefined;\n\n /**\n * Called when a task's reservation is closed without being replaced,\n * i.e. when the task no longer holds a reservation.\n */\n onEnd?: ((taskID: string) => void) | undefined;\n};\n\nexport class SnapshotReservations {\n readonly #lc: LogContext;\n readonly #backupConfig: BackupConfig;\n readonly #callbacks: ReservationCallbacks;\n readonly #reservations = new Map<string, Reservation>();\n\n constructor(\n lc: LogContext,\n backupConfig: BackupConfig,\n callbacks: ReservationCallbacks = {},\n ) {\n this.#lc = lc.withContext('component', 'snapshot-reserver');\n this.#backupConfig = backupConfig;\n this.#callbacks = callbacks;\n }\n\n open(taskID: string): Source<SnapshotMessage> {\n this.#close(taskID, undefined, true);\n\n const instanceID = {};\n const downstream = Subscription.create<SnapshotMessage>({\n cleanup: () => this.#close(taskID, instanceID),\n });\n this.#reservations.set(taskID, new Reservation(instanceID, downstream));\n this.#lc.info?.(`created snapshot reservation for ${taskID}`);\n return downstream;\n }\n\n close(taskID: string) {\n this.#close(taskID, undefined);\n }\n\n isCurrent(taskID: string, source: Source<SnapshotMessage>): boolean {\n return this.#reservations.get(taskID)?.owns(source) ?? false;\n }\n\n #metricAttrs() {\n return litestreamMonitorMetricAttrs(\n this.#backupConfig.backupURL,\n this.#backupConfig.litestreamVersion,\n 'view_syncer',\n );\n }\n\n #close(\n taskID: string,\n cancelledInstanceID: InstanceID | undefined,\n replaced = false,\n ) {\n const res = this.#reservations.get(taskID);\n if (\n res &&\n (!cancelledInstanceID || res.instanceID === cancelledInstanceID)\n ) {\n // Note: delete first, so that the reservation is gone when close() is called.\n this.#reservations.delete(taskID);\n this.#callbacks.onClose?.(taskID);\n if (!replaced) {\n this.#callbacks.onEnd?.(taskID);\n }\n res.close();\n\n const duration = Date.now() - res.startTime.getTime();\n this.#lc.info?.(\n `ended snapshot reservation for ${taskID} (${duration} ms)`,\n );\n // `result` reports how the reservation ended and `confirmed` whether it\n // ever received its bounds. They are separate dimensions: a follower\n // that gives up while waiting for a confirmation is\n // `result=cancelled, confirmed=false`, which a single collapsed\n // attribute cannot distinguish from a client that simply went away.\n litestreamSnapshotReservationDuration().recordMs(duration, {\n ...this.#metricAttrs(),\n result: cancelledInstanceID ? 'cancelled' : 'closed',\n confirmed: res.confirmed(),\n });\n }\n }\n\n confirmationsRequired() {\n for (const res of this.#reservations.values()) {\n if (!res.confirmed()) {\n return true;\n }\n }\n return false;\n }\n\n unconfirmedTaskIDs(): string[] {\n return [...this.#reservations.entries()]\n .filter(([, reservation]) => !reservation.confirmed())\n .map(([taskID]) => taskID);\n }\n\n /** Confirms one reservation with the bounds of its pinned read source. */\n confirmFor(\n taskID: string,\n replicaVersion: string,\n minWatermark: string,\n source: ChangeLogReadSource,\n ): void {\n const res = this.#reservations.get(taskID);\n if (res && !res.confirmed()) {\n this.#lc.info?.(\n `reserving change-log entries since ${minWatermark} for ${taskID}`,\n );\n res.confirm(\n this.#backupConfig.backupURL,\n replicaVersion,\n minWatermark,\n this.#replicaSize(),\n );\n // Measured from `open()`, not from the first confirmation attempt: what\n // matters is how long the follower waited before it could restore.\n litestreamSnapshotReservationConfirmDuration().recordMs(\n Date.now() - res.startTime.getTime(),\n {...this.#metricAttrs(), source},\n );\n }\n }\n\n #replicaSize(): number | undefined {\n const {replicaFile} = this.#backupConfig;\n if (!replicaFile) {\n return undefined;\n }\n try {\n return statSync(replicaFile).size;\n } catch (e) {\n this.#lc.warn?.(`Unable to get the size of ${replicaFile}`, e);\n return undefined;\n }\n }\n\n /**\n * Notes that a confirmation was deferred, returning true only the first time\n * for this reservation. Confirmation is retried on every backup, so an\n * undeduplicated count would measure backups rather than delayed followers.\n */\n noteConfirmationDelayed(taskID: string): boolean {\n return this.#reservations.get(taskID)?.noteDelayed() ?? false;\n }\n\n getReservedWatermarks() {\n return Array.from(\n this.#reservations.values(),\n ({reservedWatermark}) => reservedWatermark,\n ).filter(watermark => watermark !== null);\n }\n}\n\ntype InstanceID = {};\n\nclass Reservation {\n readonly instanceID: InstanceID;\n readonly startTime: Date = new Date();\n readonly #downstream: Subscription<SnapshotMessage>;\n #watermark: string | null = null;\n #delayNoted = false;\n\n constructor(\n instanceID: InstanceID,\n downstream: Subscription<SnapshotMessage>,\n ) {\n this.instanceID = instanceID;\n this.#downstream = downstream;\n }\n\n get reservedWatermark() {\n return this.#watermark;\n }\n\n confirmed() {\n return this.#watermark !== null;\n }\n\n owns(source: Source<SnapshotMessage>): boolean {\n return this.#downstream === source;\n }\n\n noteDelayed(): boolean {\n if (this.#delayNoted) {\n return false;\n }\n this.#delayNoted = true;\n return true;\n }\n\n confirm(\n backupURL: string,\n replicaVersion: string,\n minWatermark: string,\n replicaSize: number | undefined,\n ) {\n if (this.#watermark === null) {\n if (this.#downstream.active) {\n this.#downstream.push([\n 'status',\n {tag: 'status', backupURL, replicaVersion, minWatermark, replicaSize},\n ]);\n }\n this.#watermark = minWatermark;\n }\n }\n\n close() {\n this.#downstream.cancel();\n }\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
+ import { resolver } from "../../../../shared/src/resolver.js";
1
2
  import { AbortError } from "../../../../shared/src/abort-error.js";
2
3
  import { sleep } from "../../../../shared/src/sleep.js";
3
4
  import { getOrCreateCounter, getOrCreateLatencyHistogram, getOrCreateValueHistogram } from "../../observability/metrics.js";
4
- import { resolver } from "@rocicorp/resolver";
5
5
  //#region ../zero-cache/src/services/change-streamer/sqlite-change-log-catchup.ts
6
6
  var DEFAULT_BARRIER_POLL_INTERVAL_MS = 1e3;
7
7
  var NOOP_CLEANUP_GUARD = { runWhilePurgeBlocked: (register) => Promise.resolve(register()) };
@@ -339,6 +339,6 @@ var SQLiteChangeLogBarrierBacklogError = class extends SQLiteChangeLogBarrierErr
339
339
  name = "SQLiteChangeLogBarrierBacklogError";
340
340
  };
341
341
  //#endregion
342
- export { SQLiteChangeLogCatchup };
342
+ export { SQLiteChangeLogBarrierBacklogError, SQLiteChangeLogBarrierTimeoutError, SQLiteChangeLogCatchup };
343
343
 
344
344
  //# sourceMappingURL=sqlite-change-log-catchup.js.map