@rocicorp/zero 1.10.0-canary.2 → 1.10.0-canary.21

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 (384) hide show
  1. package/out/replicache/src/btree/read.d.ts +12 -1
  2. package/out/replicache/src/btree/read.d.ts.map +1 -1
  3. package/out/replicache/src/btree/read.js +24 -12
  4. package/out/replicache/src/btree/read.js.map +1 -1
  5. package/out/replicache/src/db/rebase.d.ts +14 -2
  6. package/out/replicache/src/db/rebase.d.ts.map +1 -1
  7. package/out/replicache/src/db/rebase.js +27 -5
  8. package/out/replicache/src/db/rebase.js.map +1 -1
  9. package/out/replicache/src/db/write.js.map +1 -1
  10. package/out/replicache/src/kv/expo-sqlite/store.d.ts.map +1 -1
  11. package/out/replicache/src/kv/expo-sqlite/store.js +27 -4
  12. package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
  13. package/out/replicache/src/kv/sqlite-store.d.ts +11 -0
  14. package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
  15. package/out/replicache/src/kv/sqlite-store.js +43 -3
  16. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  17. package/out/replicache/src/persist/persist.d.ts.map +1 -1
  18. package/out/replicache/src/persist/persist.js +12 -5
  19. package/out/replicache/src/persist/persist.js.map +1 -1
  20. package/out/replicache/src/persist/refresh.js +6 -2
  21. package/out/replicache/src/persist/refresh.js.map +1 -1
  22. package/out/replicache/src/replicache-impl.d.ts.map +1 -1
  23. package/out/replicache/src/replicache-impl.js +14 -2
  24. package/out/replicache/src/replicache-impl.js.map +1 -1
  25. package/out/replicache/src/replicache-options.d.ts +9 -0
  26. package/out/replicache/src/replicache-options.d.ts.map +1 -1
  27. package/out/replicache/src/transactions.d.ts.map +1 -1
  28. package/out/replicache/src/transactions.js +1 -1
  29. package/out/replicache/src/transactions.js.map +1 -1
  30. package/out/shared/src/error.d.ts +11 -0
  31. package/out/shared/src/error.d.ts.map +1 -1
  32. package/out/shared/src/error.js +35 -1
  33. package/out/shared/src/error.js.map +1 -1
  34. package/out/shared/src/hash.d.ts.map +1 -1
  35. package/out/shared/src/hash.js +105 -10
  36. package/out/shared/src/hash.js.map +1 -1
  37. package/out/shared/src/map.d.ts +7 -2
  38. package/out/shared/src/map.d.ts.map +1 -1
  39. package/out/shared/src/map.js.map +1 -1
  40. package/out/shared/src/promise-race.d.ts +14 -0
  41. package/out/shared/src/promise-race.d.ts.map +1 -1
  42. package/out/shared/src/promise-race.js +32 -1
  43. package/out/shared/src/promise-race.js.map +1 -1
  44. package/out/zero/package.js +2 -3
  45. package/out/zero/package.js.map +1 -1
  46. package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
  47. package/out/zero-cache/src/auth/write-authorizer.js +8 -1
  48. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  49. package/out/zero-cache/src/config/normalize.d.ts +5 -0
  50. package/out/zero-cache/src/config/normalize.d.ts.map +1 -1
  51. package/out/zero-cache/src/config/normalize.js +16 -3
  52. package/out/zero-cache/src/config/normalize.js.map +1 -1
  53. package/out/zero-cache/src/config/zero-config.d.ts +36 -7
  54. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  55. package/out/zero-cache/src/config/zero-config.js +121 -46
  56. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  57. package/out/zero-cache/src/db/create.d.ts.map +1 -1
  58. package/out/zero-cache/src/db/create.js +4 -1
  59. package/out/zero-cache/src/db/create.js.map +1 -1
  60. package/out/zero-cache/src/db/index-predicate.d.ts +4 -0
  61. package/out/zero-cache/src/db/index-predicate.d.ts.map +1 -0
  62. package/out/zero-cache/src/db/index-predicate.js +46 -0
  63. package/out/zero-cache/src/db/index-predicate.js.map +1 -0
  64. package/out/zero-cache/src/db/lite-tables.d.ts +10 -2
  65. package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
  66. package/out/zero-cache/src/db/lite-tables.js +8 -3
  67. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  68. package/out/zero-cache/src/db/migration-lite.d.ts.map +1 -1
  69. package/out/zero-cache/src/db/migration-lite.js +5 -3
  70. package/out/zero-cache/src/db/migration-lite.js.map +1 -1
  71. package/out/zero-cache/src/db/specs.d.ts +41 -0
  72. package/out/zero-cache/src/db/specs.d.ts.map +1 -1
  73. package/out/zero-cache/src/db/specs.js +30 -3
  74. package/out/zero-cache/src/db/specs.js.map +1 -1
  75. package/out/zero-cache/src/db/transaction-pool.d.ts +5 -15
  76. package/out/zero-cache/src/db/transaction-pool.d.ts.map +1 -1
  77. package/out/zero-cache/src/db/transaction-pool.js +39 -73
  78. package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
  79. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  80. package/out/zero-cache/src/server/change-streamer.js +27 -15
  81. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  82. package/out/zero-cache/src/server/main.d.ts.map +1 -1
  83. package/out/zero-cache/src/server/main.js +4 -4
  84. package/out/zero-cache/src/server/main.js.map +1 -1
  85. package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
  86. package/out/zero-cache/src/server/replicator.js +8 -4
  87. package/out/zero-cache/src/server/replicator.js.map +1 -1
  88. package/out/zero-cache/src/server/runner/run-worker.js +1 -1
  89. package/out/zero-cache/src/server/worker-urls.d.ts +0 -1
  90. package/out/zero-cache/src/server/worker-urls.d.ts.map +1 -1
  91. package/out/zero-cache/src/server/worker-urls.js +1 -2
  92. package/out/zero-cache/src/server/worker-urls.js.map +1 -1
  93. package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
  94. package/out/zero-cache/src/services/analyze.js +1 -0
  95. package/out/zero-cache/src/services/analyze.js.map +1 -1
  96. package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts +12 -2
  97. package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts.map +1 -1
  98. package/out/zero-cache/src/services/change-source/common/backfill-manager.js +14 -3
  99. package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
  100. package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts.map +1 -1
  101. package/out/zero-cache/src/services/change-source/common/replica-schema.js +10 -41
  102. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  103. package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts +3 -2
  104. package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts.map +1 -1
  105. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +42 -16
  106. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  107. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts +34 -2
  108. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  109. package/out/zero-cache/src/services/change-source/pg/change-source.js +47 -19
  110. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  111. package/out/zero-cache/src/services/change-source/pg/decommission.d.ts.map +1 -1
  112. package/out/zero-cache/src/services/change-source/pg/decommission.js +20 -11
  113. package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
  114. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +1 -0
  115. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  116. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +58 -80
  117. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  118. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts +20 -0
  119. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts.map +1 -1
  120. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +43 -3
  121. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
  122. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +24 -1
  123. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -1
  124. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +35 -5
  125. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  126. package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts +1513 -229
  127. package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts.map +1 -1
  128. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +2 -2
  129. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
  130. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts +19 -0
  131. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts.map +1 -0
  132. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js +581 -0
  133. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js.map +1 -0
  134. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts +1 -1
  135. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
  136. package/out/zero-cache/src/services/change-source/pg/schema/init.js +15 -6
  137. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  138. package/out/zero-cache/src/services/change-source/pg/schema/published.d.ts +137 -20
  139. package/out/zero-cache/src/services/change-source/pg/schema/published.d.ts.map +1 -1
  140. package/out/zero-cache/src/services/change-source/pg/schema/published.js +82 -13
  141. package/out/zero-cache/src/services/change-source/pg/schema/published.js.map +1 -1
  142. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +128 -21
  143. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
  144. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +5 -5
  145. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  146. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts +2 -0
  147. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts.map +1 -1
  148. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +3 -0
  149. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts.map +1 -1
  150. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts +1 -4
  151. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts.map +1 -1
  152. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js +11 -18
  153. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
  154. package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts.map +1 -1
  155. package/out/zero-cache/src/services/change-streamer/backup-monitor.js +8 -3
  156. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
  157. package/out/zero-cache/src/services/change-streamer/broadcast.d.ts.map +1 -1
  158. package/out/zero-cache/src/services/change-streamer/broadcast.js +4 -1
  159. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  160. package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.d.ts +34 -0
  161. package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.d.ts.map +1 -0
  162. package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.js +67 -0
  163. package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.js.map +1 -0
  164. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +2 -4
  165. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  166. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +8 -2
  167. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  168. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +34 -13
  169. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
  170. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +165 -36
  171. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  172. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +4 -9
  173. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
  174. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  175. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts.map +1 -1
  176. package/out/zero-cache/src/services/change-streamer/forwarder.js +12 -3
  177. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  178. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.d.ts.map +1 -1
  179. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js +11 -5
  180. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js.map +1 -1
  181. package/out/zero-cache/src/services/change-streamer/shard-sampling.d.ts +11 -0
  182. package/out/zero-cache/src/services/change-streamer/shard-sampling.d.ts.map +1 -0
  183. package/out/zero-cache/src/services/change-streamer/shard-sampling.js +19 -0
  184. package/out/zero-cache/src/services/change-streamer/shard-sampling.js.map +1 -0
  185. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts +11 -1
  186. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts.map +1 -1
  187. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js +38 -5
  188. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -1
  189. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.d.ts +25 -1
  190. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.d.ts.map +1 -1
  191. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js +95 -12
  192. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js.map +1 -1
  193. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.d.ts +83 -0
  194. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.d.ts.map +1 -0
  195. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js +417 -0
  196. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js.map +1 -0
  197. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.d.ts +73 -0
  198. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.d.ts.map +1 -0
  199. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.js +157 -0
  200. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.js.map +1 -0
  201. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts +1 -1
  202. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts.map +1 -1
  203. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js +8 -3
  204. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js.map +1 -1
  205. package/out/zero-cache/src/services/change-streamer/storer.d.ts +63 -1
  206. package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
  207. package/out/zero-cache/src/services/change-streamer/storer.js +217 -49
  208. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  209. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +8 -1
  210. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
  211. package/out/zero-cache/src/services/change-streamer/subscriber.js +10 -1
  212. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  213. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts +29 -0
  214. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts.map +1 -0
  215. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js +154 -0
  216. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js.map +1 -0
  217. package/out/zero-cache/src/services/http-service.d.ts +1 -0
  218. package/out/zero-cache/src/services/http-service.d.ts.map +1 -1
  219. package/out/zero-cache/src/services/http-service.js +7 -3
  220. package/out/zero-cache/src/services/http-service.js.map +1 -1
  221. package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
  222. package/out/zero-cache/src/services/litestream/commands.js +16 -7
  223. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  224. package/out/zero-cache/src/services/litestream/config-v5.yml +15 -1
  225. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js +131 -0
  226. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js.map +1 -0
  227. package/out/zero-cache/src/services/litestream/litestream-controller.d.ts +70 -0
  228. package/out/zero-cache/src/services/litestream/litestream-controller.d.ts.map +1 -0
  229. package/out/zero-cache/src/services/litestream/litestream-controller.js +101 -0
  230. package/out/zero-cache/src/services/litestream/litestream-controller.js.map +1 -0
  231. package/out/zero-cache/src/services/litestream/metrics.d.ts +1 -0
  232. package/out/zero-cache/src/services/litestream/metrics.d.ts.map +1 -1
  233. package/out/zero-cache/src/services/litestream/metrics.js +4 -1
  234. package/out/zero-cache/src/services/litestream/metrics.js.map +1 -1
  235. package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
  236. package/out/zero-cache/src/services/mutagen/mutagen.js +1 -1
  237. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  238. package/out/zero-cache/src/services/replicator/change-log-db.d.ts +10 -0
  239. package/out/zero-cache/src/services/replicator/change-log-db.d.ts.map +1 -1
  240. package/out/zero-cache/src/services/replicator/change-log-db.js +15 -1
  241. package/out/zero-cache/src/services/replicator/change-log-db.js.map +1 -1
  242. package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +1 -1
  243. package/out/zero-cache/src/services/replicator/change-processor.js +18 -21
  244. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  245. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts +1 -1
  246. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
  247. package/out/zero-cache/src/services/replicator/incremental-sync.js +2 -6
  248. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  249. package/out/zero-cache/src/services/replicator/replicator.d.ts +1 -1
  250. package/out/zero-cache/src/services/replicator/replicator.d.ts.map +1 -1
  251. package/out/zero-cache/src/services/replicator/replicator.js +2 -2
  252. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  253. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts +3 -2
  254. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts.map +1 -1
  255. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +8 -6
  256. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -1
  257. package/out/zero-cache/src/services/replicator/write-worker-client.d.ts +27 -2
  258. package/out/zero-cache/src/services/replicator/write-worker-client.d.ts.map +1 -1
  259. package/out/zero-cache/src/services/replicator/write-worker-client.js +3 -2
  260. package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
  261. package/out/zero-cache/src/services/replicator/write-worker.js +24 -5
  262. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  263. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts +3 -2
  264. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +1 -1
  265. package/out/zero-cache/src/services/view-syncer/client-handler.js +25 -17
  266. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  267. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
  268. package/out/zero-cache/src/services/view-syncer/cvr-store.js +14 -1
  269. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  270. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +3 -3
  271. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  272. package/out/zero-cache/src/services/view-syncer/view-syncer.js +6 -2
  273. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  274. package/out/zero-cache/src/types/downstream.d.ts +11 -0
  275. package/out/zero-cache/src/types/downstream.d.ts.map +1 -0
  276. package/out/zero-cache/src/types/pg.d.ts +15 -2
  277. package/out/zero-cache/src/types/pg.d.ts.map +1 -1
  278. package/out/zero-cache/src/types/pg.js +21 -8
  279. package/out/zero-cache/src/types/pg.js.map +1 -1
  280. package/out/zero-cache/src/types/poke-chunk.d.ts +18 -0
  281. package/out/zero-cache/src/types/poke-chunk.d.ts.map +1 -0
  282. package/out/zero-cache/src/types/poke-chunk.js +57 -0
  283. package/out/zero-cache/src/types/poke-chunk.js.map +1 -0
  284. package/out/zero-cache/src/types/processes.js +1 -4
  285. package/out/zero-cache/src/types/processes.js.map +1 -1
  286. package/out/zero-cache/src/types/streams.d.ts +4 -9
  287. package/out/zero-cache/src/types/streams.d.ts.map +1 -1
  288. package/out/zero-cache/src/types/streams.js.map +1 -1
  289. package/out/zero-cache/src/types/subscription.d.ts +2 -11
  290. package/out/zero-cache/src/types/subscription.d.ts.map +1 -1
  291. package/out/zero-cache/src/types/subscription.js +4 -25
  292. package/out/zero-cache/src/types/subscription.js.map +1 -1
  293. package/out/zero-cache/src/workers/connection.d.ts +22 -4
  294. package/out/zero-cache/src/workers/connection.d.ts.map +1 -1
  295. package/out/zero-cache/src/workers/connection.js +112 -14
  296. package/out/zero-cache/src/workers/connection.js.map +1 -1
  297. package/out/zero-cache/src/workers/replicator.d.ts +1 -0
  298. package/out/zero-cache/src/workers/replicator.d.ts.map +1 -1
  299. package/out/zero-cache/src/workers/replicator.js +4 -2
  300. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  301. package/out/zero-client/src/client/connection-manager.d.ts.map +1 -1
  302. package/out/zero-client/src/client/connection-manager.js +46 -0
  303. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  304. package/out/zero-client/src/client/context.d.ts +15 -0
  305. package/out/zero-client/src/client/context.d.ts.map +1 -1
  306. package/out/zero-client/src/client/context.js +44 -0
  307. package/out/zero-client/src/client/context.js.map +1 -1
  308. package/out/zero-client/src/client/inspector/lazy-inspector.d.ts.map +1 -1
  309. package/out/zero-client/src/client/inspector/lazy-inspector.js +19 -12
  310. package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
  311. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  312. package/out/zero-client/src/client/version.js +1 -1
  313. package/out/zero-client/src/client/zero-poke-handler.d.ts +7 -2
  314. package/out/zero-client/src/client/zero-poke-handler.d.ts.map +1 -1
  315. package/out/zero-client/src/client/zero-poke-handler.js +52 -2
  316. package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
  317. package/out/zero-client/src/client/zero-rep.d.ts +1 -0
  318. package/out/zero-client/src/client/zero-rep.d.ts.map +1 -1
  319. package/out/zero-client/src/client/zero-rep.js +9 -6
  320. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  321. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  322. package/out/zero-client/src/client/zero.js +98 -25
  323. package/out/zero-client/src/client/zero.js.map +1 -1
  324. package/out/zero-protocol/src/custom-queries.d.ts +1 -1
  325. package/out/zero-protocol/src/down.d.ts +1 -1
  326. package/out/zero-protocol/src/error.d.ts +4 -4
  327. package/out/zero-protocol/src/inspect-down.js +1 -1
  328. package/out/zero-protocol/src/mutate-server.d.ts +2 -2
  329. package/out/zero-protocol/src/poke.d.ts +89 -7
  330. package/out/zero-protocol/src/poke.d.ts.map +1 -1
  331. package/out/zero-protocol/src/poke.js +16 -8
  332. package/out/zero-protocol/src/poke.js.map +1 -1
  333. package/out/zero-protocol/src/protocol-version.d.ts +12 -7
  334. package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
  335. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  336. package/out/zero-protocol/src/push.d.ts +1 -1
  337. package/out/zero-protocol/src/query-server.d.ts +2 -2
  338. package/out/zql/src/ivm/array-view.d.ts.map +1 -1
  339. package/out/zql/src/ivm/array-view.js +5 -3
  340. package/out/zql/src/ivm/array-view.js.map +1 -1
  341. package/out/zql/src/ivm/data.d.ts.map +1 -1
  342. package/out/zql/src/ivm/data.js +8 -3
  343. package/out/zql/src/ivm/data.js.map +1 -1
  344. package/out/zql/src/ivm/deferred-input.d.ts +44 -0
  345. package/out/zql/src/ivm/deferred-input.d.ts.map +1 -0
  346. package/out/zql/src/ivm/deferred-input.js +88 -0
  347. package/out/zql/src/ivm/deferred-input.js.map +1 -0
  348. package/out/zql/src/ivm/memory-source.d.ts +10 -2
  349. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  350. package/out/zql/src/ivm/memory-source.js +19 -16
  351. package/out/zql/src/ivm/memory-source.js.map +1 -1
  352. package/out/zql/src/ivm/stream.d.ts +0 -1
  353. package/out/zql/src/ivm/stream.d.ts.map +1 -1
  354. package/out/zql/src/ivm/stream.js +1 -7
  355. package/out/zql/src/ivm/stream.js.map +1 -1
  356. package/out/zql/src/ivm/union-fan-in.d.ts +1 -1
  357. package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
  358. package/out/zql/src/ivm/union-fan-in.js +11 -2
  359. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  360. package/out/zql/src/ivm/view-apply-change.js +6 -1
  361. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  362. package/out/zql/src/query/query-delegate-base.d.ts +6 -0
  363. package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
  364. package/out/zql/src/query/query-delegate-base.js +73 -10
  365. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  366. package/out/zql/src/query/query-delegate.d.ts +22 -0
  367. package/out/zql/src/query/query-delegate.d.ts.map +1 -1
  368. package/out/zqlite/src/query-builder.d.ts.map +1 -1
  369. package/out/zqlite/src/query-builder.js +2 -1
  370. package/out/zqlite/src/query-builder.js.map +1 -1
  371. package/out/zqlite/src/sqlite-stat-fanout.d.ts.map +1 -1
  372. package/out/zqlite/src/sqlite-stat-fanout.js +1 -0
  373. package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
  374. package/out/zqlite/src/table-source.d.ts +1 -1
  375. package/out/zqlite/src/table-source.d.ts.map +1 -1
  376. package/out/zqlite/src/table-source.js +3 -2
  377. package/out/zqlite/src/table-source.js.map +1 -1
  378. package/package.json +2 -3
  379. package/out/zero-cache/src/server/backup-watermark-poller.js +0 -31
  380. package/out/zero-cache/src/server/backup-watermark-poller.js.map +0 -1
  381. package/out/zero-cache/src/services/litestream/vfs-watermark-poller.d.ts +0 -57
  382. package/out/zero-cache/src/services/litestream/vfs-watermark-poller.d.ts.map +0 -1
  383. package/out/zero-cache/src/services/litestream/vfs-watermark-poller.js +0 -137
  384. package/out/zero-cache/src/services/litestream/vfs-watermark-poller.js.map +0 -1
@@ -77,6 +77,7 @@ var Broadcast = class {
77
77
  #setTimeout;
78
78
  #clearTimeout;
79
79
  #earlyReleaseTimer;
80
+ #needsBackupResponse = false;
80
81
  /**
81
82
  * Broadcasts the `change` to the `subscribers` and tracks their
82
83
  * completion.
@@ -92,6 +93,7 @@ var Broadcast = class {
92
93
  this.#clearTimeout = earlyRelease?.clearTimeoutFn ?? clearTimeout;
93
94
  for (const sub of this.#pending) {
94
95
  const changes = sub.numPending + 1;
96
+ if (sub.mode === "backup") this.#needsBackupResponse ||= !sub.isBacklogged();
95
97
  sub.send(change).catch(() => {}).finally(() => this.#markCompleted(sub, changes));
96
98
  }
97
99
  if (this.#pending.size === 0) this.#setDone("all-subscribers");
@@ -100,6 +102,7 @@ var Broadcast = class {
100
102
  if (this.#isDone) return;
101
103
  const elapsed = performance.now() - this.#start;
102
104
  sub.trackResponseResult("on-time");
105
+ if (sub.mode === "backup") this.#needsBackupResponse = false;
103
106
  this.#completed.push({
104
107
  sub,
105
108
  changes,
@@ -110,7 +113,7 @@ var Broadcast = class {
110
113
  this.#setDone("all-subscribers");
111
114
  return;
112
115
  }
113
- if (this.#earlyReleaseTimeoutProportion !== void 0 && this.#earlyReleaseTimer === void 0 && this.#completed.length === this.#majority) {
116
+ if (this.#earlyReleaseTimeoutProportion !== void 0 && this.#earlyReleaseTimer === void 0 && this.#completed.length >= this.#majority && !this.#needsBackupResponse) {
114
117
  const timeoutMs = Math.ceil(elapsed * this.#earlyReleaseTimeoutProportion);
115
118
  this.#earlyReleaseTimer = this.#setTimeout(() => this.#setDone("consensus-timeout"), timeoutMs);
116
119
  }
@@ -1 +1 @@
1
- {"version":3,"file":"broadcast.js","names":["#lc","#pending","#completed","#done","#watermark","#majority","#start","#earlyReleaseTimeoutProportion","#setTimeout","#clearTimeout","#markCompleted","#setDone","#isDone","#earlyReleaseTimer","#logWithState","#releaseMode"],"sources":["../../../../../../zero-cache/src/services/change-streamer/broadcast.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport type {WatermarkedChange} from './change-streamer.ts';\nimport type {Subscriber} from './subscriber.ts';\n\nexport type BroadcastReleaseMode = 'all-subscribers' | 'consensus-timeout';\n\n/**\n * Enables event-driven early release of a {@link Broadcast}. When provided and a\n * majority has acked, the broadcast schedules its own consensus-timeout release\n * proportional to how long the majority took to ack, instead of relying on the\n * Forwarder's periodic progress tick.\n */\nexport type EarlyReleaseOptions = {\n /** Time to wait, proportional to majority completion, before releasing. */\n consensusTimeoutProportion: number;\n /** Injectable timer, for deterministic testing. */\n setTimeoutFn?: typeof setTimeout;\n /** Injectable timer cancellation, for deterministic testing. */\n clearTimeoutFn?: typeof clearTimeout;\n};\n\n/**\n * Initiates and tracks the progress of a change broadcasted to\n * a set of subscribers, implementing **consensus-based timeouts**\n * to effect short-term graceful degradation when a minority of\n * subscribers are behind.\n *\n * ### Background\n *\n * The purpose of flow control is to pull upstream replication changes\n * no faster than the rate they are processed by downstream subscribers\n * in the steady state. In the change-streamer, this is done by occasionally\n * waiting for ACKs from subscribers before continuing; without doing so,\n * I/O buffers fill up and cause the system to spend most of its time in GC.\n *\n * However, the naive algorithm of always waiting for all subscribers (e.g.\n * `Promise.all()`) can behave poorly in scenarios where subscribers\n * are imbalanced:\n * * New subscribers may have a backlog of changes to catch up with.\n * Having all subscribers wait for the new subscriber to catch up results\n * in delaying the entire application.\n * * Broken TCP connections similarly require all subscribers to wait until\n * connection liveness checks kick in and disconnect the subscriber.\n *\n * A simplistic approach is to add a limit to the amount of time waiting for\n * subscribers, i.e. an ack timeout. However, deciding what this timeout\n * should be is non-trivial because of the heterogeneous nature of changes;\n * while most changes operate on single rows and are relatively predictable\n * in terms of running time, some changes are table-wide operations and can\n * legitimately take an arbitrary amount of time. In such scenarios, a\n * timeout that is too short can stop progress on replication altogether.\n *\n * ### Consensus-based Timeout Algorithm\n *\n * To address these shortcomings, a \"consensus-based timeout\" algorithm is\n * used:\n * * Wait for more than half of the subscribers to finish. (In\n * case of a single node, or the case of one replication-manager\n * and one view-syncer, this reduces to waiting for all subscribers.)\n * * Once more than half of the subscribers have finished, proceed after\n * the configured timeout, proportional to the time it took for the\n * majority of subscribers to ack, even if not all subscribers have\n * finished.\n *\n * In other words, the subscribers themselves are used to determine the\n * timeout of each batch of changes; the majority determines this when\n * they complete, upon which a timeout is logically started.\n *\n * In the common case, the remaining subscribers finish soon afterward and\n * the timeout never elapses. However, in pathological cases where a minority\n * of subscribers have a disproportionate amount of load, some will still\n * be processing (or otherwise unresponsive). These subscribers are given\n * a bounded amount of time to catch up at each flushed batch, up to the\n * timeout interval. This guarantees eventual catchup because the\n * subscribers with a backlog of changes necessarily have a higher\n * processing rate than the subscribers that finished (and are made to wait).\n */\nexport class Broadcast {\n /**\n * Sends the change to the subscribers without the tracking machinery.\n * This is suitable for fire-and-forget (i.e. pipelined) sends.\n */\n static withoutTracking(\n subscribers: Iterable<Subscriber>,\n change: WatermarkedChange,\n ) {\n for (const sub of subscribers) {\n void sub.send(change);\n }\n }\n\n readonly #lc: LogContext;\n readonly #pending: Set<Subscriber>;\n readonly #completed: Completed[];\n readonly #done = resolver();\n #isDone = false;\n #releaseMode: BroadcastReleaseMode | undefined;\n\n readonly #watermark: string;\n readonly #majority: number;\n\n readonly #start = performance.now();\n\n // When set, the broadcast schedules its own consensus-timeout release instead\n // of waiting for the Forwarder's periodic progress tick.\n readonly #earlyReleaseTimeoutProportion: number | undefined;\n readonly #setTimeout: typeof setTimeout;\n readonly #clearTimeout: typeof clearTimeout;\n #earlyReleaseTimer: ReturnType<typeof setTimeout> | undefined;\n\n /**\n * Broadcasts the `change` to the `subscribers` and tracks their\n * completion.\n */\n constructor(\n lc: LogContext,\n subscribers: Iterable<Subscriber>,\n change: WatermarkedChange,\n earlyRelease?: EarlyReleaseOptions,\n ) {\n this.#lc = lc;\n this.#pending = new Set(subscribers);\n this.#completed = [];\n this.#watermark = change[0];\n this.#majority = Math.floor(this.#pending.size / 2) + 1;\n this.#earlyReleaseTimeoutProportion =\n earlyRelease?.consensusTimeoutProportion;\n this.#setTimeout = earlyRelease?.setTimeoutFn ?? setTimeout;\n this.#clearTimeout = earlyRelease?.clearTimeoutFn ?? clearTimeout;\n\n for (const sub of this.#pending) {\n const changes = sub.numPending + 1; // add one for this `change`\n void sub\n .send(change)\n .catch(() => {})\n .finally(() => this.#markCompleted(sub, changes));\n }\n\n // set done if there are no subscribers (mainly for tests)\n if (this.#pending.size === 0) {\n this.#setDone('all-subscribers');\n }\n }\n\n #markCompleted(sub: Subscriber, changes: number) {\n if (this.#isDone) {\n return;\n }\n const elapsed = performance.now() - this.#start;\n sub.trackResponseResult('on-time');\n\n this.#completed.push({sub, changes, elapsed});\n this.#pending.delete(sub);\n if (this.#pending.size === 0) {\n this.#setDone('all-subscribers');\n return;\n }\n // Event-driven consensus-timeout: the moment a majority acks, arm a single\n // release timer proportional to how long the majority took to ack. The\n // broadcast resolves when it fires, unless all subscribers ack first.\n if (\n this.#earlyReleaseTimeoutProportion !== undefined &&\n this.#earlyReleaseTimer === undefined &&\n this.#completed.length === this.#majority\n ) {\n const timeoutMs = Math.ceil(\n elapsed * this.#earlyReleaseTimeoutProportion,\n );\n this.#earlyReleaseTimer = this.#setTimeout(\n () => this.#setDone('consensus-timeout'),\n timeoutMs,\n );\n }\n }\n\n #setDone(releaseMode: BroadcastReleaseMode) {\n if (this.#isDone) {\n return;\n }\n if (releaseMode === 'consensus-timeout') {\n const elapsed = performance.now() - this.#start;\n this.#logWithState(\n `continuing with ${this.#pending.size} subscriber(s) still pending`,\n elapsed,\n );\n }\n this.#pending.forEach(sub => sub.trackResponseResult('timed-out'));\n this.#isDone = true;\n this.#releaseMode = releaseMode;\n this.#clearTimeout(this.#earlyReleaseTimer);\n this.#earlyReleaseTimer = undefined;\n this.#done.resolve();\n }\n\n get isDone(): boolean {\n return this.#isDone;\n }\n\n get done(): Promise<void> {\n return this.#done.promise;\n }\n\n get releaseMode(): BroadcastReleaseMode {\n return this.#releaseMode ?? 'all-subscribers';\n }\n\n logSlowRequests(now: number) {\n if (this.#isDone || this.#pending.size === 0) {\n return;\n }\n const elapsed = now - this.#start;\n if (this.#completed.length < this.#majority) {\n if (elapsed >= 1000) {\n this.#logWithState(\n `waiting for at least ${this.#majority} subscribers to finish`,\n elapsed,\n );\n }\n }\n }\n\n #logWithState(msg: string, elapsed: number) {\n this.#lc\n .withContext('watermark', this.#watermark)\n .info?.(`${msg} (${elapsed.toFixed(3)} ms)`, {\n completed: this.#completed.map(d => ({\n id: d.sub.id,\n processed: d.changes,\n elapsed: d.elapsed,\n })),\n pending: Array.from(this.#pending, sub => ({\n id: sub.id,\n ...sub.getStats(),\n })),\n });\n }\n}\n\n/** Tracks the completed result of a single subscriber. */\ntype Completed = {\n sub: Subscriber;\n /** The number of changes processed. */\n changes: number;\n /** The elapsed milliseconds. */\n elapsed: number;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8EA,IAAa,YAAb,MAAuB;;;;;CAKrB,OAAO,gBACL,aACA,QACA;EACA,KAAK,MAAM,OAAO,aAChB,IAAS,KAAK,MAAM;CAExB;CAEA;CACA;CACA;CACA,QAAiB,SAAS;CAC1B,UAAU;CACV;CAEA;CACA;CAEA,SAAkB,YAAY,IAAI;CAIlC;CACA;CACA;CACA;;;;;CAMA,YACE,IACA,aACA,QACA,cACA;EACA,KAAKA,MAAM;EACX,KAAKC,WAAW,IAAI,IAAI,WAAW;EACnC,KAAKC,aAAa,CAAC;EACnB,KAAKE,aAAa,OAAO;EACzB,KAAKC,YAAY,KAAK,MAAM,KAAKJ,SAAS,OAAO,CAAC,IAAI;EACtD,KAAKM,iCACH,cAAc;EAChB,KAAKC,cAAc,cAAc,gBAAgB;EACjD,KAAKC,gBAAgB,cAAc,kBAAkB;EAErD,KAAK,MAAM,OAAO,KAAKR,UAAU;GAC/B,MAAM,UAAU,IAAI,aAAa;GACjC,IACG,KAAK,MAAM,EACX,YAAY,CAAC,CAAC,EACd,cAAc,KAAKS,eAAe,KAAK,OAAO,CAAC;EACpD;EAGA,IAAI,KAAKT,SAAS,SAAS,GACzB,KAAKU,SAAS,iBAAiB;CAEnC;CAEA,eAAe,KAAiB,SAAiB;EAC/C,IAAI,KAAKC,SACP;EAEF,MAAM,UAAU,YAAY,IAAI,IAAI,KAAKN;EACzC,IAAI,oBAAoB,SAAS;EAEjC,KAAKJ,WAAW,KAAK;GAAC;GAAK;GAAS;EAAO,CAAC;EAC5C,KAAKD,SAAS,OAAO,GAAG;EACxB,IAAI,KAAKA,SAAS,SAAS,GAAG;GAC5B,KAAKU,SAAS,iBAAiB;GAC/B;EACF;EAIA,IACE,KAAKJ,mCAAmC,KAAA,KACxC,KAAKM,uBAAuB,KAAA,KAC5B,KAAKX,WAAW,WAAW,KAAKG,WAChC;GACA,MAAM,YAAY,KAAK,KACrB,UAAU,KAAKE,8BACjB;GACA,KAAKM,qBAAqB,KAAKL,kBACvB,KAAKG,SAAS,mBAAmB,GACvC,SACF;EACF;CACF;CAEA,SAAS,aAAmC;EAC1C,IAAI,KAAKC,SACP;EAEF,IAAI,gBAAgB,qBAAqB;GACvC,MAAM,UAAU,YAAY,IAAI,IAAI,KAAKN;GACzC,KAAKQ,cACH,mBAAmB,KAAKb,SAAS,KAAK,+BACtC,OACF;EACF;EACA,KAAKA,SAAS,SAAQ,QAAO,IAAI,oBAAoB,WAAW,CAAC;EACjE,KAAKW,UAAU;EACf,KAAKG,eAAe;EACpB,KAAKN,cAAc,KAAKI,kBAAkB;EAC1C,KAAKA,qBAAqB,KAAA;EAC1B,KAAKV,MAAM,QAAQ;CACrB;CAEA,IAAI,SAAkB;EACpB,OAAO,KAAKS;CACd;CAEA,IAAI,OAAsB;EACxB,OAAO,KAAKT,MAAM;CACpB;CAEA,IAAI,cAAoC;EACtC,OAAO,KAAKY,gBAAgB;CAC9B;CAEA,gBAAgB,KAAa;EAC3B,IAAI,KAAKH,WAAW,KAAKX,SAAS,SAAS,GACzC;EAEF,MAAM,UAAU,MAAM,KAAKK;EAC3B,IAAI,KAAKJ,WAAW,SAAS,KAAKG;OAC5B,WAAW,KACb,KAAKS,cACH,wBAAwB,KAAKT,UAAU,yBACvC,OACF;EAAA;CAGN;CAEA,cAAc,KAAa,SAAiB;EAC1C,KAAKL,IACF,YAAY,aAAa,KAAKI,UAAU,EACxC,OAAO,GAAG,IAAI,IAAI,QAAQ,QAAQ,CAAC,EAAE,OAAO;GAC3C,WAAW,KAAKF,WAAW,KAAI,OAAM;IACnC,IAAI,EAAE,IAAI;IACV,WAAW,EAAE;IACb,SAAS,EAAE;GACb,EAAE;GACF,SAAS,MAAM,KAAK,KAAKD,WAAU,SAAQ;IACzC,IAAI,IAAI;IACR,GAAG,IAAI,SAAS;GAClB,EAAE;EACJ,CAAC;CACL;AACF"}
1
+ {"version":3,"file":"broadcast.js","names":["#lc","#pending","#completed","#done","#watermark","#majority","#start","#earlyReleaseTimeoutProportion","#setTimeout","#clearTimeout","#needsBackupResponse","#markCompleted","#setDone","#isDone","#earlyReleaseTimer","#logWithState","#releaseMode"],"sources":["../../../../../../zero-cache/src/services/change-streamer/broadcast.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport type {WatermarkedChange} from './change-streamer.ts';\nimport type {Subscriber} from './subscriber.ts';\n\nexport type BroadcastReleaseMode = 'all-subscribers' | 'consensus-timeout';\n\n/**\n * Enables event-driven early release of a {@link Broadcast}. When provided and a\n * majority has acked, the broadcast schedules its own consensus-timeout release\n * proportional to how long the majority took to ack, instead of relying on the\n * Forwarder's periodic progress tick.\n */\nexport type EarlyReleaseOptions = {\n /** Time to wait, proportional to majority completion, before releasing. */\n consensusTimeoutProportion: number;\n /** Injectable timer, for deterministic testing. */\n setTimeoutFn?: typeof setTimeout;\n /** Injectable timer cancellation, for deterministic testing. */\n clearTimeoutFn?: typeof clearTimeout;\n};\n\n/**\n * Initiates and tracks the progress of a change broadcasted to\n * a set of subscribers, implementing **consensus-based timeouts**\n * to effect short-term graceful degradation when a minority of\n * subscribers are behind.\n *\n * ### Background\n *\n * The purpose of flow control is to pull upstream replication changes\n * no faster than the rate they are processed by downstream subscribers\n * in the steady state. In the change-streamer, this is done by occasionally\n * waiting for ACKs from subscribers before continuing; without doing so,\n * I/O buffers fill up and cause the system to spend most of its time in GC.\n *\n * However, the naive algorithm of always waiting for all subscribers (e.g.\n * `Promise.all()`) can behave poorly in scenarios where subscribers\n * are imbalanced:\n * * New subscribers may have a backlog of changes to catch up with.\n * Having all subscribers wait for the new subscriber to catch up results\n * in delaying the entire application.\n * * Broken TCP connections similarly require all subscribers to wait until\n * connection liveness checks kick in and disconnect the subscriber.\n *\n * A simplistic approach is to add a limit to the amount of time waiting for\n * subscribers, i.e. an ack timeout. However, deciding what this timeout\n * should be is non-trivial because of the heterogeneous nature of changes;\n * while most changes operate on single rows and are relatively predictable\n * in terms of running time, some changes are table-wide operations and can\n * legitimately take an arbitrary amount of time. In such scenarios, a\n * timeout that is too short can stop progress on replication altogether.\n *\n * ### Consensus-based Timeout Algorithm\n *\n * To address these shortcomings, a \"consensus-based timeout\" algorithm is\n * used:\n * * Wait for more than half of the subscribers to finish. (In\n * case of a single node, or the case of one replication-manager\n * and one view-syncer, this reduces to waiting for all subscribers.)\n * * Once more than half of the subscribers have finished, proceed after\n * the configured timeout, proportional to the time it took for the\n * majority of subscribers to ack, even if not all subscribers have\n * finished.\n *\n * In other words, the subscribers themselves are used to determine the\n * timeout of each batch of changes; the majority determines this when\n * they complete, upon which a timeout is logically started.\n *\n * In the common case, the remaining subscribers finish soon afterward and\n * the timeout never elapses. However, in pathological cases where a minority\n * of subscribers have a disproportionate amount of load, some will still\n * be processing (or otherwise unresponsive). These subscribers are given\n * a bounded amount of time to catch up at each flushed batch, up to the\n * timeout interval. This guarantees eventual catchup because the\n * subscribers with a backlog of changes necessarily have a higher\n * processing rate than the subscribers that finished (and are made to wait).\n */\nexport class Broadcast {\n /**\n * Sends the change to the subscribers without the tracking machinery.\n * This is suitable for fire-and-forget (i.e. pipelined) sends.\n */\n static withoutTracking(\n subscribers: Iterable<Subscriber>,\n change: WatermarkedChange,\n ) {\n for (const sub of subscribers) {\n void sub.send(change);\n }\n }\n\n readonly #lc: LogContext;\n readonly #pending: Set<Subscriber>;\n readonly #completed: Completed[];\n readonly #done = resolver();\n #isDone = false;\n #releaseMode: BroadcastReleaseMode | undefined;\n\n readonly #watermark: string;\n readonly #majority: number;\n\n readonly #start = performance.now();\n\n // When set, the broadcast schedules its own consensus-timeout release instead\n // of waiting for the Forwarder's periodic progress tick.\n readonly #earlyReleaseTimeoutProportion: number | undefined;\n readonly #setTimeout: typeof setTimeout;\n readonly #clearTimeout: typeof clearTimeout;\n #earlyReleaseTimer: ReturnType<typeof setTimeout> | undefined;\n\n // Tracks whether the backup-replicator responded. It is a required member\n // of the consensus timeout since it cannot be dropped as a laggard (since\n // that would shutdown the replication-manager itself).\n #needsBackupResponse = false;\n\n /**\n * Broadcasts the `change` to the `subscribers` and tracks their\n * completion.\n */\n constructor(\n lc: LogContext,\n subscribers: Iterable<Subscriber>,\n change: WatermarkedChange,\n earlyRelease?: EarlyReleaseOptions,\n ) {\n this.#lc = lc;\n this.#pending = new Set(subscribers);\n this.#completed = [];\n this.#watermark = change[0];\n this.#majority = Math.floor(this.#pending.size / 2) + 1;\n this.#earlyReleaseTimeoutProportion =\n earlyRelease?.consensusTimeoutProportion;\n this.#setTimeout = earlyRelease?.setTimeoutFn ?? setTimeout;\n this.#clearTimeout = earlyRelease?.clearTimeoutFn ?? clearTimeout;\n\n for (const sub of this.#pending) {\n const changes = sub.numPending + 1; // add one for this `change`\n if (sub.mode === 'backup') {\n // Only gate consensus on the backup-replicator after it has finished\n // its initial catchup.\n this.#needsBackupResponse ||= !sub.isBacklogged();\n }\n void sub\n .send(change)\n .catch(() => {})\n .finally(() => this.#markCompleted(sub, changes));\n }\n\n // set done if there are no subscribers (mainly for tests)\n if (this.#pending.size === 0) {\n this.#setDone('all-subscribers');\n }\n }\n\n #markCompleted(sub: Subscriber, changes: number) {\n if (this.#isDone) {\n return;\n }\n const elapsed = performance.now() - this.#start;\n sub.trackResponseResult('on-time');\n if (sub.mode === 'backup') {\n this.#needsBackupResponse = false;\n }\n this.#completed.push({sub, changes, elapsed});\n this.#pending.delete(sub);\n if (this.#pending.size === 0) {\n this.#setDone('all-subscribers');\n return;\n }\n // Event-driven consensus-timeout: the moment a majority acks, and it\n // includes the backup-replicator, arm a single release timer\n // proportional to how long the majority took to ack. The broadcast\n // resolves when it fires, unless all subscribers ack first.\n if (\n this.#earlyReleaseTimeoutProportion !== undefined &&\n this.#earlyReleaseTimer === undefined &&\n this.#completed.length >= this.#majority &&\n !this.#needsBackupResponse\n ) {\n const timeoutMs = Math.ceil(\n elapsed * this.#earlyReleaseTimeoutProportion,\n );\n this.#earlyReleaseTimer = this.#setTimeout(\n () => this.#setDone('consensus-timeout'),\n timeoutMs,\n );\n }\n }\n\n #setDone(releaseMode: BroadcastReleaseMode) {\n if (this.#isDone) {\n return;\n }\n if (releaseMode === 'consensus-timeout') {\n const elapsed = performance.now() - this.#start;\n this.#logWithState(\n `continuing with ${this.#pending.size} subscriber(s) still pending`,\n elapsed,\n );\n }\n this.#pending.forEach(sub => sub.trackResponseResult('timed-out'));\n this.#isDone = true;\n this.#releaseMode = releaseMode;\n this.#clearTimeout(this.#earlyReleaseTimer);\n this.#earlyReleaseTimer = undefined;\n this.#done.resolve();\n }\n\n get isDone(): boolean {\n return this.#isDone;\n }\n\n get done(): Promise<void> {\n return this.#done.promise;\n }\n\n get releaseMode(): BroadcastReleaseMode {\n return this.#releaseMode ?? 'all-subscribers';\n }\n\n logSlowRequests(now: number) {\n if (this.#isDone || this.#pending.size === 0) {\n return;\n }\n const elapsed = now - this.#start;\n if (this.#completed.length < this.#majority) {\n if (elapsed >= 1000) {\n this.#logWithState(\n `waiting for at least ${this.#majority} subscribers to finish`,\n elapsed,\n );\n }\n }\n }\n\n #logWithState(msg: string, elapsed: number) {\n this.#lc\n .withContext('watermark', this.#watermark)\n .info?.(`${msg} (${elapsed.toFixed(3)} ms)`, {\n completed: this.#completed.map(d => ({\n id: d.sub.id,\n processed: d.changes,\n elapsed: d.elapsed,\n })),\n pending: Array.from(this.#pending, sub => ({\n id: sub.id,\n ...sub.getStats(),\n })),\n });\n }\n}\n\n/** Tracks the completed result of a single subscriber. */\ntype Completed = {\n sub: Subscriber;\n /** The number of changes processed. */\n changes: number;\n /** The elapsed milliseconds. */\n elapsed: number;\n};\n"],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Pure building blocks for comparing catchup output between the Postgres and
3
+ * SQLite change logs: how to reduce a catchup range to a single comparable
4
+ * digest. Which transactions to sample is `isSampledForShard`, in
5
+ * `shard-sampling.ts`.
6
+ *
7
+ * Nothing here touches either store. The comparator service supplies the
8
+ * batches.
9
+ */
10
+ import type { WatermarkedChange } from './change-streamer.ts';
11
+ export type CatchupRangeDigest = {
12
+ readonly digest: string;
13
+ readonly rows: number;
14
+ /** Change text hashed. This is the payload cost of the range. */
15
+ readonly bytes: number;
16
+ /** A row or byte limit was reached before the closing commit. */
17
+ readonly limitReached: boolean;
18
+ };
19
+ /**
20
+ * Builds one ordered digest for a catchup range.
21
+ *
22
+ * The row position makes missing, extra, changed, and reordered rows change the digest.
23
+ * The digest omits `precommit` because a commit already includes its watermark.
24
+ *
25
+ * The change text is hashed as stored. Both logs write the same
26
+ * `extractChangeSubstring` output, and the Postgres column is `json`, which
27
+ * keeps the input text exactly, so the two sides are byte-comparable without
28
+ * reparsing. Comparing the stored text also catches an encoding difference
29
+ * that a semantic comparison would hide.
30
+ *
31
+ * The read stops at `maxRows` or at `maxBytes`, whichever comes first.
32
+ */
33
+ export declare function digestCatchupRange(batches: AsyncIterable<readonly WatermarkedChange[]>, throughWatermark: string, maxRows: number, maxBytes: number): Promise<CatchupRangeDigest>;
34
+ //# sourceMappingURL=change-log-compare-digest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-log-compare-digest.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/change-log-compare-digest.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,sBAAsB,CAAC;AAE5D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iEAAiE;IACjE,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;CAChC,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,aAAa,CAAC,SAAS,iBAAiB,EAAE,CAAC,EACpD,gBAAgB,EAAE,MAAM,EACxB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,CAAC,CAsC7B"}
@@ -0,0 +1,67 @@
1
+ import { extractChangeSubstring } from "./change-log-codec.js";
2
+ import { ChangeLogTransactionHasher } from "./change-log-transaction-hash.js";
3
+ //#region ../zero-cache/src/services/change-streamer/change-log-compare-digest.ts
4
+ /**
5
+ * Pure building blocks for comparing catchup output between the Postgres and
6
+ * SQLite change logs: how to reduce a catchup range to a single comparable
7
+ * digest. Which transactions to sample is `isSampledForShard`, in
8
+ * `shard-sampling.ts`.
9
+ *
10
+ * Nothing here touches either store. The comparator service supplies the
11
+ * batches.
12
+ */
13
+ /**
14
+ * Builds one ordered digest for a catchup range.
15
+ *
16
+ * The row position makes missing, extra, changed, and reordered rows change the digest.
17
+ * The digest omits `precommit` because a commit already includes its watermark.
18
+ *
19
+ * The change text is hashed as stored. Both logs write the same
20
+ * `extractChangeSubstring` output, and the Postgres column is `json`, which
21
+ * keeps the input text exactly, so the two sides are byte-comparable without
22
+ * reparsing. Comparing the stored text also catches an encoding difference
23
+ * that a semantic comparison would hide.
24
+ *
25
+ * The read stops at `maxRows` or at `maxBytes`, whichever comes first.
26
+ */
27
+ async function digestCatchupRange(batches, throughWatermark, maxRows, maxBytes) {
28
+ const hasher = new ChangeLogTransactionHasher();
29
+ let rows = 0;
30
+ let bytes = 0;
31
+ let servedClosingCommit = false;
32
+ for await (const batch of batches) for (const [watermark, tag, json] of batch) {
33
+ if (rows === maxRows || bytes >= maxBytes) return {
34
+ digest: hasher.digest(),
35
+ rows,
36
+ bytes,
37
+ limitReached: true
38
+ };
39
+ if (json.length > maxBytes) return {
40
+ digest: hasher.digest(),
41
+ rows,
42
+ bytes,
43
+ limitReached: true
44
+ };
45
+ const change = extractChangeSubstring(json, tag);
46
+ hasher.add({
47
+ watermark,
48
+ pos: rows,
49
+ tag,
50
+ change,
51
+ precommit: null
52
+ });
53
+ rows++;
54
+ bytes += change.length;
55
+ if (tag === "commit" && watermark === throughWatermark) servedClosingCommit = true;
56
+ }
57
+ return {
58
+ digest: hasher.digest(),
59
+ rows,
60
+ bytes,
61
+ limitReached: (rows === maxRows || bytes >= maxBytes) && !servedClosingCommit
62
+ };
63
+ }
64
+ //#endregion
65
+ export { digestCatchupRange };
66
+
67
+ //# sourceMappingURL=change-log-compare-digest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-log-compare-digest.js","names":[],"sources":["../../../../../../zero-cache/src/services/change-streamer/change-log-compare-digest.ts"],"sourcesContent":["/**\n * Pure building blocks for comparing catchup output between the Postgres and\n * SQLite change logs: how to reduce a catchup range to a single comparable\n * digest. Which transactions to sample is `isSampledForShard`, in\n * `shard-sampling.ts`.\n *\n * Nothing here touches either store. The comparator service supplies the\n * batches.\n */\n\nimport {extractChangeSubstring} from './change-log-codec.ts';\nimport {ChangeLogTransactionHasher} from './change-log-transaction-hash.ts';\nimport type {WatermarkedChange} from './change-streamer.ts';\n\nexport type CatchupRangeDigest = {\n readonly digest: string;\n readonly rows: number;\n /** Change text hashed. This is the payload cost of the range. */\n readonly bytes: number;\n /** A row or byte limit was reached before the closing commit. */\n readonly limitReached: boolean;\n};\n\n/**\n * Builds one ordered digest for a catchup range.\n *\n * The row position makes missing, extra, changed, and reordered rows change the digest.\n * The digest omits `precommit` because a commit already includes its watermark.\n *\n * The change text is hashed as stored. Both logs write the same\n * `extractChangeSubstring` output, and the Postgres column is `json`, which\n * keeps the input text exactly, so the two sides are byte-comparable without\n * reparsing. Comparing the stored text also catches an encoding difference\n * that a semantic comparison would hide.\n *\n * The read stops at `maxRows` or at `maxBytes`, whichever comes first.\n */\nexport async function digestCatchupRange(\n batches: AsyncIterable<readonly WatermarkedChange[]>,\n throughWatermark: string,\n maxRows: number,\n maxBytes: number,\n): Promise<CatchupRangeDigest> {\n const hasher = new ChangeLogTransactionHasher();\n let rows = 0;\n let bytes = 0;\n let servedClosingCommit = false;\n\n for await (const batch of batches) {\n for (const [watermark, tag, json] of batch) {\n if (rows === maxRows || bytes >= maxBytes) {\n return {digest: hasher.digest(), rows, bytes, limitReached: true};\n }\n // Check the whole stream message first. A row wider than the entire\n // budget never fits, so it is rejected before any per-row work.\n if (json.length > maxBytes) {\n return {digest: hasher.digest(), rows, bytes, limitReached: true};\n }\n const change = extractChangeSubstring(json, tag);\n hasher.add({\n watermark,\n pos: rows,\n tag,\n change,\n precommit: null,\n });\n rows++;\n bytes += change.length;\n if (tag === 'commit' && watermark === throughWatermark) {\n servedClosingCommit = true;\n }\n }\n }\n return {\n digest: hasher.digest(),\n rows,\n bytes,\n limitReached:\n (rows === maxRows || bytes >= maxBytes) && !servedClosingCommit,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,eAAsB,mBACpB,SACA,kBACA,SACA,UAC6B;CAC7B,MAAM,SAAS,IAAI,2BAA2B;CAC9C,IAAI,OAAO;CACX,IAAI,QAAQ;CACZ,IAAI,sBAAsB;CAE1B,WAAW,MAAM,SAAS,SACxB,KAAK,MAAM,CAAC,WAAW,KAAK,SAAS,OAAO;EAC1C,IAAI,SAAS,WAAW,SAAS,UAC/B,OAAO;GAAC,QAAQ,OAAO,OAAO;GAAG;GAAM;GAAO,cAAc;EAAI;EAIlE,IAAI,KAAK,SAAS,UAChB,OAAO;GAAC,QAAQ,OAAO,OAAO;GAAG;GAAM;GAAO,cAAc;EAAI;EAElE,MAAM,SAAS,uBAAuB,MAAM,GAAG;EAC/C,OAAO,IAAI;GACT;GACA,KAAK;GACL;GACA;GACA,WAAW;EACb,CAAC;EACD;EACA,SAAS,OAAO;EAChB,IAAI,QAAQ,YAAY,cAAc,kBACpC,sBAAsB;CAE1B;CAEF,OAAO;EACL,QAAQ,OAAO,OAAO;EACtB;EACA;EACA,eACG,SAAS,WAAW,SAAS,aAAa,CAAC;CAChD;AACF"}
@@ -3,12 +3,10 @@ import type { LogContext } from '@rocicorp/logger';
3
3
  import { type Worker } from '../../types/processes.ts';
4
4
  import { type ShardID } from '../../types/shards.ts';
5
5
  import { type Source } from '../../types/streams.ts';
6
- import { HttpService } from '../http-service.ts';
6
+ import { HttpService, type Options as HttpOptions } from '../http-service.ts';
7
7
  import { type ChangeStreamer, type ChangeStreamerService, type SerializedDownstream, type SubscriberContext } from './change-streamer.ts';
8
8
  import { type SnapshotMessage } from './snapshot.ts';
9
- type Options = {
10
- port: number;
11
- keepaliveTimeoutMs: number | undefined;
9
+ type Options = HttpOptions & {
12
10
  startupDelayMs: number;
13
11
  };
14
12
  export declare class ChangeStreamerHttpServer extends HttpService {
@@ -1 +1 @@
1
- {"version":3,"file":"change-streamer-http.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/change-streamer-http.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,WAAW,CAAC;AAE/C,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAKjD,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAC,KAAK,OAAO,EAAC,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAKL,KAAK,MAAM,EACZ,MAAM,wBAAwB,CAAC;AAIhC,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACvB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAwB,KAAK,eAAe,EAAC,MAAM,eAAe,CAAC;AAW1E,KAAK,OAAO,GAAG;IACb,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,qBAAa,wBAAyB,SAAQ,WAAW;;IACvD,QAAQ,CAAC,EAAE,iCAAiC;gBAM1C,EAAE,EAAE,UAAU,EACd,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,qBAAqB;cAgGpB,QAAQ,IAAI,IAAI;cAWV,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAKlD;AAED,qBAAa,wBAAyB,YAAW,cAAc;;gBAO3D,EAAE,EAAE,UAAU,EACd,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAChB,iBAAiB,EAAE,MAAM,GAAG,SAAS;IA8BjC,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IASjE,SAAS,CACb,GAAG,EAAE,iBAAiB,GACrB,OAAO,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;CAQzC;AAED,KAAK,cAAc,GAAG,IAAI,CAAC,eAAe,EAAE,KAAK,GAAG,SAAS,CAAC,CAAC;AAE/D,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,iBAAiB,CAkB3E"}
1
+ {"version":3,"file":"change-streamer-http.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/change-streamer-http.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,WAAW,CAAC;AAE/C,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAMjD,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAC,KAAK,OAAO,EAAC,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAKL,KAAK,MAAM,EACZ,MAAM,wBAAwB,CAAC;AAIhC,OAAO,EAAC,WAAW,EAAE,KAAK,OAAO,IAAI,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACvB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAwB,KAAK,eAAe,EAAC,MAAM,eAAe,CAAC;AAW1E,KAAK,OAAO,GAAG,WAAW,GAAG;IAC3B,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,qBAAa,wBAAyB,SAAQ,WAAW;;IACvD,QAAQ,CAAC,EAAE,iCAAiC;gBAM1C,EAAE,EAAE,UAAU,EACd,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,qBAAqB;cAgGpB,QAAQ,IAAI,IAAI;cA6BV,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAKlD;AAED,qBAAa,wBAAyB,YAAW,cAAc;;gBAO3D,EAAE,EAAE,UAAU,EACd,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAChB,iBAAiB,EAAE,MAAM,GAAG,SAAS;IA8BjC,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IASjE,SAAS,CACb,GAAG,EAAE,iBAAiB,GACrB,OAAO,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;CAQzC;AAED,KAAK,cAAc,GAAG,IAAI,CAAC,eAAe,EAAE,KAAK,GAAG,SAAS,CAAC,CAAC;AAE/D,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,iBAAiB,CAkB3E"}
@@ -1,4 +1,5 @@
1
1
  import { assert } from "../../../../shared/src/asserts.js";
2
+ import { promiseVoid } from "../../../../shared/src/resolved-promises.js";
2
3
  import "../../types/processes.js";
3
4
  import { pgClient } from "../../types/pg.js";
4
5
  import { HttpService } from "../http-service.js";
@@ -75,8 +76,13 @@ var ChangeStreamerHttpServer = class extends HttpService {
75
76
  }
76
77
  }
77
78
  _onStart() {
78
- const { startupDelayMs } = this.#opts;
79
- this._state.setTimeout(() => this.#ensureChangeStreamerStarted(`startup delay elapsed (${startupDelayMs} ms)`), startupDelayMs);
79
+ const { startupDelayMs, readinessGate = promiseVoid } = this.#opts;
80
+ readinessGate.then(() => {
81
+ if (startupDelayMs > 0) {
82
+ this.#lc.info?.(`waiting ${startupDelayMs}ms before taking over the change log`);
83
+ this._state.setTimeout(() => this.#ensureChangeStreamerStarted(`startup delay elapsed (${startupDelayMs} ms)`), startupDelayMs);
84
+ } else this.#ensureChangeStreamerStarted("no startup delay configured");
85
+ });
80
86
  }
81
87
  async _onStop() {
82
88
  if (this.#changeStreamerStarted) await this.#changeStreamer.stop();
@@ -1 +1 @@
1
- {"version":3,"file":"change-streamer-http.js","names":["#lc","#opts","#changeStreamer","#subscribe","#reserveSnapshot","#receiveWebsocket","#ensureChangeStreamerStarted","#changeStreamerStarted","#shardID","#changeDB","#changeStreamerURI","#resolveChangeStreamer"],"sources":["../../../../../../zero-cache/src/services/change-streamer/change-streamer-http.ts"],"sourcesContent":["import type {IncomingMessage} from 'node:http';\nimport websocket from '@fastify/websocket';\nimport type {LogContext} from '@rocicorp/logger';\nimport WebSocket from 'ws';\nimport {assert} from '../../../../shared/src/asserts.ts';\nimport type {IncomingMessageSubset} from '../../types/http.ts';\nimport {pgClient, type PostgresDB} from '../../types/pg.ts';\nimport {type Worker} from '../../types/processes.ts';\nimport {type ShardID} from '../../types/shards.ts';\nimport {\n streamIn,\n streamInStringified,\n streamOut,\n streamOutStringified,\n type Source,\n} from '../../types/streams.ts';\nimport {URLParams} from '../../types/url-params.ts';\nimport {installWebSocketReceiver} from '../../types/websocket-handoff.ts';\nimport {closeWithError, PROTOCOL_ERROR} from '../../types/ws.ts';\nimport {HttpService} from '../http-service.ts';\nimport {\n downstreamSchema,\n PROTOCOL_VERSION,\n type ChangeStreamer,\n type ChangeStreamerService,\n type SerializedDownstream,\n type SubscriberContext,\n} from './change-streamer.ts';\nimport {discoverChangeStreamerAddress} from './schema/tables.ts';\nimport {snapshotMessageSchema, type SnapshotMessage} from './snapshot.ts';\n\nconst MIN_SUPPORTED_PROTOCOL_VERSION = 4;\n\nconst SNAPSHOT_PATH_PATTERN = '/replication/:version/snapshot';\nconst CHANGES_PATH_PATTERN = '/replication/:version/changes';\nconst PATH_REGEX = /\\/replication\\/v(?<version>\\d+)\\/(changes|snapshot)$/;\n\nconst SNAPSHOT_PATH = `/replication/v${PROTOCOL_VERSION}/snapshot`;\nconst CHANGES_PATH = `/replication/v${PROTOCOL_VERSION}/changes`;\n\ntype Options = {\n port: number;\n keepaliveTimeoutMs: number | undefined;\n startupDelayMs: number;\n};\n\nexport class ChangeStreamerHttpServer extends HttpService {\n readonly id = 'change-streamer-http-server';\n readonly #lc: LogContext;\n readonly #opts: Options;\n readonly #changeStreamer: ChangeStreamerService;\n\n constructor(\n lc: LogContext,\n opts: Options,\n parent: Worker,\n changeStreamer: ChangeStreamerService,\n ) {\n super('change-streamer-http-server', lc, opts, async fastify => {\n await fastify.register(websocket);\n\n fastify.get(CHANGES_PATH_PATTERN, {websocket: true}, this.#subscribe);\n fastify.get(\n SNAPSHOT_PATH_PATTERN,\n {websocket: true},\n this.#reserveSnapshot,\n );\n\n installWebSocketReceiver<'snapshot' | 'changes'>(\n lc,\n fastify.websocketServer,\n this.#receiveWebsocket,\n parent,\n );\n });\n\n this.#lc = lc;\n this.#opts = opts;\n this.#changeStreamer = changeStreamer;\n }\n\n // Called when receiving a web socket via the main dispatcher handoff.\n readonly #receiveWebsocket = (\n ws: WebSocket,\n action: 'changes' | 'snapshot',\n msg: IncomingMessageSubset,\n ) => {\n switch (action) {\n case 'snapshot':\n return this.#reserveSnapshot(ws, msg);\n case 'changes':\n return this.#subscribe(ws, msg);\n default:\n closeWithError(\n this._lc,\n ws,\n `invalid action \"${action}\" received in handoff`,\n );\n return;\n }\n };\n\n readonly #reserveSnapshot = async (ws: WebSocket, req: RequestHeaders) => {\n this.#ensureChangeStreamerStarted('incoming snapshot reservation');\n try {\n const url = new URL(\n req.url ?? '',\n req.headers.origin ?? 'http://localhost',\n );\n checkProtocolVersion(url.pathname);\n const taskID = url.searchParams.get('taskID');\n if (!taskID) {\n throw new Error('Missing taskID in snapshot request');\n }\n const downstream =\n await this.#changeStreamer.startSnapshotReservation(taskID);\n void streamOut(this._lc, downstream, ws);\n } catch (err) {\n closeWithError(this._lc, ws, err, PROTOCOL_ERROR);\n }\n };\n\n readonly #subscribe = async (ws: WebSocket, req: RequestHeaders) => {\n try {\n const ctx = getSubscriberContext(req);\n if (ctx.mode === 'serving') {\n this.#ensureChangeStreamerStarted('incoming subscription');\n }\n\n const downstream = await this.#changeStreamer.subscribe(ctx);\n void streamOutStringified(this._lc, downstream, ws);\n } catch (err) {\n closeWithError(this._lc, ws, err, PROTOCOL_ERROR);\n }\n };\n\n #changeStreamerStarted = false;\n\n #ensureChangeStreamerStarted(reason: string) {\n if (!this.#changeStreamerStarted && this._state.shouldRun()) {\n this.#lc.info?.(`starting ChangeStreamerService: ${reason}`);\n void this.#changeStreamer\n .run()\n .catch(e =>\n this.#lc.warn?.(`ChangeStreamerService ended with error`, e),\n )\n .finally(() => this.stop());\n\n this.#changeStreamerStarted = true;\n }\n }\n\n protected override _onStart(): void {\n const {startupDelayMs} = this.#opts;\n this._state.setTimeout(\n () =>\n this.#ensureChangeStreamerStarted(\n `startup delay elapsed (${startupDelayMs} ms)`,\n ),\n startupDelayMs,\n );\n }\n\n protected override async _onStop(): Promise<void> {\n if (this.#changeStreamerStarted) {\n await this.#changeStreamer.stop();\n }\n }\n}\n\nexport class ChangeStreamerHttpClient implements ChangeStreamer {\n readonly #lc: LogContext;\n readonly #shardID: ShardID;\n readonly #changeDB: PostgresDB;\n readonly #changeStreamerURI: string | undefined;\n\n constructor(\n lc: LogContext,\n shardID: ShardID,\n changeDB: string,\n changeStreamerURI: string | undefined,\n ) {\n this.#lc = lc;\n this.#shardID = shardID;\n // Create a pg client with a single short-lived connection for the purpose\n // of change-streamer discovery (i.e. ChangeDB as DNS).\n this.#changeDB = pgClient(lc, changeDB, 'change-streamer-discovery', {\n max: 1,\n ['idle_timeout']: 15,\n });\n this.#changeStreamerURI = changeStreamerURI;\n }\n\n async #resolveChangeStreamer(path: string) {\n let baseURL = this.#changeStreamerURI;\n if (!baseURL) {\n const address = await discoverChangeStreamerAddress(\n this.#shardID,\n this.#changeDB,\n );\n if (!address) {\n throw new Error(`no change-streamer is running`);\n }\n baseURL = address.includes('://') ? `${address}/` : `ws://${address}/`;\n }\n const uri = new URL(path, baseURL);\n this.#lc.info?.(`connecting to change-streamer@${uri}`);\n return uri;\n }\n\n async reserveSnapshot(taskID: string): Promise<Source<SnapshotMessage>> {\n const uri = await this.#resolveChangeStreamer(SNAPSHOT_PATH);\n\n const params = new URLSearchParams({taskID});\n const ws = new WebSocket(uri + `?${params.toString()}`);\n\n return streamIn(this.#lc, ws, snapshotMessageSchema);\n }\n\n async subscribe(\n ctx: SubscriberContext,\n ): Promise<Source<SerializedDownstream>> {\n const uri = await this.#resolveChangeStreamer(CHANGES_PATH);\n\n const params = getParams(ctx);\n const ws = new WebSocket(uri + `?${params.toString()}`);\n\n return streamInStringified(this.#lc, ws, downstreamSchema);\n }\n}\n\ntype RequestHeaders = Pick<IncomingMessage, 'url' | 'headers'>;\n\nexport function getSubscriberContext(req: RequestHeaders): SubscriberContext {\n const url = new URL(req.url ?? '', req.headers.origin ?? 'http://localhost');\n const protocolVersion = checkProtocolVersion(url.pathname);\n const params = new URLParams(url);\n\n return {\n protocolVersion,\n id: params.get('id', true),\n taskID: params.get('taskID', true),\n mode: params.get('mode', false) === 'backup' ? 'backup' : 'serving',\n replicaVersion: params.get('replicaVersion', true),\n watermark: params.get('watermark', true),\n initial: params.getBoolean('initial'),\n // Absent for subscribers that predate the parameter, which is the safe\n // default: the barrier falls back to polling rather than waiting on an\n // ACK that would never be attributed to a writer.\n logsChangeStream: params.getBoolean('logsChangeStream'),\n };\n}\n\nfunction checkProtocolVersion(pathname: string): number {\n const match = PATH_REGEX.exec(pathname);\n if (!match) {\n throw new Error(`invalid path: ${pathname}`);\n }\n const v = Number(match.groups?.version);\n if (\n Number.isNaN(v) ||\n v > PROTOCOL_VERSION ||\n v < MIN_SUPPORTED_PROTOCOL_VERSION\n ) {\n throw new Error(\n `Cannot service client at protocol v${v}. ` +\n `Supported protocols: [v${MIN_SUPPORTED_PROTOCOL_VERSION} ... v${PROTOCOL_VERSION}]`,\n );\n }\n return v;\n}\n\n// This is called from the client-side (i.e. the replicator).\nfunction getParams(ctx: SubscriberContext): URLSearchParams {\n // The protocolVersion is hard-coded into the CHANGES_PATH.\n const {protocolVersion, ...stringParams} = ctx;\n assert(\n protocolVersion === PROTOCOL_VERSION,\n `replicator should be setting protocolVersion to ${PROTOCOL_VERSION}`,\n );\n return new URLSearchParams({\n ...stringParams,\n initial: ctx.initial ? 'true' : 'false',\n logsChangeStream: ctx.logsChangeStream ? 'true' : 'false',\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"change-streamer-http.js","names":["#lc","#opts","#changeStreamer","#subscribe","#reserveSnapshot","#receiveWebsocket","#ensureChangeStreamerStarted","#changeStreamerStarted","#shardID","#changeDB","#changeStreamerURI","#resolveChangeStreamer"],"sources":["../../../../../../zero-cache/src/services/change-streamer/change-streamer-http.ts"],"sourcesContent":["import type {IncomingMessage} from 'node:http';\nimport websocket from '@fastify/websocket';\nimport type {LogContext} from '@rocicorp/logger';\nimport WebSocket from 'ws';\nimport {assert} from '../../../../shared/src/asserts.ts';\nimport {promiseVoid} from '../../../../shared/src/resolved-promises.ts';\nimport type {IncomingMessageSubset} from '../../types/http.ts';\nimport {pgClient, type PostgresDB} from '../../types/pg.ts';\nimport {type Worker} from '../../types/processes.ts';\nimport {type ShardID} from '../../types/shards.ts';\nimport {\n streamIn,\n streamInStringified,\n streamOut,\n streamOutStringified,\n type Source,\n} from '../../types/streams.ts';\nimport {URLParams} from '../../types/url-params.ts';\nimport {installWebSocketReceiver} from '../../types/websocket-handoff.ts';\nimport {closeWithError, PROTOCOL_ERROR} from '../../types/ws.ts';\nimport {HttpService, type Options as HttpOptions} from '../http-service.ts';\nimport {\n downstreamSchema,\n PROTOCOL_VERSION,\n type ChangeStreamer,\n type ChangeStreamerService,\n type SerializedDownstream,\n type SubscriberContext,\n} from './change-streamer.ts';\nimport {discoverChangeStreamerAddress} from './schema/tables.ts';\nimport {snapshotMessageSchema, type SnapshotMessage} from './snapshot.ts';\n\nconst MIN_SUPPORTED_PROTOCOL_VERSION = 4;\n\nconst SNAPSHOT_PATH_PATTERN = '/replication/:version/snapshot';\nconst CHANGES_PATH_PATTERN = '/replication/:version/changes';\nconst PATH_REGEX = /\\/replication\\/v(?<version>\\d+)\\/(changes|snapshot)$/;\n\nconst SNAPSHOT_PATH = `/replication/v${PROTOCOL_VERSION}/snapshot`;\nconst CHANGES_PATH = `/replication/v${PROTOCOL_VERSION}/changes`;\n\ntype Options = HttpOptions & {\n startupDelayMs: number;\n};\n\nexport class ChangeStreamerHttpServer extends HttpService {\n readonly id = 'change-streamer-http-server';\n readonly #lc: LogContext;\n readonly #opts: Options;\n readonly #changeStreamer: ChangeStreamerService;\n\n constructor(\n lc: LogContext,\n opts: Options,\n parent: Worker,\n changeStreamer: ChangeStreamerService,\n ) {\n super('change-streamer-http-server', lc, opts, async fastify => {\n await fastify.register(websocket);\n\n fastify.get(CHANGES_PATH_PATTERN, {websocket: true}, this.#subscribe);\n fastify.get(\n SNAPSHOT_PATH_PATTERN,\n {websocket: true},\n this.#reserveSnapshot,\n );\n\n installWebSocketReceiver<'snapshot' | 'changes'>(\n lc,\n fastify.websocketServer,\n this.#receiveWebsocket,\n parent,\n );\n });\n\n this.#lc = lc;\n this.#opts = opts;\n this.#changeStreamer = changeStreamer;\n }\n\n // Called when receiving a web socket via the main dispatcher handoff.\n readonly #receiveWebsocket = (\n ws: WebSocket,\n action: 'changes' | 'snapshot',\n msg: IncomingMessageSubset,\n ) => {\n switch (action) {\n case 'snapshot':\n return this.#reserveSnapshot(ws, msg);\n case 'changes':\n return this.#subscribe(ws, msg);\n default:\n closeWithError(\n this._lc,\n ws,\n `invalid action \"${action}\" received in handoff`,\n );\n return;\n }\n };\n\n readonly #reserveSnapshot = async (ws: WebSocket, req: RequestHeaders) => {\n this.#ensureChangeStreamerStarted('incoming snapshot reservation');\n try {\n const url = new URL(\n req.url ?? '',\n req.headers.origin ?? 'http://localhost',\n );\n checkProtocolVersion(url.pathname);\n const taskID = url.searchParams.get('taskID');\n if (!taskID) {\n throw new Error('Missing taskID in snapshot request');\n }\n const downstream =\n await this.#changeStreamer.startSnapshotReservation(taskID);\n void streamOut(this._lc, downstream, ws);\n } catch (err) {\n closeWithError(this._lc, ws, err, PROTOCOL_ERROR);\n }\n };\n\n readonly #subscribe = async (ws: WebSocket, req: RequestHeaders) => {\n try {\n const ctx = getSubscriberContext(req);\n if (ctx.mode === 'serving') {\n this.#ensureChangeStreamerStarted('incoming subscription');\n }\n\n const downstream = await this.#changeStreamer.subscribe(ctx);\n void streamOutStringified(this._lc, downstream, ws);\n } catch (err) {\n closeWithError(this._lc, ws, err, PROTOCOL_ERROR);\n }\n };\n\n #changeStreamerStarted = false;\n\n #ensureChangeStreamerStarted(reason: string) {\n if (!this.#changeStreamerStarted && this._state.shouldRun()) {\n this.#lc.info?.(`starting ChangeStreamerService: ${reason}`);\n void this.#changeStreamer\n .run()\n .catch(e =>\n this.#lc.warn?.(`ChangeStreamerService ended with error`, e),\n )\n .finally(() => this.stop());\n\n this.#changeStreamerStarted = true;\n }\n }\n\n protected override _onStart(): void {\n const {startupDelayMs, readinessGate = promiseVoid} = this.#opts;\n void readinessGate.then(() => {\n if (startupDelayMs > 0) {\n // In RMv1, starting the change-streamer forcibly shuts down the\n // previous change-streamer, causing view-syncers to reconnect.\n // If this replication-manager has just started, the routing layer may\n // not have registered it with DNS, as that only happens after it\n // confirms health checks. To minimize downtime, the takeover is\n // delayed for the configured startupDelayMs _after_ beginning to\n // advertise readiness.\n this.#lc.info?.(\n `waiting ${startupDelayMs}ms before taking over the change log`,\n );\n this._state.setTimeout(\n () =>\n this.#ensureChangeStreamerStarted(\n `startup delay elapsed (${startupDelayMs} ms)`,\n ),\n startupDelayMs,\n );\n } else {\n // In RMv2, this is not necessary because a new RM does not kill the\n // old one; it is started as soon as possible.\n this.#ensureChangeStreamerStarted('no startup delay configured');\n }\n });\n }\n\n protected override async _onStop(): Promise<void> {\n if (this.#changeStreamerStarted) {\n await this.#changeStreamer.stop();\n }\n }\n}\n\nexport class ChangeStreamerHttpClient implements ChangeStreamer {\n readonly #lc: LogContext;\n readonly #shardID: ShardID;\n readonly #changeDB: PostgresDB;\n readonly #changeStreamerURI: string | undefined;\n\n constructor(\n lc: LogContext,\n shardID: ShardID,\n changeDB: string,\n changeStreamerURI: string | undefined,\n ) {\n this.#lc = lc;\n this.#shardID = shardID;\n // Create a pg client with a single short-lived connection for the purpose\n // of change-streamer discovery (i.e. ChangeDB as DNS).\n this.#changeDB = pgClient(lc, changeDB, 'change-streamer-discovery', {\n max: 1,\n ['idle_timeout']: 15,\n });\n this.#changeStreamerURI = changeStreamerURI;\n }\n\n async #resolveChangeStreamer(path: string) {\n let baseURL = this.#changeStreamerURI;\n if (!baseURL) {\n const address = await discoverChangeStreamerAddress(\n this.#shardID,\n this.#changeDB,\n );\n if (!address) {\n throw new Error(`no change-streamer is running`);\n }\n baseURL = address.includes('://') ? `${address}/` : `ws://${address}/`;\n }\n const uri = new URL(path, baseURL);\n this.#lc.info?.(`connecting to change-streamer@${uri}`);\n return uri;\n }\n\n async reserveSnapshot(taskID: string): Promise<Source<SnapshotMessage>> {\n const uri = await this.#resolveChangeStreamer(SNAPSHOT_PATH);\n\n const params = new URLSearchParams({taskID});\n const ws = new WebSocket(uri + `?${params.toString()}`);\n\n return streamIn(this.#lc, ws, snapshotMessageSchema);\n }\n\n async subscribe(\n ctx: SubscriberContext,\n ): Promise<Source<SerializedDownstream>> {\n const uri = await this.#resolveChangeStreamer(CHANGES_PATH);\n\n const params = getParams(ctx);\n const ws = new WebSocket(uri + `?${params.toString()}`);\n\n return streamInStringified(this.#lc, ws, downstreamSchema);\n }\n}\n\ntype RequestHeaders = Pick<IncomingMessage, 'url' | 'headers'>;\n\nexport function getSubscriberContext(req: RequestHeaders): SubscriberContext {\n const url = new URL(req.url ?? '', req.headers.origin ?? 'http://localhost');\n const protocolVersion = checkProtocolVersion(url.pathname);\n const params = new URLParams(url);\n\n return {\n protocolVersion,\n id: params.get('id', true),\n taskID: params.get('taskID', true),\n mode: params.get('mode', false) === 'backup' ? 'backup' : 'serving',\n replicaVersion: params.get('replicaVersion', true),\n watermark: params.get('watermark', true),\n initial: params.getBoolean('initial'),\n // Absent for subscribers that predate the parameter, which is the safe\n // default: the barrier falls back to polling rather than waiting on an\n // ACK that would never be attributed to a writer.\n logsChangeStream: params.getBoolean('logsChangeStream'),\n };\n}\n\nfunction checkProtocolVersion(pathname: string): number {\n const match = PATH_REGEX.exec(pathname);\n if (!match) {\n throw new Error(`invalid path: ${pathname}`);\n }\n const v = Number(match.groups?.version);\n if (\n Number.isNaN(v) ||\n v > PROTOCOL_VERSION ||\n v < MIN_SUPPORTED_PROTOCOL_VERSION\n ) {\n throw new Error(\n `Cannot service client at protocol v${v}. ` +\n `Supported protocols: [v${MIN_SUPPORTED_PROTOCOL_VERSION} ... v${PROTOCOL_VERSION}]`,\n );\n }\n return v;\n}\n\n// This is called from the client-side (i.e. the replicator).\nfunction getParams(ctx: SubscriberContext): URLSearchParams {\n // The protocolVersion is hard-coded into the CHANGES_PATH.\n const {protocolVersion, ...stringParams} = ctx;\n assert(\n protocolVersion === PROTOCOL_VERSION,\n `replicator should be setting protocolVersion to ${PROTOCOL_VERSION}`,\n );\n return new URLSearchParams({\n ...stringParams,\n initial: ctx.initial ? 'true' : 'false',\n logsChangeStream: ctx.logsChangeStream ? 'true' : 'false',\n });\n}\n"],"mappings":"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"}
@@ -7,7 +7,9 @@ import { type ReplicationStatusPublisher } from '../replicator/replication-statu
7
7
  import type { SubscriptionState } from '../replicator/schema/replication-state.ts';
8
8
  import { type ChangeStreamerService, type SubscriberContext } from './change-streamer.ts';
9
9
  import { type SQLiteChangeLogCleanupGuard } from './sqlite-change-log-catchup.ts';
10
+ import { type SQLiteChangeLogCompareOptions } from './sqlite-change-log-comparator.ts';
10
11
  import { type SQLiteChangeLogPurgeSchedulerOptions } from './sqlite-change-log-purge-scheduler.ts';
12
+ import { type SQLiteChangeLogCoverage } from './sqlite-change-log-read-router.ts';
11
13
  import { type SQLiteChangeLogWriterOptions } from './sqlite-change-log-writer.ts';
12
14
  import { type PurgeLock, type TuningOptions as StorerOptions } from './storer.ts';
13
15
  export type BackupConfig = {
@@ -25,11 +27,9 @@ export type SQLiteCatchupOptions = {
25
27
  */
26
28
  barrierPollIntervalMs?: number | undefined;
27
29
  /**
28
- * Slice 11 supplies the production canary selector.
29
- *
30
- * It does not need to exclude backup subscribers or the change-log writer:
31
- * eligibility checks reject both before invoking this selector. Serving a
32
- * writer from SQLite would make it wait on its own ACK.
30
+ * Optional policy hook used by focused tests. Production canary selection is
31
+ * supplied by `sqliteChangeLogServe` and is stable by shard plus task ID.
32
+ * Backup subscribers are rejected before either selector is invoked.
33
33
  */
34
34
  shouldUse?: ((ctx: SubscriberContext) => boolean) | undefined;
35
35
  /**
@@ -45,6 +45,26 @@ export type SQLiteCatchupOptions = {
45
45
  */
46
46
  notReadyWarnThresholdMs?: number | undefined;
47
47
  };
48
+ export type SQLiteChangeLogServeOptions = {
49
+ /** Stable percentage of eligible serving tasks routed to SQLite. */
50
+ readPercent: number;
51
+ /**
52
+ * Stable percentage of eligible serving tasks routed to a log that has not
53
+ * yet aged through {@link retentionMs}. Zero keeps a reseeded log on PG for
54
+ * that whole window; above zero it serves that share of tasks, and a
55
+ * reservation it cannot cover is demoted rather than held pending.
56
+ * Defaults to zero.
57
+ */
58
+ coldReadPercent?: number | undefined;
59
+ /** The warm-up window a reseeded log ages through. */
60
+ retentionMs: number;
61
+ /** Injectable for deterministic breaker tests. */
62
+ failureCooldownMs?: number | undefined;
63
+ /** Injectable for deterministic warm-up and breaker tests. */
64
+ now?: (() => number) | undefined;
65
+ /** Injectable readiness inspection for service-level tests. */
66
+ inspect?: (() => SQLiteChangeLogCoverage | undefined) | undefined;
67
+ };
48
68
  export type TuningOptions = StorerOptions & {
49
69
  flowControlConsensusTimeoutProportion: number;
50
70
  flowControlSlowSubscriberGracePeriodMs?: number | undefined;
@@ -64,16 +84,17 @@ export type TuningOptions = StorerOptions & {
64
84
  */
65
85
  sqliteChangeLogPurge?: SQLiteChangeLogPurgeSchedulerOptions | undefined;
66
86
  /**
67
- * Supplied at `sqliteChangeLogMode >= compare`, and the gate on deriving the
68
- * stream's initialization parameters from the replica as well as from
69
- * Postgres. Postgres stays authoritative; the two are compared and the
70
- * result is reported. Not a new configuration option -- the mode ladder
71
- * carries it, because a state in which the log's buffer and its cookies were
72
- * at different rollout stages is what invariant 15 exists to forbid.
87
+ * Supplied in `compare` mode and later modes.
88
+ * `replicaFile` enables the initialization comparison.
89
+ * The other fields configure sampled catchup comparisons.
90
+ * Both checks are advisory, and Postgres remains authoritative.
91
+ * The comparator also requires the writer and catchup options.
73
92
  */
74
- sqliteChangeLogCompare?: {
93
+ sqliteChangeLogCompare?: (SQLiteChangeLogCompareOptions & {
75
94
  replicaFile: string;
76
- } | undefined;
95
+ }) | undefined;
96
+ /** Supplied only in `serve` mode. A zero percentage keeps every read on PG. */
97
+ sqliteChangeLogServe?: SQLiteChangeLogServeOptions | undefined;
77
98
  };
78
99
  /**
79
100
  * Performs initialization and schema migrations to initialize a ChangeStreamerImpl.
@@ -1 +1 @@
1
- {"version":3,"file":"change-streamer-service.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/change-streamer-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAWjD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,uBAAuB,CAAC;AAGnD,OAAO,KAAK,EACV,YAAY,EAEb,MAAM,mCAAmC,CAAC;AAM3C,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAGL,KAAK,0BAA0B,EAChC,MAAM,qCAAqC,CAAC;AAC7C,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,2CAA2C,CAAC;AAUjF,OAAO,EACL,KAAK,qBAAqB,EAE1B,KAAK,iBAAiB,EAEvB,MAAM,sBAAsB,CAAC;AAU9B,OAAO,EAEL,KAAK,2BAA2B,EACjC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAGL,KAAK,oCAAoC,EAC1C,MAAM,wCAAwC,CAAC;AAEhD,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,aAAa,IAAI,aAAa,EACpC,MAAM,aAAa,CAAC;AAIrB,MAAM,MAAM,YAAY,GAAG;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,iBAAiB,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,sEAAsE;IACtE,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,iBAAiB,KAAK,OAAO,CAAC,GAAG,SAAS,CAAC;IAC9D;;;;OAIG;IACH,YAAY,CAAC,EAAE,2BAA2B,GAAG,SAAS,CAAC;IACvD;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG;IAC1C,qCAAqC,EAAE,MAAM,CAAC;IAC9C,sCAAsC,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5D,aAAa,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;IACjD;;;OAGG;IACH,qBAAqB,CAAC,EAClB,IAAI,CACF,4BAA4B,EAC5B,UAAU,GAAG,YAAY,GAAG,WAAW,CACxC,GACD,SAAS,CAAC;IACd;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE,oCAAoC,GAAG,SAAS,CAAC;IACxE;;;;;;;OAOG;IACH,sBAAsB,CAAC,EAAE;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,GAAG,SAAS,CAAC;CAC5D,CAAC;AAEF;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM,EACxB,iBAAiB,EAAE,MAAM,EACzB,QAAQ,EAAE,UAAU,EACpB,YAAY,EAAE,YAAY,EAC1B,0BAA0B,EAAE,0BAA0B,EACtD,iBAAiB,EAAE,iBAAiB,EACpC,YAAY,EAAE,YAAY,GAAG,IAAI,EACjC,SAAS,EAAE,SAAS,GAAG,IAAI,EAC3B,SAAS,EAAE,OAAO,EAClB,IAAI,EAAE,aAAa,EACnB,YAAY,oBAAa,GACxB,OAAO,CAAC,qBAAqB,CAAC,CA4BhC"}
1
+ {"version":3,"file":"change-streamer-service.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/change-streamer-service.ts"],"names":[],"mappings":"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"}