@rocicorp/zero 1.10.0-canary.0 → 1.10.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 (484) hide show
  1. package/out/replicache/src/db/commit.js +1 -4
  2. package/out/replicache/src/db/commit.js.map +1 -1
  3. package/out/replicache/src/db/read.d.ts +1 -0
  4. package/out/replicache/src/db/read.d.ts.map +1 -1
  5. package/out/replicache/src/db/read.js +1 -4
  6. package/out/replicache/src/db/read.js.map +1 -1
  7. package/out/replicache/src/db/write.d.ts +2 -1
  8. package/out/replicache/src/db/write.d.ts.map +1 -1
  9. package/out/replicache/src/db/write.js +4 -51
  10. package/out/replicache/src/db/write.js.map +1 -1
  11. package/out/replicache/src/persist/clients.d.ts +1 -0
  12. package/out/replicache/src/persist/clients.d.ts.map +1 -1
  13. package/out/replicache/src/persist/clients.js +4 -31
  14. package/out/replicache/src/persist/clients.js.map +1 -1
  15. package/out/replicache/src/process-env.d.ts +28 -0
  16. package/out/replicache/src/process-env.d.ts.map +1 -0
  17. package/out/replicache/src/replicache-impl.d.ts.map +1 -1
  18. package/out/replicache/src/replicache-impl.js.map +1 -1
  19. package/out/replicache/src/scan-iterator.js +2 -14
  20. package/out/replicache/src/scan-iterator.js.map +1 -1
  21. package/out/replicache/src/subscriptions.d.ts +1 -0
  22. package/out/replicache/src/subscriptions.d.ts.map +1 -1
  23. package/out/replicache/src/subscriptions.js +2 -10
  24. package/out/replicache/src/subscriptions.js.map +1 -1
  25. package/out/replicache/src/transactions.d.ts +1 -0
  26. package/out/replicache/src/transactions.d.ts.map +1 -1
  27. package/out/replicache/src/transactions.js +2 -4
  28. package/out/replicache/src/transactions.js.map +1 -1
  29. package/out/shared/src/promise-race.d.ts +14 -0
  30. package/out/shared/src/promise-race.d.ts.map +1 -1
  31. package/out/shared/src/promise-race.js +32 -1
  32. package/out/shared/src/promise-race.js.map +1 -1
  33. package/out/z2s/src/compiler.d.ts.map +1 -1
  34. package/out/z2s/src/compiler.js +1 -22
  35. package/out/z2s/src/compiler.js.map +1 -1
  36. package/out/zero/package.js +3 -3
  37. package/out/zero/package.js.map +1 -1
  38. package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
  39. package/out/zero-cache/src/auth/write-authorizer.js +8 -1
  40. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  41. package/out/zero-cache/src/config/normalize.d.ts +6 -0
  42. package/out/zero-cache/src/config/normalize.d.ts.map +1 -1
  43. package/out/zero-cache/src/config/normalize.js +24 -2
  44. package/out/zero-cache/src/config/normalize.js.map +1 -1
  45. package/out/zero-cache/src/config/zero-config.d.ts +65 -4
  46. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  47. package/out/zero-cache/src/config/zero-config.js +202 -35
  48. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  49. package/out/zero-cache/src/custom/fetch.js +1 -1
  50. package/out/zero-cache/src/custom/fetch.js.map +1 -1
  51. package/out/zero-cache/src/db/delete-lite-db.js +2 -1
  52. package/out/zero-cache/src/db/delete-lite-db.js.map +1 -1
  53. package/out/zero-cache/src/db/migration-lite.d.ts.map +1 -1
  54. package/out/zero-cache/src/db/migration-lite.js +10 -5
  55. package/out/zero-cache/src/db/migration-lite.js.map +1 -1
  56. package/out/zero-cache/src/db/migration.d.ts.map +1 -1
  57. package/out/zero-cache/src/db/migration.js +4 -2
  58. package/out/zero-cache/src/db/migration.js.map +1 -1
  59. package/out/zero-cache/src/db/sqlite-corruption.d.ts +3 -2
  60. package/out/zero-cache/src/db/sqlite-corruption.d.ts.map +1 -1
  61. package/out/zero-cache/src/db/sqlite-corruption.js +13 -8
  62. package/out/zero-cache/src/db/sqlite-corruption.js.map +1 -1
  63. package/out/zero-cache/src/db/transaction-pool.d.ts +5 -15
  64. package/out/zero-cache/src/db/transaction-pool.d.ts.map +1 -1
  65. package/out/zero-cache/src/db/transaction-pool.js +8 -72
  66. package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
  67. package/out/zero-cache/src/observability/metrics.d.ts +6 -1
  68. package/out/zero-cache/src/observability/metrics.d.ts.map +1 -1
  69. package/out/zero-cache/src/observability/metrics.js +20 -2
  70. package/out/zero-cache/src/observability/metrics.js.map +1 -1
  71. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  72. package/out/zero-cache/src/server/change-streamer.js +105 -34
  73. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  74. package/out/zero-cache/src/server/main.d.ts.map +1 -1
  75. package/out/zero-cache/src/server/main.js +8 -18
  76. package/out/zero-cache/src/server/main.js.map +1 -1
  77. package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
  78. package/out/zero-cache/src/server/replicator.js +48 -5
  79. package/out/zero-cache/src/server/replicator.js.map +1 -1
  80. package/out/zero-cache/src/server/runner/run-worker.js +3 -2
  81. package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
  82. package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
  83. package/out/zero-cache/src/server/syncer.js +1 -1
  84. package/out/zero-cache/src/server/syncer.js.map +1 -1
  85. package/out/zero-cache/src/server/worker-urls.d.ts +0 -1
  86. package/out/zero-cache/src/server/worker-urls.d.ts.map +1 -1
  87. package/out/zero-cache/src/server/worker-urls.js +1 -2
  88. package/out/zero-cache/src/server/worker-urls.js.map +1 -1
  89. package/out/zero-cache/src/services/change-source/change-source.d.ts +4 -2
  90. package/out/zero-cache/src/services/change-source/change-source.d.ts.map +1 -1
  91. package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts +35 -0
  92. package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts.map +1 -0
  93. package/out/zero-cache/src/services/change-source/common/replica-restore.js +34 -0
  94. package/out/zero-cache/src/services/change-source/common/replica-restore.js.map +1 -0
  95. package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts +2 -0
  96. package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts.map +1 -1
  97. package/out/zero-cache/src/services/change-source/common/replica-schema.js +81 -3
  98. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  99. package/out/zero-cache/src/services/change-source/custom/change-source.d.ts +2 -6
  100. package/out/zero-cache/src/services/change-source/custom/change-source.d.ts.map +1 -1
  101. package/out/zero-cache/src/services/change-source/custom/change-source.js +14 -5
  102. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  103. package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts.map +1 -1
  104. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +22 -11
  105. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  106. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts +7 -7
  107. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  108. package/out/zero-cache/src/services/change-source/pg/change-source.js +90 -31
  109. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  110. package/out/zero-cache/src/services/change-source/pg/decommission.d.ts.map +1 -1
  111. package/out/zero-cache/src/services/change-source/pg/decommission.js +20 -11
  112. package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
  113. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +9 -1
  114. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  115. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +61 -76
  116. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  117. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts +65 -2
  118. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts.map +1 -1
  119. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +101 -10
  120. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
  121. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +27 -2
  122. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -1
  123. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +41 -8
  124. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  125. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts +0 -5
  126. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
  127. package/out/zero-cache/src/services/change-source/pg/schema/init.js +29 -41
  128. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  129. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +35 -16
  130. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
  131. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +77 -7
  132. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  133. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts +2 -1
  134. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts.map +1 -1
  135. package/out/zero-cache/src/services/change-source/protocol/current/data.js +5 -13
  136. package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
  137. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +6 -3
  138. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts.map +1 -1
  139. package/out/zero-cache/src/services/change-source/protocol/current/schema-change-tags.d.ts +6 -0
  140. package/out/zero-cache/src/services/change-source/protocol/current/schema-change-tags.d.ts.map +1 -0
  141. package/out/zero-cache/src/services/change-source/protocol/current/schema-change-tags.js +21 -0
  142. package/out/zero-cache/src/services/change-source/protocol/current/schema-change-tags.js.map +1 -0
  143. package/out/zero-cache/src/services/change-source/protocol/current/status.d.ts +1 -0
  144. package/out/zero-cache/src/services/change-source/protocol/current/status.d.ts.map +1 -1
  145. package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts +1 -0
  146. package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts.map +1 -1
  147. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts +3 -7
  148. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts.map +1 -1
  149. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js +26 -10
  150. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
  151. package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts +15 -5
  152. package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts.map +1 -1
  153. package/out/zero-cache/src/services/change-streamer/backup-monitor.js +71 -0
  154. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -0
  155. package/out/zero-cache/src/services/change-streamer/broadcast.d.ts +68 -78
  156. package/out/zero-cache/src/services/change-streamer/broadcast.d.ts.map +1 -1
  157. package/out/zero-cache/src/services/change-streamer/broadcast.js +84 -92
  158. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  159. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts +19 -0
  160. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts.map +1 -0
  161. package/out/zero-cache/src/services/change-streamer/change-log-codec.js +50 -0
  162. package/out/zero-cache/src/services/change-streamer/change-log-codec.js.map +1 -0
  163. package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.d.ts +34 -0
  164. package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.d.ts.map +1 -0
  165. package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.js +67 -0
  166. package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.js.map +1 -0
  167. package/out/zero-cache/src/services/change-streamer/change-log-initializer.d.ts +141 -0
  168. package/out/zero-cache/src/services/change-streamer/change-log-initializer.d.ts.map +1 -0
  169. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js +270 -0
  170. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js.map +1 -0
  171. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.d.ts +19 -0
  172. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.d.ts.map +1 -0
  173. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.js +39 -0
  174. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.js.map +1 -0
  175. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +5 -8
  176. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  177. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +20 -19
  178. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  179. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +90 -24
  180. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
  181. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +456 -66
  182. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  183. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +50 -13
  184. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
  185. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  186. package/out/zero-cache/src/services/change-streamer/error-type-enum.d.ts +8 -0
  187. package/out/zero-cache/src/services/change-streamer/error-type-enum.d.ts.map +1 -1
  188. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts +14 -3
  189. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts.map +1 -1
  190. package/out/zero-cache/src/services/change-streamer/forwarder.js +42 -12
  191. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  192. package/out/zero-cache/src/services/change-streamer/{litestream3-backup-monitor.d.ts → litestream3-prometheus-poller.d.ts} +19 -45
  193. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.d.ts.map +1 -0
  194. package/out/zero-cache/src/services/change-streamer/{litestream3-backup-monitor.js → litestream3-prometheus-poller.js} +68 -176
  195. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js.map +1 -0
  196. package/out/zero-cache/src/services/change-streamer/replica-poller.d.ts +15 -0
  197. package/out/zero-cache/src/services/change-streamer/replica-poller.d.ts.map +1 -0
  198. package/out/zero-cache/src/services/change-streamer/replica-poller.js +55 -0
  199. package/out/zero-cache/src/services/change-streamer/replica-poller.js.map +1 -0
  200. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts +5 -1
  201. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts.map +1 -1
  202. package/out/zero-cache/src/services/change-streamer/schema/tables.js +2 -1
  203. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  204. package/out/zero-cache/src/services/change-streamer/shard-sampling.d.ts +11 -0
  205. package/out/zero-cache/src/services/change-streamer/shard-sampling.d.ts.map +1 -0
  206. package/out/zero-cache/src/services/change-streamer/shard-sampling.js +19 -0
  207. package/out/zero-cache/src/services/change-streamer/shard-sampling.js.map +1 -0
  208. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts +24 -0
  209. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts.map +1 -0
  210. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js +119 -0
  211. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -0
  212. package/out/zero-cache/src/services/change-streamer/snapshot.d.ts +4 -1
  213. package/out/zero-cache/src/services/change-streamer/snapshot.d.ts.map +1 -1
  214. package/out/zero-cache/src/services/change-streamer/snapshot.js +39 -14
  215. package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
  216. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.d.ts +99 -0
  217. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.d.ts.map +1 -0
  218. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js +344 -0
  219. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js.map +1 -0
  220. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.d.ts +83 -0
  221. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.d.ts.map +1 -0
  222. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js +417 -0
  223. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js.map +1 -0
  224. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.d.ts +121 -0
  225. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.d.ts.map +1 -0
  226. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.js +391 -0
  227. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.js.map +1 -0
  228. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.d.ts +73 -0
  229. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.d.ts.map +1 -0
  230. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.js +157 -0
  231. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.js.map +1 -0
  232. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts +62 -0
  233. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts.map +1 -0
  234. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js +164 -0
  235. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js.map +1 -0
  236. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts +109 -0
  237. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts.map +1 -0
  238. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +258 -0
  239. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js.map +1 -0
  240. package/out/zero-cache/src/services/change-streamer/storer.d.ts +85 -12
  241. package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
  242. package/out/zero-cache/src/services/change-streamer/storer.js +286 -141
  243. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  244. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +61 -10
  245. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
  246. package/out/zero-cache/src/services/change-streamer/subscriber.js +101 -8
  247. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  248. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts +34 -0
  249. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts.map +1 -0
  250. package/out/zero-cache/src/services/change-streamer/upstream-acker.js +86 -0
  251. package/out/zero-cache/src/services/change-streamer/upstream-acker.js.map +1 -0
  252. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts +29 -0
  253. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts.map +1 -0
  254. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js +154 -0
  255. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js.map +1 -0
  256. package/out/zero-cache/src/services/http-service.d.ts +1 -0
  257. package/out/zero-cache/src/services/http-service.d.ts.map +1 -1
  258. package/out/zero-cache/src/services/http-service.js +7 -3
  259. package/out/zero-cache/src/services/http-service.js.map +1 -1
  260. package/out/zero-cache/src/services/life-cycle.d.ts +1 -1
  261. package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
  262. package/out/zero-cache/src/services/life-cycle.js +4 -3
  263. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  264. package/out/zero-cache/src/services/litestream/commands.d.ts +12 -11
  265. package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
  266. package/out/zero-cache/src/services/litestream/commands.js +115 -152
  267. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  268. package/out/zero-cache/src/services/litestream/config-v5.yml +42 -0
  269. package/out/zero-cache/src/services/litestream/config.yml +3 -2
  270. package/out/zero-cache/src/services/litestream/metrics.d.ts +5 -4
  271. package/out/zero-cache/src/services/litestream/metrics.d.ts.map +1 -1
  272. package/out/zero-cache/src/services/litestream/metrics.js +10 -10
  273. package/out/zero-cache/src/services/litestream/metrics.js.map +1 -1
  274. package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
  275. package/out/zero-cache/src/services/mutagen/mutagen.js +5 -2
  276. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  277. package/out/zero-cache/src/services/replicator/change-log-cookies.d.ts +169 -0
  278. package/out/zero-cache/src/services/replicator/change-log-cookies.d.ts.map +1 -0
  279. package/out/zero-cache/src/services/replicator/change-log-cookies.js +434 -0
  280. package/out/zero-cache/src/services/replicator/change-log-cookies.js.map +1 -0
  281. package/out/zero-cache/src/services/replicator/change-log-db.d.ts +368 -0
  282. package/out/zero-cache/src/services/replicator/change-log-db.d.ts.map +1 -0
  283. package/out/zero-cache/src/services/replicator/change-log-db.js +491 -0
  284. package/out/zero-cache/src/services/replicator/change-log-db.js.map +1 -0
  285. package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts +55 -0
  286. package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts.map +1 -0
  287. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js +150 -0
  288. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js.map +1 -0
  289. package/out/zero-cache/src/services/replicator/change-processor.d.ts +6 -0
  290. package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +1 -1
  291. package/out/zero-cache/src/services/replicator/change-processor.js +70 -25
  292. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  293. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts +1 -1
  294. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
  295. package/out/zero-cache/src/services/replicator/incremental-sync.js +19 -6
  296. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  297. package/out/zero-cache/src/services/replicator/notifier.d.ts +0 -22
  298. package/out/zero-cache/src/services/replicator/notifier.d.ts.map +1 -1
  299. package/out/zero-cache/src/services/replicator/notifier.js +7 -1
  300. package/out/zero-cache/src/services/replicator/notifier.js.map +1 -1
  301. package/out/zero-cache/src/services/replicator/replicator.d.ts +17 -2
  302. package/out/zero-cache/src/services/replicator/replicator.d.ts.map +1 -1
  303. package/out/zero-cache/src/services/replicator/replicator.js +9 -3
  304. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  305. package/out/zero-cache/src/services/replicator/reporter/recorder.d.ts +27 -0
  306. package/out/zero-cache/src/services/replicator/reporter/recorder.d.ts.map +1 -1
  307. package/out/zero-cache/src/services/replicator/reporter/recorder.js +32 -0
  308. package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
  309. package/out/zero-cache/src/services/replicator/reporter/report-schema.d.ts +1 -1
  310. package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts +119 -0
  311. package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts.map +1 -0
  312. package/out/zero-cache/src/services/replicator/schema/backfilling.js +214 -0
  313. package/out/zero-cache/src/services/replicator/schema/backfilling.js.map +1 -0
  314. package/out/zero-cache/src/services/replicator/schema/change-log.d.ts +3 -3
  315. package/out/zero-cache/src/services/replicator/schema/column-metadata.js +1 -1
  316. package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
  317. package/out/zero-cache/src/services/replicator/schema/replication-state.d.ts +1 -1
  318. package/out/zero-cache/src/services/replicator/schema/replication-state.d.ts.map +1 -1
  319. package/out/zero-cache/src/services/replicator/schema/replication-state.js +9 -5
  320. package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
  321. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts +132 -0
  322. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts.map +1 -0
  323. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +415 -0
  324. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -0
  325. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.d.ts +120 -0
  326. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.d.ts.map +1 -0
  327. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js +283 -0
  328. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js.map +1 -0
  329. package/out/zero-cache/src/services/replicator/write-worker.js +23 -12
  330. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  331. package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
  332. package/out/zero-cache/src/services/run-ast.js +2 -1
  333. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  334. package/out/zero-cache/src/services/running-state.d.ts +1 -1
  335. package/out/zero-cache/src/services/running-state.d.ts.map +1 -1
  336. package/out/zero-cache/src/services/running-state.js +1 -0
  337. package/out/zero-cache/src/services/running-state.js.map +1 -1
  338. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts +3 -2
  339. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +1 -1
  340. package/out/zero-cache/src/services/view-syncer/client-handler.js +25 -17
  341. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  342. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
  343. package/out/zero-cache/src/services/view-syncer/cvr-store.js +14 -1
  344. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  345. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.d.ts +53 -0
  346. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.d.ts.map +1 -0
  347. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.js +68 -0
  348. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.js.map +1 -0
  349. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  350. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +18 -3
  351. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  352. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
  353. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  354. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +14 -4
  355. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  356. package/out/zero-cache/src/services/view-syncer/view-syncer.js +70 -8
  357. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  358. package/out/zero-cache/src/types/downstream.d.ts +11 -0
  359. package/out/zero-cache/src/types/downstream.d.ts.map +1 -0
  360. package/out/zero-cache/src/types/pg.d.ts.map +1 -1
  361. package/out/zero-cache/src/types/pg.js +14 -4
  362. package/out/zero-cache/src/types/pg.js.map +1 -1
  363. package/out/zero-cache/src/types/poke-chunk.d.ts +18 -0
  364. package/out/zero-cache/src/types/poke-chunk.d.ts.map +1 -0
  365. package/out/zero-cache/src/types/poke-chunk.js +57 -0
  366. package/out/zero-cache/src/types/poke-chunk.js.map +1 -0
  367. package/out/zero-cache/src/types/processes.d.ts +1 -2
  368. package/out/zero-cache/src/types/processes.d.ts.map +1 -1
  369. package/out/zero-cache/src/types/processes.js +1 -28
  370. package/out/zero-cache/src/types/processes.js.map +1 -1
  371. package/out/zero-cache/src/types/streams.d.ts +16 -0
  372. package/out/zero-cache/src/types/streams.d.ts.map +1 -1
  373. package/out/zero-cache/src/types/streams.js +25 -4
  374. package/out/zero-cache/src/types/streams.js.map +1 -1
  375. package/out/zero-cache/src/types/subscription.d.ts +2 -0
  376. package/out/zero-cache/src/types/subscription.d.ts.map +1 -1
  377. package/out/zero-cache/src/types/subscription.js +6 -0
  378. package/out/zero-cache/src/types/subscription.js.map +1 -1
  379. package/out/zero-cache/src/workers/connection.d.ts +23 -4
  380. package/out/zero-cache/src/workers/connection.d.ts.map +1 -1
  381. package/out/zero-cache/src/workers/connection.js +130 -10
  382. package/out/zero-cache/src/workers/connection.js.map +1 -1
  383. package/out/zero-cache/src/workers/replicator.d.ts +25 -0
  384. package/out/zero-cache/src/workers/replicator.d.ts.map +1 -1
  385. package/out/zero-cache/src/workers/replicator.js +33 -1
  386. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  387. package/out/zero-cache/src/workers/syncer.d.ts +12 -0
  388. package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
  389. package/out/zero-cache/src/workers/syncer.js +75 -0
  390. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  391. package/out/zero-client/src/client/version.js +1 -1
  392. package/out/zero-client/src/client/zero-poke-handler.d.ts +7 -2
  393. package/out/zero-client/src/client/zero-poke-handler.d.ts.map +1 -1
  394. package/out/zero-client/src/client/zero-poke-handler.js +52 -2
  395. package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
  396. package/out/zero-client/src/client/zero.d.ts +6 -7
  397. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  398. package/out/zero-client/src/client/zero.js +194 -107
  399. package/out/zero-client/src/client/zero.js.map +1 -1
  400. package/out/zero-protocol/src/down.d.ts +1 -1
  401. package/out/zero-protocol/src/poke.d.ts +89 -7
  402. package/out/zero-protocol/src/poke.d.ts.map +1 -1
  403. package/out/zero-protocol/src/poke.js +16 -8
  404. package/out/zero-protocol/src/poke.js.map +1 -1
  405. package/out/zero-protocol/src/protocol-version.d.ts +12 -7
  406. package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
  407. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  408. package/out/zero-schema/src/builder/table-builder.d.ts +23 -0
  409. package/out/zero-schema/src/builder/table-builder.d.ts.map +1 -1
  410. package/out/zero-schema/src/builder/table-builder.js +18 -0
  411. package/out/zero-schema/src/builder/table-builder.js.map +1 -1
  412. package/out/zero-server/src/custom.js +10 -3
  413. package/out/zero-server/src/custom.js.map +1 -1
  414. package/out/zero-server/src/schema.d.ts +8 -0
  415. package/out/zero-server/src/schema.d.ts.map +1 -1
  416. package/out/zero-server/src/schema.js +63 -24
  417. package/out/zero-server/src/schema.js.map +1 -1
  418. package/out/zero-types/src/schema.d.ts +11 -0
  419. package/out/zero-types/src/schema.d.ts.map +1 -1
  420. package/out/zql/src/ivm/memory-source.d.ts +10 -2
  421. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  422. package/out/zql/src/ivm/memory-source.js +14 -6
  423. package/out/zql/src/ivm/memory-source.js.map +1 -1
  424. package/out/zql/src/ivm/stream.d.ts +0 -1
  425. package/out/zql/src/ivm/stream.d.ts.map +1 -1
  426. package/out/zql/src/ivm/stream.js +1 -7
  427. package/out/zql/src/ivm/stream.js.map +1 -1
  428. package/out/zql/src/ivm/union-fan-in.d.ts +1 -1
  429. package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
  430. package/out/zql/src/ivm/union-fan-in.js +11 -2
  431. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  432. package/out/zql/src/mutate/crud.d.ts +10 -8
  433. package/out/zql/src/mutate/crud.d.ts.map +1 -1
  434. package/out/zql/src/mutate/crud.js.map +1 -1
  435. package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
  436. package/out/zql/src/query/query-delegate-base.js +5 -1
  437. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  438. package/out/zql/src/query/query-impl.d.ts +2 -2
  439. package/out/zql/src/query/query-impl.d.ts.map +1 -1
  440. package/out/zql/src/query/query-impl.js.map +1 -1
  441. package/out/zql/src/query/query.d.ts +83 -14
  442. package/out/zql/src/query/query.d.ts.map +1 -1
  443. package/out/zql/src/query/static-query.d.ts +1 -1
  444. package/out/zql/src/query/static-query.d.ts.map +1 -1
  445. package/out/zqlite/src/db.js +2 -2
  446. package/out/zqlite/src/resolve-scalar-subqueries.d.ts +15 -0
  447. package/out/zqlite/src/resolve-scalar-subqueries.d.ts.map +1 -1
  448. package/out/zqlite/src/resolve-scalar-subqueries.js +33 -14
  449. package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
  450. package/out/zqlite/src/sqlite-cost-model.js +5 -5
  451. package/out/zqlite/src/table-source.d.ts.map +1 -1
  452. package/out/zqlite/src/table-source.js +4 -4
  453. package/out/zqlite/src/table-source.js.map +1 -1
  454. package/package.json +3 -3
  455. package/out/replicache/src/db/index.js +0 -91
  456. package/out/replicache/src/db/index.js.map +0 -1
  457. package/out/replicache/src/lazy.d.ts +0 -2
  458. package/out/replicache/src/lazy.d.ts.map +0 -1
  459. package/out/replicache/src/lazy.js +0 -12
  460. package/out/replicache/src/lazy.js.map +0 -1
  461. package/out/zero-cache/src/server/backup-watermark-reader.js +0 -35
  462. package/out/zero-cache/src/server/backup-watermark-reader.js.map +0 -1
  463. package/out/zero-cache/src/services/change-streamer/litestream3-backup-monitor.d.ts.map +0 -1
  464. package/out/zero-cache/src/services/change-streamer/litestream3-backup-monitor.js.map +0 -1
  465. package/out/zero-cache/src/services/change-streamer/replica-monitor.d.ts +0 -16
  466. package/out/zero-cache/src/services/change-streamer/replica-monitor.d.ts.map +0 -1
  467. package/out/zero-cache/src/services/change-streamer/replica-monitor.js +0 -54
  468. package/out/zero-cache/src/services/change-streamer/replica-monitor.js.map +0 -1
  469. package/out/zero-cache/src/services/change-streamer/vfs-backup-monitor.d.ts +0 -25
  470. package/out/zero-cache/src/services/change-streamer/vfs-backup-monitor.d.ts.map +0 -1
  471. package/out/zero-cache/src/services/change-streamer/vfs-backup-monitor.js +0 -159
  472. package/out/zero-cache/src/services/change-streamer/vfs-backup-monitor.js.map +0 -1
  473. package/out/zero-cache/src/services/litestream/vfs-watermark-reader.d.ts +0 -44
  474. package/out/zero-cache/src/services/litestream/vfs-watermark-reader.d.ts.map +0 -1
  475. package/out/zero-cache/src/services/litestream/vfs-watermark-reader.js +0 -65
  476. package/out/zero-cache/src/services/litestream/vfs-watermark-reader.js.map +0 -1
  477. package/out/zero-cache/src/services/litestream/vfs-watermark-worker-source.d.ts +0 -11
  478. package/out/zero-cache/src/services/litestream/vfs-watermark-worker-source.d.ts.map +0 -1
  479. package/out/zero-cache/src/services/litestream/vfs-watermark-worker-source.js +0 -74
  480. package/out/zero-cache/src/services/litestream/vfs-watermark-worker-source.js.map +0 -1
  481. package/out/zero-cache/src/services/litestream/vfs-watermark-worker.d.ts +0 -42
  482. package/out/zero-cache/src/services/litestream/vfs-watermark-worker.d.ts.map +0 -1
  483. package/out/zero-cache/src/services/litestream/vfs-watermark-worker.js +0 -117
  484. package/out/zero-cache/src/services/litestream/vfs-watermark-worker.js.map +0 -1
@@ -1,31 +1,41 @@
1
- import { unreachable } from "../../../../shared/src/asserts.js";
1
+ import { assert, unreachable } from "../../../../shared/src/asserts.js";
2
2
  import { promiseVoid } from "../../../../shared/src/resolved-promises.js";
3
+ import { must } from "../../../../shared/src/must.js";
4
+ import { promiseOrAbort } from "../../../../shared/src/promise-race.js";
3
5
  import "../change-source/protocol/current/downstream.js";
4
- import { getOrCreateCounter } from "../../observability/metrics.js";
6
+ import { getOrCreateCounter, getOrCreateLatencyHistogram } from "../../observability/metrics.js";
5
7
  import { DEFAULT_MAX_RETRY_DELAY_MS, RunningState, UnrecoverableError } from "../running-state.js";
6
8
  import { min } from "../../types/lexi-version.js";
7
9
  import { publishCriticalEvent } from "../../observability/events.js";
8
- import { AutoResetSignal, ensureReplicationConfig, markResetRequired } from "./schema/tables.js";
9
10
  import { Subscription } from "../../types/subscription.js";
11
+ import { AutoResetSignal, ensureReplicationConfig, markResetRequired } from "./schema/tables.js";
10
12
  import { publishReplicationError, replicationStatusError } from "../replicator/replication-status.js";
11
13
  import "./change-streamer.js";
14
+ import { SQLiteChangeLogReader } from "./sqlite-change-log-reader.js";
15
+ import { ChangeLogInitializer, replicaInitializationSource } from "./change-log-initializer.js";
12
16
  import { Forwarder } from "./forwarder.js";
13
- import { initChangeStreamerSchema } from "./schema/init.js";
17
+ import { SnapshotReservations } from "./snapshot-reservations.js";
18
+ import { SQLiteChangeLogCatchup } from "./sqlite-change-log-catchup.js";
19
+ import { SQLiteChangeLogComparator } from "./sqlite-change-log-comparator.js";
20
+ import { SQLiteChangeLogPurgeScheduler } from "./sqlite-change-log-purge-scheduler.js";
21
+ import { SQLiteChangeLogReadRouter, inspectSQLiteChangeLog } from "./sqlite-change-log-read-router.js";
22
+ import { SQLiteChangeLogWriter } from "./sqlite-change-log-writer.js";
14
23
  import { Storer } from "./storer.js";
15
24
  import { Subscriber } from "./subscriber.js";
25
+ import { UpstreamAcker } from "./upstream-acker.js";
16
26
  import { resolver } from "@rocicorp/resolver";
17
27
  import { getDefaultHighWaterMark } from "node:stream";
18
28
  //#region ../zero-cache/src/services/change-streamer/change-streamer-service.ts
19
29
  /**
20
30
  * Performs initialization and schema migrations to initialize a ChangeStreamerImpl.
21
31
  */
22
- async function initializeStreamer(lc, shard, taskID, discoveryAddress, discoveryProtocol, changeDB, changeSource, replicationStatusPublisher, subscriptionState, purgeLock, autoReset, opts, setTimeoutFn = setTimeout) {
23
- await initChangeStreamerSchema(lc, changeDB, shard);
24
- await ensureReplicationConfig(lc, changeDB, subscriptionState, shard, autoReset, setTimeoutFn);
32
+ async function initializeStreamer(lc, shard, taskID, discoveryAddress, discoveryProtocol, changeDB, changeSource, replicationStatusPublisher, subscriptionState, backupConfig, purgeLock, autoReset, opts, setTimeoutFn = setTimeout) {
33
+ await ensureReplicationConfig(lc, changeDB, subscriptionState, shard, autoReset, purgeLock ?? void 0, setTimeoutFn);
25
34
  const { replicaVersion } = subscriptionState;
26
- return new ChangeStreamerImpl(lc, shard, taskID, discoveryAddress, discoveryProtocol, changeDB, replicaVersion, changeSource, replicationStatusPublisher, purgeLock, autoReset, opts, setTimeoutFn);
35
+ return new ChangeStreamerImpl(lc, shard, taskID, discoveryAddress, discoveryProtocol, changeDB, replicaVersion, changeSource, replicationStatusPublisher, backupConfig, purgeLock, autoReset, opts, setTimeoutFn);
27
36
  }
28
37
  var REPLICATION_STATUS_ERROR_DELAY_THRESHOLD_MS = 5e3;
38
+ var DEFAULT_CHANGE_LOG_UNAVAILABLE_WARN_THRESHOLD_MS = 6e4;
29
39
  /**
30
40
  * Upstream-agnostic dispatch of messages in a {@link ChangeStreamMessage} to a
31
41
  * {@link Forwarder} and {@link Storer} to execute the forward-store-ack
@@ -173,30 +183,115 @@ var ChangeStreamerImpl = class {
173
183
  #source;
174
184
  #storer;
175
185
  #forwarder;
186
+ #reservations;
176
187
  #replicationStatusPublisher;
188
+ #sqliteCatchupOptions;
189
+ #changeLogWriter;
190
+ #purgeScheduler;
191
+ #comparator;
192
+ #acker;
193
+ #initializer;
194
+ #readRouter;
177
195
  #autoReset;
178
196
  #state;
179
- #initialWatermarks = /* @__PURE__ */ new Set();
180
197
  #serving = resolver();
181
198
  #txCounter = getOrCreateCounter("replication", "transactions", "Count of replicated transactions");
182
199
  #changeCounter = getOrCreateCounter("replication", "changes", "Count of replicated changes (DML or DDL statements)");
200
+ #transactionForwardDuration = getOrCreateLatencyHistogram("replication", "transaction_forward_duration", "Time from receiving a transaction's `begin` to forwarding its `commit`, i.e. the change-streamer half of forward-to-subscriber latency.");
201
+ #catchupRoutes = getOrCreateCounter("replication", "sqlite_change_log.catchup_routes", "Catchup subscriptions by selected source and low-cardinality reason.");
202
+ #reservationDemotions = getOrCreateCounter("replication", "sqlite_change_log.reservation_demotions", "Snapshot reservations demoted from SQLite to PG because the change log could not cover the backup being restored. With PG retired these followers have no fallback, so this is the rate at which one would instead have to wait for a later backup.");
203
+ #reservationConfirmDelays = getOrCreateCounter("replication", "sqlite_change_log.reservation_confirm_delays", "Snapshot reservations whose confirmation was deferred because the selected source's change-log minimum was later than the backup watermark. Counted once per reservation, by that source.");
183
204
  #latestStatus;
205
+ #latestLagReportCommitTimeMs = 0;
206
+ #backupWatermark;
207
+ #pgPurgedWatermark = "";
208
+ /**
209
+ * The floor last logged as held back by a laggard. The level-triggered
210
+ * retry re-evaluates every {@link CLEANUP_DELAY_MS}; logging only when the
211
+ * blocking floor moves keeps a stuck subscriber from emitting the same
212
+ * line indefinitely.
213
+ */
214
+ #loggedBehindWatermark;
215
+ #sqlitePurgeContinuation;
184
216
  #purgeLock;
217
+ #pgPurgeScheduled = false;
218
+ #pgPurgeRunning = false;
219
+ #sqlitePurgeScheduled = false;
220
+ #sqlitePurgeRunning = false;
185
221
  #stream;
186
- constructor(lc, shard, taskID, discoveryAddress, discoveryProtocol, changeDB, replicaVersion, source, replicationStatusPublisher, initialPurgeLock, autoReset, opts, setTimeoutFn = setTimeout) {
222
+ #sqliteCatchup;
223
+ #changeLogUnavailableSince;
224
+ #lastForwardedCommitWatermark;
225
+ #transactionForwardStartedAt;
226
+ #currentTransactionCompletion;
227
+ constructor(lc, shard, taskID, discoveryAddress, discoveryProtocol, changeDB, replicaVersion, source, replicationStatusPublisher, backupConfig, initialPurgeLock, autoReset, opts, setTimeoutFn = setTimeout) {
187
228
  this.id = `change-streamer`;
188
229
  this.#lc = lc.withContext("component", "change-streamer");
189
230
  this.#shard = shard;
190
231
  this.#changeDB = changeDB;
191
232
  this.#replicaVersion = replicaVersion;
192
233
  this.#source = source;
193
- this.#storer = new Storer(lc, shard, taskID, discoveryAddress, discoveryProtocol, changeDB, replicaVersion, (consumed) => this.#stream?.acks.push([
194
- "status",
195
- consumed[1],
196
- consumed[2]
197
- ]), (err) => this.stop(err), opts);
198
- this.#forwarder = new Forwarder(lc, { flowControlConsensusPaddingSeconds: opts.flowControlConsensusPaddingSeconds });
234
+ this.#storer = new Storer(lc, shard, taskID, discoveryAddress, discoveryProtocol, changeDB, replicaVersion, (consumed) => this.#acker.trackPgChangeLog(consumed[2].watermark), (err) => this.stop(err), opts);
235
+ this.#forwarder = new Forwarder(lc, {
236
+ flowControlConsensusTimeoutProportion: opts.flowControlConsensusTimeoutProportion,
237
+ flowControlSlowSubscriberGracePeriodMs: opts.flowControlSlowSubscriberGracePeriodMs
238
+ });
239
+ const serveOptions = opts.sqliteChangeLogServe;
240
+ const writerOptions = opts.sqliteChangeLogWriter;
241
+ const catchupOptions = opts.sqliteCatchup;
242
+ this.#readRouter = serveOptions && writerOptions && catchupOptions ? new SQLiteChangeLogReadRouter({
243
+ shard,
244
+ readPercent: serveOptions.readPercent,
245
+ coldReadPercent: serveOptions.coldReadPercent,
246
+ retentionMs: serveOptions.retentionMs,
247
+ failureCooldownMs: serveOptions.failureCooldownMs,
248
+ now: serveOptions.now,
249
+ inspect: serveOptions.inspect ?? (() => inspectSQLiteChangeLog(lc, catchupOptions.changeLogFile, writerOptions.identity))
250
+ }) : void 0;
251
+ this.#reservations = backupConfig ? new SnapshotReservations(lc, backupConfig, (taskID) => {
252
+ this.#readRouter?.release(taskID);
253
+ this.#purgeScheduler?.resume(taskID);
254
+ }) : void 0;
199
255
  this.#replicationStatusPublisher = replicationStatusPublisher;
256
+ this.#changeLogWriter = opts.sqliteChangeLogWriter ? new SQLiteChangeLogWriter(lc, {
257
+ ...opts.sqliteChangeLogWriter,
258
+ onCommit: (watermark) => {
259
+ this.#sqliteCatchup?.onChangeLogCommit(watermark);
260
+ this.#purgeScheduler?.onWriterIdle();
261
+ },
262
+ onDisabled: () => {
263
+ this.#readRouter?.trip(true);
264
+ this.#closeSQLiteCatchup();
265
+ this.#comparator?.stop();
266
+ },
267
+ onRebuilt: () => {
268
+ this.#closeSQLiteCatchup();
269
+ this.#comparator?.invalidate();
270
+ }
271
+ }) : void 0;
272
+ this.#purgeScheduler = opts.sqliteChangeLogPurge && this.#changeLogWriter ? new SQLiteChangeLogPurgeScheduler(lc, () => this.#changeLogWriter?.connection, () => this.#forwarder.getAcks(), opts.sqliteChangeLogPurge) : void 0;
273
+ this.#acker = new UpstreamAcker({
274
+ trackPgChangeLog: true,
275
+ trackBackup: backupConfig?.litestreamVersion === "v5"
276
+ });
277
+ const replicaSource = opts.sqliteChangeLogCompare ? replicaInitializationSource(lc, opts.sqliteChangeLogCompare.replicaFile) : void 0;
278
+ this.#initializer = new ChangeLogInitializer(lc, {
279
+ initFromPgChangeLog: true,
280
+ initFromReplica: replicaSource !== void 0
281
+ }, {
282
+ pgChangeLog: () => this.#storer.getStartStreamInitializationParameters(),
283
+ replica: () => must(replicaSource)(),
284
+ reconcileChangeLog: (resumeFrom, seed) => resumeFrom ? this.#changeLogWriter?.reconcile(resumeFrom) : this.#changeLogWriter?.reconcileFromLog(seed),
285
+ changeLog: () => this.#changeLogWriter?.connection
286
+ });
287
+ this.#sqliteCatchupOptions = opts.sqliteCatchup ? {
288
+ ...opts.sqliteCatchup,
289
+ cleanupGuard: opts.sqliteCatchup.cleanupGuard ?? this.#purgeScheduler?.cleanupGuard
290
+ } : void 0;
291
+ this.#comparator = opts.sqliteChangeLogCompare && opts.sqliteChangeLogWriter && opts.sqliteCatchup ? new SQLiteChangeLogComparator(lc, shard, opts.sqliteCatchup.changeLogFile, opts.sqliteChangeLogWriter.identity, this.#storer, {
292
+ setTimeoutFn,
293
+ ...opts.sqliteChangeLogCompare
294
+ }) : void 0;
200
295
  this.#purgeLock = initialPurgeLock;
201
296
  this.#autoReset = autoReset;
202
297
  this.#state = new RunningState(this.id, void 0, setTimeoutFn);
@@ -205,8 +300,11 @@ var ChangeStreamerImpl = class {
205
300
  async run() {
206
301
  this.#lc.info?.("starting change stream");
207
302
  this.#forwarder.startProgressMonitor();
208
- const lagReport = await this.#source.startLagReporter();
209
- if (lagReport) this.#latestStatus.lagReport = lagReport;
303
+ const lagReportInit = await this.#source.startLagReporter();
304
+ if (lagReportInit) {
305
+ this.#latestStatus.lagReport = { nextSendTimeMs: lagReportInit.nextSendTimeMs };
306
+ this.#latestLagReportCommitTimeMs = lagReportInit.firstCommitTimeMs;
307
+ }
210
308
  await this.#storer.assumeOwnership(this.#purgeLock);
211
309
  this.#purgeLock = null;
212
310
  const flushBytesThreshold = getDefaultHighWaterMark(false);
@@ -215,19 +313,22 @@ var ChangeStreamerImpl = class {
215
313
  let watermark = null;
216
314
  let unflushedBytes = 0;
217
315
  try {
218
- const { lastWatermark, backfillRequests } = await this.#storer.getStartStreamInitializationParameters();
316
+ const { lastWatermark, backfillRequests } = await this.#initializer.initialize();
317
+ this.#lastForwardedCommitWatermark = lastWatermark;
219
318
  const stream = await this.#source.startStream(lastWatermark, backfillRequests);
220
319
  this.#storer.run().catch((e) => stream.changes.cancel(e));
221
320
  this.#stream = stream;
222
321
  if (this.#state.resetBackoff() > REPLICATION_STATUS_ERROR_DELAY_THRESHOLD_MS) this.#replicationStatusPublisher.publish(this.#lc, "Replicating", `Replicating from ${lastWatermark}`);
223
322
  watermark = null;
323
+ this.#acker.reset(stream.acks);
224
324
  for await (const change of stream.changes) {
325
+ this.#acker.trackDownstream(change);
225
326
  const [type, msg] = change;
226
327
  switch (type) {
227
328
  case "status":
228
- if (msg.ack) this.#storer.status(change);
229
- if (msg.lagReport) {
329
+ if (msg.lagReport && msg.lagReport.lastTimings.commitTimeMs >= this.#latestLagReportCommitTimeMs) {
230
330
  this.#latestStatus.lagReport = msg.lagReport;
331
+ this.#latestLagReportCommitTimeMs = msg.lagReport.lastTimings.commitTimeMs;
231
332
  this.#forwarder.sendStatus(this.#latestStatus);
232
333
  }
233
334
  continue;
@@ -247,20 +348,25 @@ var ChangeStreamerImpl = class {
247
348
  break;
248
349
  }
249
350
  const json = this.#storer.store(watermark, change);
351
+ this.#changeLogWriter?.write(change, json);
250
352
  const entry = [
251
353
  watermark,
252
354
  change[1].tag,
253
355
  json
254
356
  ];
255
357
  unflushedBytes += json.length;
256
- if (unflushedBytes < flushBytesThreshold) this.#forwarder.forward(entry);
257
- else {
258
- await this.#forwarder.forwardWithFlowControl(entry);
358
+ if (unflushedBytes < flushBytesThreshold) {
359
+ this.#forwarder.forward(entry);
360
+ this.#recordForwardedTransactionBoundary(type, entry[0]);
361
+ } else {
362
+ const forwarded = this.#forwarder.forwardWithFlowControl(entry);
363
+ this.#recordForwardedTransactionBoundary(type, entry[0]);
364
+ await promiseOrAbort(forwarded, stream.changes.signal, this.#state.signal);
259
365
  unflushedBytes = 0;
260
366
  }
261
367
  if (type === "commit" || type === "rollback") watermark = null;
262
368
  const readyForMore = this.#storer.readyForMore();
263
- if (readyForMore) await readyForMore;
369
+ if (readyForMore) await promiseOrAbort(readyForMore, stream.changes.signal, this.#state.signal);
264
370
  }
265
371
  } catch (e) {
266
372
  err = e;
@@ -271,11 +377,14 @@ var ChangeStreamerImpl = class {
271
377
  if (watermark) {
272
378
  this.#lc.warn?.(`aborting interrupted transaction ${watermark}`);
273
379
  this.#storer.abort();
380
+ this.#changeLogWriter?.abort();
381
+ this.#purgeScheduler?.onWriterIdle();
274
382
  this.#forwarder.forward([
275
383
  watermark,
276
384
  "rollback",
277
385
  ROLLBACK_JSON
278
386
  ]);
387
+ this.#recordForwardedTransactionBoundary("rollback", watermark);
279
388
  }
280
389
  await Promise.all([
281
390
  this.#storer.stop(),
@@ -301,27 +410,135 @@ var ChangeStreamerImpl = class {
301
410
  default: unreachable(tag);
302
411
  }
303
412
  }
304
- subscribe(ctx) {
413
+ async subscribe(ctx) {
305
414
  const { protocolVersion, id, mode, replicaVersion, watermark } = ctx;
306
415
  if (mode === "serving") this.#serving.resolve();
307
- const downstream = Subscription.create({ cleanup: () => this.#forwarder.remove(subscriber) });
308
- const subscriber = new Subscriber(protocolVersion, id, watermark, downstream, () => this.#latestStatus);
416
+ let cleanupSubscriber = () => {};
417
+ const downstream = Subscription.create({ cleanup: () => cleanupSubscriber() });
418
+ const subscriber = new Subscriber(protocolVersion, id, watermark, downstream, () => this.#latestStatus, {});
419
+ const lc = this.#lc.withContext("subscriber", subscriber.id);
420
+ const removeFromForwarder = () => {
421
+ lc.info?.(`removing subscriber ${subscriber.id}`);
422
+ this.#forwarder.remove(subscriber);
423
+ };
424
+ cleanupSubscriber = removeFromForwarder;
309
425
  if (replicaVersion !== this.#replicaVersion) {
310
- this.#lc.warn?.(`rejecting subscriber at replica version ${replicaVersion}`);
426
+ lc.warn?.(`rejecting subscriber at replica version ${replicaVersion}`);
311
427
  subscriber.close(1, `current replica version is ${this.#replicaVersion} (requested ${replicaVersion})`);
312
428
  } else {
313
- this.#lc.debug?.(`adding subscriber ${subscriber.id}`);
314
- this.#forwarder.add(subscriber);
315
- this.#storer.catchup(subscriber, mode);
429
+ lc.info?.(`adding subscriber ${subscriber.id}`);
430
+ const catchupFromPG = () => {
431
+ cleanupSubscriber = removeFromForwarder;
432
+ this.#forwarder.add(subscriber);
433
+ this.#storer.catchup(subscriber, mode);
434
+ };
435
+ const sqliteSelection = this.#selectSQLiteCatchup(lc, ctx);
436
+ if (!sqliteSelection) catchupFromPG();
437
+ else {
438
+ const { catchup, reason, coverage, logWarm } = sqliteSelection;
439
+ cleanupSubscriber = () => catchup.remove(subscriber);
440
+ const registration = await catchup.catchup(subscriber, () => this.#captureRequiredHead(), { logWarm });
441
+ switch (registration.kind) {
442
+ case "registered":
443
+ lc.debug?.(`serving ${ctx.id} from SQLite catchup`, ...coverage ? [{ sqliteChangeLogCoverage: coverage }] : []);
444
+ this.#recordCatchupRoute("sqlite", reason);
445
+ break;
446
+ case "uncovered":
447
+ lc.info?.(`serving ${ctx.id} from PG catchup: subscriber watermark ${ctx.watermark} is below the SQLite change-log minimum ` + registration.minWatermark);
448
+ this.#recordCatchupRoute("pg", "watermark-uncovered");
449
+ catchupFromPG();
450
+ break;
451
+ case "declined":
452
+ lc.error?.(`serving ${ctx.id} from PG catchup: SQLite catchup registration failed`, registration.error);
453
+ this.#recordCatchupRoute("pg", "registration-failed");
454
+ catchupFromPG();
455
+ break;
456
+ case "handled":
457
+ this.#recordCatchupRoute("sqlite", "registration-handled");
458
+ break;
459
+ default: unreachable(registration);
460
+ }
461
+ }
462
+ }
463
+ this.#reservations?.close(ctx.taskID);
464
+ return downstream;
465
+ }
466
+ async startSnapshotReservation(taskID) {
467
+ if (!this.#reservations) throw new Error("backups are not configured");
468
+ const downstream = this.#reservations.open(taskID);
469
+ try {
470
+ await (this.#purgeScheduler?.pause(taskID) ?? promiseVoid);
471
+ if (!this.#reservations.isCurrent(taskID, downstream)) return downstream;
472
+ this.#readRouter?.pin(taskID);
473
+ await this.#confirmReservations();
474
+ return downstream;
475
+ } catch (e) {
476
+ downstream.cancel();
477
+ throw e;
316
478
  }
317
- return Promise.resolve(downstream);
318
479
  }
319
- scheduleCleanup(watermark) {
320
- const origSize = this.#initialWatermarks.size;
321
- this.#initialWatermarks.add(watermark);
322
- if (origSize === 0) this.#state.setTimeout(() => this.#purgeOldChanges(), CLEANUP_DELAY_MS);
480
+ trackBackupWatermark(watermark) {
481
+ this.#backupWatermark = watermark;
482
+ this.#acker.trackBackup(watermark);
483
+ this.#requestSQLitePurge("deferred");
484
+ this.#maybeSchedulePGPurge();
485
+ this.#maybeScheduleSQLitePurge();
486
+ this.#confirmReservations().catch((e) => this.#lc.warn?.(`error confirming snapshot reservation`, e));
323
487
  }
324
- async getChangeLogState() {
488
+ async #confirmReservations() {
489
+ const backupWatermark = this.#backupWatermark;
490
+ const reservations = this.#reservations;
491
+ if (backupWatermark === void 0 || !reservations?.confirmationsRequired()) return;
492
+ const pgState = await this.#getChangeLogState();
493
+ for (const taskID of reservations.unconfirmedTaskIDs()) {
494
+ let route = this.#readRouter?.peek(taskID);
495
+ if (this.#readRouter && route === void 0) continue;
496
+ if (route?.source === "sqlite") {
497
+ const coverage = must(route.coverage, "a pinned SQLite route must carry its covered range");
498
+ if (coverage.minWatermark > backupWatermark) {
499
+ this.#lc.info?.(`demoting ${taskID} to PG catchup: SQLite change-log minimum ${coverage.minWatermark} is later than backupWatermark ` + backupWatermark);
500
+ this.#reservationDemotions.add(1);
501
+ route = must(this.#readRouter).demote(taskID);
502
+ }
503
+ }
504
+ const source = route?.source ?? "pg";
505
+ let minWatermark;
506
+ if (route?.source === "sqlite") minWatermark = must(route.coverage, "a pinned SQLite route must carry its covered range").minWatermark;
507
+ else ({minWatermark} = pgState);
508
+ if (minWatermark <= backupWatermark) reservations.confirmFor(taskID, pgState.replicaVersion, backupWatermark, source);
509
+ else {
510
+ if (reservations.noteConfirmationDelayed(taskID)) this.#reservationConfirmDelays.add(1);
511
+ this.#lc.error?.(`pg change-log minWatermark ${minWatermark} is later than backupWatermark ${backupWatermark}. Delaying confirmation of snapshot reservation until next backup.`);
512
+ }
513
+ }
514
+ }
515
+ #maybeSchedulePGPurge() {
516
+ const backupWatermark = this.#backupWatermark;
517
+ if (this.#pgPurgeScheduled || this.#pgPurgeRunning || backupWatermark === void 0 || this.#pgPurgedWatermark >= backupWatermark) return;
518
+ this.#pgPurgeScheduled = true;
519
+ this.#state.setTimeout(() => {
520
+ this.#pgPurgeScheduled = false;
521
+ this.#pgPurgeRunning = true;
522
+ return this.#purgePGChangeLog().finally(() => {
523
+ this.#pgPurgeRunning = false;
524
+ this.#maybeSchedulePGPurge();
525
+ });
526
+ }, CLEANUP_DELAY_MS);
527
+ }
528
+ #maybeScheduleSQLitePurge() {
529
+ if (this.#sqlitePurgeScheduled || this.#sqlitePurgeRunning || this.#backupWatermark === void 0 || this.#sqlitePurgeContinuation === void 0) return;
530
+ const delay = this.#sqlitePurgeContinuation === "immediate" ? 0 : CLEANUP_DELAY_MS;
531
+ this.#sqlitePurgeScheduled = true;
532
+ this.#state.setTimeout(() => {
533
+ this.#sqlitePurgeScheduled = false;
534
+ this.#sqlitePurgeRunning = true;
535
+ return this.#purgeSQLiteChangeLog().finally(() => {
536
+ this.#sqlitePurgeRunning = false;
537
+ this.#maybeScheduleSQLitePurge();
538
+ });
539
+ }, delay);
540
+ }
541
+ async #getChangeLogState() {
325
542
  const minWatermark = await this.#storer.getMinWatermarkForCatchup();
326
543
  if (!minWatermark) this.#lc.warn?.(`Unexpected empty changeLog. Resync if "Local replica watermark" errors arise`);
327
544
  return {
@@ -329,45 +546,218 @@ var ChangeStreamerImpl = class {
329
546
  minWatermark: minWatermark ?? this.#replicaVersion
330
547
  };
331
548
  }
549
+ #getCleanupFloor() {
550
+ const backupWatermark = this.#backupWatermark;
551
+ assert(backupWatermark !== void 0, "cleanup cannot run without a backup watermark");
552
+ const current = [...this.#forwarder.getAcks(), ...this.#reservations?.getReservedWatermarks() ?? []];
553
+ return {
554
+ backupWatermark,
555
+ purgeWatermark: min(backupWatermark, ...current),
556
+ current
557
+ };
558
+ }
559
+ async #purgePGChangeLog() {
560
+ try {
561
+ const { backupWatermark, purgeWatermark, current } = this.#getCleanupFloor();
562
+ if (purgeWatermark < backupWatermark) {
563
+ if (this.#loggedBehindWatermark !== purgeWatermark) {
564
+ this.#loggedBehindWatermark = purgeWatermark;
565
+ this.#lc.info?.(`At least one client is behind backup ${backupWatermark}`, { watermarks: current });
566
+ }
567
+ } else this.#loggedBehindWatermark = void 0;
568
+ if (purgeWatermark <= this.#pgPurgedWatermark) return;
569
+ this.#lc.info?.(`Purging PG changes before ${purgeWatermark} ...`);
570
+ const start = performance.now();
571
+ const deleted = await this.#storer.purgeRecordsBefore(purgeWatermark);
572
+ const elapsed = (performance.now() - start).toFixed(2);
573
+ this.#lc.info?.(`Purged ${deleted} PG changes before ${purgeWatermark} (${elapsed} ms)`);
574
+ this.#pgPurgedWatermark = purgeWatermark;
575
+ } catch (e) {
576
+ this.#lc.warn?.(`error purging the PG change log`, e);
577
+ }
578
+ }
579
+ async #purgeSQLiteChangeLog() {
580
+ const scheduler = this.#purgeScheduler;
581
+ if (!scheduler) return;
582
+ try {
583
+ const { backupWatermark, purgeWatermark } = this.#getCleanupFloor();
584
+ this.#sqlitePurgeContinuation = void 0;
585
+ if (purgeWatermark < backupWatermark) this.#requestSQLitePurge("deferred");
586
+ const result = await scheduler.purge(purgeWatermark);
587
+ this.#requestSQLitePurge(result.continuation);
588
+ } catch (e) {
589
+ this.#requestSQLitePurge("deferred");
590
+ this.#lc.warn?.(`error purging the SQLite change log`, e);
591
+ }
592
+ }
593
+ #requestSQLitePurge(continuation) {
594
+ if (!this.#purgeScheduler || continuation === void 0) return;
595
+ if (continuation === "immediate" || this.#sqlitePurgeContinuation === void 0) this.#sqlitePurgeContinuation = continuation;
596
+ }
597
+ async stop(err) {
598
+ this.#state.stop(this.#lc, err);
599
+ this.#stream?.changes.cancel();
600
+ this.#purgeScheduler?.stop();
601
+ this.#comparator?.stop();
602
+ this.#sqliteCatchup?.close();
603
+ this.#changeLogWriter?.close();
604
+ await Promise.allSettled([this.#storer.stop(), this.#source.stop()]);
605
+ }
606
+ #recordForwardedTransactionBoundary(type, watermark) {
607
+ switch (type) {
608
+ case "begin":
609
+ assert(this.#currentTransactionCompletion === void 0, "forwarded begin while a transaction is already in progress");
610
+ this.#currentTransactionCompletion = resolver();
611
+ this.#transactionForwardStartedAt = performance.now();
612
+ break;
613
+ case "commit": {
614
+ const completion = this.#currentTransactionCompletion;
615
+ assert(completion, "forwarded commit without a pending transaction");
616
+ this.#recordTransactionForwardDuration();
617
+ this.#lastForwardedCommitWatermark = watermark;
618
+ this.#currentTransactionCompletion = void 0;
619
+ completion.resolve({
620
+ kind: "committed",
621
+ watermark
622
+ });
623
+ break;
624
+ }
625
+ case "rollback": {
626
+ const completion = this.#currentTransactionCompletion;
627
+ assert(completion, "forwarded rollback without a pending transaction");
628
+ const committed = this.#lastForwardedCommitWatermark;
629
+ assert(committed, "last forwarded commit watermark is not initialized");
630
+ this.#transactionForwardStartedAt = void 0;
631
+ this.#currentTransactionCompletion = void 0;
632
+ completion.resolve({
633
+ kind: "rolled-back",
634
+ watermark: committed
635
+ });
636
+ break;
637
+ }
638
+ }
639
+ }
640
+ #recordTransactionForwardDuration() {
641
+ const startedAt = this.#transactionForwardStartedAt;
642
+ this.#transactionForwardStartedAt = void 0;
643
+ if (startedAt !== void 0) this.#transactionForwardDuration.recordMs(performance.now() - startedAt, { sqlite_change_log: this.#changeLogWriter?.enabled ? "on" : "off" });
644
+ }
332
645
  /**
333
- * Makes a best effort to purge the change log. In the event of a database
334
- * error, exceptions will be logged and swallowed, so this method is safe
335
- * to run in a timeout.
646
+ * Closes and forgets the cached catchup coordinator, so that a later
647
+ * subscription re-opens the log from scratch rather than reading a handle
648
+ * whose file is gone.
336
649
  */
337
- async #purgeOldChanges() {
338
- const initial = [...this.#initialWatermarks];
339
- if (initial.length === 0) {
340
- this.#lc.warn?.("No initial watermarks to check for cleanup");
650
+ #closeSQLiteCatchup() {
651
+ this.#sqliteCatchup?.close();
652
+ this.#sqliteCatchup = void 0;
653
+ }
654
+ #captureRequiredHead() {
655
+ const committed = this.#lastForwardedCommitWatermark;
656
+ assert(committed, "last forwarded commit watermark is not initialized");
657
+ return this.#currentTransactionCompletion?.promise.then((completion) => completion.watermark) ?? committed;
658
+ }
659
+ #selectSQLiteCatchup(lc, ctx) {
660
+ const opts = this.#sqliteCatchupOptions;
661
+ if (this.#lastForwardedCommitWatermark === void 0 || !opts) {
662
+ this.#recordCatchupRoute("pg", "not-ready");
663
+ return;
664
+ }
665
+ if (ctx.mode !== "serving") {
666
+ lc.info?.(`not serving backup subscriber ${ctx.id} from SQLite catchup`);
667
+ this.#recordCatchupRoute("pg", "ineligible-mode");
668
+ return;
669
+ }
670
+ let route;
671
+ if (this.#readRouter) {
672
+ route = this.#readRouter.consume(ctx.taskID);
673
+ if (route.source === "pg") {
674
+ if (route.reason === "cold-log") lc.info?.(`serving ${ctx.id} from PG catchup: SQLite change log is still warming`, { sqliteChangeLogCoverage: route.coverage });
675
+ else if (route.reason === "log-unavailable") this.#declineSQLiteCatchup(lc, ctx, opts, `${opts.changeLogFile} is unavailable or incompatible`);
676
+ else lc.debug?.(`serving ${ctx.id} from PG catchup: SQLite route ${route.reason}`, ...route.coverage ? [{ sqliteChangeLogCoverage: route.coverage }] : []);
677
+ this.#recordCatchupRoute("pg", route.reason);
678
+ return;
679
+ }
680
+ const coverage = route.coverage;
681
+ assert(coverage, "a SQLite route must carry its covered range");
682
+ if (ctx.watermark < coverage.minWatermark) {
683
+ lc.info?.(`serving ${ctx.id} from PG catchup: subscriber watermark ${ctx.watermark} is below the SQLite change-log minimum ` + coverage.minWatermark, { sqliteChangeLogCoverage: coverage });
684
+ this.#recordCatchupRoute("pg", "watermark-uncovered");
685
+ return;
686
+ }
687
+ }
688
+ if (!this.#readRouter && !opts.shouldUse?.(ctx)) {
689
+ this.#recordCatchupRoute("pg", "selector");
341
690
  return;
342
691
  }
343
- const current = [...this.#forwarder.getAcks()];
344
- if (current.length === 0) {
345
- this.#lc.warn?.("No subscribers to confirm cleanup");
692
+ if (this.#readRouter && opts.shouldUse && !opts.shouldUse(ctx)) {
693
+ this.#recordCatchupRoute("pg", "selector");
346
694
  return;
347
695
  }
696
+ const catchup = this.#sqliteCatchup ?? this.#openSQLiteCatchup(lc, opts, ctx);
697
+ if (catchup) return {
698
+ catchup,
699
+ reason: route?.reason ?? "selector",
700
+ coverage: route?.coverage,
701
+ logWarm: route === void 0 ? void 0 : route.reason !== "selected-cold"
702
+ };
703
+ else this.#recordCatchupRoute("pg", "log-unavailable");
704
+ }
705
+ #recordCatchupRoute(source, reason) {
706
+ this.#catchupRoutes.add(1, {
707
+ source,
708
+ reason
709
+ });
710
+ }
711
+ /**
712
+ * Opens the change log and, if it can serve, wraps it in the coordinator that
713
+ * subsequent subscriptions reuse.
714
+ *
715
+ * The log may not exist yet -- the writer creates it at the stream loop's
716
+ * first reconcile, and deletes it when it fails soft -- or may exist without
717
+ * content, or may be unreadable. None of those can be allowed to fail a
718
+ * subscription:
719
+ * this is the last point at which PG catchup is still available -- past
720
+ * `Forwarder.add()` the subscriber is committed to SQLite -- so each declines
721
+ * here instead. Neither the failure nor the reader is retained, so a later
722
+ * subscription retries from scratch.
723
+ */
724
+ #openSQLiteCatchup(lc, opts, ctx) {
725
+ let reader;
348
726
  try {
349
- const earliestInitial = min(...initial);
350
- const earliestCurrent = min(...current);
351
- if (earliestCurrent < earliestInitial) this.#lc.info?.(`At least one client is behind backup (${earliestCurrent} < ${earliestInitial})`);
352
- else {
353
- this.#lc.info?.(`Purging changes before ${earliestInitial} ...`);
354
- const start = performance.now();
355
- const deleted = await this.#storer.purgeRecordsBefore(earliestInitial);
356
- const elapsed = (performance.now() - start).toFixed(2);
357
- this.#lc.info?.(`Purged ${deleted} changes before ${earliestInitial} (${elapsed} ms)`);
358
- this.#initialWatermarks.delete(earliestInitial);
727
+ reader = new SQLiteChangeLogReader(this.#lc, opts.changeLogFile);
728
+ if (reader.plan(ctx.watermark).kind === "not-ready") {
729
+ reader.close();
730
+ this.#declineSQLiteCatchup(lc, ctx, opts, `${opts.changeLogFile} has no changes to serve yet`);
731
+ return;
359
732
  }
360
733
  } catch (e) {
361
- this.#lc.warn?.(`error purging change log`, e);
362
- } finally {
363
- if (this.#initialWatermarks.size) this.#state.setTimeout(() => this.#purgeOldChanges(), CLEANUP_DELAY_MS);
734
+ reader?.close();
735
+ this.#declineSQLiteCatchup(lc, ctx, opts, `cannot read ${opts.changeLogFile}`, e);
736
+ return;
364
737
  }
738
+ this.#changeLogUnavailableSince = void 0;
739
+ this.#sqliteCatchup = new SQLiteChangeLogCatchup(this.#lc, this.#forwarder, reader, {
740
+ batchSize: opts.readBatchRows,
741
+ barrierTimeoutMs: opts.barrierTimeoutMs,
742
+ barrierPollIntervalMs: opts.barrierPollIntervalMs,
743
+ cleanupGuard: opts.cleanupGuard,
744
+ onFailure: (failure) => {
745
+ this.#lc.warn?.(`temporarily disabling SQLite catchup after ${failure}`);
746
+ this.#readRouter?.trip();
747
+ }
748
+ });
749
+ return this.#sqliteCatchup;
365
750
  }
366
- async stop(err) {
367
- this.#state.stop(this.#lc, err);
368
- this.#stream?.changes.cancel();
369
- await this.#storer.stop();
370
- await this.#source.stop();
751
+ /**
752
+ * Reports falling back to PG catchup, tracking how long the log has been
753
+ * unavailable so that a subscription that merely arrived before the writer's
754
+ * first reconcile does not look like an incident.
755
+ */
756
+ #declineSQLiteCatchup(lc, ctx, opts, reason, error) {
757
+ const now = Date.now();
758
+ this.#changeLogUnavailableSince ??= now;
759
+ const unavailableMs = now - this.#changeLogUnavailableSince;
760
+ lc[unavailableMs >= (opts.notReadyWarnThresholdMs ?? DEFAULT_CHANGE_LOG_UNAVAILABLE_WARN_THRESHOLD_MS) ? "warn" : "debug"]?.(`serving ${ctx.id} from PG catchup: ${reason} (unavailable for ${unavailableMs} ms)`, ...error === void 0 ? [] : [error]);
371
761
  }
372
762
  };
373
763
  var CLEANUP_DELAY_MS = DEFAULT_MAX_RETRY_DELAY_MS * 3;