@rocicorp/zero 1.9.1-canary.1 → 1.10.0-canary.1

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 (344) hide show
  1. package/out/analyze-query/src/analyze-cli.js +2 -2
  2. package/out/analyze-query/src/analyze-cli.js.map +1 -1
  3. package/out/replicache/src/db/commit.js +1 -4
  4. package/out/replicache/src/db/commit.js.map +1 -1
  5. package/out/replicache/src/db/read.d.ts +1 -0
  6. package/out/replicache/src/db/read.d.ts.map +1 -1
  7. package/out/replicache/src/db/read.js +1 -4
  8. package/out/replicache/src/db/read.js.map +1 -1
  9. package/out/replicache/src/db/write.d.ts +2 -1
  10. package/out/replicache/src/db/write.d.ts.map +1 -1
  11. package/out/replicache/src/db/write.js +4 -51
  12. package/out/replicache/src/db/write.js.map +1 -1
  13. package/out/replicache/src/persist/clients.d.ts +1 -0
  14. package/out/replicache/src/persist/clients.d.ts.map +1 -1
  15. package/out/replicache/src/persist/clients.js +4 -31
  16. package/out/replicache/src/persist/clients.js.map +1 -1
  17. package/out/replicache/src/process-env.d.ts +28 -0
  18. package/out/replicache/src/process-env.d.ts.map +1 -0
  19. package/out/replicache/src/replicache-impl.d.ts.map +1 -1
  20. package/out/replicache/src/replicache-impl.js.map +1 -1
  21. package/out/replicache/src/scan-iterator.js +2 -14
  22. package/out/replicache/src/scan-iterator.js.map +1 -1
  23. package/out/replicache/src/subscriptions.d.ts +1 -0
  24. package/out/replicache/src/subscriptions.d.ts.map +1 -1
  25. package/out/replicache/src/subscriptions.js +2 -10
  26. package/out/replicache/src/subscriptions.js.map +1 -1
  27. package/out/replicache/src/transactions.d.ts +1 -0
  28. package/out/replicache/src/transactions.d.ts.map +1 -1
  29. package/out/replicache/src/transactions.js +2 -4
  30. package/out/replicache/src/transactions.js.map +1 -1
  31. package/out/zero/package.js +2 -2
  32. package/out/zero/package.js.map +1 -1
  33. package/out/zero-cache/src/config/normalize.d.ts +1 -0
  34. package/out/zero-cache/src/config/normalize.d.ts.map +1 -1
  35. package/out/zero-cache/src/config/normalize.js +10 -1
  36. package/out/zero-cache/src/config/normalize.js.map +1 -1
  37. package/out/zero-cache/src/config/zero-config.d.ts +43 -1
  38. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  39. package/out/zero-cache/src/config/zero-config.js +139 -20
  40. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  41. package/out/zero-cache/src/db/migration.js +1 -1
  42. package/out/zero-cache/src/observability/metrics.d.ts +5 -0
  43. package/out/zero-cache/src/observability/metrics.d.ts.map +1 -1
  44. package/out/zero-cache/src/observability/metrics.js +15 -1
  45. package/out/zero-cache/src/observability/metrics.js.map +1 -1
  46. package/out/zero-cache/src/server/backup-watermark-poller.js +31 -0
  47. package/out/zero-cache/src/server/backup-watermark-poller.js.map +1 -0
  48. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  49. package/out/zero-cache/src/server/change-streamer.js +90 -30
  50. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  51. package/out/zero-cache/src/server/main.d.ts.map +1 -1
  52. package/out/zero-cache/src/server/main.js +5 -15
  53. package/out/zero-cache/src/server/main.js.map +1 -1
  54. package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
  55. package/out/zero-cache/src/server/replicator.js +46 -3
  56. package/out/zero-cache/src/server/replicator.js.map +1 -1
  57. package/out/zero-cache/src/server/runner/run-worker.js +2 -1
  58. package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
  59. package/out/zero-cache/src/server/worker-urls.d.ts +1 -1
  60. package/out/zero-cache/src/server/worker-urls.js +2 -2
  61. package/out/zero-cache/src/server/worker-urls.js.map +1 -1
  62. package/out/zero-cache/src/services/change-source/change-source.d.ts +4 -2
  63. package/out/zero-cache/src/services/change-source/change-source.d.ts.map +1 -1
  64. package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts +35 -0
  65. package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts.map +1 -0
  66. package/out/zero-cache/src/services/change-source/common/replica-restore.js +34 -0
  67. package/out/zero-cache/src/services/change-source/common/replica-restore.js.map +1 -0
  68. package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts +2 -0
  69. package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts.map +1 -1
  70. package/out/zero-cache/src/services/change-source/common/replica-schema.js +73 -1
  71. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  72. package/out/zero-cache/src/services/change-source/custom/change-source.d.ts +2 -6
  73. package/out/zero-cache/src/services/change-source/custom/change-source.d.ts.map +1 -1
  74. package/out/zero-cache/src/services/change-source/custom/change-source.js +16 -7
  75. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  76. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +4 -7
  77. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  78. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts +7 -7
  79. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  80. package/out/zero-cache/src/services/change-source/pg/change-source.js +75 -23
  81. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  82. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +9 -1
  83. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  84. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +34 -4
  85. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  86. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +1 -1
  87. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +4 -2
  88. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -1
  89. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +7 -4
  90. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  91. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts +0 -5
  92. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
  93. package/out/zero-cache/src/services/change-source/pg/schema/init.js +29 -41
  94. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  95. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +35 -16
  96. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
  97. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +77 -7
  98. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  99. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts +1 -1
  100. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts.map +1 -1
  101. package/out/zero-cache/src/services/change-source/protocol/current/data.js +1 -12
  102. package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
  103. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +3 -3
  104. package/out/zero-cache/src/services/change-source/protocol/current/schema-change-tags.d.ts +6 -0
  105. package/out/zero-cache/src/services/change-source/protocol/current/schema-change-tags.d.ts.map +1 -0
  106. package/out/zero-cache/src/services/change-source/protocol/current/schema-change-tags.js +21 -0
  107. package/out/zero-cache/src/services/change-source/protocol/current/schema-change-tags.js.map +1 -0
  108. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts +5 -6
  109. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts.map +1 -1
  110. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js +32 -9
  111. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
  112. package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts +15 -5
  113. package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts.map +1 -1
  114. package/out/zero-cache/src/services/change-streamer/backup-monitor.js +66 -0
  115. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -0
  116. package/out/zero-cache/src/services/change-streamer/broadcast.d.ts +68 -78
  117. package/out/zero-cache/src/services/change-streamer/broadcast.d.ts.map +1 -1
  118. package/out/zero-cache/src/services/change-streamer/broadcast.js +84 -92
  119. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  120. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts +19 -0
  121. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts.map +1 -0
  122. package/out/zero-cache/src/services/change-streamer/change-log-codec.js +50 -0
  123. package/out/zero-cache/src/services/change-streamer/change-log-codec.js.map +1 -0
  124. package/out/zero-cache/src/services/change-streamer/change-log-initializer.d.ts +141 -0
  125. package/out/zero-cache/src/services/change-streamer/change-log-initializer.d.ts.map +1 -0
  126. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js +270 -0
  127. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js.map +1 -0
  128. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.d.ts +19 -0
  129. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.d.ts.map +1 -0
  130. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.js +39 -0
  131. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.js.map +1 -0
  132. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +3 -4
  133. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  134. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +15 -20
  135. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  136. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +69 -24
  137. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
  138. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +330 -63
  139. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  140. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +55 -13
  141. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
  142. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  143. package/out/zero-cache/src/services/change-streamer/error-type-enum.d.ts +8 -0
  144. package/out/zero-cache/src/services/change-streamer/error-type-enum.d.ts.map +1 -1
  145. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts +14 -3
  146. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts.map +1 -1
  147. package/out/zero-cache/src/services/change-streamer/forwarder.js +41 -11
  148. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  149. package/out/zero-cache/src/services/change-streamer/{litestream3-backup-monitor.d.ts → litestream3-prometheus-poller.d.ts} +19 -45
  150. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.d.ts.map +1 -0
  151. package/out/zero-cache/src/services/change-streamer/{litestream3-backup-monitor.js → litestream3-prometheus-poller.js} +57 -171
  152. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js.map +1 -0
  153. package/out/zero-cache/src/services/change-streamer/replica-poller.d.ts +15 -0
  154. package/out/zero-cache/src/services/change-streamer/replica-poller.d.ts.map +1 -0
  155. package/out/zero-cache/src/services/change-streamer/replica-poller.js +55 -0
  156. package/out/zero-cache/src/services/change-streamer/replica-poller.js.map +1 -0
  157. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts +5 -1
  158. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts.map +1 -1
  159. package/out/zero-cache/src/services/change-streamer/schema/tables.js +2 -1
  160. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  161. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts +14 -0
  162. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts.map +1 -0
  163. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js +86 -0
  164. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -0
  165. package/out/zero-cache/src/services/change-streamer/snapshot.d.ts +4 -1
  166. package/out/zero-cache/src/services/change-streamer/snapshot.d.ts.map +1 -1
  167. package/out/zero-cache/src/services/change-streamer/snapshot.js +39 -14
  168. package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
  169. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.d.ts +75 -0
  170. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.d.ts.map +1 -0
  171. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js +261 -0
  172. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js.map +1 -0
  173. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.d.ts +121 -0
  174. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.d.ts.map +1 -0
  175. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.js +391 -0
  176. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.js.map +1 -0
  177. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts +62 -0
  178. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts.map +1 -0
  179. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js +159 -0
  180. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js.map +1 -0
  181. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts +109 -0
  182. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts.map +1 -0
  183. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +258 -0
  184. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js.map +1 -0
  185. package/out/zero-cache/src/services/change-streamer/storer.d.ts +22 -12
  186. package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
  187. package/out/zero-cache/src/services/change-streamer/storer.js +93 -117
  188. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  189. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +61 -10
  190. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
  191. package/out/zero-cache/src/services/change-streamer/subscriber.js +101 -8
  192. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  193. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts +34 -0
  194. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts.map +1 -0
  195. package/out/zero-cache/src/services/change-streamer/upstream-acker.js +86 -0
  196. package/out/zero-cache/src/services/change-streamer/upstream-acker.js.map +1 -0
  197. package/out/zero-cache/src/services/life-cycle.d.ts +1 -1
  198. package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
  199. package/out/zero-cache/src/services/life-cycle.js +3 -2
  200. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  201. package/out/zero-cache/src/services/litestream/commands.d.ts +12 -11
  202. package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
  203. package/out/zero-cache/src/services/litestream/commands.js +50 -140
  204. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  205. package/out/zero-cache/src/services/litestream/config-v5.yml +42 -0
  206. package/out/zero-cache/src/services/litestream/metrics.d.ts +4 -4
  207. package/out/zero-cache/src/services/litestream/metrics.d.ts.map +1 -1
  208. package/out/zero-cache/src/services/litestream/metrics.js +7 -10
  209. package/out/zero-cache/src/services/litestream/metrics.js.map +1 -1
  210. package/out/zero-cache/src/services/litestream/vfs-watermark-poller.d.ts +57 -0
  211. package/out/zero-cache/src/services/litestream/vfs-watermark-poller.d.ts.map +1 -0
  212. package/out/zero-cache/src/services/litestream/vfs-watermark-poller.js +137 -0
  213. package/out/zero-cache/src/services/litestream/vfs-watermark-poller.js.map +1 -0
  214. package/out/zero-cache/src/services/replicator/change-log-cookies.d.ts +169 -0
  215. package/out/zero-cache/src/services/replicator/change-log-cookies.d.ts.map +1 -0
  216. package/out/zero-cache/src/services/replicator/change-log-cookies.js +434 -0
  217. package/out/zero-cache/src/services/replicator/change-log-cookies.js.map +1 -0
  218. package/out/zero-cache/src/services/replicator/change-log-db.d.ts +358 -0
  219. package/out/zero-cache/src/services/replicator/change-log-db.d.ts.map +1 -0
  220. package/out/zero-cache/src/services/replicator/change-log-db.js +477 -0
  221. package/out/zero-cache/src/services/replicator/change-log-db.js.map +1 -0
  222. package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts +55 -0
  223. package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts.map +1 -0
  224. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js +150 -0
  225. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js.map +1 -0
  226. package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +1 -1
  227. package/out/zero-cache/src/services/replicator/change-processor.js +44 -17
  228. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  229. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts +1 -1
  230. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
  231. package/out/zero-cache/src/services/replicator/incremental-sync.js +11 -4
  232. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  233. package/out/zero-cache/src/services/replicator/replicator.d.ts +1 -1
  234. package/out/zero-cache/src/services/replicator/replicator.d.ts.map +1 -1
  235. package/out/zero-cache/src/services/replicator/replicator.js +2 -2
  236. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  237. package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts +119 -0
  238. package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts.map +1 -0
  239. package/out/zero-cache/src/services/replicator/schema/backfilling.js +214 -0
  240. package/out/zero-cache/src/services/replicator/schema/backfilling.js.map +1 -0
  241. package/out/zero-cache/src/services/replicator/schema/change-log.d.ts +3 -3
  242. package/out/zero-cache/src/services/replicator/schema/column-metadata.js +1 -1
  243. package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
  244. package/out/zero-cache/src/services/replicator/schema/replication-state.d.ts +1 -1
  245. package/out/zero-cache/src/services/replicator/schema/replication-state.d.ts.map +1 -1
  246. package/out/zero-cache/src/services/replicator/schema/replication-state.js +9 -5
  247. package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
  248. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts +131 -0
  249. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts.map +1 -0
  250. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +413 -0
  251. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -0
  252. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.d.ts +120 -0
  253. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.d.ts.map +1 -0
  254. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js +283 -0
  255. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js.map +1 -0
  256. package/out/zero-cache/src/services/replicator/write-worker.js +9 -6
  257. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  258. package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
  259. package/out/zero-cache/src/services/run-ast.js +2 -1
  260. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  261. package/out/zero-cache/src/services/running-state.d.ts +1 -1
  262. package/out/zero-cache/src/services/running-state.d.ts.map +1 -1
  263. package/out/zero-cache/src/services/running-state.js +1 -0
  264. package/out/zero-cache/src/services/running-state.js.map +1 -1
  265. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.d.ts +9 -0
  266. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.d.ts.map +1 -1
  267. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.js +9 -0
  268. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.js.map +1 -1
  269. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  270. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +18 -3
  271. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  272. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +11 -1
  273. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  274. package/out/zero-cache/src/services/view-syncer/view-syncer.js +48 -7
  275. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  276. package/out/zero-cache/src/types/processes.d.ts +1 -2
  277. package/out/zero-cache/src/types/processes.d.ts.map +1 -1
  278. package/out/zero-cache/src/types/processes.js +1 -25
  279. package/out/zero-cache/src/types/processes.js.map +1 -1
  280. package/out/zero-cache/src/types/streams.d.ts +10 -0
  281. package/out/zero-cache/src/types/streams.d.ts.map +1 -1
  282. package/out/zero-cache/src/types/streams.js +25 -4
  283. package/out/zero-cache/src/types/streams.js.map +1 -1
  284. package/out/zero-cache/src/workers/connection.d.ts +1 -0
  285. package/out/zero-cache/src/workers/connection.d.ts.map +1 -1
  286. package/out/zero-cache/src/workers/connection.js +25 -3
  287. package/out/zero-cache/src/workers/connection.js.map +1 -1
  288. package/out/zero-cache/src/workers/replicator.d.ts +25 -0
  289. package/out/zero-cache/src/workers/replicator.d.ts.map +1 -1
  290. package/out/zero-cache/src/workers/replicator.js +33 -1
  291. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  292. package/out/zero-cache/src/workers/syncer.d.ts +12 -0
  293. package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
  294. package/out/zero-cache/src/workers/syncer.js +75 -0
  295. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  296. package/out/zero-client/src/client/version.js +1 -1
  297. package/out/zero-schema/src/builder/table-builder.d.ts +23 -0
  298. package/out/zero-schema/src/builder/table-builder.d.ts.map +1 -1
  299. package/out/zero-schema/src/builder/table-builder.js +18 -0
  300. package/out/zero-schema/src/builder/table-builder.js.map +1 -1
  301. package/out/zero-types/src/schema.d.ts +11 -0
  302. package/out/zero-types/src/schema.d.ts.map +1 -1
  303. package/out/zql/src/query/query-impl.d.ts +2 -2
  304. package/out/zql/src/query/query-impl.d.ts.map +1 -1
  305. package/out/zql/src/query/query-impl.js.map +1 -1
  306. package/out/zql/src/query/query.d.ts +83 -14
  307. package/out/zql/src/query/query.d.ts.map +1 -1
  308. package/out/zql/src/query/static-query.d.ts +1 -1
  309. package/out/zql/src/query/static-query.d.ts.map +1 -1
  310. package/out/zqlite/src/resolve-scalar-subqueries.d.ts +15 -0
  311. package/out/zqlite/src/resolve-scalar-subqueries.d.ts.map +1 -1
  312. package/out/zqlite/src/resolve-scalar-subqueries.js +20 -13
  313. package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
  314. package/package.json +2 -2
  315. package/out/replicache/src/db/index.js +0 -91
  316. package/out/replicache/src/db/index.js.map +0 -1
  317. package/out/replicache/src/lazy.d.ts +0 -2
  318. package/out/replicache/src/lazy.d.ts.map +0 -1
  319. package/out/replicache/src/lazy.js +0 -12
  320. package/out/replicache/src/lazy.js.map +0 -1
  321. package/out/zero-cache/src/server/backup-watermark-reader.js +0 -35
  322. package/out/zero-cache/src/server/backup-watermark-reader.js.map +0 -1
  323. package/out/zero-cache/src/services/change-streamer/litestream3-backup-monitor.d.ts.map +0 -1
  324. package/out/zero-cache/src/services/change-streamer/litestream3-backup-monitor.js.map +0 -1
  325. package/out/zero-cache/src/services/change-streamer/replica-monitor.d.ts +0 -16
  326. package/out/zero-cache/src/services/change-streamer/replica-monitor.d.ts.map +0 -1
  327. package/out/zero-cache/src/services/change-streamer/replica-monitor.js +0 -54
  328. package/out/zero-cache/src/services/change-streamer/replica-monitor.js.map +0 -1
  329. package/out/zero-cache/src/services/change-streamer/vfs-backup-monitor.d.ts +0 -25
  330. package/out/zero-cache/src/services/change-streamer/vfs-backup-monitor.d.ts.map +0 -1
  331. package/out/zero-cache/src/services/change-streamer/vfs-backup-monitor.js +0 -159
  332. package/out/zero-cache/src/services/change-streamer/vfs-backup-monitor.js.map +0 -1
  333. package/out/zero-cache/src/services/litestream/vfs-watermark-reader.d.ts +0 -44
  334. package/out/zero-cache/src/services/litestream/vfs-watermark-reader.d.ts.map +0 -1
  335. package/out/zero-cache/src/services/litestream/vfs-watermark-reader.js +0 -65
  336. package/out/zero-cache/src/services/litestream/vfs-watermark-reader.js.map +0 -1
  337. package/out/zero-cache/src/services/litestream/vfs-watermark-worker-source.d.ts +0 -11
  338. package/out/zero-cache/src/services/litestream/vfs-watermark-worker-source.d.ts.map +0 -1
  339. package/out/zero-cache/src/services/litestream/vfs-watermark-worker-source.js +0 -74
  340. package/out/zero-cache/src/services/litestream/vfs-watermark-worker-source.js.map +0 -1
  341. package/out/zero-cache/src/services/litestream/vfs-watermark-worker.d.ts +0 -42
  342. package/out/zero-cache/src/services/litestream/vfs-watermark-worker.d.ts.map +0 -1
  343. package/out/zero-cache/src/services/litestream/vfs-watermark-worker.js +0 -117
  344. package/out/zero-cache/src/services/litestream/vfs-watermark-worker.js.map +0 -1
@@ -13,7 +13,7 @@ import type { WriteWorkerClient } from './write-worker-client.ts';
13
13
  */
14
14
  export declare class IncrementalSyncer {
15
15
  #private;
16
- constructor(lc: LogContext, taskID: string, id: string, changeStreamer: ChangeStreamer, worker: WriteWorkerClient, mode: ReplicatorMode, statusPublisher: ReplicationStatusPublisher | null);
16
+ constructor(lc: LogContext, taskID: string, id: string, changeStreamer: ChangeStreamer, worker: WriteWorkerClient, mode: ReplicatorMode, replicaDbPath: string, statusPublisher: ReplicationStatusPublisher | null);
17
17
  run(): Promise<void>;
18
18
  subscribe(): Source<ReplicaState>;
19
19
  stop(lc: LogContext, err?: unknown): void;
@@ -1 +1 @@
1
- {"version":3,"file":"incremental-sync.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/replicator/incremental-sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAIjD,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,wBAAwB,CAAC;AAGnD,OAAO,EAGL,KAAK,cAAc,EAEpB,MAAM,uCAAuC,CAAC;AAK/C,OAAO,KAAK,EAAC,0BAA0B,EAAC,MAAM,yBAAyB,CAAC;AACxE,OAAO,KAAK,EAAC,YAAY,EAAE,cAAc,EAAC,MAAM,iBAAiB,CAAC;AAGlE,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,0BAA0B,CAAC;AAIhE;;;;;;GAMG;AACH,qBAAa,iBAAiB;;gBAoB1B,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,cAAc,EAAE,cAAc,EAC9B,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,cAAc,EACpB,eAAe,EAAE,0BAA0B,GAAG,IAAI;IAa9C,GAAG;IAuKT,SAAS,IAAI,MAAM,CAAC,YAAY,CAAC;IAIjC,IAAI,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,OAAO;CAGnC"}
1
+ {"version":3,"file":"incremental-sync.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/replicator/incremental-sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAKjD,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,wBAAwB,CAAC;AAGnD,OAAO,EAGL,KAAK,cAAc,EAEpB,MAAM,uCAAuC,CAAC;AAK/C,OAAO,KAAK,EAAC,0BAA0B,EAAC,MAAM,yBAAyB,CAAC;AACxE,OAAO,KAAK,EAAC,YAAY,EAAE,cAAc,EAAC,MAAM,iBAAiB,CAAC;AAGlE,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,0BAA0B,CAAC;AAIhE;;;;;;GAMG;AACH,qBAAa,iBAAiB;;gBAqB1B,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,cAAc,EAAE,cAAc,EAC9B,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,cAAc,EACpB,aAAa,EAAE,MAAM,EACrB,eAAe,EAAE,0BAA0B,GAAG,IAAI;IAc9C,GAAG;IAqLT,SAAS,IAAI,MAAM,CAAC,YAAY,CAAC;IAIjC,IAAI,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,OAAO;CAGnC"}
@@ -1,6 +1,7 @@
1
1
  import { AbortError } from "../../../../shared/src/abort-error.js";
2
2
  import { getOrCreateCounter } from "../../observability/metrics.js";
3
3
  import { RunningState } from "../running-state.js";
4
+ import { deleteLiteDB } from "../../db/delete-lite-db.js";
4
5
  import { errorTypeToReadableName } from "../change-streamer/change-streamer.js";
5
6
  import { Notifier } from "./notifier.js";
6
7
  import { ReplicationReportRecorder } from "./reporter/recorder.js";
@@ -19,18 +20,20 @@ var IncrementalSyncer = class {
19
20
  #changeStreamer;
20
21
  #worker;
21
22
  #mode;
23
+ #replicaDbPath;
22
24
  #statusPublisher;
23
25
  #notifier;
24
26
  #reporter;
25
27
  #state = new RunningState("IncrementalSyncer");
26
28
  #replicationEvents = getOrCreateCounter("replication", "events", "Number of replication events processed");
27
- constructor(lc, taskID, id, changeStreamer, worker, mode, statusPublisher) {
29
+ constructor(lc, taskID, id, changeStreamer, worker, mode, replicaDbPath, statusPublisher) {
28
30
  this.#lc = lc;
29
31
  this.#taskID = taskID;
30
32
  this.#id = id;
31
33
  this.#changeStreamer = changeStreamer;
32
34
  this.#worker = worker;
33
35
  this.#mode = mode;
36
+ this.#replicaDbPath = replicaDbPath;
34
37
  this.#statusPublisher = statusPublisher;
35
38
  this.#notifier = new Notifier();
36
39
  this.#reporter = new ReplicationReportRecorder(lc);
@@ -61,13 +64,14 @@ var IncrementalSyncer = class {
61
64
  mode: this.#mode,
62
65
  watermark,
63
66
  replicaVersion,
64
- initial: watermark === initialWatermark
67
+ initial: watermark === initialWatermark,
68
+ logsChangeStream: false
65
69
  });
66
70
  this.#state.resetBackoff();
67
71
  unregister = this.#state.cancelOnStop(downstream);
68
72
  this.#statusPublisher?.publish(lc, "Replicating", `Replicating from ${watermark}`);
69
73
  let backfillStatus;
70
- for await (const message of downstream) {
74
+ for await (const { data: message } of downstream) {
71
75
  this.#replicationEvents.add(1);
72
76
  switch (message[0]) {
73
77
  case "status": {
@@ -84,6 +88,8 @@ var IncrementalSyncer = class {
84
88
  }
85
89
  case "error": {
86
90
  const { type, message: msg } = message[1];
91
+ lc.warn?.(`received error from change-streamer. deleting replica file`, { error: message[1] });
92
+ deleteLiteDB(this.#replicaDbPath);
87
93
  this.stop(lc, new AbortError(`${errorTypeToReadableName(type)}: ${msg}`));
88
94
  break;
89
95
  }
@@ -101,7 +107,8 @@ var IncrementalSyncer = class {
101
107
  }
102
108
  backfillStatus = status;
103
109
  }
104
- const result = await this.#worker.processMessage(message);
110
+ const data = message;
111
+ const result = await this.#worker.processMessage(data);
105
112
  this.#handleResult(lc, result);
106
113
  if (result?.completedBackfill) backfillStatus = void 0;
107
114
  break;
@@ -1 +1 @@
1
- {"version":3,"file":"incremental-sync.js","names":["#lc","#taskID","#id","#changeStreamer","#worker","#mode","#statusPublisher","#notifier","#reporter","#state","#replicationEvents","#handleResult"],"sources":["../../../../../../zero-cache/src/services/replicator/incremental-sync.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {AbortError} from '../../../../shared/src/abort-error.ts';\nimport type {Enum} from '../../../../shared/src/enum.ts';\nimport {getOrCreateCounter} from '../../observability/metrics.ts';\nimport type {Source} from '../../types/streams.ts';\nimport type {DownloadStatus} from '../change-source/protocol/current.ts';\nimport type {ChangeStreamData} from '../change-source/protocol/current/downstream.ts';\nimport {\n errorTypeToReadableName,\n PROTOCOL_VERSION,\n type ChangeStreamer,\n type Downstream,\n} from '../change-streamer/change-streamer.ts';\nimport type * as ErrorType from '../change-streamer/error-type-enum.ts';\nimport {RunningState} from '../running-state.ts';\nimport type {CommitResult} from './change-processor.ts';\nimport {Notifier} from './notifier.ts';\nimport type {ReplicationStatusPublisher} from './replication-status.ts';\nimport type {ReplicaState, ReplicatorMode} from './replicator.ts';\nimport {ReplicationReportRecorder} from './reporter/recorder.ts';\nimport type {ReplicationReport} from './reporter/report-schema.ts';\nimport type {WriteWorkerClient} from './write-worker-client.ts';\n\ntype ErrorType = Enum<typeof ErrorType>;\n\n/**\n * The {@link IncrementalSyncer} manages a logical replication stream from upstream,\n * handling application lifecycle events (start, stop) and retrying the\n * connection with exponential backoff. The actual handling of the logical\n * replication messages is done by the {@link ChangeProcessor}, which runs\n * in a worker thread via the {@link WriteWorkerClient}.\n */\nexport class IncrementalSyncer {\n readonly #lc: LogContext;\n readonly #taskID: string;\n readonly #id: string;\n readonly #changeStreamer: ChangeStreamer;\n readonly #worker: WriteWorkerClient;\n readonly #mode: ReplicatorMode;\n readonly #statusPublisher: ReplicationStatusPublisher | null;\n readonly #notifier: Notifier;\n readonly #reporter: ReplicationReportRecorder;\n\n readonly #state = new RunningState('IncrementalSyncer');\n\n readonly #replicationEvents = getOrCreateCounter(\n 'replication',\n 'events',\n 'Number of replication events processed',\n );\n\n constructor(\n lc: LogContext,\n taskID: string,\n id: string,\n changeStreamer: ChangeStreamer,\n worker: WriteWorkerClient,\n mode: ReplicatorMode,\n statusPublisher: ReplicationStatusPublisher | null,\n ) {\n this.#lc = lc;\n this.#taskID = taskID;\n this.#id = id;\n this.#changeStreamer = changeStreamer;\n this.#worker = worker;\n this.#mode = mode;\n this.#statusPublisher = statusPublisher;\n this.#notifier = new Notifier();\n this.#reporter = new ReplicationReportRecorder(lc);\n }\n\n async run() {\n const lc = this.#lc;\n let workerError: Error | undefined;\n this.#worker.onError(err => {\n workerError ??= err;\n this.#state.stop(lc, err);\n });\n lc.info?.(`Starting IncrementalSyncer`);\n const {watermark: initialWatermark} =\n await this.#worker.getSubscriptionState();\n\n // Notify any waiting subscribers that the replica is ready to be read.\n // This initial notification intentionally omits replicaReadyTimeMs because\n // it represents already-current state, not newly-unserved work.\n void this.#notifier.notifySubscribers({\n state: 'version-ready',\n watermark: initialWatermark,\n });\n\n while (this.#state.shouldRun()) {\n const {replicaVersion, watermark} =\n await this.#worker.getSubscriptionState();\n\n let downstream: Source<Downstream> | undefined;\n let unregister = () => {};\n let err: unknown | undefined;\n\n try {\n downstream = await this.#changeStreamer.subscribe({\n protocolVersion: PROTOCOL_VERSION,\n taskID: this.#taskID,\n id: this.#id,\n mode: this.#mode,\n watermark,\n replicaVersion,\n initial: watermark === initialWatermark,\n });\n this.#state.resetBackoff();\n unregister = this.#state.cancelOnStop(downstream);\n this.#statusPublisher?.publish(\n lc,\n 'Replicating',\n `Replicating from ${watermark}`,\n );\n\n let backfillStatus: DownloadStatus | undefined;\n\n for await (const message of downstream) {\n this.#replicationEvents.add(1);\n switch (message[0]) {\n case 'status': {\n const {lagReport} = message[1];\n if (lagReport) {\n const report: ReplicationReport = {\n nextSendTimeMs: lagReport.nextSendTimeMs,\n };\n if (lagReport.lastTimings) {\n report.lastTimings = {\n ...lagReport.lastTimings,\n replicateTimeMs: Date.now(),\n };\n }\n this.#reporter.record(report);\n }\n break;\n }\n case 'error': {\n // Signal from the replication-manager that the view-syncer must\n // shut down and restore a new backup from litestream.\n const {type, message: msg} = message[1];\n this.stop(\n lc,\n // Note: The AbortError indicates a clean / intentional shutdown.\n new AbortError(\n `${errorTypeToReadableName(type as ErrorType)}: ${msg}`,\n ),\n );\n break;\n }\n default: {\n const msg = message[1];\n if (msg.tag === 'backfill' && msg.status) {\n const {status} = msg;\n if (!backfillStatus) {\n // Start publishing the status every 3 seconds.\n backfillStatus = status;\n this.#statusPublisher?.publish(\n lc,\n 'Replicating',\n `Backfilling ${msg.relation.name} table`,\n 3000,\n () =>\n backfillStatus\n ? {\n downloadStatus: [\n {\n ...backfillStatus,\n table: msg.relation.name,\n columns: [\n ...msg.relation.rowKey.columns,\n ...msg.columns,\n ],\n },\n ],\n }\n : {},\n );\n }\n backfillStatus = status; // Update the current status\n }\n\n const result = await this.#worker.processMessage(\n message as ChangeStreamData,\n );\n\n this.#handleResult(lc, result);\n if (result?.completedBackfill) {\n backfillStatus = undefined;\n }\n break;\n }\n }\n }\n this.#worker.abort();\n } catch (e) {\n err = e;\n this.#worker.abort();\n } finally {\n downstream?.cancel();\n unregister();\n this.#statusPublisher?.stop();\n }\n await this.#state.backoff(lc, err);\n }\n lc.info?.('IncrementalSyncer stopped');\n if (workerError) {\n throw workerError;\n }\n }\n\n #handleResult(lc: LogContext, result: CommitResult | null) {\n if (!result) {\n return;\n }\n if (result.completedBackfill) {\n // Publish the final status\n const status = result.completedBackfill;\n this.#statusPublisher?.publish(\n lc,\n 'Replicating',\n `Backfilled ${status.table} table`,\n 0,\n () => ({downloadStatus: [status]}),\n );\n } else if (result.schemaUpdated) {\n this.#statusPublisher?.publish(lc, 'Replicating', 'Schema updated');\n }\n if (result.watermark && result.changeLogUpdated) {\n void this.#notifier.notifySubscribers({\n state: 'version-ready',\n watermark: result.watermark,\n replicaReadyTimeMs: Date.now(),\n upstreamCommitTimeMs: result.upstreamCommitTimeMs,\n });\n }\n }\n\n subscribe(): Source<ReplicaState> {\n return this.#notifier.subscribe();\n }\n\n stop(lc: LogContext, err?: unknown) {\n this.#state.stop(lc, err);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"incremental-sync.js","names":["#lc","#taskID","#id","#changeStreamer","#worker","#mode","#replicaDbPath","#statusPublisher","#notifier","#reporter","#state","#replicationEvents","#handleResult"],"sources":["../../../../../../zero-cache/src/services/replicator/incremental-sync.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {AbortError} from '../../../../shared/src/abort-error.ts';\nimport type {Enum} from '../../../../shared/src/enum.ts';\nimport {deleteLiteDB} from '../../db/delete-lite-db.ts';\nimport {getOrCreateCounter} from '../../observability/metrics.ts';\nimport type {Source} from '../../types/streams.ts';\nimport type {DownloadStatus} from '../change-source/protocol/current.ts';\nimport type {ChangeStreamData} from '../change-source/protocol/current/downstream.ts';\nimport {\n errorTypeToReadableName,\n PROTOCOL_VERSION,\n type ChangeStreamer,\n type SerializedDownstream,\n} from '../change-streamer/change-streamer.ts';\nimport type * as ErrorType from '../change-streamer/error-type-enum.ts';\nimport {RunningState} from '../running-state.ts';\nimport type {CommitResult} from './change-processor.ts';\nimport {Notifier} from './notifier.ts';\nimport type {ReplicationStatusPublisher} from './replication-status.ts';\nimport type {ReplicaState, ReplicatorMode} from './replicator.ts';\nimport {ReplicationReportRecorder} from './reporter/recorder.ts';\nimport type {ReplicationReport} from './reporter/report-schema.ts';\nimport type {WriteWorkerClient} from './write-worker-client.ts';\n\ntype ErrorType = Enum<typeof ErrorType>;\n\n/**\n * The {@link IncrementalSyncer} manages a logical replication stream from upstream,\n * handling application lifecycle events (start, stop) and retrying the\n * connection with exponential backoff. The actual handling of the logical\n * replication messages is done by the {@link ChangeProcessor}, which runs\n * in a worker thread via the {@link WriteWorkerClient}.\n */\nexport class IncrementalSyncer {\n readonly #lc: LogContext;\n readonly #taskID: string;\n readonly #id: string;\n readonly #changeStreamer: ChangeStreamer;\n readonly #worker: WriteWorkerClient;\n readonly #mode: ReplicatorMode;\n readonly #replicaDbPath: string;\n readonly #statusPublisher: ReplicationStatusPublisher | null;\n readonly #notifier: Notifier;\n readonly #reporter: ReplicationReportRecorder;\n\n readonly #state = new RunningState('IncrementalSyncer');\n\n readonly #replicationEvents = getOrCreateCounter(\n 'replication',\n 'events',\n 'Number of replication events processed',\n );\n\n constructor(\n lc: LogContext,\n taskID: string,\n id: string,\n changeStreamer: ChangeStreamer,\n worker: WriteWorkerClient,\n mode: ReplicatorMode,\n replicaDbPath: string,\n statusPublisher: ReplicationStatusPublisher | null,\n ) {\n this.#lc = lc;\n this.#taskID = taskID;\n this.#id = id;\n this.#changeStreamer = changeStreamer;\n this.#worker = worker;\n this.#mode = mode;\n this.#replicaDbPath = replicaDbPath;\n this.#statusPublisher = statusPublisher;\n this.#notifier = new Notifier();\n this.#reporter = new ReplicationReportRecorder(lc);\n }\n\n async run() {\n const lc = this.#lc;\n let workerError: Error | undefined;\n this.#worker.onError(err => {\n workerError ??= err;\n this.#state.stop(lc, err);\n });\n lc.info?.(`Starting IncrementalSyncer`);\n const {watermark: initialWatermark} =\n await this.#worker.getSubscriptionState();\n\n // Notify any waiting subscribers that the replica is ready to be read.\n // This initial notification intentionally omits replicaReadyTimeMs because\n // it represents already-current state, not newly-unserved work.\n void this.#notifier.notifySubscribers({\n state: 'version-ready',\n watermark: initialWatermark,\n });\n\n while (this.#state.shouldRun()) {\n const {replicaVersion, watermark} =\n await this.#worker.getSubscriptionState();\n\n let downstream: Source<SerializedDownstream> | undefined;\n let unregister = () => {};\n let err: unknown | undefined;\n\n try {\n downstream = await this.#changeStreamer.subscribe({\n protocolVersion: PROTOCOL_VERSION,\n taskID: this.#taskID,\n id: this.#id,\n mode: this.#mode,\n watermark,\n replicaVersion,\n initial: watermark === initialWatermark,\n // The SQLite change log is written by the change-streamer itself, so\n // no replicator logs the change stream any more. The parameter stays\n // on the wire for change-streamers that still exclude a writer from\n // SQLite catchup.\n logsChangeStream: false,\n });\n this.#state.resetBackoff();\n unregister = this.#state.cancelOnStop(downstream);\n this.#statusPublisher?.publish(\n lc,\n 'Replicating',\n `Replicating from ${watermark}`,\n );\n\n let backfillStatus: DownloadStatus | undefined;\n\n for await (const {data: message} of downstream) {\n this.#replicationEvents.add(1);\n switch (message[0]) {\n case 'status': {\n const {lagReport} = message[1];\n if (lagReport) {\n const report: ReplicationReport = {\n nextSendTimeMs: lagReport.nextSendTimeMs,\n };\n if (lagReport.lastTimings) {\n report.lastTimings = {\n ...lagReport.lastTimings,\n replicateTimeMs: Date.now(),\n };\n }\n this.#reporter.record(report);\n }\n break;\n }\n case 'error': {\n // Signal from the replication-manager that the view-syncer must\n // shut down and restore a new backup from litestream.\n const {type, message: msg} = message[1];\n // Explicit errors from the change-streamer (e.g. watermark too\n // old) often indicate a problem with the replica. As a\n // conservative measure, delete the replica before shutting down\n // so that the process restarts from scratch even if reusing the\n // same volume.\n lc.warn?.(\n `received error from change-streamer. deleting replica file`,\n {error: message[1]},\n );\n deleteLiteDB(this.#replicaDbPath);\n this.stop(\n lc,\n // Note: The AbortError indicates a clean / intentional shutdown.\n new AbortError(\n `${errorTypeToReadableName(type as ErrorType)}: ${msg}`,\n ),\n );\n break;\n }\n default: {\n const msg = message[1];\n if (msg.tag === 'backfill' && msg.status) {\n const {status} = msg;\n if (!backfillStatus) {\n // Start publishing the status every 3 seconds.\n backfillStatus = status;\n this.#statusPublisher?.publish(\n lc,\n 'Replicating',\n `Backfilling ${msg.relation.name} table`,\n 3000,\n () =>\n backfillStatus\n ? {\n downloadStatus: [\n {\n ...backfillStatus,\n table: msg.relation.name,\n columns: [\n ...msg.relation.rowKey.columns,\n ...msg.columns,\n ],\n },\n ],\n }\n : {},\n );\n }\n backfillStatus = status; // Update the current status\n }\n\n const data = message as ChangeStreamData;\n const result = await this.#worker.processMessage(data);\n\n this.#handleResult(lc, result);\n if (result?.completedBackfill) {\n backfillStatus = undefined;\n }\n break;\n }\n }\n }\n this.#worker.abort();\n } catch (e) {\n err = e;\n this.#worker.abort();\n } finally {\n downstream?.cancel();\n unregister();\n this.#statusPublisher?.stop();\n }\n await this.#state.backoff(lc, err);\n }\n lc.info?.('IncrementalSyncer stopped');\n if (workerError) {\n throw workerError;\n }\n }\n\n #handleResult(lc: LogContext, result: CommitResult | null) {\n if (!result) {\n return;\n }\n if (result.completedBackfill) {\n // Publish the final status\n const status = result.completedBackfill;\n this.#statusPublisher?.publish(\n lc,\n 'Replicating',\n `Backfilled ${status.table} table`,\n 0,\n () => ({downloadStatus: [status]}),\n );\n } else if (result.schemaUpdated) {\n this.#statusPublisher?.publish(lc, 'Replicating', 'Schema updated');\n }\n if (result.watermark && result.changeLogUpdated) {\n void this.#notifier.notifySubscribers({\n state: 'version-ready',\n watermark: result.watermark,\n replicaReadyTimeMs: Date.now(),\n upstreamCommitTimeMs: result.upstreamCommitTimeMs,\n });\n }\n }\n\n subscribe(): Source<ReplicaState> {\n return this.#notifier.subscribe();\n }\n\n stop(lc: LogContext, err?: unknown) {\n this.#state.stop(lc, err);\n }\n}\n"],"mappings":"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"}
@@ -71,7 +71,7 @@ export type ReplicatorMode = 'backup' | 'serving';
71
71
  export declare class ReplicatorService implements Replicator, Service {
72
72
  #private;
73
73
  readonly id: string;
74
- constructor(lc: LogContext, taskID: string, id: string, mode: ReplicatorMode, changeStreamer: ChangeStreamer, worker: WriteWorkerClient, statusPublisher: ReplicationStatusPublisher | null);
74
+ constructor(lc: LogContext, taskID: string, id: string, mode: ReplicatorMode, replicaDbPath: string, changeStreamer: ChangeStreamer, worker: WriteWorkerClient, statusPublisher: ReplicationStatusPublisher | null);
75
75
  status(): Promise<{
76
76
  status: string;
77
77
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"replicator.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/replicator/replicator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,wBAAwB,CAAC;AACnD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,uCAAuC,CAAC;AAC1E,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,eAAe,CAAC;AAE3C,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,0BAA0B,CAAC;AAEhE,oDAAoD;AACpD,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC;;OAEG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAEjD;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAInD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,SAAS,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD;;;;OAIG;IACH,MAAM,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACvC;AAED,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,CAAC;AAElD,qBAAa,iBAAkB,YAAW,UAAU,EAAE,OAAO;;IAC3D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;gBAQlB,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,cAAc,EACpB,cAAc,EAAE,cAAc,EAC9B,MAAM,EAAE,iBAAiB,EACzB,eAAe,EAAE,0BAA0B,GAAG,IAAI;IAoBpD,MAAM;;;IAIN,GAAG;IAcH,SAAS,IAAI,MAAM,CAAC,YAAY,CAAC;IAI3B,IAAI;CAQX"}
1
+ {"version":3,"file":"replicator.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/replicator/replicator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,wBAAwB,CAAC;AACnD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,uCAAuC,CAAC;AAC1E,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,eAAe,CAAC;AAE3C,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,0BAA0B,CAAC;AAEhE,oDAAoD;AACpD,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC;;OAEG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAEjD;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAInD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,SAAS,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD;;;;OAIG;IACH,MAAM,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACvC;AAED,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,CAAC;AAElD,qBAAa,iBAAkB,YAAW,UAAU,EAAE,OAAO;;IAC3D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;gBAQlB,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,cAAc,EACpB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,cAAc,EAC9B,MAAM,EAAE,iBAAiB,EACzB,eAAe,EAAE,0BAA0B,GAAG,IAAI;IAqBpD,MAAM;;;IAIN,GAAG;IAcH,SAAS,IAAI,MAAM,CAAC,YAAY,CAAC;IAI3B,IAAI;CAQX"}
@@ -8,12 +8,12 @@ var ReplicatorService = class {
8
8
  #shouldPublishErrors;
9
9
  #worker;
10
10
  #runPromise;
11
- constructor(lc, taskID, id, mode, changeStreamer, worker, statusPublisher) {
11
+ constructor(lc, taskID, id, mode, replicaDbPath, changeStreamer, worker, statusPublisher) {
12
12
  this.id = id;
13
13
  this.#lc = lc.withContext("component", "replicator").withContext("serviceID", this.id);
14
14
  this.#worker = worker;
15
15
  this.#shouldPublishErrors = statusPublisher !== null;
16
- this.#incrementalSyncer = new IncrementalSyncer(this.#lc, taskID, `${taskID}/${id}`, changeStreamer, worker, mode, statusPublisher);
16
+ this.#incrementalSyncer = new IncrementalSyncer(this.#lc, taskID, `${taskID}/${id}`, changeStreamer, worker, mode, replicaDbPath, statusPublisher);
17
17
  }
18
18
  status() {
19
19
  return Promise.resolve({ status: "ok" });
@@ -1 +1 @@
1
- {"version":3,"file":"replicator.js","names":["#lc","#incrementalSyncer","#shouldPublishErrors","#worker","#runPromise"],"sources":["../../../../../../zero-cache/src/services/replicator/replicator.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {ReadonlyJSONObject} from '../../../../shared/src/json.ts';\nimport type {Source} from '../../types/streams.ts';\nimport type {ChangeStreamer} from '../change-streamer/change-streamer.ts';\nimport type {Service} from '../service.ts';\nimport {IncrementalSyncer} from './incremental-sync.ts';\nimport {\n publishReplicationError,\n type ReplicationStatusPublisher,\n} from './replication-status.ts';\nimport type {WriteWorkerClient} from './write-worker-client.ts';\n\n/** See {@link ReplicaStateNotifier.subscribe()}. */\nexport type ReplicaState = {\n readonly state: 'version-ready';\n /**\n * The replica watermark that became ready. Omitted for the initial\n * \"current state is ready\" notification, which should not count as newly\n * unserved work for a freshly-started ViewSyncer.\n */\n readonly watermark?: string | undefined;\n /**\n * Millisecond epoch when `watermark` became ready to read from the replica.\n */\n readonly replicaReadyTimeMs?: number | undefined;\n\n /**\n * Millisecond epoch at which the transaction behind `watermark` committed\n * *upstream*, as reported by the ChangeSource. Unlike `replicaReadyTimeMs`,\n * which is stamped locally when the change lands in the replica, this is the\n * origin of the whole pipeline, so `now - upstreamCommitTimeMs` at the point\n * the change is poked to clients is the end-to-end serving lag.\n *\n * Note that this crosses a clock domain: it comes from the upstream\n * database, whereas it is subtracted from the local clock in the ViewSyncer.\n * The existing `zero.replication.total_lag` metric spans the same boundary.\n *\n * Undefined when the ChangeSource does not report a commit time, and for the\n * initial \"current state is ready\" notification.\n */\n readonly upstreamCommitTimeMs?: number | undefined;\n\n // Used in tests to verify behavior when additional information\n // is ferried in the future. Not set in production.\n readonly testSeqNum?: number | undefined;\n};\n\nexport interface ReplicaStateNotifier {\n /**\n * Creates a cancelable subscription of changes in the replica state.\n *\n * A `version-ready` message indicates that the replica is ready to be\n * read, and henceforth that a _new_ version is ready, i.e. whenever a\n * change is committed to the replica. The `version-ready` message itself\n * otherwise contains no other information; the subscriber queries the\n * replica for the current data.\n *\n * A `maintenance` state indicates that the replica should not be read from.\n * If a subscriber is holding any transaction locks, it should release them\n * until the next `version-ready` signal.\n *\n * Upon subscription, the current state of the replica is sent immediately\n * if known. If multiple notifications occur before the subscriber\n * can consume them, all but the last notification are discarded by the\n * Subscription object (i.e. not buffered). Thus, a subscriber only\n * ever consumes the current (i.e. known) state of the replica. This avoids\n * a buildup of \"work\" if a subscriber is too busy to consume all\n * notifications.\n */\n subscribe(): Source<ReplicaState>;\n}\n\nexport interface Replicator extends ReplicaStateNotifier {\n /**\n * Returns an opaque message for human-readable consumption. This is\n * purely for ensuring that the Replicator has started at least once to\n * bootstrap a new replica.\n */\n status(): Promise<ReadonlyJSONObject>;\n}\n\nexport type ReplicatorMode = 'backup' | 'serving';\n\nexport class ReplicatorService implements Replicator, Service {\n readonly id: string;\n readonly #lc: LogContext;\n readonly #incrementalSyncer: IncrementalSyncer;\n readonly #shouldPublishErrors: boolean;\n readonly #worker: WriteWorkerClient;\n #runPromise: Promise<void> | undefined;\n\n constructor(\n lc: LogContext,\n taskID: string,\n id: string,\n mode: ReplicatorMode,\n changeStreamer: ChangeStreamer,\n worker: WriteWorkerClient,\n statusPublisher: ReplicationStatusPublisher | null,\n ) {\n this.id = id;\n this.#lc = lc\n .withContext('component', 'replicator')\n .withContext('serviceID', this.id);\n this.#worker = worker;\n this.#shouldPublishErrors = statusPublisher !== null;\n\n this.#incrementalSyncer = new IncrementalSyncer(\n this.#lc,\n taskID,\n `${taskID}/${id}`,\n changeStreamer,\n worker,\n mode,\n statusPublisher,\n );\n }\n\n status() {\n return Promise.resolve({status: 'ok'});\n }\n\n run() {\n this.#runPromise = this.#incrementalSyncer.run().catch(async error => {\n if (this.#shouldPublishErrors) {\n await publishReplicationError(\n this.#lc,\n 'Replicating',\n 'Replication stopped because the replica writer failed',\n );\n }\n throw error;\n });\n return this.#runPromise;\n }\n\n subscribe(): Source<ReplicaState> {\n return this.#incrementalSyncer.subscribe();\n }\n\n async stop() {\n this.#incrementalSyncer.stop(this.#lc);\n // Wait for the syncer's run loop to finish so that any in-flight\n // worker.processMessage() call completes and clears #pending\n // before we send the 'stop' message to the worker.\n await this.#runPromise;\n await this.#worker.stop();\n }\n}\n"],"mappings":";;;AAmFA,IAAa,oBAAb,MAA8D;CAC5D;CACA;CACA;CACA;CACA;CACA;CAEA,YACE,IACA,QACA,IACA,MACA,gBACA,QACA,iBACA;EACA,KAAK,KAAK;EACV,KAAKA,MAAM,GACR,YAAY,aAAa,YAAY,EACrC,YAAY,aAAa,KAAK,EAAE;EACnC,KAAKG,UAAU;EACf,KAAKD,uBAAuB,oBAAoB;EAEhD,KAAKD,qBAAqB,IAAI,kBAC5B,KAAKD,KACL,QACA,GAAG,OAAO,GAAG,MACb,gBACA,QACA,MACA,eACF;CACF;CAEA,SAAS;EACP,OAAO,QAAQ,QAAQ,EAAC,QAAQ,KAAI,CAAC;CACvC;CAEA,MAAM;EACJ,KAAKI,cAAc,KAAKH,mBAAmB,IAAI,EAAE,MAAM,OAAM,UAAS;GACpE,IAAI,KAAKC,sBACP,MAAM,wBACJ,KAAKF,KACL,eACA,uDACF;GAEF,MAAM;EACR,CAAC;EACD,OAAO,KAAKI;CACd;CAEA,YAAkC;EAChC,OAAO,KAAKH,mBAAmB,UAAU;CAC3C;CAEA,MAAM,OAAO;EACX,KAAKA,mBAAmB,KAAK,KAAKD,GAAG;EAIrC,MAAM,KAAKI;EACX,MAAM,KAAKD,QAAQ,KAAK;CAC1B;AACF"}
1
+ {"version":3,"file":"replicator.js","names":["#lc","#incrementalSyncer","#shouldPublishErrors","#worker","#runPromise"],"sources":["../../../../../../zero-cache/src/services/replicator/replicator.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {ReadonlyJSONObject} from '../../../../shared/src/json.ts';\nimport type {Source} from '../../types/streams.ts';\nimport type {ChangeStreamer} from '../change-streamer/change-streamer.ts';\nimport type {Service} from '../service.ts';\nimport {IncrementalSyncer} from './incremental-sync.ts';\nimport {\n publishReplicationError,\n type ReplicationStatusPublisher,\n} from './replication-status.ts';\nimport type {WriteWorkerClient} from './write-worker-client.ts';\n\n/** See {@link ReplicaStateNotifier.subscribe()}. */\nexport type ReplicaState = {\n readonly state: 'version-ready';\n /**\n * The replica watermark that became ready. Omitted for the initial\n * \"current state is ready\" notification, which should not count as newly\n * unserved work for a freshly-started ViewSyncer.\n */\n readonly watermark?: string | undefined;\n /**\n * Millisecond epoch when `watermark` became ready to read from the replica.\n */\n readonly replicaReadyTimeMs?: number | undefined;\n\n /**\n * Millisecond epoch at which the transaction behind `watermark` committed\n * *upstream*, as reported by the ChangeSource. Unlike `replicaReadyTimeMs`,\n * which is stamped locally when the change lands in the replica, this is the\n * origin of the whole pipeline, so `now - upstreamCommitTimeMs` at the point\n * the change is poked to clients is the end-to-end serving lag.\n *\n * Note that this crosses a clock domain: it comes from the upstream\n * database, whereas it is subtracted from the local clock in the ViewSyncer.\n * The existing `zero.replication.total_lag` metric spans the same boundary.\n *\n * Undefined when the ChangeSource does not report a commit time, and for the\n * initial \"current state is ready\" notification.\n */\n readonly upstreamCommitTimeMs?: number | undefined;\n\n // Used in tests to verify behavior when additional information\n // is ferried in the future. Not set in production.\n readonly testSeqNum?: number | undefined;\n};\n\nexport interface ReplicaStateNotifier {\n /**\n * Creates a cancelable subscription of changes in the replica state.\n *\n * A `version-ready` message indicates that the replica is ready to be\n * read, and henceforth that a _new_ version is ready, i.e. whenever a\n * change is committed to the replica. The `version-ready` message itself\n * otherwise contains no other information; the subscriber queries the\n * replica for the current data.\n *\n * A `maintenance` state indicates that the replica should not be read from.\n * If a subscriber is holding any transaction locks, it should release them\n * until the next `version-ready` signal.\n *\n * Upon subscription, the current state of the replica is sent immediately\n * if known. If multiple notifications occur before the subscriber\n * can consume them, all but the last notification are discarded by the\n * Subscription object (i.e. not buffered). Thus, a subscriber only\n * ever consumes the current (i.e. known) state of the replica. This avoids\n * a buildup of \"work\" if a subscriber is too busy to consume all\n * notifications.\n */\n subscribe(): Source<ReplicaState>;\n}\n\nexport interface Replicator extends ReplicaStateNotifier {\n /**\n * Returns an opaque message for human-readable consumption. This is\n * purely for ensuring that the Replicator has started at least once to\n * bootstrap a new replica.\n */\n status(): Promise<ReadonlyJSONObject>;\n}\n\nexport type ReplicatorMode = 'backup' | 'serving';\n\nexport class ReplicatorService implements Replicator, Service {\n readonly id: string;\n readonly #lc: LogContext;\n readonly #incrementalSyncer: IncrementalSyncer;\n readonly #shouldPublishErrors: boolean;\n readonly #worker: WriteWorkerClient;\n #runPromise: Promise<void> | undefined;\n\n constructor(\n lc: LogContext,\n taskID: string,\n id: string,\n mode: ReplicatorMode,\n replicaDbPath: string,\n changeStreamer: ChangeStreamer,\n worker: WriteWorkerClient,\n statusPublisher: ReplicationStatusPublisher | null,\n ) {\n this.id = id;\n this.#lc = lc\n .withContext('component', 'replicator')\n .withContext('serviceID', this.id);\n this.#worker = worker;\n this.#shouldPublishErrors = statusPublisher !== null;\n\n this.#incrementalSyncer = new IncrementalSyncer(\n this.#lc,\n taskID,\n `${taskID}/${id}`,\n changeStreamer,\n worker,\n mode,\n replicaDbPath,\n statusPublisher,\n );\n }\n\n status() {\n return Promise.resolve({status: 'ok'});\n }\n\n run() {\n this.#runPromise = this.#incrementalSyncer.run().catch(async error => {\n if (this.#shouldPublishErrors) {\n await publishReplicationError(\n this.#lc,\n 'Replicating',\n 'Replication stopped because the replica writer failed',\n );\n }\n throw error;\n });\n return this.#runPromise;\n }\n\n subscribe(): Source<ReplicaState> {\n return this.#incrementalSyncer.subscribe();\n }\n\n async stop() {\n this.#incrementalSyncer.stop(this.#lc);\n // Wait for the syncer's run loop to finish so that any in-flight\n // worker.processMessage() call completes and clears #pending\n // before we send the 'stop' message to the worker.\n await this.#runPromise;\n await this.#worker.stop();\n }\n}\n"],"mappings":";;;AAmFA,IAAa,oBAAb,MAA8D;CAC5D;CACA;CACA;CACA;CACA;CACA;CAEA,YACE,IACA,QACA,IACA,MACA,eACA,gBACA,QACA,iBACA;EACA,KAAK,KAAK;EACV,KAAKA,MAAM,GACR,YAAY,aAAa,YAAY,EACrC,YAAY,aAAa,KAAK,EAAE;EACnC,KAAKG,UAAU;EACf,KAAKD,uBAAuB,oBAAoB;EAEhD,KAAKD,qBAAqB,IAAI,kBAC5B,KAAKD,KACL,QACA,GAAG,OAAO,GAAG,MACb,gBACA,QACA,MACA,eACA,eACF;CACF;CAEA,SAAS;EACP,OAAO,QAAQ,QAAQ,EAAC,QAAQ,KAAI,CAAC;CACvC;CAEA,MAAM;EACJ,KAAKI,cAAc,KAAKH,mBAAmB,IAAI,EAAE,MAAM,OAAM,UAAS;GACpE,IAAI,KAAKC,sBACP,MAAM,wBACJ,KAAKF,KACL,eACA,uDACF;GAEF,MAAM;EACR,CAAC;EACD,OAAO,KAAKI;CACd;CAEA,YAAkC;EAChC,OAAO,KAAKH,mBAAmB,UAAU;CAC3C;CAEA,MAAM,OAAO;EACX,KAAKA,mBAAmB,KAAK,KAAKD,GAAG;EAIrC,MAAM,KAAKI;EACX,MAAM,KAAKD,QAAQ,KAAK;CAC1B;AACF"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * The replica's backfill cookie, keyed by upstream identity.
3
+ *
4
+ * `_zero.column_metadata.backfill` already records which columns are being
5
+ * backfilled, and it stays authoritative for `isBackfilling`, which is what the
6
+ * replica's own read paths use. What it cannot do is name the table an upstream
7
+ * would recognize: it is keyed by *lite* table name, and `liteTableName()`
8
+ * collapses the `public` schema and has no inverse anywhere in the tree. A lite
9
+ * name containing a dot is ambiguous — `{public, "a.b"}` and `{a, b}` produce
10
+ * the same string — and a table with backfilling columns and no
11
+ * `_zero.tableMetadata` row (legal, since its metadata is optional) cannot be
12
+ * resolved at all.
13
+ *
14
+ * That is the whole of what blocks a change log from being initialized out of a
15
+ * restored replica, so this table carries the identity the reconstruction needs:
16
+ * a straight row copy into the change log's own cookie tables, with nothing to
17
+ * reconstruct and nothing to guess. The replica's copy is the *seed* source; the
18
+ * change log's copy (`replicator/change-log-cookies.ts`) is the *working* set,
19
+ * folded forward from the seed as the log's head advances.
20
+ *
21
+ * The cost is a second copy of `backfill` inside the replica, taken
22
+ * deliberately — though not to save writes: `_zero.column_metadata` is written
23
+ * only by initial sync, the DDL handlers, and backfill completion, so two more
24
+ * columns on it would be free. It is a matter of shape. That table is keyed by
25
+ * lite table name, so `schema`/`table` would be non-key duplicates of its own
26
+ * key, to be held in sync with it across every rename with no answer to which
27
+ * encoding is authoritative. This one is keyed exactly as `cdc.backfilling` and
28
+ * `_zero.changeLogBackfilling` are, which is what makes the change log's
29
+ * initialization comparison a row-set diff rather than a translation, and what
30
+ * lets all three stores interpret the same fold. It also holds in-flight backfills and nothing else,
31
+ * where the same query against `column_metadata` is an unindexed scan over a row
32
+ * per column of every table.
33
+ *
34
+ * This table is write-only from the replica's perspective. Nothing in the
35
+ * replicator reads it; it exists to be read by the change-streamer.
36
+ */
37
+ import type { LogContext } from '@rocicorp/logger';
38
+ import type { Database } from '../../../../../zqlite/src/db.ts';
39
+ import type { SchemaChange } from '../../change-source/protocol/current/data.ts';
40
+ import type { BackfillRequest } from '../../change-source/protocol/current/upstream.ts';
41
+ import { type CookieOp, type CookieSet } from '../change-log-cookies.ts';
42
+ export declare const BACKFILLING_TABLE = "_zero.backfilling";
43
+ export declare const CREATE_BACKFILLING_TABLE = "\n CREATE TABLE \"_zero.backfilling\" (\n \"schema\" TEXT NOT NULL,\n \"table\" TEXT NOT NULL,\n \"column\" TEXT NOT NULL,\n \"backfill\" TEXT NOT NULL,\n PRIMARY KEY (\"schema\", \"table\", \"column\")\n );\n";
44
+ /**
45
+ * The replica's interpreter of the cookie fold in
46
+ * `replicator/change-log-cookies.ts`.
47
+ *
48
+ * The fold is shared rather than re-implemented for the same reason the
49
+ * Postgres and SQLite change logs share it: the three stores must agree on every
50
+ * transition forever, and the failure mode when they drift is a backfill that is
51
+ * silently never re-requested, on a column that is then silently never
52
+ * populated. Sharing it makes the replica-versus-Postgres comparison a
53
+ * regression test on the interpreters rather than on three implementations of
54
+ * the same switch.
55
+ *
56
+ * Only the backfill half of the fold is interpreted here. The replica's metadata
57
+ * cookie is `_zero.tableMetadata.upstreamMetadata`, which `TableMetadataTracker`
58
+ * already maintains at the same sites, so an `upsert-metadata` op is inert.
59
+ *
60
+ * Statements are lazily prepared, matching `TableMetadataTracker`: a replica
61
+ * that never sees a schema change never prepares any of them.
62
+ */
63
+ export declare class BackfillingTracker {
64
+ #private;
65
+ constructor(db: Database);
66
+ /**
67
+ * Applies the change's backfill-cookie ops, returning the ops that were
68
+ * applied. Changes that carry no backfill state — index changes, metadata-only
69
+ * updates, and the `create-table` / `add-column` variants from a change source
70
+ * that does not support backfill — apply nothing.
71
+ */
72
+ apply(change: SchemaChange): CookieOp[];
73
+ }
74
+ /**
75
+ * Returns the {@link BackfillRequest}s from the current replica cookies. The
76
+ * result has the same shape as the Postgres initialization result.
77
+ *
78
+ * The caller must pair this snapshot with the replica state version. When the
79
+ * replica trails the change log, the comparison applies the missing schema
80
+ * changes before it compares the results.
81
+ */
82
+ export declare function readBackfillRequests(db: Database): BackfillRequest[];
83
+ /**
84
+ * The replica's whole cookie set, in the shape the change log holds it — the
85
+ * seed a change log would be initialized from, and the replica-derived half of
86
+ * the initialization comparison.
87
+ *
88
+ * The metadata half is filtered on `upstreamMetadata IS NOT NULL` because
89
+ * `_zero.tableMetadata` carries `minRowVersion` as well, so a row exists for
90
+ * every table whose rows were ever force-re-downloaded, whether or not upstream
91
+ * ever sent metadata for it. Neither cookie store can represent that row:
92
+ * `cdc.tableMetadata."metadata"` and `_zero.changeLogTableMetadata."metadata"`
93
+ * are both `NOT NULL`.
94
+ *
95
+ * Only meaningful paired with `_zero.replicationState.stateVersion`, read in
96
+ * the same snapshot (invariant 15).
97
+ */
98
+ export declare function readReplicaCookies(db: Database): CookieSet;
99
+ /**
100
+ * Seeds {@link BACKFILLING_TABLE} from `_zero.column_metadata`, resolving each
101
+ * lite table name back to its upstream identity. Used by the v17 migration.
102
+ *
103
+ * The resolution is exact for a table that has a `_zero.tableMetadata` row, and
104
+ * exact for a lite name with no dot in it — `liteTableName()` only omits the
105
+ * schema for `public`. What is left over is an in-flight backfill, on a
106
+ * non-`public` schema, on a table with no metadata row, at the instant of
107
+ * upgrade. Those get the best-effort first-dot split and are counted, because a
108
+ * wrong identity produces a `BackfillRequest` the change source will reject
109
+ * rather than a silently wrong one, and because the initialization comparison
110
+ * surfaces it before anything depends on it. The expected count is zero.
111
+ *
112
+ * A full replace rather than an insert-if-absent: `migrateData` re-runs after a
113
+ * rollback and roll-forward, during which a v16 zero-cache advanced the replica
114
+ * without knowing this table existed. Rebuilding it from `column_metadata` —
115
+ * which that zero-cache *did* maintain — is what makes the re-run both
116
+ * idempotent and correct.
117
+ */
118
+ export declare function populateBackfillingFromColumnMetadata(lc: LogContext, db: Database): void;
119
+ //# sourceMappingURL=backfilling.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backfilling.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/replicator/schema/backfilling.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAGjD,OAAO,KAAK,EAAC,QAAQ,EAAY,MAAM,iCAAiC,CAAC;AAGzE,OAAO,KAAK,EAGV,YAAY,EAEb,MAAM,8CAA8C,CAAC;AACtD,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,kDAAkD,CAAC;AACtF,OAAO,EAGL,KAAK,QAAQ,EACb,KAAK,SAAS,EACf,MAAM,0BAA0B,CAAC;AAElC,eAAO,MAAM,iBAAiB,sBAAsB,CAAC;AAKrD,eAAO,MAAM,wBAAwB,8OAQpC,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,kBAAkB;;gBASjB,EAAE,EAAE,QAAQ;IAIxB;;;;;OAKG;IACH,KAAK,CAAC,MAAM,EAAE,YAAY,GAAG,QAAQ,EAAE;CAyExC;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,QAAQ,GAAG,eAAe,EAAE,CAEpE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,GAAG,SAAS,CA8B1D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qCAAqC,CACnD,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,QAAQ,GACX,IAAI,CA4DN"}
@@ -0,0 +1,214 @@
1
+ import { unreachable } from "../../../../../shared/src/asserts.js";
2
+ import { BigIntJSON } from "../../../../../shared/src/bigint-json.js";
3
+ import { liteTableName } from "../../../types/names.js";
4
+ import { getOrCreateCounter } from "../../../observability/metrics.js";
5
+ import { backfillRequestsFrom, cookieOps } from "../change-log-cookies.js";
6
+ //#region ../zero-cache/src/services/replicator/schema/backfilling.ts
7
+ var BACKFILLING_TABLE = "_zero.backfilling";
8
+ var CREATE_BACKFILLING_TABLE = `
9
+ CREATE TABLE "${BACKFILLING_TABLE}" (
10
+ "schema" TEXT NOT NULL,
11
+ "table" TEXT NOT NULL,
12
+ "column" TEXT NOT NULL,
13
+ "backfill" TEXT NOT NULL,
14
+ PRIMARY KEY ("schema", "table", "column")
15
+ );
16
+ `;
17
+ /**
18
+ * The replica's interpreter of the cookie fold in
19
+ * `replicator/change-log-cookies.ts`.
20
+ *
21
+ * The fold is shared rather than re-implemented for the same reason the
22
+ * Postgres and SQLite change logs share it: the three stores must agree on every
23
+ * transition forever, and the failure mode when they drift is a backfill that is
24
+ * silently never re-requested, on a column that is then silently never
25
+ * populated. Sharing it makes the replica-versus-Postgres comparison a
26
+ * regression test on the interpreters rather than on three implementations of
27
+ * the same switch.
28
+ *
29
+ * Only the backfill half of the fold is interpreted here. The replica's metadata
30
+ * cookie is `_zero.tableMetadata.upstreamMetadata`, which `TableMetadataTracker`
31
+ * already maintains at the same sites, so an `upsert-metadata` op is inert.
32
+ *
33
+ * Statements are lazily prepared, matching `TableMetadataTracker`: a replica
34
+ * that never sees a schema change never prepares any of them.
35
+ */
36
+ var BackfillingTracker = class {
37
+ #db;
38
+ #upsert;
39
+ #renameTable;
40
+ #dropTable;
41
+ #renameColumn;
42
+ #dropColumn;
43
+ constructor(db) {
44
+ this.#db = db;
45
+ }
46
+ /**
47
+ * Applies the change's backfill-cookie ops, returning the ops that were
48
+ * applied. Changes that carry no backfill state — index changes, metadata-only
49
+ * updates, and the `create-table` / `add-column` variants from a change source
50
+ * that does not support backfill — apply nothing.
51
+ */
52
+ apply(change) {
53
+ const ops = cookieOps(change);
54
+ for (const op of ops) this.#run(op);
55
+ return ops;
56
+ }
57
+ #run(op) {
58
+ switch (op.op) {
59
+ case "upsert-metadata": break;
60
+ case "upsert-backfill":
61
+ (this.#upsert ??= this.#db.prepare(`
62
+ INSERT INTO "${BACKFILLING_TABLE}"
63
+ ("schema", "table", "column", "backfill") VALUES (?, ?, ?, ?)
64
+ ON CONFLICT ("schema", "table", "column")
65
+ DO UPDATE SET "backfill" = excluded."backfill"
66
+ `)).run(op.table.schema, op.table.name, op.column, BigIntJSON.stringify(op.backfill));
67
+ break;
68
+ case "rename-table":
69
+ (this.#renameTable ??= this.#db.prepare(`
70
+ UPDATE "${BACKFILLING_TABLE}"
71
+ SET "schema" = ?, "table" = ? WHERE "schema" = ? AND "table" = ?
72
+ `)).run(op.new.schema, op.new.name, op.old.schema, op.old.name);
73
+ break;
74
+ case "drop-table":
75
+ (this.#dropTable ??= this.#db.prepare(`
76
+ DELETE FROM "${BACKFILLING_TABLE}"
77
+ WHERE "schema" = ? AND "table" = ?
78
+ `)).run(op.table.schema, op.table.name);
79
+ break;
80
+ case "rename-column":
81
+ (this.#renameColumn ??= this.#db.prepare(`
82
+ UPDATE "${BACKFILLING_TABLE}" SET "column" = ?
83
+ WHERE "schema" = ? AND "table" = ? AND "column" = ?
84
+ `)).run(op.new, op.table.schema, op.table.name, op.old);
85
+ break;
86
+ case "drop-column":
87
+ this.#deleteColumn(op.table, op.column);
88
+ break;
89
+ case "complete-backfill":
90
+ for (const column of op.columns) this.#deleteColumn(op.table, column);
91
+ break;
92
+ default: unreachable(op);
93
+ }
94
+ }
95
+ #deleteColumn(table, column) {
96
+ (this.#dropColumn ??= this.#db.prepare(`
97
+ DELETE FROM "${BACKFILLING_TABLE}"
98
+ WHERE "schema" = ? AND "table" = ? AND "column" = ?
99
+ `)).run(table.schema, table.name, column);
100
+ }
101
+ };
102
+ /**
103
+ * Returns the {@link BackfillRequest}s from the current replica cookies. The
104
+ * result has the same shape as the Postgres initialization result.
105
+ *
106
+ * The caller must pair this snapshot with the replica state version. When the
107
+ * replica trails the change log, the comparison applies the missing schema
108
+ * changes before it compares the results.
109
+ */
110
+ function readBackfillRequests(db) {
111
+ return backfillRequestsFrom(readReplicaCookies(db));
112
+ }
113
+ /**
114
+ * The replica's whole cookie set, in the shape the change log holds it — the
115
+ * seed a change log would be initialized from, and the replica-derived half of
116
+ * the initialization comparison.
117
+ *
118
+ * The metadata half is filtered on `upstreamMetadata IS NOT NULL` because
119
+ * `_zero.tableMetadata` carries `minRowVersion` as well, so a row exists for
120
+ * every table whose rows were ever force-re-downloaded, whether or not upstream
121
+ * ever sent metadata for it. Neither cookie store can represent that row:
122
+ * `cdc.tableMetadata."metadata"` and `_zero.changeLogTableMetadata."metadata"`
123
+ * are both `NOT NULL`.
124
+ *
125
+ * Only meaningful paired with `_zero.replicationState.stateVersion`, read in
126
+ * the same snapshot (invariant 15).
127
+ */
128
+ function readReplicaCookies(db) {
129
+ return {
130
+ tableMetadata: db.prepare(`
131
+ SELECT "schema", "table", "upstreamMetadata" AS "metadata"
132
+ FROM "_zero.tableMetadata"
133
+ WHERE "upstreamMetadata" IS NOT NULL
134
+ ORDER BY "schema", "table"
135
+ `).all().map(({ schema, table, metadata }) => ({
136
+ schema,
137
+ table,
138
+ metadata: BigIntJSON.parse(metadata)
139
+ })),
140
+ backfilling: db.prepare(`
141
+ SELECT "schema", "table", "column", "backfill"
142
+ FROM "${BACKFILLING_TABLE}"
143
+ ORDER BY "schema", "table", "column"
144
+ `).all().map(({ schema, table, column, backfill }) => ({
145
+ schema,
146
+ table,
147
+ column,
148
+ backfill: BigIntJSON.parse(backfill)
149
+ }))
150
+ };
151
+ }
152
+ /**
153
+ * Seeds {@link BACKFILLING_TABLE} from `_zero.column_metadata`, resolving each
154
+ * lite table name back to its upstream identity. Used by the v17 migration.
155
+ *
156
+ * The resolution is exact for a table that has a `_zero.tableMetadata` row, and
157
+ * exact for a lite name with no dot in it — `liteTableName()` only omits the
158
+ * schema for `public`. What is left over is an in-flight backfill, on a
159
+ * non-`public` schema, on a table with no metadata row, at the instant of
160
+ * upgrade. Those get the best-effort first-dot split and are counted, because a
161
+ * wrong identity produces a `BackfillRequest` the change source will reject
162
+ * rather than a silently wrong one, and because the initialization comparison
163
+ * surfaces it before anything depends on it. The expected count is zero.
164
+ *
165
+ * A full replace rather than an insert-if-absent: `migrateData` re-runs after a
166
+ * rollback and roll-forward, during which a v16 zero-cache advanced the replica
167
+ * without knowing this table existed. Rebuilding it from `column_metadata` —
168
+ * which that zero-cache *did* maintain — is what makes the re-run both
169
+ * idempotent and correct.
170
+ */
171
+ function populateBackfillingFromColumnMetadata(lc, db) {
172
+ db.prepare(`DELETE FROM "${BACKFILLING_TABLE}"`).run();
173
+ const backfilling = db.prepare(`
174
+ SELECT table_name, column_name, backfill FROM "_zero.column_metadata"
175
+ WHERE backfill IS NOT NULL
176
+ ORDER BY table_name, column_name
177
+ `).all();
178
+ const unresolvable = getOrCreateCounter("replica", "backfilling_unresolvable_rows", "In-flight backfills whose upstream table identity could not be recovered from the replica when \"" + BACKFILLING_TABLE + "\" was created. A best-effort identity is stored; the expected count is 0.");
179
+ if (backfilling.length === 0) {
180
+ unresolvable.add(0);
181
+ return;
182
+ }
183
+ const byLiteName = new Map(db.prepare(`SELECT "schema", "table" FROM "_zero.tableMetadata"`).all().map(({ schema, table }) => [liteTableName({
184
+ schema,
185
+ name: table
186
+ }), {
187
+ schema,
188
+ name: table
189
+ }]));
190
+ const insert = db.prepare(`
191
+ INSERT INTO "${BACKFILLING_TABLE}"
192
+ ("schema", "table", "column", "backfill") VALUES (?, ?, ?, ?)
193
+ `);
194
+ let unresolved = 0;
195
+ for (const { table_name: liteName, column_name, backfill } of backfilling) {
196
+ const known = byLiteName.get(liteName);
197
+ const dot = known ? -1 : liteName.indexOf(".");
198
+ const table = known ?? {
199
+ schema: dot < 0 ? "public" : liteName.slice(0, dot),
200
+ name: dot < 0 ? liteName : liteName.slice(dot + 1)
201
+ };
202
+ if (!known && dot >= 0) {
203
+ unresolved++;
204
+ lc.warn?.(`no metadata for backfilling table "${liteName}"; assuming {schema: "${table.schema}", name: "${table.name}"}`);
205
+ }
206
+ insert.run(table.schema, table.name, column_name, backfill);
207
+ }
208
+ unresolvable.add(unresolved);
209
+ lc.info?.(`seeded "${BACKFILLING_TABLE}" with ${backfilling.length} in-flight backfill(s), ${unresolved} of which could not be resolved`);
210
+ }
211
+ //#endregion
212
+ export { BackfillingTracker, CREATE_BACKFILLING_TABLE, populateBackfillingFromColumnMetadata, readBackfillRequests, readReplicaCookies };
213
+
214
+ //# sourceMappingURL=backfilling.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backfilling.js","names":["#db","#run","#upsert","#renameTable","#dropTable","#renameColumn","#deleteColumn","#dropColumn"],"sources":["../../../../../../../zero-cache/src/services/replicator/schema/backfilling.ts"],"sourcesContent":["/**\n * The replica's backfill cookie, keyed by upstream identity.\n *\n * `_zero.column_metadata.backfill` already records which columns are being\n * backfilled, and it stays authoritative for `isBackfilling`, which is what the\n * replica's own read paths use. What it cannot do is name the table an upstream\n * would recognize: it is keyed by *lite* table name, and `liteTableName()`\n * collapses the `public` schema and has no inverse anywhere in the tree. A lite\n * name containing a dot is ambiguous — `{public, \"a.b\"}` and `{a, b}` produce\n * the same string — and a table with backfilling columns and no\n * `_zero.tableMetadata` row (legal, since its metadata is optional) cannot be\n * resolved at all.\n *\n * That is the whole of what blocks a change log from being initialized out of a\n * restored replica, so this table carries the identity the reconstruction needs:\n * a straight row copy into the change log's own cookie tables, with nothing to\n * reconstruct and nothing to guess. The replica's copy is the *seed* source; the\n * change log's copy (`replicator/change-log-cookies.ts`) is the *working* set,\n * folded forward from the seed as the log's head advances.\n *\n * The cost is a second copy of `backfill` inside the replica, taken\n * deliberately — though not to save writes: `_zero.column_metadata` is written\n * only by initial sync, the DDL handlers, and backfill completion, so two more\n * columns on it would be free. It is a matter of shape. That table is keyed by\n * lite table name, so `schema`/`table` would be non-key duplicates of its own\n * key, to be held in sync with it across every rename with no answer to which\n * encoding is authoritative. This one is keyed exactly as `cdc.backfilling` and\n * `_zero.changeLogBackfilling` are, which is what makes the change log's\n * initialization comparison a row-set diff rather than a translation, and what\n * lets all three stores interpret the same fold. It also holds in-flight backfills and nothing else,\n * where the same query against `column_metadata` is an unindexed scan over a row\n * per column of every table.\n *\n * This table is write-only from the replica's perspective. Nothing in the\n * replicator reads it; it exists to be read by the change-streamer.\n */\n\nimport type {LogContext} from '@rocicorp/logger';\nimport {unreachable} from '../../../../../shared/src/asserts.ts';\nimport {BigIntJSON} from '../../../../../shared/src/bigint-json.ts';\nimport type {Database, Statement} from '../../../../../zqlite/src/db.ts';\nimport {getOrCreateCounter} from '../../../observability/metrics.ts';\nimport {liteTableName} from '../../../types/names.ts';\nimport type {\n BackfillID,\n Identifier,\n SchemaChange,\n TableMetadata,\n} from '../../change-source/protocol/current/data.ts';\nimport type {BackfillRequest} from '../../change-source/protocol/current/upstream.ts';\nimport {\n backfillRequestsFrom,\n cookieOps,\n type CookieOp,\n type CookieSet,\n} from '../change-log-cookies.ts';\n\nexport const BACKFILLING_TABLE = '_zero.backfilling';\n\n// `backfill` holds the JSON that `cdc.backfilling` holds as JSONB. SQLite has\n// no JSONB, and nothing here queries into the document: it is stored to be\n// handed back to the change source verbatim.\nexport const CREATE_BACKFILLING_TABLE = /*sql*/ `\n CREATE TABLE \"${BACKFILLING_TABLE}\" (\n \"schema\" TEXT NOT NULL,\n \"table\" TEXT NOT NULL,\n \"column\" TEXT NOT NULL,\n \"backfill\" TEXT NOT NULL,\n PRIMARY KEY (\"schema\", \"table\", \"column\")\n );\n`;\n\n/**\n * The replica's interpreter of the cookie fold in\n * `replicator/change-log-cookies.ts`.\n *\n * The fold is shared rather than re-implemented for the same reason the\n * Postgres and SQLite change logs share it: the three stores must agree on every\n * transition forever, and the failure mode when they drift is a backfill that is\n * silently never re-requested, on a column that is then silently never\n * populated. Sharing it makes the replica-versus-Postgres comparison a\n * regression test on the interpreters rather than on three implementations of\n * the same switch.\n *\n * Only the backfill half of the fold is interpreted here. The replica's metadata\n * cookie is `_zero.tableMetadata.upstreamMetadata`, which `TableMetadataTracker`\n * already maintains at the same sites, so an `upsert-metadata` op is inert.\n *\n * Statements are lazily prepared, matching `TableMetadataTracker`: a replica\n * that never sees a schema change never prepares any of them.\n */\nexport class BackfillingTracker {\n readonly #db: Database;\n\n #upsert: Statement | undefined;\n #renameTable: Statement | undefined;\n #dropTable: Statement | undefined;\n #renameColumn: Statement | undefined;\n #dropColumn: Statement | undefined;\n\n constructor(db: Database) {\n this.#db = db;\n }\n\n /**\n * Applies the change's backfill-cookie ops, returning the ops that were\n * applied. Changes that carry no backfill state — index changes, metadata-only\n * updates, and the `create-table` / `add-column` variants from a change source\n * that does not support backfill — apply nothing.\n */\n apply(change: SchemaChange): CookieOp[] {\n const ops = cookieOps(change);\n for (const op of ops) {\n this.#run(op);\n }\n return ops;\n }\n\n #run(op: CookieOp): void {\n switch (op.op) {\n case 'upsert-metadata':\n // Maintained by TableMetadataTracker in \"_zero.tableMetadata\".\n break;\n\n case 'upsert-backfill':\n (this.#upsert ??= this.#db.prepare(/*sql*/ `\n INSERT INTO \"${BACKFILLING_TABLE}\"\n (\"schema\", \"table\", \"column\", \"backfill\") VALUES (?, ?, ?, ?)\n ON CONFLICT (\"schema\", \"table\", \"column\")\n DO UPDATE SET \"backfill\" = excluded.\"backfill\"\n `)).run(\n op.table.schema,\n op.table.name,\n op.column,\n BigIntJSON.stringify(op.backfill),\n );\n break;\n\n case 'rename-table':\n (this.#renameTable ??= this.#db.prepare(/*sql*/ `\n UPDATE \"${BACKFILLING_TABLE}\"\n SET \"schema\" = ?, \"table\" = ? WHERE \"schema\" = ? AND \"table\" = ?\n `)).run(op.new.schema, op.new.name, op.old.schema, op.old.name);\n break;\n\n case 'drop-table':\n (this.#dropTable ??= this.#db.prepare(/*sql*/ `\n DELETE FROM \"${BACKFILLING_TABLE}\"\n WHERE \"schema\" = ? AND \"table\" = ?\n `)).run(op.table.schema, op.table.name);\n break;\n\n case 'rename-column':\n (this.#renameColumn ??= this.#db.prepare(/*sql*/ `\n UPDATE \"${BACKFILLING_TABLE}\" SET \"column\" = ?\n WHERE \"schema\" = ? AND \"table\" = ? AND \"column\" = ?\n `)).run(op.new, op.table.schema, op.table.name, op.old);\n break;\n\n case 'drop-column':\n this.#deleteColumn(op.table, op.column);\n break;\n\n case 'complete-backfill':\n // A per-column delete rather than an `IN` list, which cannot be a\n // single prepared statement across arities. The columns of one\n // completed backfill are few, and this runs only on schema changes.\n for (const column of op.columns) {\n this.#deleteColumn(op.table, column);\n }\n break;\n\n default:\n unreachable(op);\n }\n }\n\n #deleteColumn(table: Identifier, column: string): void {\n (this.#dropColumn ??= this.#db.prepare(/*sql*/ `\n DELETE FROM \"${BACKFILLING_TABLE}\"\n WHERE \"schema\" = ? AND \"table\" = ? AND \"column\" = ?\n `)).run(table.schema, table.name, column);\n }\n}\n\n/**\n * Returns the {@link BackfillRequest}s from the current replica cookies. The\n * result has the same shape as the Postgres initialization result.\n *\n * The caller must pair this snapshot with the replica state version. When the\n * replica trails the change log, the comparison applies the missing schema\n * changes before it compares the results.\n */\nexport function readBackfillRequests(db: Database): BackfillRequest[] {\n return backfillRequestsFrom(readReplicaCookies(db));\n}\n\n/**\n * The replica's whole cookie set, in the shape the change log holds it — the\n * seed a change log would be initialized from, and the replica-derived half of\n * the initialization comparison.\n *\n * The metadata half is filtered on `upstreamMetadata IS NOT NULL` because\n * `_zero.tableMetadata` carries `minRowVersion` as well, so a row exists for\n * every table whose rows were ever force-re-downloaded, whether or not upstream\n * ever sent metadata for it. Neither cookie store can represent that row:\n * `cdc.tableMetadata.\"metadata\"` and `_zero.changeLogTableMetadata.\"metadata\"`\n * are both `NOT NULL`.\n *\n * Only meaningful paired with `_zero.replicationState.stateVersion`, read in\n * the same snapshot (invariant 15).\n */\nexport function readReplicaCookies(db: Database): CookieSet {\n const tableMetadata = db\n .prepare(/*sql*/ `\n SELECT \"schema\", \"table\", \"upstreamMetadata\" AS \"metadata\"\n FROM \"_zero.tableMetadata\"\n WHERE \"upstreamMetadata\" IS NOT NULL\n ORDER BY \"schema\", \"table\"\n `)\n .all<{schema: string; table: string; metadata: string}>()\n .map(({schema, table, metadata}) => ({\n schema,\n table,\n metadata: BigIntJSON.parse(metadata) as TableMetadata,\n }));\n\n const backfilling = db\n .prepare(/*sql*/ `\n SELECT \"schema\", \"table\", \"column\", \"backfill\"\n FROM \"${BACKFILLING_TABLE}\"\n ORDER BY \"schema\", \"table\", \"column\"\n `)\n .all<{schema: string; table: string; column: string; backfill: string}>()\n .map(({schema, table, column, backfill}) => ({\n schema,\n table,\n column,\n backfill: BigIntJSON.parse(backfill) as BackfillID,\n }));\n\n return {tableMetadata, backfilling};\n}\n\n/**\n * Seeds {@link BACKFILLING_TABLE} from `_zero.column_metadata`, resolving each\n * lite table name back to its upstream identity. Used by the v17 migration.\n *\n * The resolution is exact for a table that has a `_zero.tableMetadata` row, and\n * exact for a lite name with no dot in it — `liteTableName()` only omits the\n * schema for `public`. What is left over is an in-flight backfill, on a\n * non-`public` schema, on a table with no metadata row, at the instant of\n * upgrade. Those get the best-effort first-dot split and are counted, because a\n * wrong identity produces a `BackfillRequest` the change source will reject\n * rather than a silently wrong one, and because the initialization comparison\n * surfaces it before anything depends on it. The expected count is zero.\n *\n * A full replace rather than an insert-if-absent: `migrateData` re-runs after a\n * rollback and roll-forward, during which a v16 zero-cache advanced the replica\n * without knowing this table existed. Rebuilding it from `column_metadata` —\n * which that zero-cache *did* maintain — is what makes the re-run both\n * idempotent and correct.\n */\nexport function populateBackfillingFromColumnMetadata(\n lc: LogContext,\n db: Database,\n): void {\n db.prepare(/*sql*/ `DELETE FROM \"${BACKFILLING_TABLE}\"`).run();\n\n const backfilling = db\n .prepare(/*sql*/ `\n SELECT table_name, column_name, backfill FROM \"_zero.column_metadata\"\n WHERE backfill IS NOT NULL\n ORDER BY table_name, column_name\n `)\n .all<{table_name: string; column_name: string; backfill: string}>();\n\n const unresolvable = getOrCreateCounter(\n 'replica',\n 'backfilling_unresolvable_rows',\n 'In-flight backfills whose upstream table identity could not be recovered ' +\n 'from the replica when \"' +\n BACKFILLING_TABLE +\n '\" was created. A best-effort identity is stored; the expected count is 0.',\n );\n if (backfilling.length === 0) {\n unresolvable.add(0); // the common case: publish the series anyway\n return;\n }\n\n const byLiteName = new Map<string, Identifier>(\n db\n .prepare(/*sql*/ `SELECT \"schema\", \"table\" FROM \"_zero.tableMetadata\"`)\n .all<{schema: string; table: string}>()\n .map(({schema, table}) => [\n liteTableName({schema, name: table}),\n {schema, name: table},\n ]),\n );\n\n const insert = db.prepare(/*sql*/ `\n INSERT INTO \"${BACKFILLING_TABLE}\"\n (\"schema\", \"table\", \"column\", \"backfill\") VALUES (?, ?, ?, ?)\n `);\n let unresolved = 0;\n for (const {table_name: liteName, column_name, backfill} of backfilling) {\n const known = byLiteName.get(liteName);\n const dot = known ? -1 : liteName.indexOf('.');\n const table = known ?? {\n schema: dot < 0 ? 'public' : liteName.slice(0, dot),\n name: dot < 0 ? liteName : liteName.slice(dot + 1),\n };\n if (!known && dot >= 0) {\n unresolved++;\n lc.warn?.(\n `no metadata for backfilling table \"${liteName}\"; ` +\n `assuming {schema: \"${table.schema}\", name: \"${table.name}\"}`,\n );\n }\n insert.run(table.schema, table.name, column_name, backfill);\n }\n unresolvable.add(unresolved);\n lc.info?.(\n `seeded \"${BACKFILLING_TABLE}\" with ${backfilling.length} in-flight ` +\n `backfill(s), ${unresolved} of which could not be resolved`,\n );\n}\n"],"mappings":"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"}
@@ -47,14 +47,14 @@ export declare const changeLogEntrySchema: v.Type<{
47
47
  rowKey: Readonly<Record<string, import("../../../../../shared/src/bigint-json.ts").JSONValue>> | null;
48
48
  stateVersion: string;
49
49
  table: string;
50
- op: "d" | "r" | "s" | "t";
50
+ op: "s" | "d" | "r" | "t";
51
51
  }>;
52
52
  export type ChangeLogEntry = v.Infer<typeof changeLogEntrySchema>;
53
53
  declare const rawChangeLogEntrySchema: v.ObjectType<{
54
54
  stateVersion: v.Type<string>;
55
55
  table: v.Type<string>;
56
56
  rowKey: v.Type<string>;
57
- op: v.Type<"d" | "r" | "s" | "t">;
57
+ op: v.Type<"s" | "d" | "r" | "t">;
58
58
  backfillingColumnVersions: v.Type<Record<string, string>>;
59
59
  }, undefined>;
60
60
  export type RawChangeLogEntry = v.Infer<typeof rawChangeLogEntrySchema>;
@@ -78,7 +78,7 @@ export declare class ChangeLog {
78
78
  stateVersion: string;
79
79
  table: string;
80
80
  rowKey: string;
81
- op: "d" | "r" | "s" | "t";
81
+ op: "s" | "d" | "r" | "t";
82
82
  backfillingColumnVersions: Record<string, string>;
83
83
  } | undefined;
84
84
  logTruncateOp(version: LexiVersion, table: string): void;
@@ -87,9 +87,9 @@ var ColumnMetadataStore = class ColumnMetadataStore {
87
87
  * Returns `undefined` if the metadata table doesn't exist yet.
88
88
  */
89
89
  static getInstance(db) {
90
- if (!db.prepare(`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = '_zero.column_metadata'`).get()) return;
91
90
  let instance = ColumnMetadataStore.#instances.get(db);
92
91
  if (!instance) {
92
+ if (!db.prepare(`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = '_zero.column_metadata'`).get()) return;
93
93
  instance = new ColumnMetadataStore(db);
94
94
  ColumnMetadataStore.#instances.set(db, instance);
95
95
  }