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

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 (408) 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/z2s/src/compiler.d.ts.map +1 -1
  30. package/out/z2s/src/compiler.js +1 -22
  31. package/out/z2s/src/compiler.js.map +1 -1
  32. package/out/zero/package.js +3 -3
  33. package/out/zero/package.js.map +1 -1
  34. package/out/zero-cache/src/config/normalize.d.ts +1 -0
  35. package/out/zero-cache/src/config/normalize.d.ts.map +1 -1
  36. package/out/zero-cache/src/config/normalize.js +10 -1
  37. package/out/zero-cache/src/config/normalize.js.map +1 -1
  38. package/out/zero-cache/src/config/zero-config.d.ts +52 -1
  39. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  40. package/out/zero-cache/src/config/zero-config.js +171 -22
  41. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  42. package/out/zero-cache/src/custom/fetch.js +1 -1
  43. package/out/zero-cache/src/custom/fetch.js.map +1 -1
  44. package/out/zero-cache/src/db/delete-lite-db.js +2 -1
  45. package/out/zero-cache/src/db/delete-lite-db.js.map +1 -1
  46. package/out/zero-cache/src/db/migration-lite.d.ts.map +1 -1
  47. package/out/zero-cache/src/db/migration-lite.js +5 -2
  48. package/out/zero-cache/src/db/migration-lite.js.map +1 -1
  49. package/out/zero-cache/src/db/migration.d.ts.map +1 -1
  50. package/out/zero-cache/src/db/migration.js +4 -2
  51. package/out/zero-cache/src/db/migration.js.map +1 -1
  52. package/out/zero-cache/src/db/sqlite-corruption.d.ts +3 -2
  53. package/out/zero-cache/src/db/sqlite-corruption.d.ts.map +1 -1
  54. package/out/zero-cache/src/db/sqlite-corruption.js +13 -8
  55. package/out/zero-cache/src/db/sqlite-corruption.js.map +1 -1
  56. package/out/zero-cache/src/observability/metrics.d.ts +6 -1
  57. package/out/zero-cache/src/observability/metrics.d.ts.map +1 -1
  58. package/out/zero-cache/src/observability/metrics.js +20 -2
  59. package/out/zero-cache/src/observability/metrics.js.map +1 -1
  60. package/out/zero-cache/src/server/backup-watermark-poller.js +31 -0
  61. package/out/zero-cache/src/server/backup-watermark-poller.js.map +1 -0
  62. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  63. package/out/zero-cache/src/server/change-streamer.js +91 -31
  64. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  65. package/out/zero-cache/src/server/main.d.ts.map +1 -1
  66. package/out/zero-cache/src/server/main.js +6 -16
  67. package/out/zero-cache/src/server/main.js.map +1 -1
  68. package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
  69. package/out/zero-cache/src/server/replicator.js +48 -5
  70. package/out/zero-cache/src/server/replicator.js.map +1 -1
  71. package/out/zero-cache/src/server/runner/run-worker.js +2 -1
  72. package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
  73. package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
  74. package/out/zero-cache/src/server/syncer.js +1 -1
  75. package/out/zero-cache/src/server/syncer.js.map +1 -1
  76. package/out/zero-cache/src/server/worker-urls.d.ts +1 -1
  77. package/out/zero-cache/src/server/worker-urls.js +2 -2
  78. package/out/zero-cache/src/server/worker-urls.js.map +1 -1
  79. package/out/zero-cache/src/services/change-source/change-source.d.ts +4 -2
  80. package/out/zero-cache/src/services/change-source/change-source.d.ts.map +1 -1
  81. package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts +35 -0
  82. package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts.map +1 -0
  83. package/out/zero-cache/src/services/change-source/common/replica-restore.js +34 -0
  84. package/out/zero-cache/src/services/change-source/common/replica-restore.js.map +1 -0
  85. package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts +2 -0
  86. package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts.map +1 -1
  87. package/out/zero-cache/src/services/change-source/common/replica-schema.js +73 -1
  88. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  89. package/out/zero-cache/src/services/change-source/custom/change-source.d.ts +2 -6
  90. package/out/zero-cache/src/services/change-source/custom/change-source.d.ts.map +1 -1
  91. package/out/zero-cache/src/services/change-source/custom/change-source.js +14 -5
  92. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  93. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +4 -7
  94. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  95. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts +7 -7
  96. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  97. package/out/zero-cache/src/services/change-source/pg/change-source.js +82 -25
  98. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  99. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +9 -1
  100. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  101. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +12 -4
  102. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  103. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts +45 -2
  104. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts.map +1 -1
  105. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +59 -8
  106. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
  107. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +4 -2
  108. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -1
  109. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +7 -4
  110. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  111. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts +0 -5
  112. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
  113. package/out/zero-cache/src/services/change-source/pg/schema/init.js +29 -41
  114. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  115. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +35 -16
  116. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
  117. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +77 -7
  118. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  119. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts +2 -1
  120. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts.map +1 -1
  121. package/out/zero-cache/src/services/change-source/protocol/current/data.js +5 -13
  122. package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
  123. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +6 -3
  124. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts.map +1 -1
  125. package/out/zero-cache/src/services/change-source/protocol/current/schema-change-tags.d.ts +6 -0
  126. package/out/zero-cache/src/services/change-source/protocol/current/schema-change-tags.d.ts.map +1 -0
  127. package/out/zero-cache/src/services/change-source/protocol/current/schema-change-tags.js +21 -0
  128. package/out/zero-cache/src/services/change-source/protocol/current/schema-change-tags.js.map +1 -0
  129. package/out/zero-cache/src/services/change-source/protocol/current/status.d.ts +1 -0
  130. package/out/zero-cache/src/services/change-source/protocol/current/status.d.ts.map +1 -1
  131. package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts +1 -0
  132. package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts.map +1 -1
  133. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts +5 -6
  134. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts.map +1 -1
  135. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js +32 -9
  136. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
  137. package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts +15 -5
  138. package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts.map +1 -1
  139. package/out/zero-cache/src/services/change-streamer/backup-monitor.js +66 -0
  140. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -0
  141. package/out/zero-cache/src/services/change-streamer/broadcast.d.ts +68 -78
  142. package/out/zero-cache/src/services/change-streamer/broadcast.d.ts.map +1 -1
  143. package/out/zero-cache/src/services/change-streamer/broadcast.js +84 -92
  144. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  145. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts +19 -0
  146. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts.map +1 -0
  147. package/out/zero-cache/src/services/change-streamer/change-log-codec.js +50 -0
  148. package/out/zero-cache/src/services/change-streamer/change-log-codec.js.map +1 -0
  149. package/out/zero-cache/src/services/change-streamer/change-log-initializer.d.ts +141 -0
  150. package/out/zero-cache/src/services/change-streamer/change-log-initializer.d.ts.map +1 -0
  151. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js +270 -0
  152. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js.map +1 -0
  153. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.d.ts +19 -0
  154. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.d.ts.map +1 -0
  155. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.js +39 -0
  156. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.js.map +1 -0
  157. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +3 -4
  158. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  159. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +12 -17
  160. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  161. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +69 -24
  162. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
  163. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +333 -66
  164. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  165. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +56 -13
  166. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
  167. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  168. package/out/zero-cache/src/services/change-streamer/error-type-enum.d.ts +8 -0
  169. package/out/zero-cache/src/services/change-streamer/error-type-enum.d.ts.map +1 -1
  170. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts +14 -3
  171. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts.map +1 -1
  172. package/out/zero-cache/src/services/change-streamer/forwarder.js +41 -11
  173. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  174. package/out/zero-cache/src/services/change-streamer/{litestream3-backup-monitor.d.ts → litestream3-prometheus-poller.d.ts} +19 -45
  175. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.d.ts.map +1 -0
  176. package/out/zero-cache/src/services/change-streamer/{litestream3-backup-monitor.js → litestream3-prometheus-poller.js} +57 -171
  177. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js.map +1 -0
  178. package/out/zero-cache/src/services/change-streamer/replica-poller.d.ts +15 -0
  179. package/out/zero-cache/src/services/change-streamer/replica-poller.d.ts.map +1 -0
  180. package/out/zero-cache/src/services/change-streamer/replica-poller.js +55 -0
  181. package/out/zero-cache/src/services/change-streamer/replica-poller.js.map +1 -0
  182. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts +5 -1
  183. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts.map +1 -1
  184. package/out/zero-cache/src/services/change-streamer/schema/tables.js +2 -1
  185. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  186. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts +14 -0
  187. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts.map +1 -0
  188. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js +86 -0
  189. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -0
  190. package/out/zero-cache/src/services/change-streamer/snapshot.d.ts +4 -1
  191. package/out/zero-cache/src/services/change-streamer/snapshot.d.ts.map +1 -1
  192. package/out/zero-cache/src/services/change-streamer/snapshot.js +39 -14
  193. package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
  194. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.d.ts +75 -0
  195. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.d.ts.map +1 -0
  196. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js +261 -0
  197. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js.map +1 -0
  198. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.d.ts +121 -0
  199. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.d.ts.map +1 -0
  200. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.js +391 -0
  201. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.js.map +1 -0
  202. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts +62 -0
  203. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts.map +1 -0
  204. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js +159 -0
  205. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js.map +1 -0
  206. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts +109 -0
  207. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts.map +1 -0
  208. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +258 -0
  209. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js.map +1 -0
  210. package/out/zero-cache/src/services/change-streamer/storer.d.ts +28 -12
  211. package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
  212. package/out/zero-cache/src/services/change-streamer/storer.js +116 -121
  213. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  214. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +61 -10
  215. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
  216. package/out/zero-cache/src/services/change-streamer/subscriber.js +101 -8
  217. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  218. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts +34 -0
  219. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts.map +1 -0
  220. package/out/zero-cache/src/services/change-streamer/upstream-acker.js +86 -0
  221. package/out/zero-cache/src/services/change-streamer/upstream-acker.js.map +1 -0
  222. package/out/zero-cache/src/services/life-cycle.d.ts +1 -1
  223. package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
  224. package/out/zero-cache/src/services/life-cycle.js +4 -3
  225. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  226. package/out/zero-cache/src/services/litestream/commands.d.ts +12 -11
  227. package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
  228. package/out/zero-cache/src/services/litestream/commands.js +107 -148
  229. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  230. package/out/zero-cache/src/services/litestream/config-v5.yml +42 -0
  231. package/out/zero-cache/src/services/litestream/config.yml +3 -2
  232. package/out/zero-cache/src/services/litestream/metrics.d.ts +4 -4
  233. package/out/zero-cache/src/services/litestream/metrics.d.ts.map +1 -1
  234. package/out/zero-cache/src/services/litestream/metrics.js +7 -10
  235. package/out/zero-cache/src/services/litestream/metrics.js.map +1 -1
  236. package/out/zero-cache/src/services/litestream/vfs-watermark-poller.d.ts +57 -0
  237. package/out/zero-cache/src/services/litestream/vfs-watermark-poller.d.ts.map +1 -0
  238. package/out/zero-cache/src/services/litestream/vfs-watermark-poller.js +137 -0
  239. package/out/zero-cache/src/services/litestream/vfs-watermark-poller.js.map +1 -0
  240. package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
  241. package/out/zero-cache/src/services/mutagen/mutagen.js +4 -1
  242. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  243. package/out/zero-cache/src/services/replicator/change-log-cookies.d.ts +169 -0
  244. package/out/zero-cache/src/services/replicator/change-log-cookies.d.ts.map +1 -0
  245. package/out/zero-cache/src/services/replicator/change-log-cookies.js +434 -0
  246. package/out/zero-cache/src/services/replicator/change-log-cookies.js.map +1 -0
  247. package/out/zero-cache/src/services/replicator/change-log-db.d.ts +358 -0
  248. package/out/zero-cache/src/services/replicator/change-log-db.d.ts.map +1 -0
  249. package/out/zero-cache/src/services/replicator/change-log-db.js +477 -0
  250. package/out/zero-cache/src/services/replicator/change-log-db.js.map +1 -0
  251. package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts +55 -0
  252. package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts.map +1 -0
  253. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js +150 -0
  254. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js.map +1 -0
  255. package/out/zero-cache/src/services/replicator/change-processor.d.ts +6 -0
  256. package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +1 -1
  257. package/out/zero-cache/src/services/replicator/change-processor.js +70 -25
  258. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  259. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts +1 -1
  260. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
  261. package/out/zero-cache/src/services/replicator/incremental-sync.js +19 -6
  262. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  263. package/out/zero-cache/src/services/replicator/notifier.d.ts +0 -22
  264. package/out/zero-cache/src/services/replicator/notifier.d.ts.map +1 -1
  265. package/out/zero-cache/src/services/replicator/notifier.js +7 -1
  266. package/out/zero-cache/src/services/replicator/notifier.js.map +1 -1
  267. package/out/zero-cache/src/services/replicator/replicator.d.ts +17 -2
  268. package/out/zero-cache/src/services/replicator/replicator.d.ts.map +1 -1
  269. package/out/zero-cache/src/services/replicator/replicator.js +9 -3
  270. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  271. package/out/zero-cache/src/services/replicator/reporter/recorder.d.ts +27 -0
  272. package/out/zero-cache/src/services/replicator/reporter/recorder.d.ts.map +1 -1
  273. package/out/zero-cache/src/services/replicator/reporter/recorder.js +32 -0
  274. package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
  275. package/out/zero-cache/src/services/replicator/reporter/report-schema.d.ts +1 -1
  276. package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts +119 -0
  277. package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts.map +1 -0
  278. package/out/zero-cache/src/services/replicator/schema/backfilling.js +214 -0
  279. package/out/zero-cache/src/services/replicator/schema/backfilling.js.map +1 -0
  280. package/out/zero-cache/src/services/replicator/schema/change-log.d.ts +3 -3
  281. package/out/zero-cache/src/services/replicator/schema/column-metadata.js +1 -1
  282. package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
  283. package/out/zero-cache/src/services/replicator/schema/replication-state.d.ts +1 -1
  284. package/out/zero-cache/src/services/replicator/schema/replication-state.d.ts.map +1 -1
  285. package/out/zero-cache/src/services/replicator/schema/replication-state.js +9 -5
  286. package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
  287. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts +131 -0
  288. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts.map +1 -0
  289. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +413 -0
  290. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -0
  291. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.d.ts +120 -0
  292. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.d.ts.map +1 -0
  293. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js +283 -0
  294. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js.map +1 -0
  295. package/out/zero-cache/src/services/replicator/write-worker.js +23 -12
  296. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  297. package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
  298. package/out/zero-cache/src/services/run-ast.js +2 -1
  299. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  300. package/out/zero-cache/src/services/running-state.d.ts +1 -1
  301. package/out/zero-cache/src/services/running-state.d.ts.map +1 -1
  302. package/out/zero-cache/src/services/running-state.js +1 -0
  303. package/out/zero-cache/src/services/running-state.js.map +1 -1
  304. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.d.ts +53 -0
  305. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.d.ts.map +1 -0
  306. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.js +68 -0
  307. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.js.map +1 -0
  308. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  309. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +18 -3
  310. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  311. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
  312. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  313. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +11 -1
  314. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  315. package/out/zero-cache/src/services/view-syncer/view-syncer.js +64 -6
  316. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  317. package/out/zero-cache/src/types/processes.d.ts +1 -2
  318. package/out/zero-cache/src/types/processes.d.ts.map +1 -1
  319. package/out/zero-cache/src/types/processes.js +1 -25
  320. package/out/zero-cache/src/types/processes.js.map +1 -1
  321. package/out/zero-cache/src/types/streams.d.ts +21 -0
  322. package/out/zero-cache/src/types/streams.d.ts.map +1 -1
  323. package/out/zero-cache/src/types/streams.js +25 -4
  324. package/out/zero-cache/src/types/streams.js.map +1 -1
  325. package/out/zero-cache/src/types/subscription.d.ts +11 -0
  326. package/out/zero-cache/src/types/subscription.d.ts.map +1 -1
  327. package/out/zero-cache/src/types/subscription.js +27 -0
  328. package/out/zero-cache/src/types/subscription.js.map +1 -1
  329. package/out/zero-cache/src/workers/connection.d.ts +1 -0
  330. package/out/zero-cache/src/workers/connection.d.ts.map +1 -1
  331. package/out/zero-cache/src/workers/connection.js +24 -2
  332. package/out/zero-cache/src/workers/connection.js.map +1 -1
  333. package/out/zero-cache/src/workers/replicator.d.ts +25 -0
  334. package/out/zero-cache/src/workers/replicator.d.ts.map +1 -1
  335. package/out/zero-cache/src/workers/replicator.js +33 -1
  336. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  337. package/out/zero-cache/src/workers/syncer.d.ts +12 -0
  338. package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
  339. package/out/zero-cache/src/workers/syncer.js +75 -0
  340. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  341. package/out/zero-client/src/client/version.js +1 -1
  342. package/out/zero-client/src/client/zero.d.ts +6 -7
  343. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  344. package/out/zero-client/src/client/zero.js +156 -101
  345. package/out/zero-client/src/client/zero.js.map +1 -1
  346. package/out/zero-schema/src/builder/table-builder.d.ts +23 -0
  347. package/out/zero-schema/src/builder/table-builder.d.ts.map +1 -1
  348. package/out/zero-schema/src/builder/table-builder.js +18 -0
  349. package/out/zero-schema/src/builder/table-builder.js.map +1 -1
  350. package/out/zero-server/src/custom.js +10 -3
  351. package/out/zero-server/src/custom.js.map +1 -1
  352. package/out/zero-server/src/schema.d.ts +8 -0
  353. package/out/zero-server/src/schema.d.ts.map +1 -1
  354. package/out/zero-server/src/schema.js +63 -24
  355. package/out/zero-server/src/schema.js.map +1 -1
  356. package/out/zero-types/src/schema.d.ts +11 -0
  357. package/out/zero-types/src/schema.d.ts.map +1 -1
  358. package/out/zql/src/mutate/crud.d.ts +10 -8
  359. package/out/zql/src/mutate/crud.d.ts.map +1 -1
  360. package/out/zql/src/mutate/crud.js.map +1 -1
  361. package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
  362. package/out/zql/src/query/query-delegate-base.js +5 -1
  363. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  364. package/out/zql/src/query/query-impl.d.ts +2 -2
  365. package/out/zql/src/query/query-impl.d.ts.map +1 -1
  366. package/out/zql/src/query/query-impl.js.map +1 -1
  367. package/out/zql/src/query/query.d.ts +83 -14
  368. package/out/zql/src/query/query.d.ts.map +1 -1
  369. package/out/zql/src/query/static-query.d.ts +1 -1
  370. package/out/zql/src/query/static-query.d.ts.map +1 -1
  371. package/out/zqlite/src/db.js +2 -2
  372. package/out/zqlite/src/resolve-scalar-subqueries.d.ts +15 -0
  373. package/out/zqlite/src/resolve-scalar-subqueries.d.ts.map +1 -1
  374. package/out/zqlite/src/resolve-scalar-subqueries.js +33 -14
  375. package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
  376. package/out/zqlite/src/sqlite-cost-model.js +5 -5
  377. package/out/zqlite/src/table-source.js +3 -3
  378. package/package.json +3 -3
  379. package/out/replicache/src/db/index.js +0 -91
  380. package/out/replicache/src/db/index.js.map +0 -1
  381. package/out/replicache/src/lazy.d.ts +0 -2
  382. package/out/replicache/src/lazy.d.ts.map +0 -1
  383. package/out/replicache/src/lazy.js +0 -12
  384. package/out/replicache/src/lazy.js.map +0 -1
  385. package/out/zero-cache/src/server/backup-watermark-reader.js +0 -35
  386. package/out/zero-cache/src/server/backup-watermark-reader.js.map +0 -1
  387. package/out/zero-cache/src/services/change-streamer/litestream3-backup-monitor.d.ts.map +0 -1
  388. package/out/zero-cache/src/services/change-streamer/litestream3-backup-monitor.js.map +0 -1
  389. package/out/zero-cache/src/services/change-streamer/replica-monitor.d.ts +0 -16
  390. package/out/zero-cache/src/services/change-streamer/replica-monitor.d.ts.map +0 -1
  391. package/out/zero-cache/src/services/change-streamer/replica-monitor.js +0 -54
  392. package/out/zero-cache/src/services/change-streamer/replica-monitor.js.map +0 -1
  393. package/out/zero-cache/src/services/change-streamer/vfs-backup-monitor.d.ts +0 -25
  394. package/out/zero-cache/src/services/change-streamer/vfs-backup-monitor.d.ts.map +0 -1
  395. package/out/zero-cache/src/services/change-streamer/vfs-backup-monitor.js +0 -159
  396. package/out/zero-cache/src/services/change-streamer/vfs-backup-monitor.js.map +0 -1
  397. package/out/zero-cache/src/services/litestream/vfs-watermark-reader.d.ts +0 -44
  398. package/out/zero-cache/src/services/litestream/vfs-watermark-reader.d.ts.map +0 -1
  399. package/out/zero-cache/src/services/litestream/vfs-watermark-reader.js +0 -65
  400. package/out/zero-cache/src/services/litestream/vfs-watermark-reader.js.map +0 -1
  401. package/out/zero-cache/src/services/litestream/vfs-watermark-worker-source.d.ts +0 -11
  402. package/out/zero-cache/src/services/litestream/vfs-watermark-worker-source.d.ts.map +0 -1
  403. package/out/zero-cache/src/services/litestream/vfs-watermark-worker-source.js +0 -74
  404. package/out/zero-cache/src/services/litestream/vfs-watermark-worker-source.js.map +0 -1
  405. package/out/zero-cache/src/services/litestream/vfs-watermark-worker.d.ts +0 -42
  406. package/out/zero-cache/src/services/litestream/vfs-watermark-worker.d.ts.map +0 -1
  407. package/out/zero-cache/src/services/litestream/vfs-watermark-worker.js +0 -117
  408. package/out/zero-cache/src/services/litestream/vfs-watermark-worker.js.map +0 -1
@@ -1,15 +1,38 @@
1
1
  import { forkChildWorker } from "../../types/processes.js";
2
- import { BACKUP_WATERMARK_READER_URL } from "../../server/worker-urls.js";
2
+ import { BACKUP_WATERMARK_POLLER_URL } from "../../server/worker-urls.js";
3
+ import { getVfsEnv } from "../litestream/vfs-watermark-poller.js";
4
+ import { Subscription } from "../../types/subscription.js";
3
5
  import { getLastBackupTime } from "../litestream/commands.js";
4
- import { VfsBackupWatermarkWorkerSource } from "../litestream/vfs-watermark-worker-source.js";
5
- import { Litestream3BackupMonitor } from "./litestream3-backup-monitor.js";
6
- import { VfsBackupMonitor } from "./vfs-backup-monitor.js";
6
+ import { BackupMonitor } from "./backup-monitor.js";
7
+ import { Litestream3PrometheusPoller } from "./litestream3-prometheus-poller.js";
8
+ import { ReplicaPoller } from "./replica-poller.js";
7
9
  //#region ../zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.ts
8
- function createBackupCleanupMonitor({ lc, config, replicaFile, changeStreamer, initialCleanupDelayMs, verifyBackupState, vfsBackupWatermarkSource, env }) {
9
- const { backupURL, port: metricsPort } = config.litestream;
10
- if (!backupURL) return null;
11
- if (config.litestream.backupUsingV5) return new VfsBackupMonitor(lc, backupURL, changeStreamer, initialCleanupDelayMs, config.litestream.vfsProbeIntervalMs, vfsBackupWatermarkSource ?? new VfsBackupWatermarkWorkerSource(lc, () => forkChildWorker(BACKUP_WATERMARK_READER_URL, env ?? process.env), config.litestream.vfsProbeTimeoutMs));
12
- return new Litestream3BackupMonitor(lc, replicaFile, backupURL, `http://localhost:${metricsPort}/metrics`, changeStreamer, initialCleanupDelayMs, verifyBackupState ?? (() => getLastBackupTime(lc, config)));
10
+ function createBackupCleanupMonitor({ lc, config, processes, replicaFile, changeStreamer, verifyBackupState, env = process.env }) {
11
+ const { litestream, replica } = config;
12
+ const { backupURL } = litestream;
13
+ let stream;
14
+ if (!backupURL) stream = new ReplicaPoller(lc, replicaFile).start();
15
+ else if (config.litestream.backupUsingV5) {
16
+ const { logLevel, endpoint, region, vfsLogFile: logFile, vfsProbeIntervalMs: remotePollIntervalMs } = litestream;
17
+ const sub = Subscription.create();
18
+ const vfsEnv = getVfsEnv({
19
+ backupURL,
20
+ endpoint,
21
+ region,
22
+ logLevel,
23
+ logFile,
24
+ remotePollIntervalMs
25
+ });
26
+ processes.addWorker(forkChildWorker(BACKUP_WATERMARK_POLLER_URL, {
27
+ ...env,
28
+ ...vfsEnv
29
+ }), "supporting", "backup-watermark-poller").onMessageType("backupWatermarkUpdate", (msg) => sub.push(msg));
30
+ stream = sub;
31
+ } else {
32
+ const { port: metricsPort } = litestream;
33
+ stream = new Litestream3PrometheusPoller(lc, replicaFile, backupURL, `http://localhost:${metricsPort}/metrics`, verifyBackupState ?? (() => getLastBackupTime(lc, litestream, replica.file))).start();
34
+ }
35
+ return new BackupMonitor(lc, stream, changeStreamer, replicaFile);
13
36
  }
14
37
  //#endregion
15
38
  export { createBackupCleanupMonitor };
@@ -1 +1 @@
1
- {"version":3,"file":"backup-cleanup-monitor-factory.js","names":[],"sources":["../../../../../../zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {NormalizedZeroConfig} from '../../config/normalize.ts';\nimport {BACKUP_WATERMARK_READER_URL} from '../../server/worker-urls.ts';\nimport {forkChildWorker} from '../../types/processes.ts';\nimport {getLastBackupTime} from '../litestream/commands.ts';\nimport {VfsBackupWatermarkWorkerSource} from '../litestream/vfs-watermark-worker-source.ts';\nimport type {BackupMonitor} from './backup-monitor.ts';\nimport type {ChangeStreamerService} from './change-streamer.ts';\nimport {\n Litestream3BackupMonitor,\n type BackupStateVerifier,\n} from './litestream3-backup-monitor.ts';\nimport {\n VfsBackupMonitor,\n type VfsBackupWatermarkSource,\n} from './vfs-backup-monitor.ts';\n\nexport type BackupCleanupMonitorFactoryOptions = {\n lc: LogContext;\n config: NormalizedZeroConfig;\n replicaFile: string;\n changeStreamer: ChangeStreamerService;\n initialCleanupDelayMs: number;\n verifyBackupState?: BackupStateVerifier | undefined;\n vfsBackupWatermarkSource?: VfsBackupWatermarkSource | undefined;\n env?: NodeJS.ProcessEnv | undefined;\n};\n\nexport function createBackupCleanupMonitor({\n lc,\n config,\n replicaFile,\n changeStreamer,\n initialCleanupDelayMs,\n verifyBackupState,\n vfsBackupWatermarkSource,\n env,\n}: BackupCleanupMonitorFactoryOptions): BackupMonitor | null {\n const {backupURL, port: metricsPort} = config.litestream;\n if (!backupURL) {\n return null;\n }\n\n if (config.litestream.backupUsingV5) {\n return new VfsBackupMonitor(\n lc,\n backupURL,\n changeStreamer,\n initialCleanupDelayMs,\n config.litestream.vfsProbeIntervalMs,\n vfsBackupWatermarkSource ??\n new VfsBackupWatermarkWorkerSource(\n lc,\n () =>\n forkChildWorker(BACKUP_WATERMARK_READER_URL, env ?? process.env),\n config.litestream.vfsProbeTimeoutMs,\n ),\n );\n }\n\n return new Litestream3BackupMonitor(\n lc,\n replicaFile,\n backupURL,\n `http://localhost:${metricsPort}/metrics`,\n changeStreamer,\n initialCleanupDelayMs,\n verifyBackupState ?? (() => getLastBackupTime(lc, config)),\n );\n}\n"],"mappings":";;;;;;;AA4BA,SAAgB,2BAA2B,EACzC,IACA,QACA,aACA,gBACA,uBACA,mBACA,0BACA,OAC2D;CAC3D,MAAM,EAAC,WAAW,MAAM,gBAAe,OAAO;CAC9C,IAAI,CAAC,WACH,OAAO;CAGT,IAAI,OAAO,WAAW,eACpB,OAAO,IAAI,iBACT,IACA,WACA,gBACA,uBACA,OAAO,WAAW,oBAClB,4BACE,IAAI,+BACF,UAEE,gBAAgB,6BAA6B,OAAO,QAAQ,GAAG,GACjE,OAAO,WAAW,iBACpB,CACJ;CAGF,OAAO,IAAI,yBACT,IACA,aACA,WACA,oBAAoB,YAAY,WAChC,gBACA,uBACA,4BAA4B,kBAAkB,IAAI,MAAM,EAC1D;AACF"}
1
+ {"version":3,"file":"backup-cleanup-monitor-factory.js","names":[],"sources":["../../../../../../zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {NormalizedZeroConfig} from '../../config/normalize.ts';\nimport {BACKUP_WATERMARK_POLLER_URL} from '../../server/worker-urls.ts';\nimport {forkChildWorker} from '../../types/processes.ts';\nimport type {Source} from '../../types/streams.ts';\nimport {Subscription} from '../../types/subscription.ts';\nimport type {ProcessManager} from '../life-cycle.ts';\nimport {getLastBackupTime} from '../litestream/commands.ts';\nimport {\n type BackupWatermarkUpdate,\n getVfsEnv,\n} from '../litestream/vfs-watermark-poller.ts';\nimport {type BackedUpWatermark, BackupMonitor} from './backup-monitor.ts';\nimport type {ChangeStreamerService} from './change-streamer.ts';\nimport {\n type BackupStateVerifier,\n Litestream3PrometheusPoller,\n} from './litestream3-prometheus-poller.ts';\nimport {ReplicaPoller} from './replica-poller.ts';\n\nexport type BackupCleanupMonitorFactoryOptions = {\n lc: LogContext;\n config: NormalizedZeroConfig;\n processes: ProcessManager;\n replicaFile: string;\n changeStreamer: ChangeStreamerService;\n verifyBackupState?: BackupStateVerifier | undefined;\n env?: NodeJS.ProcessEnv | undefined;\n};\n\nexport function createBackupCleanupMonitor({\n lc,\n config,\n processes,\n replicaFile,\n changeStreamer,\n verifyBackupState,\n env = process.env,\n}: BackupCleanupMonitorFactoryOptions): BackupMonitor {\n const {litestream, replica} = config;\n const {backupURL} = litestream;\n\n let stream: Source<BackedUpWatermark>;\n\n if (!backupURL) {\n stream = new ReplicaPoller(lc, replicaFile).start();\n } else if (config.litestream.backupUsingV5) {\n const {\n logLevel,\n endpoint,\n region,\n vfsLogFile: logFile,\n vfsProbeIntervalMs: remotePollIntervalMs,\n } = litestream;\n const sub = Subscription.create<BackedUpWatermark>();\n const vfsEnv = getVfsEnv({\n backupURL,\n endpoint,\n region,\n logLevel,\n logFile,\n remotePollIntervalMs,\n });\n processes\n .addWorker(\n forkChildWorker(BACKUP_WATERMARK_POLLER_URL, {...env, ...vfsEnv}),\n 'supporting',\n 'backup-watermark-poller',\n )\n .onMessageType<BackupWatermarkUpdate>('backupWatermarkUpdate', msg =>\n sub.push(msg),\n );\n stream = sub;\n } else {\n const {port: metricsPort} = litestream;\n stream = new Litestream3PrometheusPoller(\n lc,\n replicaFile,\n backupURL,\n `http://localhost:${metricsPort}/metrics`,\n verifyBackupState ??\n (() => getLastBackupTime(lc, litestream, replica.file)),\n ).start();\n }\n\n return new BackupMonitor(lc, stream, changeStreamer, replicaFile);\n}\n"],"mappings":";;;;;;;;;AA8BA,SAAgB,2BAA2B,EACzC,IACA,QACA,WACA,aACA,gBACA,mBACA,MAAM,QAAQ,OACsC;CACpD,MAAM,EAAC,YAAY,YAAW;CAC9B,MAAM,EAAC,cAAa;CAEpB,IAAI;CAEJ,IAAI,CAAC,WACH,SAAS,IAAI,cAAc,IAAI,WAAW,EAAE,MAAM;MAC7C,IAAI,OAAO,WAAW,eAAe;EAC1C,MAAM,EACJ,UACA,UACA,QACA,YAAY,SACZ,oBAAoB,yBAClB;EACJ,MAAM,MAAM,aAAa,OAA0B;EACnD,MAAM,SAAS,UAAU;GACvB;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,UACG,UACC,gBAAgB,6BAA6B;GAAC,GAAG;GAAK,GAAG;EAAM,CAAC,GAChE,cACA,yBACF,EACC,cAAqC,0BAAyB,QAC7D,IAAI,KAAK,GAAG,CACd;EACF,SAAS;CACX,OAAO;EACL,MAAM,EAAC,MAAM,gBAAe;EAC5B,SAAS,IAAI,4BACX,IACA,aACA,WACA,oBAAoB,YAAY,WAChC,4BACS,kBAAkB,IAAI,YAAY,QAAQ,IAAI,EACzD,EAAE,MAAM;CACV;CAEA,OAAO,IAAI,cAAc,IAAI,QAAQ,gBAAgB,WAAW;AAClE"}
@@ -1,8 +1,18 @@
1
- import type { Subscription } from '../../types/subscription.ts';
1
+ import type { LogContext } from '@rocicorp/logger';
2
+ import type { Source } from '../../types/streams.ts';
2
3
  import type { SingletonService } from '../service.ts';
3
- import type { SnapshotMessage } from './snapshot.ts';
4
- export interface BackupMonitor extends SingletonService {
5
- startSnapshotReservation(taskID: string): Subscription<SnapshotMessage>;
6
- endReservation(taskID: string, updateCleanupDelay?: boolean | undefined): void;
4
+ import type { ChangeStreamerService } from './change-streamer.ts';
5
+ export type BackedUpWatermark = {
6
+ watermark: string;
7
+ writeTimeMs?: number | undefined;
8
+ backupTimeMs: number;
9
+ };
10
+ export declare class BackupMonitor implements SingletonService {
11
+ #private;
12
+ readonly id = "backup-monitor";
13
+ constructor(lc: LogContext, watermarks: Source<BackedUpWatermark>, changeStreamer: ChangeStreamerService, replicaFile: string);
14
+ firstBackupReceived(): Promise<void>;
15
+ run(): Promise<void>;
16
+ stop(): Promise<void>;
7
17
  }
8
18
  //# sourceMappingURL=backup-monitor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"backup-monitor.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/backup-monitor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,eAAe,CAAC;AAEnD,MAAM,WAAW,aAAc,SAAQ,gBAAgB;IACrD,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAAC,eAAe,CAAC,CAAC;IACxE,cAAc,CACZ,MAAM,EAAE,MAAM,EACd,kBAAkB,CAAC,EAAE,OAAO,GAAG,SAAS,GACvC,IAAI,CAAC;CACT"}
1
+ {"version":3,"file":"backup-monitor.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/backup-monitor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAKjD,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,wBAAwB,CAAC;AACnD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,sBAAsB,CAAC;AAEhE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,qBAAa,aAAc,YAAW,gBAAgB;;IACpD,QAAQ,CAAC,EAAE,oBAAoB;gBAU7B,EAAE,EAAE,UAAU,EACd,UAAU,EAAE,MAAM,CAAC,iBAAiB,CAAC,EACrC,cAAc,EAAE,qBAAqB,EACrC,WAAW,EAAE,MAAM;IAQrB,mBAAmB;IAIb,GAAG;IAeT,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAkCtB"}
@@ -0,0 +1,66 @@
1
+ import { promiseVoid } from "../../../../shared/src/resolved-promises.js";
2
+ import { Database } from "../../../../zqlite/src/db.js";
3
+ import { getOrCreateGauge } from "../../observability/metrics.js";
4
+ import { resolver } from "@rocicorp/resolver";
5
+ //#region ../zero-cache/src/services/change-streamer/backup-monitor.ts
6
+ var BackupMonitor = class {
7
+ id = "backup-monitor";
8
+ #lc;
9
+ #watermarks;
10
+ #changeStreamer;
11
+ #replicaFile;
12
+ #firstBackupReceived = resolver();
13
+ #latestBackup;
14
+ constructor(lc, watermarks, changeStreamer, replicaFile) {
15
+ this.#lc = lc;
16
+ this.#watermarks = watermarks;
17
+ this.#changeStreamer = changeStreamer;
18
+ this.#replicaFile = replicaFile;
19
+ }
20
+ firstBackupReceived() {
21
+ return this.#firstBackupReceived.promise;
22
+ }
23
+ async run() {
24
+ this.#lc.info?.("starting backup monitor");
25
+ this.#initBackupLagMetric();
26
+ for await (const backedUp of this.#watermarks) {
27
+ if (!this.#latestBackup) {
28
+ this.#lc.info?.(`received first backup up watermark`, { backedUp });
29
+ this.#firstBackupReceived.resolve();
30
+ }
31
+ this.#latestBackup = backedUp;
32
+ this.#changeStreamer.trackBackupWatermark(backedUp.watermark);
33
+ }
34
+ this.#lc.info?.("watermark stream closed. BackupMonitor stopped.");
35
+ }
36
+ stop() {
37
+ this.#watermarks.cancel();
38
+ return promiseVoid;
39
+ }
40
+ #initBackupLagMetric() {
41
+ getOrCreateGauge("replica", "backup_lag", {
42
+ description: "Latency from when a change is written to the replica to when it is backed up to litestream. It is expected to create a saw pattern from 0 to the configured ZERO_LITESTREAM_INCREMENTAL_BACKUP_INTERVAL_MINUTES.",
43
+ unit: "millisecond"
44
+ }).addCallback((o) => {
45
+ const latestBackup = this.#latestBackup;
46
+ if (!latestBackup) {
47
+ this.#lc.warn?.(`no backed up watermarks. unable to report replica.backup_lag`);
48
+ return;
49
+ }
50
+ const db = new Database(this.#lc, this.#replicaFile, { readonly: true });
51
+ try {
52
+ const { writeTimeMs } = db.prepare(`SELECT writeTimeMs FROM "_zero.replicationState"`).get();
53
+ const backupLag = Math.max(0, writeTimeMs - latestBackup.backupTimeMs);
54
+ o.observe(backupLag);
55
+ } catch (e) {
56
+ this.#lc.warn?.(`error measuring replica.backup_lag metric`, e);
57
+ } finally {
58
+ db.close();
59
+ }
60
+ });
61
+ }
62
+ };
63
+ //#endregion
64
+ export { BackupMonitor };
65
+
66
+ //# sourceMappingURL=backup-monitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup-monitor.js","names":["#lc","#watermarks","#changeStreamer","#replicaFile","#firstBackupReceived","#initBackupLagMetric","#latestBackup"],"sources":["../../../../../../zero-cache/src/services/change-streamer/backup-monitor.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {promiseVoid} from '../../../../shared/src/resolved-promises.ts';\nimport {Database} from '../../../../zqlite/src/db.ts';\nimport {getOrCreateGauge} from '../../observability/metrics.ts';\nimport type {Source} from '../../types/streams.ts';\nimport type {SingletonService} from '../service.ts';\nimport type {ChangeStreamerService} from './change-streamer.ts';\n\nexport type BackedUpWatermark = {\n watermark: string;\n writeTimeMs?: number | undefined; // optional for debuggin; available on v5\n backupTimeMs: number;\n};\n\nexport class BackupMonitor implements SingletonService {\n readonly id = 'backup-monitor';\n readonly #lc: LogContext;\n readonly #watermarks: Source<BackedUpWatermark>;\n readonly #changeStreamer: ChangeStreamerService;\n readonly #replicaFile: string;\n readonly #firstBackupReceived = resolver();\n\n #latestBackup: BackedUpWatermark | undefined;\n\n constructor(\n lc: LogContext,\n watermarks: Source<BackedUpWatermark>,\n changeStreamer: ChangeStreamerService,\n replicaFile: string,\n ) {\n this.#lc = lc;\n this.#watermarks = watermarks;\n this.#changeStreamer = changeStreamer;\n this.#replicaFile = replicaFile;\n }\n\n firstBackupReceived() {\n return this.#firstBackupReceived.promise;\n }\n\n async run() {\n this.#lc.info?.('starting backup monitor');\n this.#initBackupLagMetric();\n\n for await (const backedUp of this.#watermarks) {\n if (!this.#latestBackup) {\n this.#lc.info?.(`received first backup up watermark`, {backedUp});\n this.#firstBackupReceived.resolve();\n }\n this.#latestBackup = backedUp;\n this.#changeStreamer.trackBackupWatermark(backedUp.watermark);\n }\n this.#lc.info?.('watermark stream closed. BackupMonitor stopped.');\n }\n\n stop(): Promise<void> {\n this.#watermarks.cancel();\n return promiseVoid;\n }\n\n #initBackupLagMetric() {\n getOrCreateGauge('replica', 'backup_lag', {\n description:\n 'Latency from when a change is written to the replica ' +\n 'to when it is backed up to litestream. It is expected to create a saw ' +\n 'pattern from 0 to the configured ZERO_LITESTREAM_INCREMENTAL_BACKUP_INTERVAL_MINUTES.',\n unit: 'millisecond',\n }).addCallback(o => {\n const latestBackup = this.#latestBackup;\n if (!latestBackup) {\n this.#lc.warn?.(\n `no backed up watermarks. unable to report replica.backup_lag`,\n );\n return;\n }\n const db = new Database(this.#lc, this.#replicaFile, {readonly: true});\n try {\n const {writeTimeMs} = db\n .prepare(/*sql*/ `SELECT writeTimeMs FROM \"_zero.replicationState\"`)\n .get<{writeTimeMs: number}>();\n const backupLag = Math.max(0, writeTimeMs - latestBackup.backupTimeMs);\n o.observe(backupLag);\n } catch (e) {\n this.#lc.warn?.(`error measuring replica.backup_lag metric`, e);\n } finally {\n db.close();\n }\n });\n }\n}\n"],"mappings":";;;;;AAeA,IAAa,gBAAb,MAAuD;CACrD,KAAc;CACd;CACA;CACA;CACA;CACA,uBAAgC,SAAS;CAEzC;CAEA,YACE,IACA,YACA,gBACA,aACA;EACA,KAAKA,MAAM;EACX,KAAKC,cAAc;EACnB,KAAKC,kBAAkB;EACvB,KAAKC,eAAe;CACtB;CAEA,sBAAsB;EACpB,OAAO,KAAKC,qBAAqB;CACnC;CAEA,MAAM,MAAM;EACV,KAAKJ,IAAI,OAAO,yBAAyB;EACzC,KAAKK,qBAAqB;EAE1B,WAAW,MAAM,YAAY,KAAKJ,aAAa;GAC7C,IAAI,CAAC,KAAKK,eAAe;IACvB,KAAKN,IAAI,OAAO,sCAAsC,EAAC,SAAQ,CAAC;IAChE,KAAKI,qBAAqB,QAAQ;GACpC;GACA,KAAKE,gBAAgB;GACrB,KAAKJ,gBAAgB,qBAAqB,SAAS,SAAS;EAC9D;EACA,KAAKF,IAAI,OAAO,iDAAiD;CACnE;CAEA,OAAsB;EACpB,KAAKC,YAAY,OAAO;EACxB,OAAO;CACT;CAEA,uBAAuB;EACrB,iBAAiB,WAAW,cAAc;GACxC,aACE;GAGF,MAAM;EACR,CAAC,EAAE,aAAY,MAAK;GAClB,MAAM,eAAe,KAAKK;GAC1B,IAAI,CAAC,cAAc;IACjB,KAAKN,IAAI,OACP,8DACF;IACA;GACF;GACA,MAAM,KAAK,IAAI,SAAS,KAAKA,KAAK,KAAKG,cAAc,EAAC,UAAU,KAAI,CAAC;GACrE,IAAI;IACF,MAAM,EAAC,gBAAe,GACnB,QAAgB,kDAAkD,EAClE,IAA2B;IAC9B,MAAM,YAAY,KAAK,IAAI,GAAG,cAAc,aAAa,YAAY;IACrE,EAAE,QAAQ,SAAS;GACrB,SAAS,GAAG;IACV,KAAKH,IAAI,OAAO,6CAA6C,CAAC;GAChE,UAAU;IACR,GAAG,MAAM;GACX;EACF,CAAC;CACH;AACF"}
@@ -1,17 +1,76 @@
1
1
  import type { LogContext } from '@rocicorp/logger';
2
- import type { WatermarkedChange } from './change-streamer-service.ts';
2
+ import type { WatermarkedChange } from './change-streamer.ts';
3
3
  import type { Subscriber } from './subscriber.ts';
4
4
  export type BroadcastReleaseMode = 'all-subscribers' | 'consensus-timeout';
5
+ /**
6
+ * Enables event-driven early release of a {@link Broadcast}. When provided and a
7
+ * majority has acked, the broadcast schedules its own consensus-timeout release
8
+ * proportional to how long the majority took to ack, instead of relying on the
9
+ * Forwarder's periodic progress tick.
10
+ */
11
+ export type EarlyReleaseOptions = {
12
+ /** Time to wait, proportional to majority completion, before releasing. */
13
+ consensusTimeoutProportion: number;
14
+ /** Injectable timer, for deterministic testing. */
15
+ setTimeoutFn?: typeof setTimeout;
16
+ /** Injectable timer cancellation, for deterministic testing. */
17
+ clearTimeoutFn?: typeof clearTimeout;
18
+ };
5
19
  /**
6
20
  * Initiates and tracks the progress of a change broadcasted to
7
- * a set of subscribers.
21
+ * a set of subscribers, implementing **consensus-based timeouts**
22
+ * to effect short-term graceful degradation when a minority of
23
+ * subscribers are behind.
24
+ *
25
+ * ### Background
26
+ *
27
+ * The purpose of flow control is to pull upstream replication changes
28
+ * no faster than the rate they are processed by downstream subscribers
29
+ * in the steady state. In the change-streamer, this is done by occasionally
30
+ * waiting for ACKs from subscribers before continuing; without doing so,
31
+ * I/O buffers fill up and cause the system to spend most of its time in GC.
32
+ *
33
+ * However, the naive algorithm of always waiting for all subscribers (e.g.
34
+ * `Promise.all()`) can behave poorly in scenarios where subscribers
35
+ * are imbalanced:
36
+ * * New subscribers may have a backlog of changes to catch up with.
37
+ * Having all subscribers wait for the new subscriber to catch up results
38
+ * in delaying the entire application.
39
+ * * Broken TCP connections similarly require all subscribers to wait until
40
+ * connection liveness checks kick in and disconnect the subscriber.
8
41
  *
9
- * Creating a `Broadcast` automatically initiates the send.
42
+ * A simplistic approach is to add a limit to the amount of time waiting for
43
+ * subscribers, i.e. an ack timeout. However, deciding what this timeout
44
+ * should be is non-trivial because of the heterogeneous nature of changes;
45
+ * while most changes operate on single rows and are relatively predictable
46
+ * in terms of running time, some changes are table-wide operations and can
47
+ * legitimately take an arbitrary amount of time. In such scenarios, a
48
+ * timeout that is too short can stop progress on replication altogether.
10
49
  *
11
- * By default, {@link Broadcast.done} resolves when all subscribers
12
- * have acked the change. However, {@link Broadcast.checkProgress()}
13
- * can be called to resolve the broadcast earlier based on the flow
14
- * control policy.
50
+ * ### Consensus-based Timeout Algorithm
51
+ *
52
+ * To address these shortcomings, a "consensus-based timeout" algorithm is
53
+ * used:
54
+ * * Wait for more than half of the subscribers to finish. (In
55
+ * case of a single node, or the case of one replication-manager
56
+ * and one view-syncer, this reduces to waiting for all subscribers.)
57
+ * * Once more than half of the subscribers have finished, proceed after
58
+ * the configured timeout, proportional to the time it took for the
59
+ * majority of subscribers to ack, even if not all subscribers have
60
+ * finished.
61
+ *
62
+ * In other words, the subscribers themselves are used to determine the
63
+ * timeout of each batch of changes; the majority determines this when
64
+ * they complete, upon which a timeout is logically started.
65
+ *
66
+ * In the common case, the remaining subscribers finish soon afterward and
67
+ * the timeout never elapses. However, in pathological cases where a minority
68
+ * of subscribers have a disproportionate amount of load, some will still
69
+ * be processing (or otherwise unresponsive). These subscribers are given
70
+ * a bounded amount of time to catch up at each flushed batch, up to the
71
+ * timeout interval. This guarantees eventual catchup because the
72
+ * subscribers with a backlog of changes necessarily have a higher
73
+ * processing rate than the subscribers that finished (and are made to wait).
15
74
  */
16
75
  export declare class Broadcast {
17
76
  #private;
@@ -24,79 +83,10 @@ export declare class Broadcast {
24
83
  * Broadcasts the `change` to the `subscribers` and tracks their
25
84
  * completion.
26
85
  */
27
- constructor(subscribers: Iterable<Subscriber>, change: WatermarkedChange);
86
+ constructor(lc: LogContext, subscribers: Iterable<Subscriber>, change: WatermarkedChange, earlyRelease?: EarlyReleaseOptions);
28
87
  get isDone(): boolean;
29
88
  get done(): Promise<void>;
30
89
  get releaseMode(): BroadcastReleaseMode;
31
- /**
32
- * Checks for pathological situations in which flow should be reenabled
33
- * before all subscribers have acked.
34
- *
35
- * ### Background
36
- *
37
- * The purpose of flow control is to pull upstream replication changes
38
- * no faster than the rate as they are processed by downstream subscribers
39
- * in the steady state. In the change-streamer, this is done by occasionally
40
- * waiting for ACKs from subscribers before continuing; without doing so,
41
- * I/O buffers fill up and cause the system to spend most of its time in GC.
42
- *
43
- * However, the naive algorithm of always waiting for all subscribers (e.g.
44
- * `Promise.all()`) can behave poorly in scenarios where subscribers
45
- * are imbalanced:
46
- * * New subscribers may have a backlog of changes to catch up with.
47
- * Having all subscribers wait for the new subscriber to catch up results
48
- * in delaying the entire application.
49
- * * Broken TCP connections similarly require all subscribers to wait until
50
- * connection liveness checks kick in and disconnect the subscriber.
51
- *
52
- * A simplistic approach is to add a limit to the amount of time waiting for
53
- * subscribers, i.e. an ack timeout. However, deciding what this timeout
54
- * should be is non-trivial because of the heterogeneous nature of changes;
55
- * while most changes operate on single rows and are relatively predictable
56
- * in terms of running time, some changes are table-wide operations and can
57
- * legitimately take an arbitrary amount of time. In such scenarios, a
58
- * timeout that is too short can stop progress on replication altogether.
59
- *
60
- * ### Consensus-based Timeout Algorithm
61
- *
62
- * To address these shortcomings, a "consensus-based timeout" algorithm is
63
- * used:
64
- * * Wait for more than half of the subscribers to finish. (In
65
- * case of a single node, or the case of one replication-manager
66
- * and one view-syncer, this reduces to waiting for all subscribers.)
67
- * * Once more than half of the subscribers have finished, proceed after
68
- * a fixed timeout elapses (e.g. 1 second), even if not all subscribers
69
- * have finished.
70
- *
71
- * In other words, the subscribers themselves are used to determine the
72
- * timeout of each batch of changes; the majority determines this when
73
- * they complete, upon which a timeout is logically started.
74
- *
75
- * In the common case, the remaining subscribers finish soon afterward and
76
- * the timeout never elapses. However, in pathological cases where a minority
77
- * of subscribers have a disproportionate amount of load, some will still
78
- * be processing (or otherwise unresponsive). These subscribers are given
79
- * a bounded amount of time to catch up at each flushed batch, up to the
80
- * timeout interval. This guarantees eventual catchup because the
81
- * subscribers with a backlog of changes necessarily have a higher
82
- * processing rate than the subscribers that finished (and are made to wait).
83
- *
84
- * ### Not implemented: Broken connection detection
85
- *
86
- * If a subscriber has not made progress for a certain interval, the
87
- * algorithm could theoretically drop it preemptively, supplementing the
88
- * existing websocket-level liveness checks.
89
- *
90
- * However, a more reliable approach would be to change the replicator
91
- * to use non-blocking writes, and subsequently increase the frequency of
92
- * connection-level liveness checks. The current synchronous replica writes
93
- * can delay both ping responsiveness and change progress arbitrarily (e.g.
94
- * a large index creation); an independently liveness check that is not
95
- * delayed by synchronous writes on the subscriber would be a more failsafe
96
- * solution.
97
- *
98
- * @returns `true` if the broadcast was already done or was marked done.
99
- */
100
- checkProgress(lc: LogContext, flowControlConsensusPaddingMs: number, now: number): boolean;
90
+ logSlowRequests(now: number): void;
101
91
  }
102
92
  //# sourceMappingURL=broadcast.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"broadcast.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/broadcast.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,8BAA8B,CAAC;AACpE,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,iBAAiB,CAAC;AAEhD,MAAM,MAAM,oBAAoB,GAAG,iBAAiB,GAAG,mBAAmB,CAAC;AAE3E;;;;;;;;;;GAUG;AACH,qBAAa,SAAS;;IACpB;;;OAGG;IACH,MAAM,CAAC,eAAe,CACpB,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,EACjC,MAAM,EAAE,iBAAiB;IAmB3B;;;OAGG;gBACS,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,iBAAiB;IAsCxE,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,IAAI,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAExB;IAED,IAAI,WAAW,IAAI,oBAAoB,CAEtC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAoEG;IACH,aAAa,CACX,EAAE,EAAE,UAAU,EACd,6BAA6B,EAAE,MAAM,EACrC,GAAG,EAAE,MAAM;CAkDd"}
1
+ {"version":3,"file":"broadcast.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/broadcast.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,iBAAiB,CAAC;AAEhD,MAAM,MAAM,oBAAoB,GAAG,iBAAiB,GAAG,mBAAmB,CAAC;AAE3E;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,2EAA2E;IAC3E,0BAA0B,EAAE,MAAM,CAAC;IACnC,mDAAmD;IACnD,YAAY,CAAC,EAAE,OAAO,UAAU,CAAC;IACjC,gEAAgE;IAChE,cAAc,CAAC,EAAE,OAAO,YAAY,CAAC;CACtC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,qBAAa,SAAS;;IACpB;;;OAGG;IACH,MAAM,CAAC,eAAe,CACpB,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,EACjC,MAAM,EAAE,iBAAiB;IA0B3B;;;OAGG;gBAED,EAAE,EAAE,UAAU,EACd,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,EACjC,MAAM,EAAE,iBAAiB,EACzB,YAAY,CAAC,EAAE,mBAAmB;IA4EpC,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,IAAI,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAExB;IAED,IAAI,WAAW,IAAI,oBAAoB,CAEtC;IAED,eAAe,CAAC,GAAG,EAAE,MAAM;CA8B5B"}
@@ -2,14 +2,59 @@ import { resolver } from "@rocicorp/resolver";
2
2
  //#region ../zero-cache/src/services/change-streamer/broadcast.ts
3
3
  /**
4
4
  * Initiates and tracks the progress of a change broadcasted to
5
- * a set of subscribers.
5
+ * a set of subscribers, implementing **consensus-based timeouts**
6
+ * to effect short-term graceful degradation when a minority of
7
+ * subscribers are behind.
6
8
  *
7
- * Creating a `Broadcast` automatically initiates the send.
9
+ * ### Background
8
10
  *
9
- * By default, {@link Broadcast.done} resolves when all subscribers
10
- * have acked the change. However, {@link Broadcast.checkProgress()}
11
- * can be called to resolve the broadcast earlier based on the flow
12
- * control policy.
11
+ * The purpose of flow control is to pull upstream replication changes
12
+ * no faster than the rate they are processed by downstream subscribers
13
+ * in the steady state. In the change-streamer, this is done by occasionally
14
+ * waiting for ACKs from subscribers before continuing; without doing so,
15
+ * I/O buffers fill up and cause the system to spend most of its time in GC.
16
+ *
17
+ * However, the naive algorithm of always waiting for all subscribers (e.g.
18
+ * `Promise.all()`) can behave poorly in scenarios where subscribers
19
+ * are imbalanced:
20
+ * * New subscribers may have a backlog of changes to catch up with.
21
+ * Having all subscribers wait for the new subscriber to catch up results
22
+ * in delaying the entire application.
23
+ * * Broken TCP connections similarly require all subscribers to wait until
24
+ * connection liveness checks kick in and disconnect the subscriber.
25
+ *
26
+ * A simplistic approach is to add a limit to the amount of time waiting for
27
+ * subscribers, i.e. an ack timeout. However, deciding what this timeout
28
+ * should be is non-trivial because of the heterogeneous nature of changes;
29
+ * while most changes operate on single rows and are relatively predictable
30
+ * in terms of running time, some changes are table-wide operations and can
31
+ * legitimately take an arbitrary amount of time. In such scenarios, a
32
+ * timeout that is too short can stop progress on replication altogether.
33
+ *
34
+ * ### Consensus-based Timeout Algorithm
35
+ *
36
+ * To address these shortcomings, a "consensus-based timeout" algorithm is
37
+ * used:
38
+ * * Wait for more than half of the subscribers to finish. (In
39
+ * case of a single node, or the case of one replication-manager
40
+ * and one view-syncer, this reduces to waiting for all subscribers.)
41
+ * * Once more than half of the subscribers have finished, proceed after
42
+ * the configured timeout, proportional to the time it took for the
43
+ * majority of subscribers to ack, even if not all subscribers have
44
+ * finished.
45
+ *
46
+ * In other words, the subscribers themselves are used to determine the
47
+ * timeout of each batch of changes; the majority determines this when
48
+ * they complete, upon which a timeout is logically started.
49
+ *
50
+ * In the common case, the remaining subscribers finish soon afterward and
51
+ * the timeout never elapses. However, in pathological cases where a minority
52
+ * of subscribers have a disproportionate amount of load, some will still
53
+ * be processing (or otherwise unresponsive). These subscribers are given
54
+ * a bounded amount of time to catch up at each flushed batch, up to the
55
+ * timeout interval. This guarantees eventual catchup because the
56
+ * subscribers with a backlog of changes necessarily have a higher
57
+ * processing rate than the subscribers that finished (and are made to wait).
13
58
  */
14
59
  var Broadcast = class {
15
60
  /**
@@ -19,6 +64,7 @@ var Broadcast = class {
19
64
  static withoutTracking(subscribers, change) {
20
65
  for (const sub of subscribers) sub.send(change);
21
66
  }
67
+ #lc;
22
68
  #pending;
23
69
  #completed;
24
70
  #done = resolver();
@@ -27,16 +73,23 @@ var Broadcast = class {
27
73
  #watermark;
28
74
  #majority;
29
75
  #start = performance.now();
30
- #latestCompleted = Number.MAX_VALUE;
76
+ #earlyReleaseTimeoutProportion;
77
+ #setTimeout;
78
+ #clearTimeout;
79
+ #earlyReleaseTimer;
31
80
  /**
32
81
  * Broadcasts the `change` to the `subscribers` and tracks their
33
82
  * completion.
34
83
  */
35
- constructor(subscribers, change) {
84
+ constructor(lc, subscribers, change, earlyRelease) {
85
+ this.#lc = lc;
36
86
  this.#pending = new Set(subscribers);
37
87
  this.#completed = [];
38
88
  this.#watermark = change[0];
39
89
  this.#majority = Math.floor(this.#pending.size / 2) + 1;
90
+ this.#earlyReleaseTimeoutProportion = earlyRelease?.consensusTimeoutProportion;
91
+ this.#setTimeout = earlyRelease?.setTimeoutFn ?? setTimeout;
92
+ this.#clearTimeout = earlyRelease?.clearTimeoutFn ?? clearTimeout;
40
93
  for (const sub of this.#pending) {
41
94
  const changes = sub.numPending + 1;
42
95
  sub.send(change).catch(() => {}).finally(() => this.#markCompleted(sub, changes));
@@ -44,19 +97,35 @@ var Broadcast = class {
44
97
  if (this.#pending.size === 0) this.#setDone("all-subscribers");
45
98
  }
46
99
  #markCompleted(sub, changes) {
47
- const elapsed = (this.#latestCompleted = performance.now()) - this.#start;
100
+ if (this.#isDone) return;
101
+ const elapsed = performance.now() - this.#start;
102
+ sub.trackResponseResult("on-time");
48
103
  this.#completed.push({
49
104
  sub,
50
105
  changes,
51
106
  elapsed
52
107
  });
53
108
  this.#pending.delete(sub);
54
- if (this.#pending.size === 0) this.#setDone("all-subscribers");
109
+ if (this.#pending.size === 0) {
110
+ this.#setDone("all-subscribers");
111
+ return;
112
+ }
113
+ if (this.#earlyReleaseTimeoutProportion !== void 0 && this.#earlyReleaseTimer === void 0 && this.#completed.length === this.#majority) {
114
+ const timeoutMs = Math.ceil(elapsed * this.#earlyReleaseTimeoutProportion);
115
+ this.#earlyReleaseTimer = this.#setTimeout(() => this.#setDone("consensus-timeout"), timeoutMs);
116
+ }
55
117
  }
56
118
  #setDone(releaseMode) {
57
119
  if (this.#isDone) return;
120
+ if (releaseMode === "consensus-timeout") {
121
+ const elapsed = performance.now() - this.#start;
122
+ this.#logWithState(`continuing with ${this.#pending.size} subscriber(s) still pending`, elapsed);
123
+ }
124
+ this.#pending.forEach((sub) => sub.trackResponseResult("timed-out"));
58
125
  this.#isDone = true;
59
126
  this.#releaseMode = releaseMode;
127
+ this.#clearTimeout(this.#earlyReleaseTimer);
128
+ this.#earlyReleaseTimer = void 0;
60
129
  this.#done.resolve();
61
130
  }
62
131
  get isDone() {
@@ -68,92 +137,15 @@ var Broadcast = class {
68
137
  get releaseMode() {
69
138
  return this.#releaseMode ?? "all-subscribers";
70
139
  }
71
- /**
72
- * Checks for pathological situations in which flow should be reenabled
73
- * before all subscribers have acked.
74
- *
75
- * ### Background
76
- *
77
- * The purpose of flow control is to pull upstream replication changes
78
- * no faster than the rate as they are processed by downstream subscribers
79
- * in the steady state. In the change-streamer, this is done by occasionally
80
- * waiting for ACKs from subscribers before continuing; without doing so,
81
- * I/O buffers fill up and cause the system to spend most of its time in GC.
82
- *
83
- * However, the naive algorithm of always waiting for all subscribers (e.g.
84
- * `Promise.all()`) can behave poorly in scenarios where subscribers
85
- * are imbalanced:
86
- * * New subscribers may have a backlog of changes to catch up with.
87
- * Having all subscribers wait for the new subscriber to catch up results
88
- * in delaying the entire application.
89
- * * Broken TCP connections similarly require all subscribers to wait until
90
- * connection liveness checks kick in and disconnect the subscriber.
91
- *
92
- * A simplistic approach is to add a limit to the amount of time waiting for
93
- * subscribers, i.e. an ack timeout. However, deciding what this timeout
94
- * should be is non-trivial because of the heterogeneous nature of changes;
95
- * while most changes operate on single rows and are relatively predictable
96
- * in terms of running time, some changes are table-wide operations and can
97
- * legitimately take an arbitrary amount of time. In such scenarios, a
98
- * timeout that is too short can stop progress on replication altogether.
99
- *
100
- * ### Consensus-based Timeout Algorithm
101
- *
102
- * To address these shortcomings, a "consensus-based timeout" algorithm is
103
- * used:
104
- * * Wait for more than half of the subscribers to finish. (In
105
- * case of a single node, or the case of one replication-manager
106
- * and one view-syncer, this reduces to waiting for all subscribers.)
107
- * * Once more than half of the subscribers have finished, proceed after
108
- * a fixed timeout elapses (e.g. 1 second), even if not all subscribers
109
- * have finished.
110
- *
111
- * In other words, the subscribers themselves are used to determine the
112
- * timeout of each batch of changes; the majority determines this when
113
- * they complete, upon which a timeout is logically started.
114
- *
115
- * In the common case, the remaining subscribers finish soon afterward and
116
- * the timeout never elapses. However, in pathological cases where a minority
117
- * of subscribers have a disproportionate amount of load, some will still
118
- * be processing (or otherwise unresponsive). These subscribers are given
119
- * a bounded amount of time to catch up at each flushed batch, up to the
120
- * timeout interval. This guarantees eventual catchup because the
121
- * subscribers with a backlog of changes necessarily have a higher
122
- * processing rate than the subscribers that finished (and are made to wait).
123
- *
124
- * ### Not implemented: Broken connection detection
125
- *
126
- * If a subscriber has not made progress for a certain interval, the
127
- * algorithm could theoretically drop it preemptively, supplementing the
128
- * existing websocket-level liveness checks.
129
- *
130
- * However, a more reliable approach would be to change the replicator
131
- * to use non-blocking writes, and subsequently increase the frequency of
132
- * connection-level liveness checks. The current synchronous replica writes
133
- * can delay both ping responsiveness and change progress arbitrarily (e.g.
134
- * a large index creation); an independently liveness check that is not
135
- * delayed by synchronous writes on the subscriber would be a more failsafe
136
- * solution.
137
- *
138
- * @returns `true` if the broadcast was already done or was marked done.
139
- */
140
- checkProgress(lc, flowControlConsensusPaddingMs, now) {
141
- if (this.#isDone) return true;
142
- if (this.#pending.size === 0) return true;
140
+ logSlowRequests(now) {
141
+ if (this.#isDone || this.#pending.size === 0) return;
143
142
  const elapsed = now - this.#start;
144
143
  if (this.#completed.length < this.#majority) {
145
- if (elapsed >= 1e3) this.#logWithState(lc, `waiting for at least ${this.#majority} subscribers to finish`, elapsed);
146
- return false;
147
- }
148
- if (now - this.#latestCompleted >= flowControlConsensusPaddingMs) {
149
- this.#logWithState(lc, `continuing with ${this.#pending.size} subscriber(s) still pending`, elapsed);
150
- this.#setDone("consensus-timeout");
151
- return true;
144
+ if (elapsed >= 1e3) this.#logWithState(`waiting for at least ${this.#majority} subscribers to finish`, elapsed);
152
145
  }
153
- return false;
154
146
  }
155
- #logWithState(lc, msg, elapsed) {
156
- lc.withContext("watermark", this.#watermark).info?.(`${msg} (${elapsed.toFixed(3)} ms)`, {
147
+ #logWithState(msg, elapsed) {
148
+ this.#lc.withContext("watermark", this.#watermark).info?.(`${msg} (${elapsed.toFixed(3)} ms)`, {
157
149
  completed: this.#completed.map((d) => ({
158
150
  id: d.sub.id,
159
151
  processed: d.changes,