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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (484) hide show
  1. package/out/replicache/src/db/commit.js +1 -4
  2. package/out/replicache/src/db/commit.js.map +1 -1
  3. package/out/replicache/src/db/read.d.ts +1 -0
  4. package/out/replicache/src/db/read.d.ts.map +1 -1
  5. package/out/replicache/src/db/read.js +1 -4
  6. package/out/replicache/src/db/read.js.map +1 -1
  7. package/out/replicache/src/db/write.d.ts +2 -1
  8. package/out/replicache/src/db/write.d.ts.map +1 -1
  9. package/out/replicache/src/db/write.js +4 -51
  10. package/out/replicache/src/db/write.js.map +1 -1
  11. package/out/replicache/src/persist/clients.d.ts +1 -0
  12. package/out/replicache/src/persist/clients.d.ts.map +1 -1
  13. package/out/replicache/src/persist/clients.js +4 -31
  14. package/out/replicache/src/persist/clients.js.map +1 -1
  15. package/out/replicache/src/process-env.d.ts +28 -0
  16. package/out/replicache/src/process-env.d.ts.map +1 -0
  17. package/out/replicache/src/replicache-impl.d.ts.map +1 -1
  18. package/out/replicache/src/replicache-impl.js.map +1 -1
  19. package/out/replicache/src/scan-iterator.js +2 -14
  20. package/out/replicache/src/scan-iterator.js.map +1 -1
  21. package/out/replicache/src/subscriptions.d.ts +1 -0
  22. package/out/replicache/src/subscriptions.d.ts.map +1 -1
  23. package/out/replicache/src/subscriptions.js +2 -10
  24. package/out/replicache/src/subscriptions.js.map +1 -1
  25. package/out/replicache/src/transactions.d.ts +1 -0
  26. package/out/replicache/src/transactions.d.ts.map +1 -1
  27. package/out/replicache/src/transactions.js +2 -4
  28. package/out/replicache/src/transactions.js.map +1 -1
  29. package/out/shared/src/promise-race.d.ts +14 -0
  30. package/out/shared/src/promise-race.d.ts.map +1 -1
  31. package/out/shared/src/promise-race.js +32 -1
  32. package/out/shared/src/promise-race.js.map +1 -1
  33. package/out/z2s/src/compiler.d.ts.map +1 -1
  34. package/out/z2s/src/compiler.js +1 -22
  35. package/out/z2s/src/compiler.js.map +1 -1
  36. package/out/zero/package.js +3 -3
  37. package/out/zero/package.js.map +1 -1
  38. package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
  39. package/out/zero-cache/src/auth/write-authorizer.js +8 -1
  40. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  41. package/out/zero-cache/src/config/normalize.d.ts +6 -0
  42. package/out/zero-cache/src/config/normalize.d.ts.map +1 -1
  43. package/out/zero-cache/src/config/normalize.js +24 -2
  44. package/out/zero-cache/src/config/normalize.js.map +1 -1
  45. package/out/zero-cache/src/config/zero-config.d.ts +65 -4
  46. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  47. package/out/zero-cache/src/config/zero-config.js +202 -35
  48. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  49. package/out/zero-cache/src/custom/fetch.js +1 -1
  50. package/out/zero-cache/src/custom/fetch.js.map +1 -1
  51. package/out/zero-cache/src/db/delete-lite-db.js +2 -1
  52. package/out/zero-cache/src/db/delete-lite-db.js.map +1 -1
  53. package/out/zero-cache/src/db/migration-lite.d.ts.map +1 -1
  54. package/out/zero-cache/src/db/migration-lite.js +10 -5
  55. package/out/zero-cache/src/db/migration-lite.js.map +1 -1
  56. package/out/zero-cache/src/db/migration.d.ts.map +1 -1
  57. package/out/zero-cache/src/db/migration.js +4 -2
  58. package/out/zero-cache/src/db/migration.js.map +1 -1
  59. package/out/zero-cache/src/db/sqlite-corruption.d.ts +3 -2
  60. package/out/zero-cache/src/db/sqlite-corruption.d.ts.map +1 -1
  61. package/out/zero-cache/src/db/sqlite-corruption.js +13 -8
  62. package/out/zero-cache/src/db/sqlite-corruption.js.map +1 -1
  63. package/out/zero-cache/src/db/transaction-pool.d.ts +5 -15
  64. package/out/zero-cache/src/db/transaction-pool.d.ts.map +1 -1
  65. package/out/zero-cache/src/db/transaction-pool.js +8 -72
  66. package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
  67. package/out/zero-cache/src/observability/metrics.d.ts +6 -1
  68. package/out/zero-cache/src/observability/metrics.d.ts.map +1 -1
  69. package/out/zero-cache/src/observability/metrics.js +20 -2
  70. package/out/zero-cache/src/observability/metrics.js.map +1 -1
  71. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  72. package/out/zero-cache/src/server/change-streamer.js +105 -34
  73. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  74. package/out/zero-cache/src/server/main.d.ts.map +1 -1
  75. package/out/zero-cache/src/server/main.js +8 -18
  76. package/out/zero-cache/src/server/main.js.map +1 -1
  77. package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
  78. package/out/zero-cache/src/server/replicator.js +48 -5
  79. package/out/zero-cache/src/server/replicator.js.map +1 -1
  80. package/out/zero-cache/src/server/runner/run-worker.js +3 -2
  81. package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
  82. package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
  83. package/out/zero-cache/src/server/syncer.js +1 -1
  84. package/out/zero-cache/src/server/syncer.js.map +1 -1
  85. package/out/zero-cache/src/server/worker-urls.d.ts +0 -1
  86. package/out/zero-cache/src/server/worker-urls.d.ts.map +1 -1
  87. package/out/zero-cache/src/server/worker-urls.js +1 -2
  88. package/out/zero-cache/src/server/worker-urls.js.map +1 -1
  89. package/out/zero-cache/src/services/change-source/change-source.d.ts +4 -2
  90. package/out/zero-cache/src/services/change-source/change-source.d.ts.map +1 -1
  91. package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts +35 -0
  92. package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts.map +1 -0
  93. package/out/zero-cache/src/services/change-source/common/replica-restore.js +34 -0
  94. package/out/zero-cache/src/services/change-source/common/replica-restore.js.map +1 -0
  95. package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts +2 -0
  96. package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts.map +1 -1
  97. package/out/zero-cache/src/services/change-source/common/replica-schema.js +81 -3
  98. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  99. package/out/zero-cache/src/services/change-source/custom/change-source.d.ts +2 -6
  100. package/out/zero-cache/src/services/change-source/custom/change-source.d.ts.map +1 -1
  101. package/out/zero-cache/src/services/change-source/custom/change-source.js +14 -5
  102. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  103. package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts.map +1 -1
  104. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +22 -11
  105. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  106. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts +7 -7
  107. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  108. package/out/zero-cache/src/services/change-source/pg/change-source.js +90 -31
  109. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  110. package/out/zero-cache/src/services/change-source/pg/decommission.d.ts.map +1 -1
  111. package/out/zero-cache/src/services/change-source/pg/decommission.js +20 -11
  112. package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
  113. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +9 -1
  114. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  115. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +61 -76
  116. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  117. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts +65 -2
  118. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts.map +1 -1
  119. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +101 -10
  120. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
  121. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +27 -2
  122. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -1
  123. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +41 -8
  124. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  125. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts +0 -5
  126. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
  127. package/out/zero-cache/src/services/change-source/pg/schema/init.js +29 -41
  128. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  129. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +35 -16
  130. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
  131. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +77 -7
  132. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  133. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts +2 -1
  134. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts.map +1 -1
  135. package/out/zero-cache/src/services/change-source/protocol/current/data.js +5 -13
  136. package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
  137. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +6 -3
  138. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts.map +1 -1
  139. package/out/zero-cache/src/services/change-source/protocol/current/schema-change-tags.d.ts +6 -0
  140. package/out/zero-cache/src/services/change-source/protocol/current/schema-change-tags.d.ts.map +1 -0
  141. package/out/zero-cache/src/services/change-source/protocol/current/schema-change-tags.js +21 -0
  142. package/out/zero-cache/src/services/change-source/protocol/current/schema-change-tags.js.map +1 -0
  143. package/out/zero-cache/src/services/change-source/protocol/current/status.d.ts +1 -0
  144. package/out/zero-cache/src/services/change-source/protocol/current/status.d.ts.map +1 -1
  145. package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts +1 -0
  146. package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts.map +1 -1
  147. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts +3 -7
  148. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts.map +1 -1
  149. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js +26 -10
  150. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
  151. package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts +15 -5
  152. package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts.map +1 -1
  153. package/out/zero-cache/src/services/change-streamer/backup-monitor.js +71 -0
  154. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -0
  155. package/out/zero-cache/src/services/change-streamer/broadcast.d.ts +68 -78
  156. package/out/zero-cache/src/services/change-streamer/broadcast.d.ts.map +1 -1
  157. package/out/zero-cache/src/services/change-streamer/broadcast.js +84 -92
  158. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  159. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts +19 -0
  160. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts.map +1 -0
  161. package/out/zero-cache/src/services/change-streamer/change-log-codec.js +50 -0
  162. package/out/zero-cache/src/services/change-streamer/change-log-codec.js.map +1 -0
  163. package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.d.ts +34 -0
  164. package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.d.ts.map +1 -0
  165. package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.js +67 -0
  166. package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.js.map +1 -0
  167. package/out/zero-cache/src/services/change-streamer/change-log-initializer.d.ts +141 -0
  168. package/out/zero-cache/src/services/change-streamer/change-log-initializer.d.ts.map +1 -0
  169. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js +270 -0
  170. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js.map +1 -0
  171. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.d.ts +19 -0
  172. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.d.ts.map +1 -0
  173. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.js +39 -0
  174. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.js.map +1 -0
  175. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +5 -8
  176. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  177. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +20 -19
  178. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  179. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +90 -24
  180. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
  181. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +456 -66
  182. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  183. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +50 -13
  184. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
  185. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  186. package/out/zero-cache/src/services/change-streamer/error-type-enum.d.ts +8 -0
  187. package/out/zero-cache/src/services/change-streamer/error-type-enum.d.ts.map +1 -1
  188. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts +14 -3
  189. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts.map +1 -1
  190. package/out/zero-cache/src/services/change-streamer/forwarder.js +42 -12
  191. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  192. package/out/zero-cache/src/services/change-streamer/{litestream3-backup-monitor.d.ts → litestream3-prometheus-poller.d.ts} +19 -45
  193. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.d.ts.map +1 -0
  194. package/out/zero-cache/src/services/change-streamer/{litestream3-backup-monitor.js → litestream3-prometheus-poller.js} +68 -176
  195. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js.map +1 -0
  196. package/out/zero-cache/src/services/change-streamer/replica-poller.d.ts +15 -0
  197. package/out/zero-cache/src/services/change-streamer/replica-poller.d.ts.map +1 -0
  198. package/out/zero-cache/src/services/change-streamer/replica-poller.js +55 -0
  199. package/out/zero-cache/src/services/change-streamer/replica-poller.js.map +1 -0
  200. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts +5 -1
  201. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts.map +1 -1
  202. package/out/zero-cache/src/services/change-streamer/schema/tables.js +2 -1
  203. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  204. package/out/zero-cache/src/services/change-streamer/shard-sampling.d.ts +11 -0
  205. package/out/zero-cache/src/services/change-streamer/shard-sampling.d.ts.map +1 -0
  206. package/out/zero-cache/src/services/change-streamer/shard-sampling.js +19 -0
  207. package/out/zero-cache/src/services/change-streamer/shard-sampling.js.map +1 -0
  208. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts +24 -0
  209. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts.map +1 -0
  210. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js +119 -0
  211. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -0
  212. package/out/zero-cache/src/services/change-streamer/snapshot.d.ts +4 -1
  213. package/out/zero-cache/src/services/change-streamer/snapshot.d.ts.map +1 -1
  214. package/out/zero-cache/src/services/change-streamer/snapshot.js +39 -14
  215. package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
  216. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.d.ts +99 -0
  217. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.d.ts.map +1 -0
  218. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js +344 -0
  219. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js.map +1 -0
  220. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.d.ts +83 -0
  221. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.d.ts.map +1 -0
  222. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js +417 -0
  223. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js.map +1 -0
  224. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.d.ts +121 -0
  225. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.d.ts.map +1 -0
  226. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.js +391 -0
  227. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.js.map +1 -0
  228. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.d.ts +73 -0
  229. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.d.ts.map +1 -0
  230. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.js +157 -0
  231. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.js.map +1 -0
  232. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts +62 -0
  233. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts.map +1 -0
  234. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js +164 -0
  235. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js.map +1 -0
  236. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts +109 -0
  237. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts.map +1 -0
  238. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +258 -0
  239. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js.map +1 -0
  240. package/out/zero-cache/src/services/change-streamer/storer.d.ts +85 -12
  241. package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
  242. package/out/zero-cache/src/services/change-streamer/storer.js +286 -141
  243. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  244. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +61 -10
  245. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
  246. package/out/zero-cache/src/services/change-streamer/subscriber.js +101 -8
  247. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  248. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts +34 -0
  249. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts.map +1 -0
  250. package/out/zero-cache/src/services/change-streamer/upstream-acker.js +86 -0
  251. package/out/zero-cache/src/services/change-streamer/upstream-acker.js.map +1 -0
  252. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts +29 -0
  253. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts.map +1 -0
  254. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js +154 -0
  255. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js.map +1 -0
  256. package/out/zero-cache/src/services/http-service.d.ts +1 -0
  257. package/out/zero-cache/src/services/http-service.d.ts.map +1 -1
  258. package/out/zero-cache/src/services/http-service.js +7 -3
  259. package/out/zero-cache/src/services/http-service.js.map +1 -1
  260. package/out/zero-cache/src/services/life-cycle.d.ts +1 -1
  261. package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
  262. package/out/zero-cache/src/services/life-cycle.js +4 -3
  263. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  264. package/out/zero-cache/src/services/litestream/commands.d.ts +12 -11
  265. package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
  266. package/out/zero-cache/src/services/litestream/commands.js +115 -152
  267. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  268. package/out/zero-cache/src/services/litestream/config-v5.yml +42 -0
  269. package/out/zero-cache/src/services/litestream/config.yml +3 -2
  270. package/out/zero-cache/src/services/litestream/metrics.d.ts +5 -4
  271. package/out/zero-cache/src/services/litestream/metrics.d.ts.map +1 -1
  272. package/out/zero-cache/src/services/litestream/metrics.js +10 -10
  273. package/out/zero-cache/src/services/litestream/metrics.js.map +1 -1
  274. package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
  275. package/out/zero-cache/src/services/mutagen/mutagen.js +5 -2
  276. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  277. package/out/zero-cache/src/services/replicator/change-log-cookies.d.ts +169 -0
  278. package/out/zero-cache/src/services/replicator/change-log-cookies.d.ts.map +1 -0
  279. package/out/zero-cache/src/services/replicator/change-log-cookies.js +434 -0
  280. package/out/zero-cache/src/services/replicator/change-log-cookies.js.map +1 -0
  281. package/out/zero-cache/src/services/replicator/change-log-db.d.ts +368 -0
  282. package/out/zero-cache/src/services/replicator/change-log-db.d.ts.map +1 -0
  283. package/out/zero-cache/src/services/replicator/change-log-db.js +491 -0
  284. package/out/zero-cache/src/services/replicator/change-log-db.js.map +1 -0
  285. package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts +55 -0
  286. package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts.map +1 -0
  287. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js +150 -0
  288. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js.map +1 -0
  289. package/out/zero-cache/src/services/replicator/change-processor.d.ts +6 -0
  290. package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +1 -1
  291. package/out/zero-cache/src/services/replicator/change-processor.js +70 -25
  292. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  293. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts +1 -1
  294. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
  295. package/out/zero-cache/src/services/replicator/incremental-sync.js +19 -6
  296. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  297. package/out/zero-cache/src/services/replicator/notifier.d.ts +0 -22
  298. package/out/zero-cache/src/services/replicator/notifier.d.ts.map +1 -1
  299. package/out/zero-cache/src/services/replicator/notifier.js +7 -1
  300. package/out/zero-cache/src/services/replicator/notifier.js.map +1 -1
  301. package/out/zero-cache/src/services/replicator/replicator.d.ts +17 -2
  302. package/out/zero-cache/src/services/replicator/replicator.d.ts.map +1 -1
  303. package/out/zero-cache/src/services/replicator/replicator.js +9 -3
  304. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  305. package/out/zero-cache/src/services/replicator/reporter/recorder.d.ts +27 -0
  306. package/out/zero-cache/src/services/replicator/reporter/recorder.d.ts.map +1 -1
  307. package/out/zero-cache/src/services/replicator/reporter/recorder.js +32 -0
  308. package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
  309. package/out/zero-cache/src/services/replicator/reporter/report-schema.d.ts +1 -1
  310. package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts +119 -0
  311. package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts.map +1 -0
  312. package/out/zero-cache/src/services/replicator/schema/backfilling.js +214 -0
  313. package/out/zero-cache/src/services/replicator/schema/backfilling.js.map +1 -0
  314. package/out/zero-cache/src/services/replicator/schema/change-log.d.ts +3 -3
  315. package/out/zero-cache/src/services/replicator/schema/column-metadata.js +1 -1
  316. package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
  317. package/out/zero-cache/src/services/replicator/schema/replication-state.d.ts +1 -1
  318. package/out/zero-cache/src/services/replicator/schema/replication-state.d.ts.map +1 -1
  319. package/out/zero-cache/src/services/replicator/schema/replication-state.js +9 -5
  320. package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
  321. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts +132 -0
  322. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts.map +1 -0
  323. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +415 -0
  324. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -0
  325. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.d.ts +120 -0
  326. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.d.ts.map +1 -0
  327. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js +283 -0
  328. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js.map +1 -0
  329. package/out/zero-cache/src/services/replicator/write-worker.js +23 -12
  330. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  331. package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
  332. package/out/zero-cache/src/services/run-ast.js +2 -1
  333. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  334. package/out/zero-cache/src/services/running-state.d.ts +1 -1
  335. package/out/zero-cache/src/services/running-state.d.ts.map +1 -1
  336. package/out/zero-cache/src/services/running-state.js +1 -0
  337. package/out/zero-cache/src/services/running-state.js.map +1 -1
  338. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts +3 -2
  339. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +1 -1
  340. package/out/zero-cache/src/services/view-syncer/client-handler.js +25 -17
  341. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  342. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
  343. package/out/zero-cache/src/services/view-syncer/cvr-store.js +14 -1
  344. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  345. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.d.ts +53 -0
  346. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.d.ts.map +1 -0
  347. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.js +68 -0
  348. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.js.map +1 -0
  349. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  350. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +18 -3
  351. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  352. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
  353. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  354. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +14 -4
  355. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  356. package/out/zero-cache/src/services/view-syncer/view-syncer.js +70 -8
  357. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  358. package/out/zero-cache/src/types/downstream.d.ts +11 -0
  359. package/out/zero-cache/src/types/downstream.d.ts.map +1 -0
  360. package/out/zero-cache/src/types/pg.d.ts.map +1 -1
  361. package/out/zero-cache/src/types/pg.js +14 -4
  362. package/out/zero-cache/src/types/pg.js.map +1 -1
  363. package/out/zero-cache/src/types/poke-chunk.d.ts +18 -0
  364. package/out/zero-cache/src/types/poke-chunk.d.ts.map +1 -0
  365. package/out/zero-cache/src/types/poke-chunk.js +57 -0
  366. package/out/zero-cache/src/types/poke-chunk.js.map +1 -0
  367. package/out/zero-cache/src/types/processes.d.ts +1 -2
  368. package/out/zero-cache/src/types/processes.d.ts.map +1 -1
  369. package/out/zero-cache/src/types/processes.js +1 -28
  370. package/out/zero-cache/src/types/processes.js.map +1 -1
  371. package/out/zero-cache/src/types/streams.d.ts +16 -0
  372. package/out/zero-cache/src/types/streams.d.ts.map +1 -1
  373. package/out/zero-cache/src/types/streams.js +25 -4
  374. package/out/zero-cache/src/types/streams.js.map +1 -1
  375. package/out/zero-cache/src/types/subscription.d.ts +2 -0
  376. package/out/zero-cache/src/types/subscription.d.ts.map +1 -1
  377. package/out/zero-cache/src/types/subscription.js +6 -0
  378. package/out/zero-cache/src/types/subscription.js.map +1 -1
  379. package/out/zero-cache/src/workers/connection.d.ts +23 -4
  380. package/out/zero-cache/src/workers/connection.d.ts.map +1 -1
  381. package/out/zero-cache/src/workers/connection.js +130 -10
  382. package/out/zero-cache/src/workers/connection.js.map +1 -1
  383. package/out/zero-cache/src/workers/replicator.d.ts +25 -0
  384. package/out/zero-cache/src/workers/replicator.d.ts.map +1 -1
  385. package/out/zero-cache/src/workers/replicator.js +33 -1
  386. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  387. package/out/zero-cache/src/workers/syncer.d.ts +12 -0
  388. package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
  389. package/out/zero-cache/src/workers/syncer.js +75 -0
  390. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  391. package/out/zero-client/src/client/version.js +1 -1
  392. package/out/zero-client/src/client/zero-poke-handler.d.ts +7 -2
  393. package/out/zero-client/src/client/zero-poke-handler.d.ts.map +1 -1
  394. package/out/zero-client/src/client/zero-poke-handler.js +52 -2
  395. package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
  396. package/out/zero-client/src/client/zero.d.ts +6 -7
  397. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  398. package/out/zero-client/src/client/zero.js +194 -107
  399. package/out/zero-client/src/client/zero.js.map +1 -1
  400. package/out/zero-protocol/src/down.d.ts +1 -1
  401. package/out/zero-protocol/src/poke.d.ts +89 -7
  402. package/out/zero-protocol/src/poke.d.ts.map +1 -1
  403. package/out/zero-protocol/src/poke.js +16 -8
  404. package/out/zero-protocol/src/poke.js.map +1 -1
  405. package/out/zero-protocol/src/protocol-version.d.ts +12 -7
  406. package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
  407. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  408. package/out/zero-schema/src/builder/table-builder.d.ts +23 -0
  409. package/out/zero-schema/src/builder/table-builder.d.ts.map +1 -1
  410. package/out/zero-schema/src/builder/table-builder.js +18 -0
  411. package/out/zero-schema/src/builder/table-builder.js.map +1 -1
  412. package/out/zero-server/src/custom.js +10 -3
  413. package/out/zero-server/src/custom.js.map +1 -1
  414. package/out/zero-server/src/schema.d.ts +8 -0
  415. package/out/zero-server/src/schema.d.ts.map +1 -1
  416. package/out/zero-server/src/schema.js +63 -24
  417. package/out/zero-server/src/schema.js.map +1 -1
  418. package/out/zero-types/src/schema.d.ts +11 -0
  419. package/out/zero-types/src/schema.d.ts.map +1 -1
  420. package/out/zql/src/ivm/memory-source.d.ts +10 -2
  421. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  422. package/out/zql/src/ivm/memory-source.js +14 -6
  423. package/out/zql/src/ivm/memory-source.js.map +1 -1
  424. package/out/zql/src/ivm/stream.d.ts +0 -1
  425. package/out/zql/src/ivm/stream.d.ts.map +1 -1
  426. package/out/zql/src/ivm/stream.js +1 -7
  427. package/out/zql/src/ivm/stream.js.map +1 -1
  428. package/out/zql/src/ivm/union-fan-in.d.ts +1 -1
  429. package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
  430. package/out/zql/src/ivm/union-fan-in.js +11 -2
  431. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  432. package/out/zql/src/mutate/crud.d.ts +10 -8
  433. package/out/zql/src/mutate/crud.d.ts.map +1 -1
  434. package/out/zql/src/mutate/crud.js.map +1 -1
  435. package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
  436. package/out/zql/src/query/query-delegate-base.js +5 -1
  437. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  438. package/out/zql/src/query/query-impl.d.ts +2 -2
  439. package/out/zql/src/query/query-impl.d.ts.map +1 -1
  440. package/out/zql/src/query/query-impl.js.map +1 -1
  441. package/out/zql/src/query/query.d.ts +83 -14
  442. package/out/zql/src/query/query.d.ts.map +1 -1
  443. package/out/zql/src/query/static-query.d.ts +1 -1
  444. package/out/zql/src/query/static-query.d.ts.map +1 -1
  445. package/out/zqlite/src/db.js +2 -2
  446. package/out/zqlite/src/resolve-scalar-subqueries.d.ts +15 -0
  447. package/out/zqlite/src/resolve-scalar-subqueries.d.ts.map +1 -1
  448. package/out/zqlite/src/resolve-scalar-subqueries.js +33 -14
  449. package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
  450. package/out/zqlite/src/sqlite-cost-model.js +5 -5
  451. package/out/zqlite/src/table-source.d.ts.map +1 -1
  452. package/out/zqlite/src/table-source.js +4 -4
  453. package/out/zqlite/src/table-source.js.map +1 -1
  454. package/package.json +3 -3
  455. package/out/replicache/src/db/index.js +0 -91
  456. package/out/replicache/src/db/index.js.map +0 -1
  457. package/out/replicache/src/lazy.d.ts +0 -2
  458. package/out/replicache/src/lazy.d.ts.map +0 -1
  459. package/out/replicache/src/lazy.js +0 -12
  460. package/out/replicache/src/lazy.js.map +0 -1
  461. package/out/zero-cache/src/server/backup-watermark-reader.js +0 -35
  462. package/out/zero-cache/src/server/backup-watermark-reader.js.map +0 -1
  463. package/out/zero-cache/src/services/change-streamer/litestream3-backup-monitor.d.ts.map +0 -1
  464. package/out/zero-cache/src/services/change-streamer/litestream3-backup-monitor.js.map +0 -1
  465. package/out/zero-cache/src/services/change-streamer/replica-monitor.d.ts +0 -16
  466. package/out/zero-cache/src/services/change-streamer/replica-monitor.d.ts.map +0 -1
  467. package/out/zero-cache/src/services/change-streamer/replica-monitor.js +0 -54
  468. package/out/zero-cache/src/services/change-streamer/replica-monitor.js.map +0 -1
  469. package/out/zero-cache/src/services/change-streamer/vfs-backup-monitor.d.ts +0 -25
  470. package/out/zero-cache/src/services/change-streamer/vfs-backup-monitor.d.ts.map +0 -1
  471. package/out/zero-cache/src/services/change-streamer/vfs-backup-monitor.js +0 -159
  472. package/out/zero-cache/src/services/change-streamer/vfs-backup-monitor.js.map +0 -1
  473. package/out/zero-cache/src/services/litestream/vfs-watermark-reader.d.ts +0 -44
  474. package/out/zero-cache/src/services/litestream/vfs-watermark-reader.d.ts.map +0 -1
  475. package/out/zero-cache/src/services/litestream/vfs-watermark-reader.js +0 -65
  476. package/out/zero-cache/src/services/litestream/vfs-watermark-reader.js.map +0 -1
  477. package/out/zero-cache/src/services/litestream/vfs-watermark-worker-source.d.ts +0 -11
  478. package/out/zero-cache/src/services/litestream/vfs-watermark-worker-source.d.ts.map +0 -1
  479. package/out/zero-cache/src/services/litestream/vfs-watermark-worker-source.js +0 -74
  480. package/out/zero-cache/src/services/litestream/vfs-watermark-worker-source.js.map +0 -1
  481. package/out/zero-cache/src/services/litestream/vfs-watermark-worker.d.ts +0 -42
  482. package/out/zero-cache/src/services/litestream/vfs-watermark-worker.d.ts.map +0 -1
  483. package/out/zero-cache/src/services/litestream/vfs-watermark-worker.js +0 -117
  484. package/out/zero-cache/src/services/litestream/vfs-watermark-worker.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"forwarder.js","names":["#lc","#progressMonitorOptions","#active","#queued","#flowControlWaits","#flowControlWaitTime","#subscriberStats","#progressMonitor","#trackProgress","#currentBroadcast","#inTransaction","#updateActiveSubscribers"],"sources":["../../../../../../zero-cache/src/services/change-streamer/forwarder.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {joinIterables, wrapIterable} from '../../../../shared/src/iterables.ts';\nimport {\n getOrCreateCounter,\n getOrCreateGauge,\n getOrCreateLatencyHistogram,\n} from '../../observability/metrics.ts';\nimport {Broadcast} from './broadcast.ts';\nimport type {ChangeTag, WatermarkedChange} from './change-streamer-service.ts';\nimport type {Status} from './change-streamer.ts';\nimport type {Subscriber} from './subscriber.ts';\n\nexport type ProgressMonitorOptions = {\n flowControlConsensusPaddingSeconds: number;\n};\n\nexport class Forwarder {\n readonly #lc: LogContext;\n readonly #progressMonitorOptions: ProgressMonitorOptions;\n readonly #active = new Set<Subscriber>();\n readonly #queued = new Set<Subscriber>();\n readonly #flowControlWaits = getOrCreateCounter(\n 'replication',\n 'flow_control.waits',\n 'Flow-control checkpoints completed, labeled by release mode.',\n );\n readonly #flowControlWaitTime = getOrCreateLatencyHistogram(\n 'replication',\n 'flow_control.wait_duration',\n 'Time replication waits at flow-control checkpoints.',\n );\n #inTransaction = false;\n\n #currentBroadcast: Broadcast | undefined;\n #progressMonitor: NodeJS.Timeout | undefined;\n\n constructor(\n lc: LogContext,\n opts: ProgressMonitorOptions = {flowControlConsensusPaddingSeconds: 1},\n ) {\n this.#lc = lc.withContext('component', 'progress-monitor');\n this.#progressMonitorOptions = opts;\n\n getOrCreateGauge(\n 'replication',\n 'flow_control.active_subscribers',\n 'Active change-stream subscribers receiving live changes.',\n ).addCallback(result => result.observe(this.#active.size));\n\n getOrCreateGauge(\n 'replication',\n 'flow_control.queued_subscribers',\n 'Change-stream subscribers waiting for the current transaction to finish before activation.',\n ).addCallback(result => result.observe(this.#queued.size));\n\n getOrCreateGauge(\n 'replication',\n 'flow_control.pending_messages',\n 'Downstream change-stream messages not yet acked by subscribers.',\n ).addCallback(result =>\n result.observe(this.#subscriberStats().pendingMessages),\n );\n\n getOrCreateGauge(\n 'replication',\n 'flow_control.backlog_messages',\n 'Live change-stream messages buffered while subscribers catch up.',\n ).addCallback(result =>\n result.observe(this.#subscriberStats().backlogMessages),\n );\n\n getOrCreateGauge('replication', 'flow_control.backlog_bytes', {\n description:\n 'Live change-stream bytes buffered while subscribers catch up.',\n unit: 'By',\n }).addCallback(result =>\n result.observe(this.#subscriberStats().backlogBytes),\n );\n\n getOrCreateGauge('replication', 'flow_control.max_backlog_bytes', {\n description:\n 'Maximum live change-stream bytes buffered by a single subscriber.',\n unit: 'By',\n }).addCallback(result =>\n result.observe(this.#subscriberStats().maxBacklogBytes),\n );\n }\n\n startProgressMonitor() {\n clearInterval(this.#progressMonitor);\n this.#progressMonitor = setInterval(this.#trackProgress, 1000);\n }\n\n readonly #trackProgress = () => {\n const now = performance.now();\n for (const sub of this.#active) {\n sub.sampleProcessRate(now);\n }\n\n const {flowControlConsensusPaddingSeconds} = this.#progressMonitorOptions;\n // A negative number disables early flow control release.\n if (flowControlConsensusPaddingSeconds >= 0) {\n this.#currentBroadcast?.checkProgress(\n this.#lc,\n flowControlConsensusPaddingSeconds * 1000,\n now,\n );\n }\n };\n\n stopProgressMonitor() {\n clearInterval(this.#progressMonitor);\n }\n\n /**\n * `add()` is called in lock step with `Storer.catchup()` so that the\n * two components have an equivalent interpretation of whether a Transaction is\n * currently being streamed.\n */\n add(sub: Subscriber) {\n if (this.#inTransaction) {\n this.#queued.add(sub);\n } else {\n this.#active.add(sub);\n }\n }\n\n remove(sub: Subscriber) {\n this.#active.delete(sub);\n this.#queued.delete(sub);\n sub.close();\n }\n\n /**\n * `forward()` is called in lockstep with `Storer.store()` so that the\n * two components have an equivalent interpretation of whether a Transaction is\n * currently being streamed.\n *\n * This version of forward is fire-and-forget, with no flow control. The\n * change-streamer should call and await {@link forwardWithFlowControl()}\n * occasionally to avoid memory blowup.\n */\n forward(entry: WatermarkedChange) {\n Broadcast.withoutTracking(this.#active.values(), entry);\n this.#updateActiveSubscribers(entry[1]);\n }\n\n sendStatus(status: Status) {\n for (const sub of this.#active.values()) {\n sub.sendStatus(status);\n }\n }\n\n /**\n * The flow-control-aware equivalent of {@link forward()}, returning a\n * Promise that resolves when replication should continue.\n */\n async forwardWithFlowControl(entry: WatermarkedChange) {\n const start = performance.now();\n const broadcast = new Broadcast(this.#active.values(), entry);\n this.#updateActiveSubscribers(entry[1]);\n\n // set for progress tracking\n this.#currentBroadcast = broadcast;\n\n try {\n await broadcast.done;\n const attributes = {'release.mode': broadcast.releaseMode};\n this.#flowControlWaits.add(1, attributes);\n this.#flowControlWaitTime.recordMs(performance.now() - start, attributes);\n } finally {\n // Technically #currentBroadcast may have changed, so only\n // unset if it if is still the same.\n if (this.#currentBroadcast === broadcast) {\n this.#currentBroadcast = undefined;\n }\n }\n }\n\n #subscriberStats() {\n let pendingMessages = 0;\n let backlogMessages = 0;\n let backlogBytes = 0;\n let maxBacklogBytes = 0;\n\n for (const sub of joinIterables(this.#active, this.#queued)) {\n const stats = sub.getStats();\n pendingMessages += stats.pending;\n backlogMessages += stats.backlog;\n backlogBytes += stats.backlogBytes;\n maxBacklogBytes = Math.max(maxBacklogBytes, stats.backlogBytes);\n }\n\n return {pendingMessages, backlogMessages, backlogBytes, maxBacklogBytes};\n }\n\n #updateActiveSubscribers(tag: ChangeTag) {\n switch (tag) {\n case 'begin':\n // While in a Transaction, all added subscribers are \"queued\" so that no\n // messages are forwarded to them. This state corresponds to being queued\n // for catchup in the Storer, which will retrieve historic changes\n // and call catchup() once the current transaction is committed.\n this.#inTransaction = true;\n break;\n case 'commit':\n case 'rollback':\n // Upon commit or rollback, all queued subscribers are transferred to\n // the active set. This means that they can receive messages starting\n // from the next transaction.\n //\n // Note that if catchup is still in progress (in the Storer), these messages\n // will be buffered in the backlog until catchup completes.\n this.#inTransaction = false;\n for (const sub of this.#queued.values()) {\n this.#active.add(sub);\n }\n this.#queued.clear();\n break;\n }\n }\n\n getAcks(): Set<string> {\n return new Set(\n joinIterables(\n wrapIterable(this.#active).map(s => s.acked),\n wrapIterable(this.#queued).map(s => s.acked),\n ),\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"forwarder.js","names":["#lc","#setTimeout","#clearTimeout","#active","#queued","#earlyReleaseOptions","#flowControlSlowSubscriberGracePeriodMs","#flowControlWaits","#flowControlWaitTime","#subscriberStats","#progressMonitor","#currentBroadcast","#inTransaction","#updateActiveSubscribers"],"sources":["../../../../../../zero-cache/src/services/change-streamer/forwarder.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {joinIterables, wrapIterable} from '../../../../shared/src/iterables.ts';\nimport {\n getOrCreateCounter,\n getOrCreateGauge,\n getOrCreateLatencyHistogram,\n} from '../../observability/metrics.ts';\nimport {Broadcast, type EarlyReleaseOptions} from './broadcast.ts';\nimport type {ChangeTag, Status, WatermarkedChange} from './change-streamer.ts';\nimport {StreamTooFarBehind} from './error-type-enum.ts';\nimport type {Subscriber, SubscriberStats} from './subscriber.ts';\n\nexport type ProgressMonitorOptions = {\n flowControlConsensusTimeoutProportion: number;\n flowControlSlowSubscriberGracePeriodMs?: number | undefined;\n // Injectable timers, for deterministic testing.\n setTimeoutFn?: typeof setTimeout | undefined;\n clearTimeoutFn?: typeof clearTimeout | undefined;\n};\n\nexport class Forwarder {\n readonly #lc: LogContext;\n readonly #setTimeout: typeof setTimeout;\n readonly #clearTimeout: typeof clearTimeout;\n readonly #active = new Set<Subscriber>();\n readonly #queued = new Set<Subscriber>();\n readonly #earlyReleaseOptions: EarlyReleaseOptions | undefined;\n readonly #flowControlSlowSubscriberGracePeriodMs: number | undefined;\n\n readonly #flowControlWaits = getOrCreateCounter(\n 'replication',\n 'flow_control.waits',\n 'Flow-control checkpoints completed, labeled by release mode.',\n );\n readonly #flowControlWaitTime = getOrCreateLatencyHistogram(\n 'replication',\n 'flow_control.wait_duration',\n 'Time replication waits at flow-control checkpoints.',\n );\n #inTransaction = false;\n\n #currentBroadcast: Broadcast | undefined;\n #progressMonitor: NodeJS.Timeout | undefined;\n\n constructor(\n lc: LogContext,\n opts: ProgressMonitorOptions = {\n flowControlConsensusTimeoutProportion: 2.0,\n },\n ) {\n this.#lc = lc.withContext('component', 'subscriber-progress-monitor');\n this.#setTimeout = opts.setTimeoutFn ?? setTimeout;\n this.#clearTimeout = opts.clearTimeoutFn ?? clearTimeout;\n\n this.#earlyReleaseOptions =\n opts.flowControlConsensusTimeoutProportion < 0\n ? undefined\n : {\n consensusTimeoutProportion:\n opts.flowControlConsensusTimeoutProportion,\n setTimeoutFn: this.#setTimeout,\n clearTimeoutFn: this.#clearTimeout,\n };\n this.#flowControlSlowSubscriberGracePeriodMs =\n opts.flowControlSlowSubscriberGracePeriodMs;\n\n getOrCreateGauge(\n 'replication',\n 'flow_control.active_subscribers',\n 'Active change-stream subscribers receiving live changes.',\n ).addCallback(result => result.observe(this.#active.size));\n\n getOrCreateGauge(\n 'replication',\n 'flow_control.queued_subscribers',\n 'Change-stream subscribers waiting for the current transaction to finish before activation.',\n ).addCallback(result => result.observe(this.#queued.size));\n\n getOrCreateGauge(\n 'replication',\n 'flow_control.pending_messages',\n 'Downstream change-stream messages not yet acked by subscribers.',\n ).addCallback(result =>\n result.observe(this.#subscriberStats().pendingMessages),\n );\n\n getOrCreateGauge(\n 'replication',\n 'flow_control.backlog_messages',\n 'Live change-stream messages buffered while subscribers catch up.',\n ).addCallback(result =>\n result.observe(this.#subscriberStats().backlogMessages),\n );\n\n getOrCreateGauge('replication', 'flow_control.backlog_bytes', {\n description:\n 'Live change-stream bytes buffered while subscribers catch up.',\n unit: 'By',\n }).addCallback(result =>\n result.observe(this.#subscriberStats().backlogBytes),\n );\n\n getOrCreateGauge('replication', 'flow_control.max_backlog_bytes', {\n description:\n 'Maximum live change-stream bytes buffered by a single subscriber.',\n unit: 'By',\n }).addCallback(result =>\n result.observe(this.#subscriberStats().maxBacklogBytes),\n );\n }\n\n startProgressMonitor() {\n clearInterval(this.#progressMonitor);\n this.#progressMonitor = setInterval(\n () => this.checkSubscriberProgress(performance.now()),\n 1000,\n );\n }\n\n /**\n * Runs one progress-monitor tick: samples each active subscriber's change\n * rate, logs slow broadcasts, and disconnects subscribers that have been\n * continuously lagging for longer than the configured grace period.\n *\n * Invoked on an interval by {@link startProgressMonitor()}; exposed (rather\n * than private) so tests can drive a single tick with an explicit `now`.\n */\n checkSubscriberProgress(now: number) {\n let timedOut: Map<Subscriber, SubscriberStats> | undefined;\n let slowestOnTime: SubscriberStats | undefined;\n\n for (const sub of this.#active) {\n const stats = sub.sampleProcessRate(now).getStats();\n if (stats.missedLastTimeout) {\n (timedOut ??= new Map()).set(sub, stats);\n } else if (\n stats.processRate <= (slowestOnTime?.processRate ?? Infinity)\n ) {\n slowestOnTime = stats;\n }\n }\n this.#currentBroadcast?.logSlowRequests(now);\n\n if (\n this.#flowControlSlowSubscriberGracePeriodMs &&\n timedOut?.size &&\n slowestOnTime\n ) {\n for (const [sub, stats] of timedOut.entries()) {\n const rate =\n stats.processRate < slowestOnTime.processRate\n ? 'lagging'\n : 'catching-up';\n const laggingDuration = sub.reportChangeRate(now, rate);\n if (laggingDuration >= this.#flowControlSlowSubscriberGracePeriodMs) {\n this.#lc.warn?.(\n `subscriber ${sub.id} has lagged for ${laggingDuration}ms. disconnecting`,\n {stats},\n );\n sub.close(\n StreamTooFarBehind,\n `lagging for over ${this.#flowControlSlowSubscriberGracePeriodMs}ms (${laggingDuration}ms)`,\n );\n }\n }\n }\n }\n\n stopProgressMonitor() {\n clearInterval(this.#progressMonitor);\n }\n\n /**\n * `add()` is called in lock step with `Storer.catchup()` so that the\n * two components have an equivalent interpretation of whether a Transaction is\n * currently being streamed.\n */\n add(sub: Subscriber) {\n if (this.#inTransaction) {\n this.#queued.add(sub);\n } else {\n this.#active.add(sub);\n }\n }\n\n remove(sub: Subscriber) {\n this.#active.delete(sub);\n this.#queued.delete(sub);\n sub.close();\n }\n\n /**\n * `forward()` is called in lockstep with `Storer.store()` so that the\n * two components have an equivalent interpretation of whether a Transaction is\n * currently being streamed.\n *\n * This version of forward is fire-and-forget, with no flow control. The\n * change-streamer should call and await {@link forwardWithFlowControl()}\n * occasionally to avoid memory blowup.\n */\n forward(entry: WatermarkedChange) {\n Broadcast.withoutTracking(this.#active.values(), entry);\n this.#updateActiveSubscribers(entry[1]);\n }\n\n sendStatus(status: Status) {\n for (const sub of this.#active.values()) {\n sub.sendStatus(status);\n }\n }\n\n /**\n * The flow-control-aware equivalent of {@link forward()}, returning a\n * Promise that resolves when replication should continue.\n */\n async forwardWithFlowControl(entry: WatermarkedChange) {\n const start = performance.now();\n const broadcast = new Broadcast(\n this.#lc,\n this.#active.values(),\n entry,\n this.#earlyReleaseOptions,\n );\n this.#updateActiveSubscribers(entry[1]);\n\n // set for progress tracking\n this.#currentBroadcast = broadcast;\n\n try {\n await broadcast.done;\n const attributes = {'release.mode': broadcast.releaseMode};\n this.#flowControlWaits.add(1, attributes);\n this.#flowControlWaitTime.recordMs(performance.now() - start, attributes);\n } finally {\n // Technically #currentBroadcast may have changed, so only\n // unset if it if is still the same.\n if (this.#currentBroadcast === broadcast) {\n this.#currentBroadcast = undefined;\n }\n }\n }\n\n #subscriberStats() {\n let pendingMessages = 0;\n let backlogMessages = 0;\n let backlogBytes = 0;\n let maxBacklogBytes = 0;\n\n for (const sub of joinIterables(this.#active, this.#queued)) {\n const stats = sub.getStats();\n pendingMessages += stats.pending;\n backlogMessages += stats.backlog;\n backlogBytes += stats.backlogBytes;\n maxBacklogBytes = Math.max(maxBacklogBytes, stats.backlogBytes);\n }\n\n return {pendingMessages, backlogMessages, backlogBytes, maxBacklogBytes};\n }\n\n #updateActiveSubscribers(tag: ChangeTag) {\n switch (tag) {\n case 'begin':\n // While in a Transaction, all added subscribers are \"queued\" so that no\n // messages are forwarded to them. This state corresponds to being queued\n // for catchup in the Storer, which will retrieve historic changes\n // and call catchup() once the current transaction is committed.\n this.#inTransaction = true;\n break;\n case 'commit':\n case 'rollback':\n // Upon commit or rollback, all queued subscribers are transferred to\n // the active set. This means that they can receive messages starting\n // from the next transaction.\n //\n // Note that if catchup is still in progress (in the Storer), these messages\n // will be buffered in the backlog until catchup completes.\n this.#inTransaction = false;\n for (const sub of this.#queued.values()) {\n this.#active.add(sub);\n }\n this.#queued.clear();\n break;\n }\n }\n\n getAcks(): Set<string> {\n return new Set(\n joinIterables(\n wrapIterable(this.#active).map(s => s.acked),\n wrapIterable(this.#queued).map(s => s.acked),\n ),\n );\n }\n}\n"],"mappings":"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"}
@@ -1,9 +1,7 @@
1
1
  import type { LogContext } from '@rocicorp/logger';
2
- import { Subscription } from '../../types/subscription.ts';
3
- import type { BackupMonitor } from './backup-monitor.ts';
4
- import type { ChangeStreamerService } from './change-streamer.ts';
5
- import type { SnapshotMessage } from './snapshot.ts';
6
- export declare const CHECK_INTERVAL_MS = 60000;
2
+ import type { Source } from '../../types/streams.ts';
3
+ import type { BackedUpWatermark } from './backup-monitor.ts';
4
+ export declare const CHECK_INTERVAL_MS = 10000;
7
5
  /**
8
6
  * Allowance for clock skew between the machine reporting litestream metrics
9
7
  * and the timestamps reported by the backup destination (e.g. S3).
@@ -62,53 +60,29 @@ export declare const RESTORABLE_BACKUP_POLL_INTERVAL_MS = 10000;
62
60
  */
63
61
  export type BackupStateVerifier = () => Promise<Date>;
64
62
  /**
65
- * The Litestream3BackupMonitor polls the litestream "/metrics" endpoint to
63
+ * The Litestream3PrometheusPoller polls the litestream "/metrics" endpoint to
66
64
  * track the watermark (label) value of the `litestream_replica_progress` gauge
67
- * and schedules cleanup of change log entries that can be purged as a result.
65
+ * and emits verified watermarks to a stream of {@link BackedUpWatermark}s.
68
66
  *
69
67
  * See: https://github.com/rocicorp/litestream/pull/3
70
68
  *
71
- * Note that change log entries cannot simply be purged as soon as they
72
- * have been applied and backed up by litestream. Consider the case in which
73
- * litestream backs up new wal segments every minute, but it takes 5 minutes
74
- * to restore a replica: if a zero-cache starts restoring a replica at
75
- * minute 0, and new watermarks are replicated at minutes 1, 2, 3, 4, and 5,
76
- * purging changelog records as soon as those watermarks are replicated would
77
- * result in the zero-cache not being able to catch up from minute 0 once it
78
- * has finished restoring the replica.
79
- *
80
- * The `/snapshot` reservation protocol is used to prevent premature change
81
- * log cleanup:
82
- * - Clients restoring a snapshot initiate a `/snapshot` request and hold that
83
- * request open while it restores its snapshot, prepares it, and
84
- * starts its subscription to the change stream. During this time, no
85
- * cleanups are scheduled.
86
- * - When the subscription is started, the interval since the beginning of
87
- * of the reservation is tracked to increase the background cleanup delay
88
- * interval if needed. The reservation is ended (and request closed), and
89
- * cleanup scheduling is resumed with the current delay interval.
90
- *
91
- * Note that the reservation request is the primary mechanism by which
92
- * premature change log cleanup is prevented. The cleanup delay interval is
93
- * a secondary safeguard.
94
- *
95
- * Additionally, because the watermarks reported by litestream metrics
96
- * reflect what litestream *believes* has been backed up (which has been
97
- * observed to diverge from reality when uploads silently fail), the cleanup
98
- * watermark is only advanced after verifying it against the actual backup
99
- * state in the replica destination via a {@link BackupStateVerifier}. If the
69
+ * Because the watermarks reported by litestream metrics reflect what
70
+ * litestream *believes* has been backed up (which has been observed to
71
+ * diverge from reality when uploads silently fail), the cleanup watermark is
72
+ * only published after verifying it against the actual backup state in the
73
+ * replica destination via a {@link BackupStateVerifier}. If the
100
74
  * actual backup state stays behind the claimed progress for longer than
101
75
  * {@link WEDGED_SHUTDOWN_GRACE_MS}, the backup is treated as wedged and the
102
76
  * process is shut down rather than risk corrupting the backup.
103
77
  */
104
- export declare class Litestream3BackupMonitor implements BackupMonitor {
78
+ export declare class Litestream3PrometheusPoller {
105
79
  #private;
106
- readonly id = "backup-monitor";
107
- constructor(lc: LogContext, replicaFile: string, backupURL: string, metricsEndpoint: string, changeStreamer: ChangeStreamerService, initialCleanupDelayMs: number, verifyBackupState: BackupStateVerifier);
108
- run(): Promise<void>;
109
- startSnapshotReservation(taskID: string): Subscription<SnapshotMessage>;
110
- endReservation(taskID: string, updateCleanupDelay?: boolean): void;
111
- readonly checkWatermarksAndScheduleCleanup: () => Promise<void>;
112
- stop(): Promise<void>;
80
+ constructor(lc: LogContext, replicaFile: string, backupURL: string, metricsEndpoint: string, verifyBackupState: BackupStateVerifier);
81
+ /**
82
+ * Starts polling for watermarks and pushes new ones to returned stream.
83
+ * Must only be called once.
84
+ */
85
+ start(): Source<BackedUpWatermark>;
86
+ readonly checkVerifyAndPublishWatermarks: () => Promise<void>;
113
87
  }
114
- //# sourceMappingURL=litestream3-backup-monitor.d.ts.map
88
+ //# sourceMappingURL=litestream3-prometheus-poller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"litestream3-prometheus-poller.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/litestream3-prometheus-poller.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAMjD,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,wBAAwB,CAAC;AAOnD,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAC;AAE3D,eAAO,MAAM,iBAAiB,QAAS,CAAC;AAExC;;;GAGG;AACH,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,wBAAwB,QAAc,CAAC;AAIpD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,gCAAgC,QAAc,CAAC;AAG5D;;;;GAIG;AACH,eAAO,MAAM,kCAAkC,QAAS,CAAC;AAEzD;;;;;;;GAOG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AAEtD;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,2BAA2B;;gBAwCpC,EAAE,EAAE,UAAU,EACd,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,MAAM,EACvB,iBAAiB,EAAE,mBAAmB;IAexC;;;OAGG;IACH,KAAK,IAAI,MAAM,CAAC,iBAAiB,CAAC;IA8BlC,QAAQ,CAAC,+BAA+B,sBAgBtC;CAmSH"}
@@ -1,17 +1,14 @@
1
- import { promiseVoid } from "../../../../shared/src/resolved-promises.js";
2
- import { sleep } from "../../../../shared/src/sleep.js";
1
+ import { assert } from "../../../../shared/src/asserts.js";
3
2
  import { must } from "../../../../shared/src/must.js";
4
- import { Database } from "../../../../zqlite/src/db.js";
5
- import { getOrCreateCounter, getOrCreateGauge } from "../../observability/metrics.js";
3
+ import { getOrCreateCounter } from "../../observability/metrics.js";
6
4
  import { RunningState, UnrecoverableError } from "../running-state.js";
5
+ import { versionFromLexi } from "../../types/lexi-version.js";
7
6
  import { Subscription } from "../../types/subscription.js";
8
- import { litestreamBackupVerificationDuration, litestreamMonitorMetricAttrs, litestreamSnapshotReservationDuration } from "../litestream/metrics.js";
9
- import { resolver } from "@rocicorp/resolver";
7
+ import { litestreamBackupVerificationDuration, litestreamMonitorMetricAttrs } from "../litestream/metrics.js";
10
8
  import { statSync } from "node:fs";
11
9
  import parsePrometheusTextFormat from "parse-prometheus-text-format";
12
- //#region ../zero-cache/src/services/change-streamer/litestream3-backup-monitor.ts
13
- var CHECK_INTERVAL_MS = 6e4;
14
- var MIN_CLEANUP_DELAY_MS = 3e4;
10
+ //#region ../zero-cache/src/services/change-streamer/litestream3-prometheus-poller.ts
11
+ var CHECK_INTERVAL_MS = 1e4;
15
12
  /**
16
13
  * Allowance for clock skew between the machine reporting litestream metrics
17
14
  * and the timestamps reported by the backup destination (e.g. S3).
@@ -57,126 +54,58 @@ var GiB = 1024 ** 3;
57
54
  var INITIAL_BACKUP_GRACE_MS_PER_UNIT = 60 * 6e4;
58
55
  var INITIAL_BACKUP_GRACE_UNIT_BYTES = 50 * GiB;
59
56
  /**
60
- * How often the BackupMonitor re-checks whether the first restorable backup has
61
- * appeared while a view-syncer holds a snapshot reservation open during a cold
62
- * start.
63
- */
64
- var RESTORABLE_BACKUP_POLL_INTERVAL_MS = 1e4;
65
- /**
66
- * The Litestream3BackupMonitor polls the litestream "/metrics" endpoint to
57
+ * The Litestream3PrometheusPoller polls the litestream "/metrics" endpoint to
67
58
  * track the watermark (label) value of the `litestream_replica_progress` gauge
68
- * and schedules cleanup of change log entries that can be purged as a result.
59
+ * and emits verified watermarks to a stream of {@link BackedUpWatermark}s.
69
60
  *
70
61
  * See: https://github.com/rocicorp/litestream/pull/3
71
62
  *
72
- * Note that change log entries cannot simply be purged as soon as they
73
- * have been applied and backed up by litestream. Consider the case in which
74
- * litestream backs up new wal segments every minute, but it takes 5 minutes
75
- * to restore a replica: if a zero-cache starts restoring a replica at
76
- * minute 0, and new watermarks are replicated at minutes 1, 2, 3, 4, and 5,
77
- * purging changelog records as soon as those watermarks are replicated would
78
- * result in the zero-cache not being able to catch up from minute 0 once it
79
- * has finished restoring the replica.
80
- *
81
- * The `/snapshot` reservation protocol is used to prevent premature change
82
- * log cleanup:
83
- * - Clients restoring a snapshot initiate a `/snapshot` request and hold that
84
- * request open while it restores its snapshot, prepares it, and
85
- * starts its subscription to the change stream. During this time, no
86
- * cleanups are scheduled.
87
- * - When the subscription is started, the interval since the beginning of
88
- * of the reservation is tracked to increase the background cleanup delay
89
- * interval if needed. The reservation is ended (and request closed), and
90
- * cleanup scheduling is resumed with the current delay interval.
91
- *
92
- * Note that the reservation request is the primary mechanism by which
93
- * premature change log cleanup is prevented. The cleanup delay interval is
94
- * a secondary safeguard.
95
- *
96
- * Additionally, because the watermarks reported by litestream metrics
97
- * reflect what litestream *believes* has been backed up (which has been
98
- * observed to diverge from reality when uploads silently fail), the cleanup
99
- * watermark is only advanced after verifying it against the actual backup
100
- * state in the replica destination via a {@link BackupStateVerifier}. If the
63
+ * Because the watermarks reported by litestream metrics reflect what
64
+ * litestream *believes* has been backed up (which has been observed to
65
+ * diverge from reality when uploads silently fail), the cleanup watermark is
66
+ * only published after verifying it against the actual backup state in the
67
+ * replica destination via a {@link BackupStateVerifier}. If the
101
68
  * actual backup state stays behind the claimed progress for longer than
102
69
  * {@link WEDGED_SHUTDOWN_GRACE_MS}, the backup is treated as wedged and the
103
70
  * process is shut down rather than risk corrupting the backup.
104
71
  */
105
- var Litestream3BackupMonitor = class {
106
- id = "backup-monitor";
72
+ var Litestream3PrometheusPoller = class {
107
73
  #lc;
108
74
  #replicaFile;
109
75
  #backupURL;
110
76
  #metricsEndpoint;
111
- #changeStreamer;
112
- #state = new RunningState(this.id);
113
- #reservations = /* @__PURE__ */ new Map();
77
+ #state = new RunningState("litestream3-prometheus-poller");
78
+ #stream;
114
79
  #watermarks = /* @__PURE__ */ new Map();
115
80
  #verifyBackupState;
116
81
  #purgesBlocked = getOrCreateCounter("replica", "purge_blocked", { description: "Number of change-log purges blocked because the actual backup state (as listed from the replica destination) could not be verified, or is older than the backup progress claimed by litestream metrics. A steadily increasing value indicates a wedged or failing backup. The \"backup-wedged\" reason is emitted once just before the process shuts itself down due to a persistently stale backup." });
117
- #fatal = resolver();
118
82
  #lastWatermark = "";
119
83
  #latestBackupTime = null;
120
84
  #lastVerifiedUploadTime = null;
121
85
  #backupStaleSince = null;
122
86
  #runStartTime = null;
123
- #cleanupDelayMs;
124
87
  #checkMetricsTimer;
125
- constructor(lc, replicaFile, backupURL, metricsEndpoint, changeStreamer, initialCleanupDelayMs, verifyBackupState) {
126
- this.#lc = lc.withContext("component", this.id);
88
+ constructor(lc, replicaFile, backupURL, metricsEndpoint, verifyBackupState) {
89
+ this.#lc = lc.withContext("component", "litestream3-prometheus-poller");
127
90
  this.#replicaFile = replicaFile;
128
91
  this.#backupURL = backupURL;
129
92
  this.#metricsEndpoint = metricsEndpoint;
130
- this.#changeStreamer = changeStreamer;
131
93
  this.#verifyBackupState = verifyBackupState;
132
- this.#cleanupDelayMs = Math.max(initialCleanupDelayMs, MIN_CLEANUP_DELAY_MS);
133
- this.#lc.info?.(`backup monitor started ${initialCleanupDelayMs} ms after snapshot restore`);
134
- }
135
- run() {
136
- this.#runStartTime = Date.now();
137
- this.#lc.info?.(`monitoring backups at ${this.#metricsEndpoint} with ${this.#cleanupDelayMs} ms cleanup delay`);
138
- this.#checkMetricsTimer = setInterval(this.checkWatermarksAndScheduleCleanup, CHECK_INTERVAL_MS);
139
- this.#initBackupLagMetric();
140
- return Promise.race([this.#state.stopped(), this.#fatal.promise]);
141
- }
142
- startSnapshotReservation(taskID) {
143
- this.#lc.info?.(`pausing change-log cleanup while ${taskID} snapshots`);
144
- this.#reservations.get(taskID)?.sub.cancel();
145
- const sub = Subscription.create({ cleanup: () => this.endReservation(taskID, false) });
146
- this.#reservations.set(taskID, {
147
- start: /* @__PURE__ */ new Date(),
148
- sub
149
- });
150
- this.#pushStatusWhenRestorable(taskID, sub);
151
- return sub;
94
+ this.#stream = Subscription.create({ cleanup: () => {
95
+ clearInterval(this.#checkMetricsTimer);
96
+ this.#state.stop(this.#lc);
97
+ } });
152
98
  }
153
99
  /**
154
- * Pushes the `status` snapshot signal once a restorable backup is confirmed
155
- * to exist in the backup destination. On a warm start this is immediate; on a
156
- * cold start (a fresh stack whose first backup is still being produced) the
157
- * reservation is held open — kept alive by websocket liveness pings — and the
158
- * view-syncer stays unready until the first backup lands, rather than being
159
- * told to restore a backup that does not yet exist.
100
+ * Starts polling for watermarks and pushes new ones to returned stream.
101
+ * Must only be called once.
160
102
  */
161
- async #pushStatusWhenRestorable(taskID, sub) {
162
- try {
163
- while (this.#lastVerifiedUploadTime === null) {
164
- if (!sub.active) return;
165
- if (await this.#confirmRestorableBackup()) break;
166
- this.#lc.info?.(`no restorable backup at ${this.#backupURL} yet; holding ${taskID}'s reservation open until it lands`);
167
- await sleep(RESTORABLE_BACKUP_POLL_INTERVAL_MS, this.#state.signal);
168
- }
169
- const changeLogState = await this.#changeStreamer.getChangeLogState();
170
- if (sub.active) sub.push(["status", {
171
- tag: "status",
172
- backupURL: this.#backupURL,
173
- ...changeLogState
174
- }]);
175
- } catch (e) {
176
- if (this.#state.signal.aborted) return;
177
- this.#lc.warn?.(`failing snapshot reservation`, e);
178
- sub.fail(e instanceof Error ? e : new Error(String(e)));
179
- }
103
+ start() {
104
+ assert(this.#runStartTime === null, `Already called start()`);
105
+ this.#runStartTime = Date.now();
106
+ this.#lc.info?.(`monitoring backups at ${this.#metricsEndpoint}`);
107
+ this.#checkMetricsTimer = setInterval(this.checkVerifyAndPublishWatermarks, CHECK_INTERVAL_MS);
108
+ return this.#stream;
180
109
  }
181
110
  /**
182
111
  * Verifies whether a restorable backup actually exists in the destination
@@ -195,33 +124,14 @@ var Litestream3BackupMonitor = class {
195
124
  return false;
196
125
  }
197
126
  }
198
- endReservation(taskID, updateCleanupDelay = true) {
199
- const res = this.#reservations.get(taskID);
200
- if (res === void 0) return;
201
- this.#reservations.delete(taskID);
202
- const { start, sub } = res;
203
- sub.cancel();
204
- const duration = Date.now() - start.getTime();
205
- litestreamSnapshotReservationDuration().recordMs(duration, {
206
- ...litestreamMonitorMetricAttrs(this.#backupURL, "legacy", "view_syncer"),
207
- result: updateCleanupDelay ? "success" : "cancelled"
208
- });
209
- if (updateCleanupDelay) {
210
- this.#lc.info?.(`snapshot initialized by ${taskID} in ${duration} ms`);
211
- if (duration > this.#cleanupDelayMs) {
212
- this.#cleanupDelayMs = duration;
213
- this.#lc.info?.(`increased cleanup delay to ${duration} ms`);
214
- }
215
- }
216
- }
217
- checkWatermarksAndScheduleCleanup = async () => {
127
+ checkVerifyAndPublishWatermarks = async () => {
218
128
  try {
219
129
  await this.#checkWatermarks();
220
130
  } catch (e) {
221
131
  this.#lc.warn?.(`unable to fetch metrics at ${this.#metricsEndpoint}`, e);
222
132
  }
223
133
  try {
224
- await this.#scheduleCleanup();
134
+ await this.#publishVerifiedWatermarks();
225
135
  } catch (e) {
226
136
  this.#lc.warn?.(`error scheduling cleanup`, e);
227
137
  }
@@ -251,11 +161,16 @@ var Litestream3BackupMonitor = class {
251
161
  const watermark = metric.labels?.watermark;
252
162
  const name = metric.labels?.name;
253
163
  const time = /* @__PURE__ */ new Date(parseFloat(metric.value) * 1e3);
254
- if (watermark) yield {
255
- watermark,
256
- time,
257
- name
258
- };
164
+ if (watermark) try {
165
+ versionFromLexi(watermark);
166
+ yield {
167
+ watermark,
168
+ time,
169
+ name
170
+ };
171
+ } catch (e) {
172
+ this.#lc.warn?.(`ignoring invalid watermark ${watermark}`, { metric }, e);
173
+ }
259
174
  }
260
175
  }
261
176
  async #checkWatermarks() {
@@ -266,15 +181,10 @@ var Litestream3BackupMonitor = class {
266
181
  }
267
182
  return this.#latestBackupTime;
268
183
  }
269
- async #scheduleCleanup() {
270
- if (this.#reservations.size > 0) {
271
- this.#lc.info?.(`watermark cleanup paused for snapshot(s): ${[...this.#reservations.keys()]}`);
272
- return;
273
- }
274
- const latestCleanupTime = Date.now() - this.#cleanupDelayMs;
275
- const maxWatermark = this.#maxWatermarkUpTo(latestCleanupTime);
276
- if (maxWatermark.length === 0) return;
277
- const claimedTime = must(this.#watermarks.get(maxWatermark));
184
+ async #publishVerifiedWatermarks() {
185
+ const maxBackup = this.#maxBackupBefore(Date.now());
186
+ if (!maxBackup) return;
187
+ const { watermark: maxWatermark, backupTime: claimedTime } = maxBackup;
278
188
  if (!this.#confirmedDurable(claimedTime)) try {
279
189
  this.#lastVerifiedUploadTime = await this.#verifyBackupStateTimed();
280
190
  } catch (e) {
@@ -283,8 +193,8 @@ var Litestream3BackupMonitor = class {
283
193
  return;
284
194
  }
285
195
  const lastUpload = must(this.#lastVerifiedUploadTime);
286
- const verifiedWatermark = this.#maxWatermarkUpTo(Math.min(latestCleanupTime, lastUpload.getTime() + BACKUP_VERIFICATION_SLACK_MS));
287
- if (verifiedWatermark.length === 0) {
196
+ const verifiedBackup = this.#maxBackupBefore(lastUpload.getTime() + BACKUP_VERIFICATION_SLACK_MS);
197
+ if (!verifiedBackup) {
288
198
  this.#purgesBlocked.add(1, { reason: "backup-stale" });
289
199
  const now = Date.now();
290
200
  if (this.#backupStaleSince === null) this.#backupStaleSince = now;
@@ -294,8 +204,12 @@ var Litestream3BackupMonitor = class {
294
204
  return;
295
205
  }
296
206
  this.#backupStaleSince = null;
297
- this.#changeStreamer.scheduleCleanup(verifiedWatermark);
207
+ const { watermark: verifiedWatermark, backupTime } = verifiedBackup;
298
208
  for (const watermark of this.#watermarks.keys()) if (watermark <= verifiedWatermark) this.#watermarks.delete(watermark);
209
+ this.#stream.push({
210
+ watermark: verifiedWatermark,
211
+ backupTimeMs: backupTime.getTime()
212
+ });
299
213
  this.#lastWatermark = verifiedWatermark;
300
214
  }
301
215
  async #verifyBackupStateTimed() {
@@ -324,8 +238,7 @@ var Litestream3BackupMonitor = class {
324
238
  this.#purgesBlocked.add(1, { reason: "backup-wedged" });
325
239
  const err = new UnrecoverableError(`backup at ${this.#backupURL} is wedged: litestream claims a backup at ${claimedTime.toISOString()}, but the last object actually uploaded was at ${lastUpload.toISOString()}, and the backup has not advanced for ${staleForMs} ms (grace period ${WEDGED_SHUTDOWN_GRACE_MS} ms). Shutting down to avoid corrupting the backup; investigate the backup destination.`);
326
240
  this.#lc.error?.(err.message);
327
- clearInterval(this.#checkMetricsTimer);
328
- this.#fatal.reject(err);
241
+ this.#stream.fail(err);
329
242
  }
330
243
  /**
331
244
  * On a fresh stack, fails loudly if no restorable backup has appeared within
@@ -365,17 +278,24 @@ var Litestream3BackupMonitor = class {
365
278
  #shutDownOnMissingInitialBackup(elapsedMs) {
366
279
  const err = new UnrecoverableError(`no restorable backup has appeared at ${this.#backupURL} within ${elapsedMs} ms of startup (grace period ${this.#initialBackupGraceMs()} ms). The initial backup pipeline appears to be broken; shutting down so that the replication-manager is flagged as the cause rather than the view-syncers waiting on it. Investigate litestream replication to ${this.#backupURL}.`);
367
280
  this.#lc.error?.(err.message);
368
- clearInterval(this.#checkMetricsTimer);
369
- this.#fatal.reject(err);
281
+ this.#stream.fail(err);
370
282
  }
371
283
  /**
372
284
  * Returns the newest watermark whose backup time is at or before `cutoff`
373
285
  * (epoch ms), or `''` if there is none.
374
286
  */
375
- #maxWatermarkUpTo(cutoff) {
287
+ #maxBackupBefore(cutoff) {
376
288
  let max = "";
377
- for (const [watermark, backupTime] of this.#watermarks) if (backupTime.getTime() <= cutoff && watermark > max) max = watermark;
378
- return max;
289
+ let maxBackupTime;
290
+ for (const [watermark, backupTime] of this.#watermarks) if (backupTime.getTime() <= cutoff && watermark > max) {
291
+ max = watermark;
292
+ maxBackupTime = backupTime;
293
+ }
294
+ if (!maxBackupTime) return null;
295
+ return {
296
+ watermark: max,
297
+ backupTime: maxBackupTime
298
+ };
379
299
  }
380
300
  /**
381
301
  * Returns `true` if the actual backup state, as last verified against the
@@ -388,36 +308,8 @@ var Litestream3BackupMonitor = class {
388
308
  const lastUpload = this.#lastVerifiedUploadTime;
389
309
  return lastUpload !== null && claimedTime.getTime() <= lastUpload.getTime() + 6e4;
390
310
  }
391
- stop() {
392
- clearInterval(this.#checkMetricsTimer);
393
- for (const { sub } of this.#reservations.values()) sub.cancel();
394
- this.#state.stop(this.#lc);
395
- return promiseVoid;
396
- }
397
- #initBackupLagMetric() {
398
- getOrCreateGauge("replica", "backup_lag", {
399
- 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.",
400
- unit: "millisecond"
401
- }).addCallback(async (o) => {
402
- const latestBackup = await this.#checkWatermarks();
403
- if (!latestBackup) {
404
- this.#lc.warn?.(`no backed up watermarks. unable to report replica.backup_lag`);
405
- return;
406
- }
407
- const db = new Database(this.#lc, this.#replicaFile, { readonly: true });
408
- try {
409
- const { writeTimeMs } = db.prepare(`SELECT writeTimeMs FROM "_zero.replicationState"`).get();
410
- const backupLag = Math.max(0, writeTimeMs - latestBackup.getTime());
411
- o.observe(backupLag);
412
- } catch (e) {
413
- this.#lc.warn?.(`error measuring replica.backup_lag metric`, e);
414
- } finally {
415
- db.close();
416
- }
417
- });
418
- }
419
311
  };
420
312
  //#endregion
421
- export { Litestream3BackupMonitor };
313
+ export { Litestream3PrometheusPoller };
422
314
 
423
- //# sourceMappingURL=litestream3-backup-monitor.js.map
315
+ //# sourceMappingURL=litestream3-prometheus-poller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"litestream3-prometheus-poller.js","names":["#lc","#replicaFile","#backupURL","#metricsEndpoint","#state","#stream","#watermarks","#verifyBackupState","#purgesBlocked","#checkMetricsTimer","#runStartTime","#confirmRestorableBackup","#lastVerifiedUploadTime","#verifyBackupStateTimed","#checkWatermarks","#publishVerifiedWatermarks","#checkInitialBackupDeadline","#fetchWatermarks","#lastWatermark","#latestBackupTime","#maxBackupBefore","#confirmedDurable","#backupStaleSince","#shutDownOnWedgedBackup","#initialBackupGraceMs","#shutDownOnMissingInitialBackup"],"sources":["../../../../../../zero-cache/src/services/change-streamer/litestream3-prometheus-poller.ts"],"sourcesContent":["import {statSync} from 'node:fs';\nimport type {LogContext} from '@rocicorp/logger';\nimport parsePrometheusTextFormat from 'parse-prometheus-text-format';\nimport {assert} from '../../../../shared/src/asserts.ts';\nimport {must} from '../../../../shared/src/must.ts';\nimport {getOrCreateCounter} from '../../observability/metrics.ts';\nimport {versionFromLexi} from '../../types/lexi-version.ts';\nimport type {Source} from '../../types/streams.ts';\nimport {Subscription} from '../../types/subscription.ts';\nimport {\n litestreamBackupVerificationDuration,\n litestreamMonitorMetricAttrs,\n} from '../litestream/metrics.ts';\nimport {RunningState, UnrecoverableError} from '../running-state.ts';\nimport type {BackedUpWatermark} from './backup-monitor.ts';\n\nexport const CHECK_INTERVAL_MS = 10_000;\n\n/**\n * Allowance for clock skew between the machine reporting litestream metrics\n * and the timestamps reported by the backup destination (e.g. S3).\n */\nexport const BACKUP_VERIFICATION_SLACK_MS = 60_000;\n\n/**\n * How long the actual backup state may remain *continuously* behind the\n * backup progress claimed by litestream metrics before the backup is\n * considered genuinely wedged and the process is shut down.\n *\n * A wedged backup is dangerous: litestream believes it is making durable\n * progress when it is not, which can silently corrupt the backup (the WAL\n * format only makes *some* such gaps detectable). Once that is confirmed to\n * be the persistent state, continuing to run is worse than crashing, so the\n * process exits loudly (non-zero, with a logged error) and the wedged backup\n * destination becomes the priority to investigate.\n *\n * The change-log is conservatively *not* purged for the entire time the\n * backup is stale, so the only cost of a generous grace period is unbounded\n * change-log growth. We therefore err well on the side of slack: the backup\n * must stay stuck across many {@link CHECK_INTERVAL_MS} checks before we tear\n * the server down, so that a transient hiccup (a slow or briefly unreachable\n * destination, or litestream restarting) does not trigger a shutdown. The\n * staleness clock is reset the moment a purge is confirmed against a real\n * upload.\n */\nexport const WEDGED_SHUTDOWN_GRACE_MS = 15 * 60_000; // 15 minutes\n\nconst GiB = 1024 ** 3;\n\n/**\n * How long the BackupMonitor waits for the *first* restorable backup to appear\n * before concluding that the backup pipeline is broken and shutting the process\n * down (see {@link BackupMonitor.#shutDownOnMissingInitialBackup}).\n *\n * On a fresh stack the first backup is not restorable until the initial\n * Postgres->replica sync completes and litestream finishes uploading the\n * initial snapshot — a multipart upload that is not listable in the destination\n * until it is committed. The producer cannot observe that sub-snapshot progress,\n * so this is necessarily a generous timeout. It is scaled by replica size to\n * mirror the platform's storage-scaled startup-probe allowance (~1 hour per\n * 50GB) so that it never fires before the platform would restart a view-syncer\n * that is waiting on the backup.\n */\nexport const INITIAL_BACKUP_GRACE_MS_PER_UNIT = 60 * 60_000; // 1 hour ...\nconst INITIAL_BACKUP_GRACE_UNIT_BYTES = 50 * GiB; // ... per 50 GiB\n\n/**\n * How often the BackupMonitor re-checks whether the first restorable backup has\n * appeared while a view-syncer holds a snapshot reservation open during a cold\n * start.\n */\nexport const RESTORABLE_BACKUP_POLL_INTERVAL_MS = 10_000;\n\n/**\n * Returns the time of the most recent object actually uploaded to the\n * backup replica destination (e.g. as determined by listing the snapshots\n * and WAL segments in S3). Rejects if the backup state cannot be determined.\n *\n * See `getLastBackupTime()` in `../litestream/commands.ts` for the\n * production implementation.\n */\nexport type BackupStateVerifier = () => Promise<Date>;\n\n/**\n * The Litestream3PrometheusPoller polls the litestream \"/metrics\" endpoint to\n * track the watermark (label) value of the `litestream_replica_progress` gauge\n * and emits verified watermarks to a stream of {@link BackedUpWatermark}s.\n *\n * See: https://github.com/rocicorp/litestream/pull/3\n *\n * Because the watermarks reported by litestream metrics reflect what\n * litestream *believes* has been backed up (which has been observed to\n * diverge from reality when uploads silently fail), the cleanup watermark is\n * only published after verifying it against the actual backup state in the\n * replica destination via a {@link BackupStateVerifier}. If the\n * actual backup state stays behind the claimed progress for longer than\n * {@link WEDGED_SHUTDOWN_GRACE_MS}, the backup is treated as wedged and the\n * process is shut down rather than risk corrupting the backup.\n */\nexport class Litestream3PrometheusPoller {\n readonly #lc: LogContext;\n readonly #replicaFile: string;\n readonly #backupURL: string;\n readonly #metricsEndpoint: string;\n readonly #state = new RunningState('litestream3-prometheus-poller');\n\n readonly #stream: Subscription<BackedUpWatermark>;\n readonly #watermarks = new Map<string, Date>();\n\n readonly #verifyBackupState: BackupStateVerifier;\n readonly #purgesBlocked = getOrCreateCounter('replica', 'purge_blocked', {\n description:\n 'Number of change-log purges blocked because the actual backup state ' +\n '(as listed from the replica destination) could not be verified, or ' +\n 'is older than the backup progress claimed by litestream metrics. ' +\n 'A steadily increasing value indicates a wedged or failing backup. ' +\n 'The \"backup-wedged\" reason is emitted once just before the process ' +\n 'shuts itself down due to a persistently stale backup.',\n });\n\n #lastWatermark: string = '';\n #latestBackupTime: Date | null = null;\n // The time of the most recent object actually verified in the backup\n // destination, or `null` if none has ever been verified. Only assigned from a\n // successful verifyBackupState() and never cleared, so a non-null value also\n // means a restorable backup has been confirmed to exist (which is what gates\n // the snapshot `status` signal and satisfies the initial-backup deadline).\n #lastVerifiedUploadTime: Date | null = null;\n // Epoch ms at which the actual backup state was first observed to be\n // continuously behind the watermark litestream claims to have backed up,\n // or `null` while the backup is keeping up. Reset whenever a purge is\n // confirmed against a real upload.\n #backupStaleSince: number | null = null;\n // Epoch ms at which run() started, used to bound how long to wait for the\n // first restorable backup to appear. `null` until run() is called.\n #runStartTime: number | null = null;\n #checkMetricsTimer: NodeJS.Timeout | undefined;\n\n constructor(\n lc: LogContext,\n replicaFile: string,\n backupURL: string,\n metricsEndpoint: string,\n verifyBackupState: BackupStateVerifier,\n ) {\n this.#lc = lc.withContext('component', 'litestream3-prometheus-poller');\n this.#replicaFile = replicaFile;\n this.#backupURL = backupURL;\n this.#metricsEndpoint = metricsEndpoint;\n this.#verifyBackupState = verifyBackupState;\n this.#stream = Subscription.create<BackedUpWatermark>({\n cleanup: () => {\n clearInterval(this.#checkMetricsTimer);\n this.#state.stop(this.#lc);\n },\n });\n }\n\n /**\n * Starts polling for watermarks and pushes new ones to returned stream.\n * Must only be called once.\n */\n start(): Source<BackedUpWatermark> {\n assert(this.#runStartTime === null, `Already called start()`);\n this.#runStartTime = Date.now();\n this.#lc.info?.(`monitoring backups at ${this.#metricsEndpoint}`);\n this.#checkMetricsTimer = setInterval(\n this.checkVerifyAndPublishWatermarks,\n CHECK_INTERVAL_MS,\n );\n return this.#stream;\n }\n\n /**\n * Verifies whether a restorable backup actually exists in the destination\n * (as opposed to what litestream metrics merely claim). On success the\n * verified upload time is cached — seeding the cleanup-verification fast path\n * ({@link #confirmedDurable}) — and the initial-backup deadline is satisfied.\n * Returns false if no backup is listable yet (e.g. the initial snapshot is\n * still uploading).\n */\n async #confirmRestorableBackup(): Promise<boolean> {\n try {\n this.#lastVerifiedUploadTime = await this.#verifyBackupStateTimed();\n return true;\n } catch (e) {\n this.#lc.info?.(`backup not yet restorable at ${this.#backupURL}`, e);\n return false;\n }\n }\n\n // Exported for testing\n readonly checkVerifyAndPublishWatermarks = async () => {\n try {\n await this.#checkWatermarks();\n } catch (e) {\n this.#lc.warn?.(`unable to fetch metrics at ${this.#metricsEndpoint}`, e);\n }\n try {\n await this.#publishVerifiedWatermarks();\n } catch (e) {\n this.#lc.warn?.(`error scheduling cleanup`, e);\n }\n try {\n await this.#checkInitialBackupDeadline();\n } catch (e) {\n this.#lc.warn?.(`error checking initial backup deadline`, e);\n }\n };\n\n async *#fetchWatermarks(): AsyncGenerator<{\n watermark: string;\n time: Date;\n name?: string | undefined;\n }> {\n const metricsEndpoint = this.#metricsEndpoint;\n const signal = this.#state.signal;\n let resp;\n try {\n resp = await fetch(metricsEndpoint, {signal});\n } catch (e) {\n if (signal.aborted) {\n // not an error.\n return;\n }\n // Treat exceptions from fetch (e.g. network errors) as non-fatal, and simply\n // log them and skip the watermark check until the next interval.\n this.#lc.warn?.(`unable to fetch metrics at ${this.#metricsEndpoint}`, e);\n return;\n }\n if (!resp.ok) {\n this.#lc.warn?.(\n `unable to fetch metrics at ${this.#metricsEndpoint}: ${await resp.text()}`,\n );\n return;\n }\n\n const families = parsePrometheusTextFormat(await resp.text());\n for (const family of families) {\n if (\n family.type === 'GAUGE' &&\n family.name === 'litestream_replica_progress'\n ) {\n for (const metric of family.metrics) {\n const watermark = metric.labels?.watermark;\n const name = metric.labels?.name;\n const time = new Date(parseFloat(metric.value) * 1000);\n if (watermark) {\n try {\n versionFromLexi(watermark); // Sanity check: Ensure that it is valid.\n yield {watermark, time, name};\n } catch (e) {\n this.#lc.warn?.(\n `ignoring invalid watermark ${watermark}`,\n {metric},\n e,\n );\n }\n }\n }\n }\n }\n }\n\n async #checkWatermarks() {\n for await (const {watermark, name, time} of this.#fetchWatermarks()) {\n if (watermark > this.#lastWatermark && !this.#watermarks.has(watermark)) {\n this.#lc.info?.(\n `replicated watermark=${watermark} to ${name}` +\n ` at ${time.toISOString()}.`,\n );\n this.#watermarks.set(watermark, time);\n this.#latestBackupTime = time;\n }\n }\n return this.#latestBackupTime;\n }\n\n async #publishVerifiedWatermarks() {\n const maxBackup = this.#maxBackupBefore(Date.now());\n if (!maxBackup) {\n return;\n }\n const {watermark: maxWatermark, backupTime: claimedTime} = maxBackup;\n // Purge guard: the watermarks (and their backup times) come from\n // litestream metrics, which are exported when litestream *believes*\n // an upload succeeded, and have been observed to advance even when\n // nothing was actually written to the backup destination. Purging the\n // change-log based on a falsely advancing watermark permanently breaks\n // the ability to restore + catch up. Before advancing the cleanup\n // watermark, verify it against the actual backup state: a claimed\n // backup time is only trusted if an object was actually uploaded to\n // the replica destination at (or after) that time, modulo clock skew.\n if (!this.#confirmedDurable(claimedTime)) {\n try {\n this.#lastVerifiedUploadTime = await this.#verifyBackupStateTimed();\n } catch (e) {\n this.#purgesBlocked.add(1, {reason: 'verification-failed'});\n // Skipping the purge is safe: the change-log just grows.\n this.#lc.warn?.(\n `unable to verify backup state. skipping change-log cleanup ` +\n `up to watermark ${maxWatermark} ` +\n `(claimed backup time ${claimedTime.toISOString()})`,\n e,\n );\n return;\n }\n }\n // Watermarks whose backup time isn't yet confirmed durable remain in the\n // map and are re-evaluated at the next check.\n const lastUpload = must(this.#lastVerifiedUploadTime);\n const verifiedBackup = this.#maxBackupBefore(\n lastUpload.getTime() + BACKUP_VERIFICATION_SLACK_MS,\n );\n if (!verifiedBackup) {\n this.#purgesBlocked.add(1, {reason: 'backup-stale'});\n const now = Date.now();\n if (this.#backupStaleSince === null) {\n this.#backupStaleSince = now;\n }\n const staleForMs = now - this.#backupStaleSince;\n this.#lc.warn?.(\n `blocked change-log cleanup up to watermark ${maxWatermark}: ` +\n `litestream claims it was backed up at ` +\n `${claimedTime.toISOString()}, but the last object actually ` +\n `uploaded to ${this.#backupURL} was at ` +\n `${lastUpload.toISOString()}. ` +\n `The backup has been stale for ${staleForMs} ms.`,\n );\n if (staleForMs >= WEDGED_SHUTDOWN_GRACE_MS) {\n this.#shutDownOnWedgedBackup(staleForMs, claimedTime, lastUpload);\n }\n return;\n }\n // The cleanup watermark advanced past a real upload, so the backup is\n // keeping up: clear the staleness clock.\n this.#backupStaleSince = null;\n const {watermark: verifiedWatermark, backupTime} = verifiedBackup;\n for (const watermark of this.#watermarks.keys()) {\n if (watermark <= verifiedWatermark) {\n this.#watermarks.delete(watermark);\n }\n }\n this.#stream.push({\n watermark: verifiedWatermark,\n backupTimeMs: backupTime.getTime(),\n });\n this.#lastWatermark = verifiedWatermark;\n }\n\n async #verifyBackupStateTimed(): Promise<Date> {\n const start = performance.now();\n let result: 'success' | 'error' = 'error';\n try {\n const ret = await this.#verifyBackupState();\n result = 'success';\n return ret;\n } finally {\n litestreamBackupVerificationDuration().recordMs(\n performance.now() - start,\n {\n ...litestreamMonitorMetricAttrs(\n this.#backupURL,\n 'legacy',\n 'replication_manager',\n ),\n result,\n },\n );\n }\n }\n\n /**\n * Called when the backup has been continuously stale for longer than\n * {@link WEDGED_SHUTDOWN_GRACE_MS}. Continuing to run risks corrupting the\n * backup, so the process is shut down by rejecting the {@link run()}\n * promise. The exit is non-zero and logged at ERROR for alerting; on\n * restart the monitor re-verifies, so a still-wedged backup keeps the\n * process down until the destination is fixed.\n */\n #shutDownOnWedgedBackup(\n staleForMs: number,\n claimedTime: Date,\n lastUpload: Date,\n ) {\n this.#purgesBlocked.add(1, {reason: 'backup-wedged'});\n const err = new UnrecoverableError(\n `backup at ${this.#backupURL} is wedged: litestream claims a backup ` +\n `at ${claimedTime.toISOString()}, but the last object actually ` +\n `uploaded was at ${lastUpload.toISOString()}, and the backup has ` +\n `not advanced for ${staleForMs} ms (grace period ` +\n `${WEDGED_SHUTDOWN_GRACE_MS} ms). Shutting down to avoid corrupting ` +\n `the backup; investigate the backup destination.`,\n );\n this.#lc.error?.(err.message);\n this.#stream.fail(err);\n }\n\n /**\n * On a fresh stack, fails loudly if no restorable backup has appeared within\n * a generous, replica-size-scaled grace period (see\n * {@link INITIAL_BACKUP_GRACE_MS_PER_UNIT}). This moves the \"give up\" decision\n * onto the replication-manager — the producer responsible for creating the\n * backup — instead of leaving view-syncers to wait (and the platform to keep\n * restarting them) while the real fault is here. Does nothing once a\n * restorable backup has been confirmed.\n */\n async #checkInitialBackupDeadline() {\n if (this.#lastVerifiedUploadTime !== null || this.#runStartTime === null) {\n return;\n }\n const elapsed = Date.now() - this.#runStartTime;\n if (elapsed < this.#initialBackupGraceMs()) {\n return;\n }\n // The deadline has elapsed without a confirmed backup. Do one definitive\n // check against the destination before giving up, in case a backup landed\n // but was never exercised by a reservation or a cleanup verification.\n if (await this.#confirmRestorableBackup()) {\n return;\n }\n this.#shutDownOnMissingInitialBackup(elapsed);\n }\n\n /**\n * The grace period for the first restorable backup, scaled by replica size to\n * mirror the platform's storage-scaled startup-probe allowance. Falls back to\n * the minimum (one unit) if the replica file size cannot be determined.\n */\n #initialBackupGraceMs(): number {\n let bytes = 0;\n try {\n bytes = statSync(this.#replicaFile).size;\n } catch {\n // Replica file not present/readable yet; fall back to the minimum grace.\n }\n const units = Math.max(\n 1,\n Math.ceil(bytes / INITIAL_BACKUP_GRACE_UNIT_BYTES),\n );\n return units * INITIAL_BACKUP_GRACE_MS_PER_UNIT;\n }\n\n /**\n * Called when no restorable backup has appeared within\n * {@link #initialBackupGraceMs}. Continuing to run is pointless — no\n * view-syncer can restore — and the fault is the backup pipeline, so the\n * process exits non-zero with a logged error for alerting, mirroring\n * {@link #shutDownOnWedgedBackup}.\n */\n #shutDownOnMissingInitialBackup(elapsedMs: number) {\n const err = new UnrecoverableError(\n `no restorable backup has appeared at ${this.#backupURL} within ` +\n `${elapsedMs} ms of startup (grace period ` +\n `${this.#initialBackupGraceMs()} ms). The initial backup pipeline ` +\n `appears to be broken; shutting down so that the replication-manager ` +\n `is flagged as the cause rather than the view-syncers waiting on it. ` +\n `Investigate litestream replication to ${this.#backupURL}.`,\n );\n this.#lc.error?.(err.message);\n this.#stream.fail(err);\n }\n\n /**\n * Returns the newest watermark whose backup time is at or before `cutoff`\n * (epoch ms), or `''` if there is none.\n */\n #maxBackupBefore(\n cutoff: number,\n ): {watermark: string; backupTime: Date} | null {\n let max = '';\n let maxBackupTime: Date | undefined;\n for (const [watermark, backupTime] of this.#watermarks) {\n if (backupTime.getTime() <= cutoff && watermark > max) {\n max = watermark;\n maxBackupTime = backupTime;\n }\n }\n if (!maxBackupTime) {\n return null;\n }\n return {watermark: max, backupTime: maxBackupTime};\n }\n\n /**\n * Returns `true` if the actual backup state, as last verified against the\n * backup destination, confirms that data claimed to be backed up at\n * `claimedTime` is durable (i.e. an object was actually uploaded at or\n * after `claimedTime`, allowing {@link BACKUP_VERIFICATION_SLACK_MS} of\n * clock skew).\n */\n #confirmedDurable(claimedTime: Date): boolean {\n const lastUpload = this.#lastVerifiedUploadTime;\n return (\n lastUpload !== null &&\n claimedTime.getTime() <=\n lastUpload.getTime() + BACKUP_VERIFICATION_SLACK_MS\n );\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { LogContext } from '@rocicorp/logger';
2
+ import type { Source } from '../../types/streams.ts';
3
+ import type { BackedUpWatermark } from './backup-monitor.ts';
4
+ /**
5
+ * The single-node equivalent of a backup monitor polls the replica file every
6
+ * 30 seconds and schedules cleanup when the watermark
7
+ * (i.e. stateVersion) moves forward.
8
+ */
9
+ export declare class ReplicaPoller {
10
+ #private;
11
+ readonly id = "replica-poller";
12
+ constructor(lc: LogContext, replicaFile: string);
13
+ start(): Source<BackedUpWatermark>;
14
+ }
15
+ //# sourceMappingURL=replica-poller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replica-poller.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/replica-poller.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAGjD,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,wBAAwB,CAAC;AAGnD,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAC;AAI3D;;;;GAIG;AACH,qBAAa,aAAa;;IACxB,QAAQ,CAAC,EAAE,oBAAoB;gBAQnB,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM;IAQ/C,KAAK,IAAI,MAAM,CAAC,iBAAiB,CAAC;CAyBnC"}