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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (832) hide show
  1. package/out/analyze-query/src/bin-analyze.js +9 -8
  2. package/out/analyze-query/src/bin-analyze.js.map +1 -1
  3. package/out/ast-to-zql/src/ast-to-zql.d.ts +8 -1
  4. package/out/ast-to-zql/src/ast-to-zql.d.ts.map +1 -1
  5. package/out/ast-to-zql/src/ast-to-zql.js +25 -25
  6. package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
  7. package/out/otel/src/log-options.d.ts +16 -0
  8. package/out/otel/src/log-options.d.ts.map +1 -1
  9. package/out/otel/src/log-options.js +54 -1
  10. package/out/otel/src/log-options.js.map +1 -1
  11. package/out/replicache/src/bg-interval.d.ts +2 -1
  12. package/out/replicache/src/bg-interval.d.ts.map +1 -1
  13. package/out/replicache/src/bg-interval.js +13 -4
  14. package/out/replicache/src/bg-interval.js.map +1 -1
  15. package/out/replicache/src/btree/node.d.ts.map +1 -1
  16. package/out/replicache/src/btree/node.js +3 -6
  17. package/out/replicache/src/btree/node.js.map +1 -1
  18. package/out/replicache/src/dag/gc.d.ts.map +1 -1
  19. package/out/replicache/src/dag/gc.js +8 -11
  20. package/out/replicache/src/dag/gc.js.map +1 -1
  21. package/out/replicache/src/dag/invalid-ref-count-error.d.ts +12 -0
  22. package/out/replicache/src/dag/invalid-ref-count-error.d.ts.map +1 -0
  23. package/out/replicache/src/dag/invalid-ref-count-error.js +19 -0
  24. package/out/replicache/src/dag/invalid-ref-count-error.js.map +1 -0
  25. package/out/replicache/src/dag/lazy-store.d.ts +17 -4
  26. package/out/replicache/src/dag/lazy-store.d.ts.map +1 -1
  27. package/out/replicache/src/dag/lazy-store.js +35 -9
  28. package/out/replicache/src/dag/lazy-store.js.map +1 -1
  29. package/out/replicache/src/dag/store-impl.d.ts +12 -2
  30. package/out/replicache/src/dag/store-impl.d.ts.map +1 -1
  31. package/out/replicache/src/dag/store-impl.js +29 -9
  32. package/out/replicache/src/dag/store-impl.js.map +1 -1
  33. package/out/replicache/src/db/rebase.d.ts +14 -2
  34. package/out/replicache/src/db/rebase.d.ts.map +1 -1
  35. package/out/replicache/src/db/rebase.js +27 -5
  36. package/out/replicache/src/db/rebase.js.map +1 -1
  37. package/out/replicache/src/get-kv-store-provider.d.ts +1 -1
  38. package/out/replicache/src/get-kv-store-provider.d.ts.map +1 -1
  39. package/out/replicache/src/get-kv-store-provider.js +4 -4
  40. package/out/replicache/src/get-kv-store-provider.js.map +1 -1
  41. package/out/replicache/src/kv/expo-sqlite/store.d.ts.map +1 -1
  42. package/out/replicache/src/kv/expo-sqlite/store.js +28 -5
  43. package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
  44. package/out/replicache/src/kv/idb-store.d.ts +18 -0
  45. package/out/replicache/src/kv/idb-store.d.ts.map +1 -1
  46. package/out/replicache/src/kv/idb-store.js +30 -2
  47. package/out/replicache/src/kv/idb-store.js.map +1 -1
  48. package/out/replicache/src/kv/mem-fallback-store.d.ts +54 -0
  49. package/out/replicache/src/kv/mem-fallback-store.d.ts.map +1 -0
  50. package/out/replicache/src/kv/mem-fallback-store.js +177 -0
  51. package/out/replicache/src/kv/mem-fallback-store.js.map +1 -0
  52. package/out/replicache/src/kv/mem-store.d.ts +1 -0
  53. package/out/replicache/src/kv/mem-store.d.ts.map +1 -1
  54. package/out/replicache/src/kv/mem-store.js +3 -0
  55. package/out/replicache/src/kv/mem-store.js.map +1 -1
  56. package/out/replicache/src/kv/op-sqlite/store.d.ts.map +1 -1
  57. package/out/replicache/src/kv/op-sqlite/store.js +4 -4
  58. package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
  59. package/out/replicache/src/kv/sqlite-store.d.ts +22 -1
  60. package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
  61. package/out/replicache/src/kv/sqlite-store.js +159 -14
  62. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  63. package/out/replicache/src/kv/store.d.ts +6 -0
  64. package/out/replicache/src/kv/store.d.ts.map +1 -1
  65. package/out/replicache/src/new-client-channel.js.map +1 -1
  66. package/out/replicache/src/persist/client-gc.d.ts +2 -1
  67. package/out/replicache/src/persist/client-gc.d.ts.map +1 -1
  68. package/out/replicache/src/persist/client-gc.js +2 -2
  69. package/out/replicache/src/persist/client-gc.js.map +1 -1
  70. package/out/replicache/src/persist/client-group-gc.d.ts +2 -1
  71. package/out/replicache/src/persist/client-group-gc.d.ts.map +1 -1
  72. package/out/replicache/src/persist/client-group-gc.js +2 -2
  73. package/out/replicache/src/persist/client-group-gc.js.map +1 -1
  74. package/out/replicache/src/persist/collect-idb-databases.d.ts +28 -8
  75. package/out/replicache/src/persist/collect-idb-databases.d.ts.map +1 -1
  76. package/out/replicache/src/persist/collect-idb-databases.js +97 -20
  77. package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
  78. package/out/replicache/src/persist/heartbeat.d.ts +2 -1
  79. package/out/replicache/src/persist/heartbeat.d.ts.map +1 -1
  80. package/out/replicache/src/persist/heartbeat.js +2 -2
  81. package/out/replicache/src/persist/heartbeat.js.map +1 -1
  82. package/out/replicache/src/persist/idb-databases-store-db-name.js +1 -1
  83. package/out/replicache/src/persist/idb-databases-store-db-name.js.map +1 -1
  84. package/out/replicache/src/persist/persist.d.ts.map +1 -1
  85. package/out/replicache/src/persist/persist.js +18 -8
  86. package/out/replicache/src/persist/persist.js.map +1 -1
  87. package/out/replicache/src/persist/refresh.js +6 -2
  88. package/out/replicache/src/persist/refresh.js.map +1 -1
  89. package/out/replicache/src/replicache-impl.d.ts +85 -3
  90. package/out/replicache/src/replicache-impl.d.ts.map +1 -1
  91. package/out/replicache/src/replicache-impl.js +321 -29
  92. package/out/replicache/src/replicache-impl.js.map +1 -1
  93. package/out/replicache/src/replicache-options.d.ts +9 -0
  94. package/out/replicache/src/replicache-options.d.ts.map +1 -1
  95. package/out/replicache/src/storage-failure.d.ts +37 -0
  96. package/out/replicache/src/storage-failure.d.ts.map +1 -0
  97. package/out/replicache/src/storage-failure.js +31 -0
  98. package/out/replicache/src/storage-failure.js.map +1 -0
  99. package/out/shared/src/arrays.d.ts +5 -0
  100. package/out/shared/src/arrays.d.ts.map +1 -1
  101. package/out/shared/src/arrays.js +8 -10
  102. package/out/shared/src/arrays.js.map +1 -1
  103. package/out/shared/src/error.d.ts +11 -0
  104. package/out/shared/src/error.d.ts.map +1 -1
  105. package/out/shared/src/error.js +35 -1
  106. package/out/shared/src/error.js.map +1 -1
  107. package/out/shared/src/iterables.d.ts +2 -0
  108. package/out/shared/src/iterables.d.ts.map +1 -1
  109. package/out/shared/src/iterables.js +18 -1
  110. package/out/shared/src/iterables.js.map +1 -1
  111. package/out/shared/src/map.d.ts +24 -2
  112. package/out/shared/src/map.d.ts.map +1 -1
  113. package/out/shared/src/map.js +18 -4
  114. package/out/shared/src/map.js.map +1 -1
  115. package/out/shared/src/random-values.js +12 -1
  116. package/out/shared/src/random-values.js.map +1 -1
  117. package/out/shared/src/ring-buffer.d.ts +10 -0
  118. package/out/shared/src/ring-buffer.d.ts.map +1 -1
  119. package/out/shared/src/ring-buffer.js +26 -1
  120. package/out/shared/src/ring-buffer.js.map +1 -1
  121. package/out/z2s/src/compiler.js +1 -1
  122. package/out/z2s/src/sql.d.ts.map +1 -1
  123. package/out/z2s/src/sql.js +2 -2
  124. package/out/z2s/src/sql.js.map +1 -1
  125. package/out/zero/package.js +3 -4
  126. package/out/zero/package.js.map +1 -1
  127. package/out/zero/src/zero.js +2 -1
  128. package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
  129. package/out/zero-cache/src/auth/write-authorizer.js +1 -0
  130. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  131. package/out/zero-cache/src/config/normalize.d.ts.map +1 -1
  132. package/out/zero-cache/src/config/normalize.js +26 -2
  133. package/out/zero-cache/src/config/normalize.js.map +1 -1
  134. package/out/zero-cache/src/config/zero-config.d.ts +102 -6
  135. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  136. package/out/zero-cache/src/config/zero-config.js +254 -57
  137. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  138. package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
  139. package/out/zero-cache/src/db/lite-tables.js +3 -4
  140. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  141. package/out/zero-cache/src/observability/events.js +4 -1
  142. package/out/zero-cache/src/observability/events.js.map +1 -1
  143. package/out/zero-cache/src/observability/log-throttle.d.ts +44 -0
  144. package/out/zero-cache/src/observability/log-throttle.d.ts.map +1 -0
  145. package/out/zero-cache/src/observability/log-throttle.js +76 -0
  146. package/out/zero-cache/src/observability/log-throttle.js.map +1 -0
  147. package/out/zero-cache/src/observability/metrics.d.ts.map +1 -1
  148. package/out/zero-cache/src/observability/metrics.js +2 -7
  149. package/out/zero-cache/src/observability/metrics.js.map +1 -1
  150. package/out/zero-cache/src/scripts/permissions.d.ts +16 -0
  151. package/out/zero-cache/src/scripts/permissions.d.ts.map +1 -1
  152. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  153. package/out/zero-cache/src/server/change-streamer.js +27 -10
  154. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  155. package/out/zero-cache/src/server/inspector-delegate.d.ts +28 -2
  156. package/out/zero-cache/src/server/inspector-delegate.d.ts.map +1 -1
  157. package/out/zero-cache/src/server/inspector-delegate.js +37 -17
  158. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  159. package/out/zero-cache/src/server/main.d.ts.map +1 -1
  160. package/out/zero-cache/src/server/main.js +8 -1
  161. package/out/zero-cache/src/server/main.js.map +1 -1
  162. package/out/zero-cache/src/server/mutator.js +2 -0
  163. package/out/zero-cache/src/server/mutator.js.map +1 -1
  164. package/out/zero-cache/src/server/reaper.d.ts.map +1 -1
  165. package/out/zero-cache/src/server/reaper.js +2 -0
  166. package/out/zero-cache/src/server/reaper.js.map +1 -1
  167. package/out/zero-cache/src/server/replicator.d.ts +3 -0
  168. package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
  169. package/out/zero-cache/src/server/replicator.js +79 -15
  170. package/out/zero-cache/src/server/replicator.js.map +1 -1
  171. package/out/zero-cache/src/server/runner/zero-dispatcher.d.ts.map +1 -1
  172. package/out/zero-cache/src/server/runner/zero-dispatcher.js +5 -0
  173. package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
  174. package/out/zero-cache/src/server/syncer-assigner.d.ts +14 -0
  175. package/out/zero-cache/src/server/syncer-assigner.d.ts.map +1 -0
  176. package/out/zero-cache/src/server/syncer-assigner.js +72 -0
  177. package/out/zero-cache/src/server/syncer-assigner.js.map +1 -0
  178. package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
  179. package/out/zero-cache/src/server/syncer.js +24 -2
  180. package/out/zero-cache/src/server/syncer.js.map +1 -1
  181. package/out/zero-cache/src/server/worker-dispatcher.d.ts.map +1 -1
  182. package/out/zero-cache/src/server/worker-dispatcher.js +21 -4
  183. package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
  184. package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
  185. package/out/zero-cache/src/services/analyze.js +11 -9
  186. package/out/zero-cache/src/services/analyze.js.map +1 -1
  187. package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts.map +1 -1
  188. package/out/zero-cache/src/services/change-source/common/backfill-manager.js +198 -24
  189. package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
  190. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.d.ts.map +1 -1
  191. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js +15 -3
  192. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
  193. package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts +1 -0
  194. package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts.map +1 -1
  195. package/out/zero-cache/src/services/change-source/common/replica-schema.js +15 -3
  196. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  197. package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts +9 -0
  198. package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts.map +1 -1
  199. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +204 -18
  200. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  201. package/out/zero-cache/src/services/change-source/pg/change-source-init.d.ts +25 -0
  202. package/out/zero-cache/src/services/change-source/pg/change-source-init.d.ts.map +1 -0
  203. package/out/zero-cache/src/services/change-source/pg/change-source-init.js +202 -0
  204. package/out/zero-cache/src/services/change-source/pg/change-source-init.js.map +1 -0
  205. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts +14 -16
  206. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  207. package/out/zero-cache/src/services/change-source/pg/change-source.js +64 -143
  208. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  209. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +37 -2
  210. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  211. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +53 -21
  212. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  213. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.d.ts +16 -0
  214. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.d.ts.map +1 -0
  215. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js +113 -0
  216. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js.map +1 -0
  217. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +21 -15
  218. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -1
  219. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +86 -23
  220. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  221. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts.map +1 -1
  222. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js +68 -8
  223. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js.map +1 -1
  224. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
  225. package/out/zero-cache/src/services/change-source/pg/schema/init.js +32 -1
  226. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  227. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.d.ts +10 -0
  228. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.d.ts.map +1 -0
  229. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.js +6 -0
  230. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.js.map +1 -0
  231. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +50 -27
  232. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
  233. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +100 -34
  234. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  235. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.d.ts +68 -0
  236. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.d.ts.map +1 -0
  237. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js +141 -0
  238. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js.map +1 -0
  239. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts +43 -0
  240. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts.map +1 -1
  241. package/out/zero-cache/src/services/change-source/protocol/current/data.js +10 -1
  242. package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
  243. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +57 -0
  244. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts.map +1 -1
  245. package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts +20 -6
  246. package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts.map +1 -1
  247. package/out/zero-cache/src/services/change-source/protocol/current/upstream.js +17 -7
  248. package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
  249. package/out/zero-cache/src/services/change-source/protocol/current.js +2 -1
  250. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.d.ts +66 -0
  251. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.d.ts.map +1 -0
  252. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js +154 -0
  253. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js.map +1 -0
  254. package/out/zero-cache/src/services/change-streamer/backfill-state.d.ts +50 -0
  255. package/out/zero-cache/src/services/change-streamer/backfill-state.d.ts.map +1 -0
  256. package/out/zero-cache/src/services/change-streamer/backfill-state.js +268 -0
  257. package/out/zero-cache/src/services/change-streamer/backfill-state.js.map +1 -0
  258. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts.map +1 -1
  259. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js +2 -0
  260. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
  261. package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts.map +1 -1
  262. package/out/zero-cache/src/services/change-streamer/backup-monitor.js +58 -7
  263. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
  264. package/out/zero-cache/src/services/change-streamer/broadcast.d.ts +8 -2
  265. package/out/zero-cache/src/services/change-streamer/broadcast.d.ts.map +1 -1
  266. package/out/zero-cache/src/services/change-streamer/broadcast.js +24 -4
  267. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  268. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts +19 -4
  269. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts.map +1 -1
  270. package/out/zero-cache/src/services/change-streamer/change-log-codec.js +28 -8
  271. package/out/zero-cache/src/services/change-streamer/change-log-codec.js.map +1 -1
  272. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +48 -6
  273. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  274. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +130 -34
  275. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  276. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +11 -0
  277. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
  278. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +118 -68
  279. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  280. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +37 -15
  281. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
  282. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  283. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts +3 -3
  284. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts.map +1 -1
  285. package/out/zero-cache/src/services/change-streamer/forwarder.js +36 -5
  286. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  287. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.d.ts +51 -0
  288. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.d.ts.map +1 -0
  289. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js +132 -0
  290. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js.map +1 -0
  291. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts +1 -1
  292. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts.map +1 -1
  293. package/out/zero-cache/src/services/change-streamer/schema/tables.js +19 -8
  294. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  295. package/out/zero-cache/src/services/change-streamer/snapshot-message.d.ts +92 -0
  296. package/out/zero-cache/src/services/change-streamer/snapshot-message.d.ts.map +1 -0
  297. package/out/zero-cache/src/services/change-streamer/snapshot-message.js +42 -0
  298. package/out/zero-cache/src/services/change-streamer/snapshot-message.js.map +1 -0
  299. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts +1 -1
  300. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts.map +1 -1
  301. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js +16 -4
  302. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -1
  303. package/out/zero-cache/src/services/change-streamer/snapshot.d.ts +7 -69
  304. package/out/zero-cache/src/services/change-streamer/snapshot.d.ts.map +1 -1
  305. package/out/zero-cache/src/services/change-streamer/snapshot.js +21 -53
  306. package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
  307. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts +6 -1
  308. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts.map +1 -1
  309. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +27 -9
  310. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js.map +1 -1
  311. package/out/zero-cache/src/services/change-streamer/storer.d.ts +2 -2
  312. package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
  313. package/out/zero-cache/src/services/change-streamer/storer.js +9 -6
  314. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  315. package/out/zero-cache/src/services/change-streamer/subscribe.d.ts +446 -0
  316. package/out/zero-cache/src/services/change-streamer/subscribe.d.ts.map +1 -0
  317. package/out/zero-cache/src/services/change-streamer/subscribe.js +76 -0
  318. package/out/zero-cache/src/services/change-streamer/subscribe.js.map +1 -0
  319. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +33 -1
  320. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
  321. package/out/zero-cache/src/services/change-streamer/subscriber.js +76 -0
  322. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  323. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts +17 -1
  324. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts.map +1 -1
  325. package/out/zero-cache/src/services/change-streamer/upstream-acker.js +19 -3
  326. package/out/zero-cache/src/services/change-streamer/upstream-acker.js.map +1 -1
  327. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts +5 -0
  328. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts.map +1 -1
  329. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js +35 -5
  330. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js.map +1 -1
  331. package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
  332. package/out/zero-cache/src/services/life-cycle.js +3 -1
  333. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  334. package/out/zero-cache/src/services/litestream/commands.d.ts +15 -3
  335. package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
  336. package/out/zero-cache/src/services/litestream/commands.js +24 -7
  337. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  338. package/out/zero-cache/src/services/litestream/config-v5.yml +11 -4
  339. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js +51 -24
  340. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js.map +1 -1
  341. package/out/zero-cache/src/services/litestream/litestream-controller.d.ts +10 -2
  342. package/out/zero-cache/src/services/litestream/litestream-controller.d.ts.map +1 -1
  343. package/out/zero-cache/src/services/litestream/litestream-controller.js +3 -2
  344. package/out/zero-cache/src/services/litestream/litestream-controller.js.map +1 -1
  345. package/out/zero-cache/src/services/litestream/restore-progress.d.ts +24 -0
  346. package/out/zero-cache/src/services/litestream/restore-progress.d.ts.map +1 -0
  347. package/out/zero-cache/src/services/litestream/restore-progress.js +91 -0
  348. package/out/zero-cache/src/services/litestream/restore-progress.js.map +1 -0
  349. package/out/zero-cache/src/services/mutagen/mutagen.d.ts +5 -0
  350. package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
  351. package/out/zero-cache/src/services/mutagen/mutagen.js +7 -1
  352. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  353. package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +1 -1
  354. package/out/zero-cache/src/services/mutagen/pusher.js +5 -6
  355. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  356. package/out/zero-cache/src/services/plannerz.d.ts +163 -0
  357. package/out/zero-cache/src/services/plannerz.d.ts.map +1 -0
  358. package/out/zero-cache/src/services/plannerz.js +267 -0
  359. package/out/zero-cache/src/services/plannerz.js.map +1 -0
  360. package/out/zero-cache/src/services/profz.d.ts +30 -0
  361. package/out/zero-cache/src/services/profz.d.ts.map +1 -0
  362. package/out/zero-cache/src/services/profz.js +145 -0
  363. package/out/zero-cache/src/services/profz.js.map +1 -0
  364. package/out/zero-cache/src/services/replicator/change-log-cookies.js +1 -1
  365. package/out/zero-cache/src/services/replicator/change-log-cookies.js.map +1 -1
  366. package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts +5 -4
  367. package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts.map +1 -1
  368. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js +20 -22
  369. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js.map +1 -1
  370. package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +1 -1
  371. package/out/zero-cache/src/services/replicator/change-processor.js +49 -5
  372. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  373. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts +3 -2
  374. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
  375. package/out/zero-cache/src/services/replicator/incremental-sync.js +50 -20
  376. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  377. package/out/zero-cache/src/services/replicator/replication-status.d.ts +35 -1
  378. package/out/zero-cache/src/services/replicator/replication-status.d.ts.map +1 -1
  379. package/out/zero-cache/src/services/replicator/replication-status.js +96 -6
  380. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  381. package/out/zero-cache/src/services/replicator/replicator.d.ts +2 -1
  382. package/out/zero-cache/src/services/replicator/replicator.d.ts.map +1 -1
  383. package/out/zero-cache/src/services/replicator/replicator.js +2 -2
  384. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  385. package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts +23 -2
  386. package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts.map +1 -1
  387. package/out/zero-cache/src/services/replicator/schema/backfilling.js +54 -2
  388. package/out/zero-cache/src/services/replicator/schema/backfilling.js.map +1 -1
  389. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts +6 -6
  390. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts.map +1 -1
  391. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +19 -14
  392. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -1
  393. package/out/zero-cache/src/services/replicator/write-worker-client.d.ts +11 -3
  394. package/out/zero-cache/src/services/replicator/write-worker-client.d.ts.map +1 -1
  395. package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
  396. package/out/zero-cache/src/services/replicator/write-worker.js +5 -1
  397. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  398. package/out/zero-cache/src/services/run-ast.d.ts +12 -0
  399. package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
  400. package/out/zero-cache/src/services/run-ast.js +26 -14
  401. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  402. package/out/zero-cache/src/services/runner.d.ts +2 -1
  403. package/out/zero-cache/src/services/runner.d.ts.map +1 -1
  404. package/out/zero-cache/src/services/runner.js +10 -2
  405. package/out/zero-cache/src/services/runner.js.map +1 -1
  406. package/out/zero-cache/src/services/statz.d.ts +4 -0
  407. package/out/zero-cache/src/services/statz.d.ts.map +1 -1
  408. package/out/zero-cache/src/services/statz.js +22 -5
  409. package/out/zero-cache/src/services/statz.js.map +1 -1
  410. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts +19 -2
  411. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +1 -1
  412. package/out/zero-cache/src/services/view-syncer/client-handler.js +42 -30
  413. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  414. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts +19 -1
  415. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
  416. package/out/zero-cache/src/services/view-syncer/cvr-store.js +34 -11
  417. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  418. package/out/zero-cache/src/services/view-syncer/cvr.d.ts +51 -2
  419. package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
  420. package/out/zero-cache/src/services/view-syncer/cvr.js +142 -6
  421. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  422. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.d.ts +81 -0
  423. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.d.ts.map +1 -0
  424. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js +128 -0
  425. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js.map +1 -0
  426. package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts +14 -1
  427. package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts.map +1 -1
  428. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js +30 -4
  429. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
  430. package/out/zero-cache/src/services/view-syncer/hydration-budget.d.ts +33 -0
  431. package/out/zero-cache/src/services/view-syncer/hydration-budget.d.ts.map +1 -0
  432. package/out/zero-cache/src/services/view-syncer/hydration-budget.js +62 -0
  433. package/out/zero-cache/src/services/view-syncer/hydration-budget.js.map +1 -0
  434. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts +51 -0
  435. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts.map +1 -0
  436. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js +78 -0
  437. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js.map +1 -0
  438. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts +1 -1
  439. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts.map +1 -1
  440. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +3 -3
  441. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  442. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +50 -2
  443. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  444. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +466 -62
  445. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  446. package/out/zero-cache/src/services/view-syncer/plan-warnings.d.ts +7 -0
  447. package/out/zero-cache/src/services/view-syncer/plan-warnings.d.ts.map +1 -0
  448. package/out/zero-cache/src/services/view-syncer/plan-warnings.js +35 -0
  449. package/out/zero-cache/src/services/view-syncer/plan-warnings.js.map +1 -0
  450. package/out/zero-cache/src/services/view-syncer/query-covering.d.ts.map +1 -1
  451. package/out/zero-cache/src/services/view-syncer/query-covering.js +2 -6
  452. package/out/zero-cache/src/services/view-syncer/query-covering.js.map +1 -1
  453. package/out/zero-cache/src/services/view-syncer/query-shape.d.ts +25 -0
  454. package/out/zero-cache/src/services/view-syncer/query-shape.d.ts.map +1 -0
  455. package/out/zero-cache/src/services/view-syncer/query-shape.js +28 -0
  456. package/out/zero-cache/src/services/view-syncer/query-shape.js.map +1 -0
  457. package/out/zero-cache/src/services/view-syncer/query-stats.d.ts +77 -0
  458. package/out/zero-cache/src/services/view-syncer/query-stats.d.ts.map +1 -0
  459. package/out/zero-cache/src/services/view-syncer/query-stats.js +206 -0
  460. package/out/zero-cache/src/services/view-syncer/query-stats.js.map +1 -0
  461. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.d.ts +95 -0
  462. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.d.ts.map +1 -0
  463. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js +155 -0
  464. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js.map +1 -0
  465. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +78 -4
  466. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
  467. package/out/zero-cache/src/services/view-syncer/snapshotter.js +158 -28
  468. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  469. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +11 -1
  470. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  471. package/out/zero-cache/src/services/view-syncer/view-syncer.js +587 -103
  472. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  473. package/out/zero-cache/src/types/processes.d.ts +39 -0
  474. package/out/zero-cache/src/types/processes.d.ts.map +1 -1
  475. package/out/zero-cache/src/types/processes.js +43 -4
  476. package/out/zero-cache/src/types/processes.js.map +1 -1
  477. package/out/zero-cache/src/types/profiler.d.ts +12 -0
  478. package/out/zero-cache/src/types/profiler.d.ts.map +1 -1
  479. package/out/zero-cache/src/types/profiler.js +46 -1
  480. package/out/zero-cache/src/types/profiler.js.map +1 -1
  481. package/out/zero-cache/src/types/row-key.d.ts.map +1 -1
  482. package/out/zero-cache/src/types/row-key.js +5 -5
  483. package/out/zero-cache/src/types/row-key.js.map +1 -1
  484. package/out/zero-cache/src/types/streams.d.ts +49 -7
  485. package/out/zero-cache/src/types/streams.d.ts.map +1 -1
  486. package/out/zero-cache/src/types/streams.js +190 -58
  487. package/out/zero-cache/src/types/streams.js.map +1 -1
  488. package/out/zero-cache/src/types/subscription.d.ts +15 -0
  489. package/out/zero-cache/src/types/subscription.d.ts.map +1 -1
  490. package/out/zero-cache/src/types/subscription.js +123 -31
  491. package/out/zero-cache/src/types/subscription.js.map +1 -1
  492. package/out/zero-cache/src/types/websocket-handoff.d.ts +1 -1
  493. package/out/zero-cache/src/types/websocket-handoff.d.ts.map +1 -1
  494. package/out/zero-cache/src/types/websocket-handoff.js +18 -9
  495. package/out/zero-cache/src/types/websocket-handoff.js.map +1 -1
  496. package/out/zero-cache/src/types/ws.d.ts.map +1 -1
  497. package/out/zero-cache/src/types/ws.js +1 -16
  498. package/out/zero-cache/src/types/ws.js.map +1 -1
  499. package/out/zero-cache/src/workers/connect-params.d.ts +1 -0
  500. package/out/zero-cache/src/workers/connect-params.d.ts.map +1 -1
  501. package/out/zero-cache/src/workers/connect-params.js.map +1 -1
  502. package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
  503. package/out/zero-cache/src/workers/syncer-ws-message-handler.js +21 -16
  504. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  505. package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
  506. package/out/zero-cache/src/workers/syncer.js +55 -4
  507. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  508. package/out/zero-client/src/client/active-clients-manager.d.ts.map +1 -1
  509. package/out/zero-client/src/client/active-clients-manager.js +4 -1
  510. package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
  511. package/out/zero-client/src/client/connection-manager.d.ts +20 -0
  512. package/out/zero-client/src/client/connection-manager.d.ts.map +1 -1
  513. package/out/zero-client/src/client/connection-manager.js +77 -13
  514. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  515. package/out/zero-client/src/client/connection-status-enum.d.ts +2 -0
  516. package/out/zero-client/src/client/connection-status-enum.d.ts.map +1 -1
  517. package/out/zero-client/src/client/connection-status-enum.js +3 -1
  518. package/out/zero-client/src/client/connection-status-enum.js.map +1 -1
  519. package/out/zero-client/src/client/connection.d.ts +6 -0
  520. package/out/zero-client/src/client/connection.d.ts.map +1 -1
  521. package/out/zero-client/src/client/connection.js +2 -1
  522. package/out/zero-client/src/client/connection.js.map +1 -1
  523. package/out/zero-client/src/client/context.d.ts +22 -12
  524. package/out/zero-client/src/client/context.d.ts.map +1 -1
  525. package/out/zero-client/src/client/context.js +92 -25
  526. package/out/zero-client/src/client/context.js.map +1 -1
  527. package/out/zero-client/src/client/custom.js +2 -2
  528. package/out/zero-client/src/client/custom.js.map +1 -1
  529. package/out/zero-client/src/client/error.js +1 -1
  530. package/out/zero-client/src/client/ivm-branch.d.ts.map +1 -1
  531. package/out/zero-client/src/client/ivm-branch.js +20 -3
  532. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  533. package/out/zero-client/src/client/mutator-proxy.d.ts.map +1 -1
  534. package/out/zero-client/src/client/mutator-proxy.js +5 -2
  535. package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
  536. package/out/zero-client/src/client/options.d.ts +33 -0
  537. package/out/zero-client/src/client/options.d.ts.map +1 -1
  538. package/out/zero-client/src/client/options.js.map +1 -1
  539. package/out/zero-client/src/client/query-manager.d.ts.map +1 -1
  540. package/out/zero-client/src/client/query-manager.js +44 -11
  541. package/out/zero-client/src/client/query-manager.js.map +1 -1
  542. package/out/zero-client/src/client/version.js +1 -1
  543. package/out/zero-client/src/client/zero-rep.d.ts +1 -0
  544. package/out/zero-client/src/client/zero-rep.d.ts.map +1 -1
  545. package/out/zero-client/src/client/zero-rep.js +8 -6
  546. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  547. package/out/zero-client/src/client/zero.d.ts +18 -3
  548. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  549. package/out/zero-client/src/client/zero.js +84 -47
  550. package/out/zero-client/src/client/zero.js.map +1 -1
  551. package/out/zero-client/src/mod.d.ts +1 -0
  552. package/out/zero-client/src/mod.d.ts.map +1 -1
  553. package/out/zero-client/src/mod.js +1 -0
  554. package/out/zero-client/src/types/query-result.d.ts +2 -0
  555. package/out/zero-client/src/types/query-result.d.ts.map +1 -1
  556. package/out/zero-events/src/status.d.ts +58 -0
  557. package/out/zero-events/src/status.d.ts.map +1 -1
  558. package/out/zero-protocol/src/ast.d.ts.map +1 -1
  559. package/out/zero-protocol/src/ast.js +13 -14
  560. package/out/zero-protocol/src/ast.js.map +1 -1
  561. package/out/zero-protocol/src/down.d.ts +29 -29
  562. package/out/zero-protocol/src/query-hash.d.ts.map +1 -1
  563. package/out/zero-protocol/src/query-hash.js +2 -5
  564. package/out/zero-protocol/src/query-hash.js.map +1 -1
  565. package/out/zero-react/src/use-query.d.ts +5 -4
  566. package/out/zero-react/src/use-query.d.ts.map +1 -1
  567. package/out/zero-react/src/use-query.js +62 -4
  568. package/out/zero-react/src/use-query.js.map +1 -1
  569. package/out/zero-react/src/zero.js +1 -0
  570. package/out/zero-server/src/adapters/drizzle.d.ts +2 -1
  571. package/out/zero-server/src/adapters/drizzle.d.ts.map +1 -1
  572. package/out/zero-server/src/adapters/drizzle.js +12 -7
  573. package/out/zero-server/src/adapters/drizzle.js.map +1 -1
  574. package/out/zero-server/src/adapters/kysely.d.ts +2 -1
  575. package/out/zero-server/src/adapters/kysely.d.ts.map +1 -1
  576. package/out/zero-server/src/adapters/kysely.js +8 -2
  577. package/out/zero-server/src/adapters/kysely.js.map +1 -1
  578. package/out/zero-server/src/adapters/pg.d.ts +1 -0
  579. package/out/zero-server/src/adapters/pg.d.ts.map +1 -1
  580. package/out/zero-server/src/adapters/pg.js +8 -2
  581. package/out/zero-server/src/adapters/pg.js.map +1 -1
  582. package/out/zero-server/src/adapters/postgresjs.d.ts +1 -0
  583. package/out/zero-server/src/adapters/postgresjs.d.ts.map +1 -1
  584. package/out/zero-server/src/adapters/postgresjs.js +7 -1
  585. package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
  586. package/out/zero-server/src/adapters/prisma.d.ts +3 -2
  587. package/out/zero-server/src/adapters/prisma.d.ts.map +1 -1
  588. package/out/zero-server/src/adapters/prisma.js +8 -2
  589. package/out/zero-server/src/adapters/prisma.js.map +1 -1
  590. package/out/zero-server/src/custom.d.ts +5 -1
  591. package/out/zero-server/src/custom.d.ts.map +1 -1
  592. package/out/zero-server/src/custom.js +6 -3
  593. package/out/zero-server/src/custom.js.map +1 -1
  594. package/out/zero-server/src/logging.d.ts +2 -2
  595. package/out/zero-server/src/logging.d.ts.map +1 -1
  596. package/out/zero-server/src/logging.js +2 -2
  597. package/out/zero-server/src/logging.js.map +1 -1
  598. package/out/zero-server/src/mod.d.ts +1 -1
  599. package/out/zero-server/src/mod.d.ts.map +1 -1
  600. package/out/zero-server/src/pg-query-executor.d.ts +2 -2
  601. package/out/zero-server/src/pg-query-executor.d.ts.map +1 -1
  602. package/out/zero-server/src/pg-query-executor.js +2 -2
  603. package/out/zero-server/src/pg-query-executor.js.map +1 -1
  604. package/out/zero-server/src/process-mutations.d.ts +12 -3
  605. package/out/zero-server/src/process-mutations.d.ts.map +1 -1
  606. package/out/zero-server/src/process-mutations.js +13 -8
  607. package/out/zero-server/src/process-mutations.js.map +1 -1
  608. package/out/zero-server/src/push-processor.d.ts +8 -2
  609. package/out/zero-server/src/push-processor.d.ts.map +1 -1
  610. package/out/zero-server/src/push-processor.js +12 -4
  611. package/out/zero-server/src/push-processor.js.map +1 -1
  612. package/out/zero-server/src/schema.d.ts +2 -2
  613. package/out/zero-server/src/schema.d.ts.map +1 -1
  614. package/out/zero-server/src/schema.js +2 -2
  615. package/out/zero-server/src/schema.js.map +1 -1
  616. package/out/zero-server/src/zql-database.d.ts +21 -1
  617. package/out/zero-server/src/zql-database.d.ts.map +1 -1
  618. package/out/zero-server/src/zql-database.js +33 -2
  619. package/out/zero-server/src/zql-database.js.map +1 -1
  620. package/out/zero-solid/src/solid-view.d.ts +17 -0
  621. package/out/zero-solid/src/solid-view.d.ts.map +1 -1
  622. package/out/zero-solid/src/solid-view.js +39 -1
  623. package/out/zero-solid/src/solid-view.js.map +1 -1
  624. package/out/zero-solid/src/use-query.js +1 -2
  625. package/out/zero-solid/src/use-query.js.map +1 -1
  626. package/out/zero-solid/src/zero.js +1 -0
  627. package/out/zql/src/builder/builder.d.ts +25 -0
  628. package/out/zql/src/builder/builder.d.ts.map +1 -1
  629. package/out/zql/src/builder/builder.js +69 -19
  630. package/out/zql/src/builder/builder.js.map +1 -1
  631. package/out/zql/src/builder/correlated-predicate-pushdown.d.ts +43 -0
  632. package/out/zql/src/builder/correlated-predicate-pushdown.d.ts.map +1 -0
  633. package/out/zql/src/builder/correlated-predicate-pushdown.js +161 -0
  634. package/out/zql/src/builder/correlated-predicate-pushdown.js.map +1 -0
  635. package/out/zql/src/builder/debug-delegate.d.ts +6 -1
  636. package/out/zql/src/builder/debug-delegate.d.ts.map +1 -1
  637. package/out/zql/src/builder/debug-delegate.js +14 -2
  638. package/out/zql/src/builder/debug-delegate.js.map +1 -1
  639. package/out/zql/src/ivm/array-view.d.ts +29 -0
  640. package/out/zql/src/ivm/array-view.d.ts.map +1 -1
  641. package/out/zql/src/ivm/array-view.js +58 -9
  642. package/out/zql/src/ivm/array-view.js.map +1 -1
  643. package/out/zql/src/ivm/cap.d.ts +2 -1
  644. package/out/zql/src/ivm/cap.d.ts.map +1 -1
  645. package/out/zql/src/ivm/cap.js +14 -4
  646. package/out/zql/src/ivm/cap.js.map +1 -1
  647. package/out/zql/src/ivm/connection-index.d.ts +68 -0
  648. package/out/zql/src/ivm/connection-index.d.ts.map +1 -0
  649. package/out/zql/src/ivm/connection-index.js +149 -0
  650. package/out/zql/src/ivm/connection-index.js.map +1 -0
  651. package/out/zql/src/ivm/data.d.ts.map +1 -1
  652. package/out/zql/src/ivm/data.js +8 -3
  653. package/out/zql/src/ivm/data.js.map +1 -1
  654. package/out/zql/src/ivm/deferred-input.d.ts.map +1 -1
  655. package/out/zql/src/ivm/deferred-input.js +3 -2
  656. package/out/zql/src/ivm/deferred-input.js.map +1 -1
  657. package/out/zql/src/ivm/exists.d.ts +3 -1
  658. package/out/zql/src/ivm/exists.d.ts.map +1 -1
  659. package/out/zql/src/ivm/exists.js +4 -1
  660. package/out/zql/src/ivm/exists.js.map +1 -1
  661. package/out/zql/src/ivm/fan-in.d.ts +6 -2
  662. package/out/zql/src/ivm/fan-in.d.ts.map +1 -1
  663. package/out/zql/src/ivm/fan-in.js +13 -5
  664. package/out/zql/src/ivm/fan-in.js.map +1 -1
  665. package/out/zql/src/ivm/fan-out.d.ts +5 -2
  666. package/out/zql/src/ivm/fan-out.d.ts.map +1 -1
  667. package/out/zql/src/ivm/fan-out.js +4 -0
  668. package/out/zql/src/ivm/fan-out.js.map +1 -1
  669. package/out/zql/src/ivm/filter-operators.d.ts +8 -6
  670. package/out/zql/src/ivm/filter-operators.d.ts.map +1 -1
  671. package/out/zql/src/ivm/filter-operators.js +19 -8
  672. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  673. package/out/zql/src/ivm/filter.d.ts +5 -2
  674. package/out/zql/src/ivm/filter.d.ts.map +1 -1
  675. package/out/zql/src/ivm/filter.js +10 -4
  676. package/out/zql/src/ivm/filter.js.map +1 -1
  677. package/out/zql/src/ivm/flipped-join.d.ts +9 -4
  678. package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
  679. package/out/zql/src/ivm/flipped-join.js +97 -46
  680. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  681. package/out/zql/src/ivm/join-index-tap.d.ts +33 -0
  682. package/out/zql/src/ivm/join-index-tap.d.ts.map +1 -0
  683. package/out/zql/src/ivm/join-index-tap.js +70 -0
  684. package/out/zql/src/ivm/join-index-tap.js.map +1 -0
  685. package/out/zql/src/ivm/join-index.d.ts +35 -0
  686. package/out/zql/src/ivm/join-index.d.ts.map +1 -0
  687. package/out/zql/src/ivm/join-index.js +141 -0
  688. package/out/zql/src/ivm/join-index.js.map +1 -0
  689. package/out/zql/src/ivm/join-utils.d.ts +6 -0
  690. package/out/zql/src/ivm/join-utils.d.ts.map +1 -1
  691. package/out/zql/src/ivm/join-utils.js +22 -1
  692. package/out/zql/src/ivm/join-utils.js.map +1 -1
  693. package/out/zql/src/ivm/join.d.ts +19 -1
  694. package/out/zql/src/ivm/join.d.ts.map +1 -1
  695. package/out/zql/src/ivm/join.js +107 -15
  696. package/out/zql/src/ivm/join.js.map +1 -1
  697. package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts +1 -1
  698. package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts.map +1 -1
  699. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js +6 -4
  700. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
  701. package/out/zql/src/ivm/memory-source.d.ts +28 -3
  702. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  703. package/out/zql/src/ivm/memory-source.js +69 -33
  704. package/out/zql/src/ivm/memory-source.js.map +1 -1
  705. package/out/zql/src/ivm/memory-storage.d.ts +1 -0
  706. package/out/zql/src/ivm/memory-storage.d.ts.map +1 -1
  707. package/out/zql/src/ivm/memory-storage.js +1 -0
  708. package/out/zql/src/ivm/memory-storage.js.map +1 -1
  709. package/out/zql/src/ivm/operator.d.ts +45 -10
  710. package/out/zql/src/ivm/operator.d.ts.map +1 -1
  711. package/out/zql/src/ivm/operator.js +9 -4
  712. package/out/zql/src/ivm/operator.js.map +1 -1
  713. package/out/zql/src/ivm/push-accumulated.d.ts +6 -1
  714. package/out/zql/src/ivm/push-accumulated.d.ts.map +1 -1
  715. package/out/zql/src/ivm/push-accumulated.js +47 -2
  716. package/out/zql/src/ivm/push-accumulated.js.map +1 -1
  717. package/out/zql/src/ivm/skip.d.ts +2 -1
  718. package/out/zql/src/ivm/skip.d.ts.map +1 -1
  719. package/out/zql/src/ivm/skip.js +3 -0
  720. package/out/zql/src/ivm/skip.js.map +1 -1
  721. package/out/zql/src/ivm/take-gate.d.ts +57 -0
  722. package/out/zql/src/ivm/take-gate.d.ts.map +1 -0
  723. package/out/zql/src/ivm/take-gate.js +106 -0
  724. package/out/zql/src/ivm/take-gate.js.map +1 -0
  725. package/out/zql/src/ivm/take.d.ts +8 -2
  726. package/out/zql/src/ivm/take.d.ts.map +1 -1
  727. package/out/zql/src/ivm/take.js +139 -103
  728. package/out/zql/src/ivm/take.js.map +1 -1
  729. package/out/zql/src/ivm/union-fan-in.d.ts +3 -0
  730. package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
  731. package/out/zql/src/ivm/union-fan-in.js +63 -10
  732. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  733. package/out/zql/src/ivm/union-fan-out.d.ts +2 -1
  734. package/out/zql/src/ivm/union-fan-out.d.ts.map +1 -1
  735. package/out/zql/src/ivm/union-fan-out.js +5 -0
  736. package/out/zql/src/ivm/union-fan-out.js.map +1 -1
  737. package/out/zql/src/ivm/view-apply-change.d.ts.map +1 -1
  738. package/out/zql/src/ivm/view-apply-change.js +9 -4
  739. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  740. package/out/zql/src/ivm/view.d.ts +15 -0
  741. package/out/zql/src/ivm/view.d.ts.map +1 -1
  742. package/out/zql/src/mutate/custom.d.ts +24 -1
  743. package/out/zql/src/mutate/custom.d.ts.map +1 -1
  744. package/out/zql/src/mutate/custom.js.map +1 -1
  745. package/out/zql/src/mutate/mutator-registry.d.ts.map +1 -1
  746. package/out/zql/src/mutate/mutator-registry.js +2 -2
  747. package/out/zql/src/mutate/mutator-registry.js.map +1 -1
  748. package/out/zql/src/planner/planner-builder.d.ts +23 -3
  749. package/out/zql/src/planner/planner-builder.d.ts.map +1 -1
  750. package/out/zql/src/planner/planner-builder.js +34 -17
  751. package/out/zql/src/planner/planner-builder.js.map +1 -1
  752. package/out/zql/src/planner/planner-connection.d.ts +58 -2
  753. package/out/zql/src/planner/planner-connection.d.ts.map +1 -1
  754. package/out/zql/src/planner/planner-connection.js +103 -11
  755. package/out/zql/src/planner/planner-connection.js.map +1 -1
  756. package/out/zql/src/planner/planner-debug.d.ts.map +1 -1
  757. package/out/zql/src/planner/planner-debug.js +3 -8
  758. package/out/zql/src/planner/planner-debug.js.map +1 -1
  759. package/out/zql/src/planner/planner-graph.d.ts +10 -0
  760. package/out/zql/src/planner/planner-graph.d.ts.map +1 -1
  761. package/out/zql/src/planner/planner-graph.js +14 -0
  762. package/out/zql/src/planner/planner-graph.js.map +1 -1
  763. package/out/zql/src/planner/planner-source.d.ts +2 -2
  764. package/out/zql/src/planner/planner-source.d.ts.map +1 -1
  765. package/out/zql/src/planner/planner-source.js +2 -2
  766. package/out/zql/src/planner/planner-source.js.map +1 -1
  767. package/out/zql/src/planner/planner-warnings.d.ts +83 -0
  768. package/out/zql/src/planner/planner-warnings.d.ts.map +1 -0
  769. package/out/zql/src/planner/planner-warnings.js +78 -0
  770. package/out/zql/src/planner/planner-warnings.js.map +1 -0
  771. package/out/zql/src/query/measure-push-operator.d.ts +2 -1
  772. package/out/zql/src/query/measure-push-operator.d.ts.map +1 -1
  773. package/out/zql/src/query/measure-push-operator.js +33 -4
  774. package/out/zql/src/query/measure-push-operator.js.map +1 -1
  775. package/out/zql/src/query/query-delegate-base.d.ts +4 -2
  776. package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
  777. package/out/zql/src/query/query-delegate-base.js +66 -33
  778. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  779. package/out/zql/src/query/query-delegate.d.ts +33 -6
  780. package/out/zql/src/query/query-delegate.d.ts.map +1 -1
  781. package/out/zql/src/query/query-impl.d.ts +1 -0
  782. package/out/zql/src/query/query-impl.d.ts.map +1 -1
  783. package/out/zql/src/query/query-impl.js +6 -23
  784. package/out/zql/src/query/query-impl.js.map +1 -1
  785. package/out/zql/src/query/query-transitions.d.ts.map +1 -1
  786. package/out/zql/src/query/query-transitions.js +2 -5
  787. package/out/zql/src/query/query-transitions.js.map +1 -1
  788. package/out/zql/src/query/query.d.ts +11 -2
  789. package/out/zql/src/query/query.d.ts.map +1 -1
  790. package/out/zql/src/query/runnable-query-impl.d.ts +1 -0
  791. package/out/zql/src/query/runnable-query-impl.d.ts.map +1 -1
  792. package/out/zql/src/query/runnable-query-impl.js +2 -10
  793. package/out/zql/src/query/runnable-query-impl.js.map +1 -1
  794. package/out/zql/src/query/typed-view.d.ts +1 -1
  795. package/out/zql/src/query/typed-view.d.ts.map +1 -1
  796. package/out/zqlite/src/database-storage.d.ts.map +1 -1
  797. package/out/zqlite/src/database-storage.js +9 -1
  798. package/out/zqlite/src/database-storage.js.map +1 -1
  799. package/out/zqlite/src/db.d.ts +9 -1
  800. package/out/zqlite/src/db.d.ts.map +1 -1
  801. package/out/zqlite/src/db.js +31 -27
  802. package/out/zqlite/src/db.js.map +1 -1
  803. package/out/zqlite/src/pending-delta.d.ts +110 -0
  804. package/out/zqlite/src/pending-delta.d.ts.map +1 -0
  805. package/out/zqlite/src/pending-delta.js +278 -0
  806. package/out/zqlite/src/pending-delta.js.map +1 -0
  807. package/out/zqlite/src/query-builder.d.ts +17 -1
  808. package/out/zqlite/src/query-builder.d.ts.map +1 -1
  809. package/out/zqlite/src/query-builder.js +111 -20
  810. package/out/zqlite/src/query-builder.js.map +1 -1
  811. package/out/zqlite/src/query-delegate.d.ts.map +1 -1
  812. package/out/zqlite/src/query-delegate.js +10 -7
  813. package/out/zqlite/src/query-delegate.js.map +1 -1
  814. package/out/zqlite/src/sqlite-cost-model.d.ts.map +1 -1
  815. package/out/zqlite/src/sqlite-cost-model.js +52 -1
  816. package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
  817. package/out/zqlite/src/sqlite-stat-fanout.d.ts.map +1 -1
  818. package/out/zqlite/src/sqlite-stat-fanout.js +5 -31
  819. package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
  820. package/out/zqlite/src/sqlite-stat4-sample.d.ts +37 -0
  821. package/out/zqlite/src/sqlite-stat4-sample.d.ts.map +1 -0
  822. package/out/zqlite/src/sqlite-stat4-sample.js +61 -0
  823. package/out/zqlite/src/sqlite-stat4-sample.js.map +1 -0
  824. package/out/zqlite/src/table-source.d.ts +82 -2
  825. package/out/zqlite/src/table-source.d.ts.map +1 -1
  826. package/out/zqlite/src/table-source.js +261 -38
  827. package/out/zqlite/src/table-source.js.map +1 -1
  828. package/package.json +3 -4
  829. package/out/replicache/src/kv/idb-store-with-mem-fallback.d.ts +0 -25
  830. package/out/replicache/src/kv/idb-store-with-mem-fallback.d.ts.map +0 -1
  831. package/out/replicache/src/kv/idb-store-with-mem-fallback.js +0 -83
  832. package/out/replicache/src/kv/idb-store-with-mem-fallback.js.map +0 -1
@@ -5,6 +5,7 @@ import { appSchema, check, upstreamSchema } from "../../../../types/shards.js";
5
5
  import { id } from "../../../../types/sql.js";
6
6
  import { getPublicationInfo, publishedSchema } from "./published.js";
7
7
  import { createEventFunctionStatements, createEventTriggerStatements } from "./ddl.js";
8
+ import { replicaStageSchema } from "./replica-stage-enum.js";
8
9
  import { validate } from "./validation.js";
9
10
  import postgres from "postgres";
10
11
  import { PG_INSUFFICIENT_PRIVILEGE } from "@drdgvhbh/postgres-error-codes";
@@ -121,10 +122,6 @@ function shardSetup(shardConfig, metadataPublication) {
121
122
  ${getClientsTableDefinition(shard)}
122
123
  ${getMutationsTableDefinition(shard)}
123
124
 
124
- DROP PUBLICATION IF EXISTS ${id(metadataPublication)};
125
- CREATE PUBLICATION ${id(metadataPublication)}
126
- FOR TABLE ${app}."permissions", TABLE ${shard}."clients", ${shard}."mutations";
127
-
128
125
  CREATE TABLE ${shard}."${SHARD_CONFIG_TABLE}" (
129
126
  "publications" TEXT[] NOT NULL,
130
127
  "ddlDetection" BOOL NOT NULL,
@@ -147,14 +144,23 @@ function shardSetup(shardConfig, metadataPublication) {
147
144
  "id" TEXT PRIMARY KEY DEFAULT replace(gen_random_uuid()::text, '-', ''),
148
145
  "rank" BIGSERIAL,
149
146
  "slot" TEXT NOT NULL,
150
- "version" TEXT NOT NULL,
151
- "generation" TEXT, -- to replace version, NULL-able in the interim
147
+ "version" TEXT, -- replaced by generation, slated for deletion
148
+ "epoch" INT4 DEFAULT 0,
149
+ "generation" TEXT NOT NULL,
150
+ "stage" INT4 DEFAULT 0,
152
151
  "backupPath" TEXT, -- subpath within the litestream backup URL
153
152
  "backupV5" BOOL DEFAULT false,
154
153
  "initialSchema" JSON, -- set after initial sync
155
154
  "initialSyncContext" JSON,
156
155
  "subscriberContext" JSON
157
156
  );
157
+
158
+ DROP PUBLICATION IF EXISTS ${id(metadataPublication)};
159
+ CREATE PUBLICATION ${id(metadataPublication)}
160
+ FOR TABLE ${app}."permissions",
161
+ TABLE ${shard}."clients",
162
+ ${shard}."mutations",
163
+ ${shard}."replicas" (id); -- advance the LSN/version with new replicas
158
164
  `;
159
165
  }
160
166
  function dropShard(appID, shardID) {
@@ -174,8 +180,9 @@ var replicaInfoSchema = valita_exports.object({
174
180
  id: valita_exports.string(),
175
181
  rank: valita_exports.bigint(),
176
182
  slot: valita_exports.string(),
177
- version: valita_exports.string(),
178
- generation: valita_exports.string().nullable(),
183
+ epoch: valita_exports.number(),
184
+ generation: valita_exports.string(),
185
+ stage: replicaStageSchema,
179
186
  backupPath: valita_exports.string().nullable(),
180
187
  backupV5: valita_exports.boolean()
181
188
  });
@@ -184,24 +191,12 @@ var fullReplicaRowSchema = replicaInfoSchema.extend({
184
191
  initialSyncContext: jsonObjectSchema.nullable(),
185
192
  subscriberContext: jsonObjectSchema.nullable()
186
193
  });
187
- var replicaSchema = internalShardConfigSchema.extend(fullReplicaRowSchema.shape).map((replica) => {
188
- const { version, generation, ...r } = replica;
189
- return {
190
- ...r,
191
- generation: generation ?? version
192
- };
193
- });
194
+ var replicaSchema = internalShardConfigSchema.extend(fullReplicaRowSchema.shape);
194
195
  var replicationSlotStateSchema = valita_exports.object({
195
196
  active: valita_exports.boolean(),
196
- confirmedFlushLsn: valita_exports.string().nullable()
197
- });
198
- var replicaStateSchema = replicaInfoSchema.extend(replicationSlotStateSchema.shape).map((replica) => {
199
- const { version, generation, ...r } = replica;
200
- return {
201
- ...r,
202
- generation: generation ?? version
203
- };
197
+ confirmedFlushLsn: valita_exports.string()
204
198
  });
199
+ var replicaStateSchema = replicaInfoSchema.extend(replicationSlotStateSchema.shape);
205
200
  function triggerSetup(shard) {
206
201
  const schema = id(upstreamSchema(shard));
207
202
  return createEventTriggerStatements(shard) + `UPDATE ${schema}."shardConfig" SET "ddlDetection" = true;`;
@@ -212,22 +207,23 @@ function triggerSetup(shard) {
212
207
  * to ensure that the slot does not get dropped by concurrent cleanup
213
208
  * logic.
214
209
  *
215
- * Once initial sync is complete, {@link initReplica} should be called to
210
+ * Once initial sync is complete, {@link initInitialSyncReplica} should be called to
216
211
  * make the replica usable for incremental sync.
217
212
  */
218
- async function createReplica(sql, shard, id, slot, replicaVersion, { backupPath, backupV5 }) {
213
+ async function createReplica(sql, shard, id, slot, epoch, replicaVersion, { backupPath, backupV5 }, stage) {
219
214
  const schema = upstreamSchema(shard);
220
215
  const values = {
221
216
  id,
222
217
  slot,
223
- version: replicaVersion,
218
+ epoch,
224
219
  generation: replicaVersion,
225
220
  backupPath,
226
- backupV5
221
+ backupV5,
222
+ stage
227
223
  };
228
224
  await sql`INSERT INTO ${sql(schema)}.replicas ${sql(values)}`;
229
225
  }
230
- async function initReplica(sql, shard, id, { tables, indexes }, initialSyncContext) {
226
+ async function initInitialSyncReplica(sql, shard, id, { tables, indexes }, initialSyncContext) {
231
227
  const schema = upstreamSchema(shard);
232
228
  const values = {
233
229
  initialSchema: {
@@ -238,6 +234,18 @@ async function initReplica(sql, shard, id, { tables, indexes }, initialSyncConte
238
234
  };
239
235
  await sql`UPDATE ${sql(schema)}.replicas SET ${sql(values)} WHERE id = ${id}`;
240
236
  }
237
+ async function initRestoreReplica(sql, shard, { sourceID, destID }) {
238
+ const schema = upstreamSchema(shard);
239
+ await sql`
240
+ UPDATE ${sql(schema)}.replicas dest
241
+ SET "generation" = source."generation",
242
+ "initialSchema" = source."initialSchema",
243
+ "initialSyncContext" = source."initialSyncContext"
244
+ FROM ${sql(schema)}.replicas source
245
+ WHERE source.id = ${sourceID} AND dest.id = ${destID};
246
+ `;
247
+ return getReplicaState(sql, shard, destID);
248
+ }
241
249
  /**
242
250
  * Gets at the given version and optional ID. If no ID is specified, gets
243
251
  * the latest (i.e. highest rank) replica with that version.
@@ -249,8 +257,9 @@ async function getReplicaAtVersion(lc, sql, shard, replicaVersion, id = null, co
249
257
  replicas."id",
250
258
  replicas."rank",
251
259
  replicas."slot",
252
- replicas."version",
260
+ replicas."epoch",
253
261
  replicas."generation",
262
+ replicas."stage",
254
263
  replicas."backupPath",
255
264
  replicas."backupV5",
256
265
  replicas."initialSchema",
@@ -259,13 +268,13 @@ async function getReplicaAtVersion(lc, sql, shard, replicaVersion, id = null, co
259
268
  "shardConfig"."publications",
260
269
  "shardConfig"."ddlDetection"
261
270
  FROM ${schema}.replicas JOIN ${schema}."shardConfig" ON true
262
- WHERE version = ${replicaVersion} AND "initialSyncContext" IS NOT NULL
271
+ WHERE generation = ${replicaVersion} AND "initialSyncContext" IS NOT NULL
263
272
  AND id = COALESCE(${id}, id)
264
273
  ORDER BY rank DESC LIMIT 1;
265
274
  `;
266
275
  if (result.length === 0) {
267
276
  const allReplicas = await sql`
268
- SELECT id, slot, version, "initialSyncContext", "subscriberContext"
277
+ SELECT id, slot, generation, "initialSyncContext", "subscriberContext"
269
278
  FROM ${schema}.replicas`;
270
279
  lc.info?.(`Replica ${id}@${replicaVersion}` + (context ? `(context: ${stringify(context)}) ` : "") + `not found in: ${stringify(allReplicas)}`);
271
280
  return null;
@@ -278,27 +287,84 @@ async function getActiveReplicas(lc, sql, shard) {
278
287
  replicas."id",
279
288
  replicas."rank",
280
289
  replicas."slot",
281
- replicas."version",
290
+ replicas."epoch",
282
291
  replicas."generation",
292
+ replicas."stage",
283
293
  replicas."backupPath",
284
294
  replicas."backupV5",
285
295
  slots."active",
286
296
  slots."confirmed_flush_lsn" as "confirmedFlushLsn"
287
297
  FROM ${sql(upstreamSchema(shard))}.replicas JOIN pg_replication_slots slots ON slot = slot_name
288
298
  WHERE "initialSyncContext" IS NOT NULL
299
+ -- Exclude replicas whose slot has been invalidated (e.g. for exceeding
300
+ -- max_slot_wal_keep_size). Such a replica can never be resumed, and
301
+ -- restoring it just results in an AutoResetSignal on every attempt.
302
+ AND slots.restart_lsn IS NOT NULL
303
+ AND slots.wal_status IS DISTINCT FROM 'lost'
289
304
  ORDER BY generation DESC, confirmed_flush_lsn DESC;
290
305
  `, valita_exports.array(replicaStateSchema), "passthrough");
291
306
  lc.info?.(`current replicas`, { replicas });
292
307
  return replicas;
293
308
  }
309
+ /**
310
+ * Returns candidates for restoring a new replica from. These include
311
+ * all replicas in the newest `generation` of the given `epoch`, in order
312
+ * of earliest available backup:
313
+ * - An active InitialSync replica, which will be the only replica in
314
+ * its generation by definition. Orphaned/inactive initial syncs are skipped.
315
+ * - Replicas in the Replicate stage, active first.
316
+ * - Replicas in the Restore stage, potentially available for resumption
317
+ * as a last resort.
318
+ */
319
+ async function getRestoreCandidates(lc, sql, shard, epoch) {
320
+ const schema = sql(upstreamSchema(shard));
321
+ const replicas = parse(await sql`
322
+ SELECT
323
+ replicas."id",
324
+ replicas."rank",
325
+ replicas."slot",
326
+ replicas."epoch",
327
+ replicas."generation",
328
+ replicas."stage",
329
+ replicas."backupPath",
330
+ replicas."backupV5",
331
+ slots."active",
332
+ slots."confirmed_flush_lsn" as "confirmedFlushLsn"
333
+ FROM ${schema}.replicas JOIN pg_replication_slots slots ON slot = slot_name
334
+
335
+ -- coalesce on the newest generation in the epoch
336
+ WHERE generation = (
337
+ SELECT MAX(r.generation)
338
+ FROM ${schema}.replicas r
339
+ JOIN pg_replication_slots s ON r.slot = s.slot_name
340
+ WHERE r."epoch" = ${epoch} AND r."backupV5"
341
+ -- ignore orphaned InitialSyncs, which cannot be restored from
342
+ AND (s."active" OR r."stage" != ${0})
343
+ AND s.restart_lsn IS NOT NULL
344
+ AND s.wal_status IS DISTINCT FROM 'lost'
345
+ )
346
+ AND "epoch" = ${epoch} AND "backupV5"
347
+ AND (slots.active OR "stage" != ${0})
348
+ AND slots.restart_lsn IS NOT NULL
349
+ AND slots.wal_status IS DISTINCT FROM 'lost'
350
+
351
+ ORDER BY
352
+ stage ASC, -- ordered by backup availalibility
353
+ active DESC, -- prefer active slots (fork over resume)
354
+ confirmed_flush_lsn DESC; -- prefer replicas further ahead
355
+ `, valita_exports.array(replicaStateSchema), "passthrough");
356
+ lc.info?.(`current replicas at epoch ${epoch}`, { replicas });
357
+ return replicas;
358
+ }
294
359
  async function getReplicaState(sql, shard, id) {
295
360
  const results = await sql`
296
361
  SELECT
297
362
  replicas."id",
298
363
  replicas."rank",
299
364
  replicas."slot",
300
- replicas."version",
365
+ replicas."epoch",
301
366
  replicas."generation",
367
+ replicas."stage",
302
368
  replicas."backupPath",
303
369
  replicas."backupV5",
304
370
  slots."active",
@@ -395,6 +461,6 @@ function replicaIdentitiesForTablesWithoutPrimaryKeys(pubs) {
395
461
  } };
396
462
  }
397
463
  //#endregion
398
- export { SHARD_CONFIG_TABLE, createReplica, dropShard, ensureGlobalTables, getActiveReplicas, getInternalShardConfig, getMutationsTableDefinition, getReplicaAtVersion, getReplicaState, initReplica, internalPublicationPrefix, legacyReplicationSlot, metadataPublicationName, replicaIdentitiesForTablesWithoutPrimaryKeys, replicationSlotExpression, replicationSlotPrefix, setupTablesAndReplication, setupTriggers, validatePublications };
464
+ export { SHARD_CONFIG_TABLE, createReplica, dropShard, ensureGlobalTables, getActiveReplicas, getInternalShardConfig, getMutationsTableDefinition, getReplicaAtVersion, getReplicaState, getRestoreCandidates, initInitialSyncReplica, initRestoreReplica, internalPublicationPrefix, legacyReplicationSlot, metadataPublicationName, replicaIdentitiesForTablesWithoutPrimaryKeys, replicationSlotExpression, replicationSlotPrefix, setupTablesAndReplication, setupTriggers, validatePublications };
399
465
 
400
466
  //# sourceMappingURL=shard.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"shard.js","names":[],"sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/shard.ts"],"sourcesContent":["import {PG_INSUFFICIENT_PRIVILEGE} from '@drdgvhbh/postgres-error-codes';\nimport type {LogContext} from '@rocicorp/logger';\nimport {literal} from 'pg-format';\nimport postgres from 'postgres';\nimport {assert} from '../../../../../../shared/src/asserts.ts';\nimport {\n jsonObjectSchema,\n stringify,\n type JSONObject,\n} from '../../../../../../shared/src/bigint-json.ts';\nimport * as v from '../../../../../../shared/src/valita.ts';\nimport {Default} from '../../../../db/postgres-replica-identity-enum.ts';\nimport type {PostgresDB, PostgresTransaction} from '../../../../types/pg.ts';\nimport type {AppID, ShardConfig, ShardID} from '../../../../types/shards.ts';\nimport {appSchema, check, upstreamSchema} from '../../../../types/shards.ts';\nimport {id} from '../../../../types/sql.ts';\nimport {\n createEventFunctionStatements,\n createEventTriggerStatements,\n} from './ddl.ts';\nimport {\n getPublicationInfo,\n publishedSchema,\n type PublicationInfo,\n type PublishedSchema,\n} from './published.ts';\nimport {validate} from './validation.ts';\n\n/**\n * PostgreSQL unquoted identifiers must start with a letter or underscore\n * and contain only letters, digits, and underscores.\n */\nconst VALID_PUBLICATION_NAME = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\n\n/**\n * Validates that a publication name is a valid PostgreSQL identifier.\n * This provides defense-in-depth against SQL injection when publication\n * names are used in replication commands.\n */\nexport function validatePublicationName(name: string): void {\n if (!VALID_PUBLICATION_NAME.test(name)) {\n throw new Error(\n `Invalid publication name \"${name}\". Publication names must start with a letter or underscore ` +\n `and contain only letters, digits, and underscores.`,\n );\n }\n if (name.length > 63) {\n throw new Error(\n `Publication name \"${name}\" exceeds PostgreSQL's 63-character identifier limit.`,\n );\n }\n}\n\nexport function internalPublicationPrefix({appID}: AppID) {\n return `_${appID}_`;\n}\n\nexport function legacyReplicationSlot({appID, shardNum}: ShardID) {\n return `${appID}_${shardNum}`;\n}\n\nexport function replicationSlotPrefix(shard: ShardID) {\n const {appID, shardNum} = check(shard);\n return `${appID}_${shardNum}_`;\n}\n\n/**\n * An expression used to match replication slots in the shard\n * in a Postgres `LIKE` operator.\n */\nexport function replicationSlotExpression(shard: ShardID) {\n // Underscores have a special meaning in LIKE values\n // so they have to be escaped.\n return `${replicationSlotPrefix(shard)}%`.replaceAll('_', '\\\\_');\n}\n\nfunction defaultPublicationName(appID: string, shardID: string | number) {\n return `_${appID}_public_${shardID}`;\n}\n\nexport function metadataPublicationName(\n appID: string,\n shardID: string | number,\n) {\n return `_${appID}_metadata_${shardID}`;\n}\n\n// The GLOBAL_SETUP must be idempotent as it can be run multiple times for different shards.\nfunction globalSetup(appID: AppID): string {\n const app = id(appSchema(appID));\n\n return /*sql*/ `\n CREATE SCHEMA IF NOT EXISTS ${app};\n\n CREATE TABLE IF NOT EXISTS ${app}.permissions (\n \"permissions\" JSONB,\n \"hash\" TEXT,\n\n -- Ensure that there is only a single row in the table.\n -- Application code can be agnostic to this column, and\n -- simply invoke UPDATE statements on the version columns.\n \"lock\" BOOL PRIMARY KEY DEFAULT true CHECK (lock)\n );\n\n CREATE OR REPLACE FUNCTION ${app}.set_permissions_hash()\n RETURNS TRIGGER AS $$\n BEGIN\n NEW.hash = md5(NEW.permissions::text);\n RETURN NEW;\n END;\n $$ LANGUAGE plpgsql;\n\n CREATE OR REPLACE TRIGGER on_set_permissions \n BEFORE INSERT OR UPDATE ON ${app}.permissions\n FOR EACH ROW\n EXECUTE FUNCTION ${app}.set_permissions_hash();\n\n INSERT INTO ${app}.permissions (permissions) VALUES (NULL) ON CONFLICT DO NOTHING;\n`;\n}\n\nexport async function ensureGlobalTables(db: PostgresDB, appID: AppID) {\n await db.unsafe(globalSetup(appID));\n}\n\nexport function getClientsTableDefinition(schema: string) {\n return /*sql*/ `\n CREATE TABLE ${schema}.\"clients\" (\n \"clientGroupID\" TEXT NOT NULL,\n \"clientID\" TEXT NOT NULL,\n \"lastMutationID\" BIGINT NOT NULL,\n \"userID\" TEXT,\n PRIMARY KEY(\"clientGroupID\", \"clientID\")\n );`;\n}\n\n/**\n * Tracks the results of mutations.\n * 1. It is an error for the same mutation ID to be used twice.\n * 2. The result is JSONB to allow for arbitrary results.\n *\n * The tables must be cleaned up as the clients\n * receive the mutation responses and as clients are removed.\n */\nexport function getMutationsTableDefinition(schema: string) {\n return /*sql*/ `\n CREATE TABLE ${schema}.\"mutations\" (\n \"clientGroupID\" TEXT NOT NULL,\n \"clientID\" TEXT NOT NULL,\n \"mutationID\" BIGINT NOT NULL,\n \"result\" JSON NOT NULL,\n PRIMARY KEY(\"clientGroupID\", \"clientID\", \"mutationID\")\n );`;\n}\n\nexport const SHARD_CONFIG_TABLE = 'shardConfig';\n\nexport function shardSetup(\n shardConfig: ShardConfig,\n metadataPublication: string,\n): string {\n const app = id(appSchema(shardConfig));\n const shard = id(upstreamSchema(shardConfig));\n\n const pubs = shardConfig.publications.toSorted();\n assert(\n pubs.includes(metadataPublication),\n () => `Publications must include ${metadataPublication}`,\n );\n\n return /*sql*/ `\n CREATE SCHEMA IF NOT EXISTS ${shard};\n\n ${getClientsTableDefinition(shard)}\n ${getMutationsTableDefinition(shard)}\n\n DROP PUBLICATION IF EXISTS ${id(metadataPublication)};\n CREATE PUBLICATION ${id(metadataPublication)}\n FOR TABLE ${app}.\"permissions\", TABLE ${shard}.\"clients\", ${shard}.\"mutations\";\n\n CREATE TABLE ${shard}.\"${SHARD_CONFIG_TABLE}\" (\n \"publications\" TEXT[] NOT NULL,\n \"ddlDetection\" BOOL NOT NULL,\n\n -- Ensure that there is only a single row in the table.\n \"lock\" BOOL PRIMARY KEY DEFAULT true CHECK (lock)\n );\n\n INSERT INTO ${shard}.\"${SHARD_CONFIG_TABLE}\" (\n \"publications\",\n \"ddlDetection\" \n ) VALUES (\n ARRAY[${literal(pubs)}], \n false -- set in SAVEPOINT with triggerSetup() statements\n );\n\n CREATE TABLE ${shard}.replicas (\n -- The DEFAULT exists purely for backwards compatibility support.\n -- New code always specifies a value based on Date.now().\n \"id\" TEXT PRIMARY KEY DEFAULT replace(gen_random_uuid()::text, '-', ''),\n \"rank\" BIGSERIAL,\n \"slot\" TEXT NOT NULL,\n \"version\" TEXT NOT NULL,\n \"generation\" TEXT, -- to replace version, NULL-able in the interim\n \"backupPath\" TEXT, -- subpath within the litestream backup URL\n \"backupV5\" BOOL DEFAULT false,\n \"initialSchema\" JSON, -- set after initial sync\n \"initialSyncContext\" JSON,\n \"subscriberContext\" JSON\n );\n `;\n}\n\nexport function dropShard(appID: string, shardID: string | number): string {\n const schema = `${appID}_${shardID}`;\n const metadataPublication = metadataPublicationName(appID, shardID);\n const defaultPublication = defaultPublicationName(appID, shardID);\n\n // DROP SCHEMA ... CASCADE does not drop dependent PUBLICATIONS,\n // so PUBLICATIONs must be dropped explicitly.\n return /*sql*/ `\n DROP PUBLICATION IF EXISTS ${id(defaultPublication)};\n DROP PUBLICATION IF EXISTS ${id(metadataPublication)};\n DROP SCHEMA IF EXISTS ${id(schema)} CASCADE;\n `;\n}\n\nconst internalShardConfigSchema = v.object({\n publications: v.array(v.string()),\n ddlDetection: v.boolean(),\n});\n\nexport type InternalShardConfig = v.Infer<typeof internalShardConfigSchema>;\n\nconst replicaInfoSchema = v.object({\n id: v.string(),\n rank: v.bigint(),\n slot: v.string(),\n version: v.string(),\n generation: v.string().nullable(),\n backupPath: v.string().nullable(),\n backupV5: v.boolean(),\n});\n\nconst fullReplicaRowSchema = replicaInfoSchema.extend({\n initialSchema: publishedSchema,\n initialSyncContext: jsonObjectSchema.nullable(),\n subscriberContext: jsonObjectSchema.nullable(),\n});\n\nconst replicaSchema = internalShardConfigSchema\n .extend(fullReplicaRowSchema.shape)\n .map(replica => {\n const {version, generation, ...r} = replica;\n return {...r, generation: generation ?? version};\n });\n\nexport type Replica = v.Infer<typeof replicaSchema>;\n\nconst replicationSlotStateSchema = v.object({\n active: v.boolean(),\n confirmedFlushLsn: v.string().nullable(),\n});\n\nconst replicaStateSchema = replicaInfoSchema\n .extend(replicationSlotStateSchema.shape)\n .map(replica => {\n const {version, generation, ...r} = replica;\n return {...r, generation: generation ?? version};\n });\n\nexport type ReplicaState = v.Infer<typeof replicaStateSchema>;\n\n// triggerSetup is run separately in a sub-transaction (i.e. SAVEPOINT) so\n// that a failure (e.g. due to lack of superuser permissions) can be handled\n// by continuing in a degraded mode (ddlDetection = false).\nfunction triggerSetup(shard: ShardConfig): string {\n const schema = id(upstreamSchema(shard));\n return (\n createEventTriggerStatements(shard) +\n /*sql*/ `UPDATE ${schema}.\"shardConfig\" SET \"ddlDetection\" = true;`\n );\n}\n\nexport type BackupOptions = {\n backupPath: string | null;\n backupV5: boolean;\n};\n\n/**\n * Creates a new replica to mark it as the owner of a specified `slot`.\n * This should be done with an advisory lock for replica/slot management\n * to ensure that the slot does not get dropped by concurrent cleanup\n * logic.\n *\n * Once initial sync is complete, {@link initReplica} should be called to\n * make the replica usable for incremental sync.\n */\nexport async function createReplica(\n sql: PostgresDB,\n shard: ShardID,\n id: string,\n slot: string,\n replicaVersion: string,\n {backupPath, backupV5}: BackupOptions,\n) {\n const schema = upstreamSchema(shard);\n const values: Partial<v.Infer<typeof fullReplicaRowSchema>> = {\n id,\n slot,\n version: replicaVersion,\n generation: replicaVersion,\n backupPath,\n backupV5,\n };\n await sql`INSERT INTO ${sql(schema)}.replicas ${sql(values)}`;\n}\n\n// Called in initial-sync to store the exact schema that was initially synced.\nexport async function initReplica(\n sql: PostgresDB,\n shard: ShardID,\n id: string,\n {tables, indexes}: PublishedSchema,\n initialSyncContext: JSONObject,\n) {\n const schema = upstreamSchema(shard);\n const synced: PublishedSchema = {tables, indexes};\n const values = {initialSchema: synced, initialSyncContext};\n await sql`UPDATE ${sql(schema)}.replicas SET ${sql(values)} WHERE id = ${id}`;\n}\n\n/**\n * Gets at the given version and optional ID. If no ID is specified, gets\n * the latest (i.e. highest rank) replica with that version.\n */\nexport async function getReplicaAtVersion(\n lc: LogContext,\n sql: PostgresDB,\n shard: ShardID,\n replicaVersion: string,\n id: string | null = null,\n context?: JSONObject,\n): Promise<Replica | null> {\n const schema = sql(upstreamSchema(shard));\n const result = await sql`\n SELECT\n replicas.\"id\",\n replicas.\"rank\",\n replicas.\"slot\",\n replicas.\"version\",\n replicas.\"generation\",\n replicas.\"backupPath\",\n replicas.\"backupV5\",\n replicas.\"initialSchema\",\n replicas.\"initialSyncContext\",\n replicas.\"subscriberContext\",\n \"shardConfig\".\"publications\",\n \"shardConfig\".\"ddlDetection\"\n FROM ${schema}.replicas JOIN ${schema}.\"shardConfig\" ON true\n WHERE version = ${replicaVersion} AND \"initialSyncContext\" IS NOT NULL\n AND id = COALESCE(${id}, id)\n ORDER BY rank DESC LIMIT 1;\n `;\n if (result.length === 0) {\n // log out all the replicas and the joined shardConfig\n const allReplicas = await sql`\n SELECT id, slot, version, \"initialSyncContext\", \"subscriberContext\" \n FROM ${schema}.replicas`;\n lc.info?.(\n `Replica ${id}@${replicaVersion}` +\n (context ? `(context: ${stringify(context)}) ` : '') +\n `not found in: ${stringify(allReplicas)}`,\n );\n return null;\n }\n return v.parse(result[0], replicaSchema, 'passthrough');\n}\n\nexport async function getActiveReplicas(\n lc: LogContext,\n sql: PostgresDB,\n shard: ShardID,\n): Promise<ReplicaState[]> {\n const schema = sql(upstreamSchema(shard));\n const results = await sql`\n SELECT\n replicas.\"id\",\n replicas.\"rank\",\n replicas.\"slot\",\n replicas.\"version\",\n replicas.\"generation\",\n replicas.\"backupPath\",\n replicas.\"backupV5\",\n slots.\"active\",\n slots.\"confirmed_flush_lsn\" as \"confirmedFlushLsn\"\n FROM ${schema}.replicas JOIN pg_replication_slots slots ON slot = slot_name\n WHERE \"initialSyncContext\" IS NOT NULL\n ORDER BY generation DESC, confirmed_flush_lsn DESC;\n `;\n const replicas = v.parse(results, v.array(replicaStateSchema), 'passthrough');\n lc.info?.(`current replicas`, {replicas});\n return replicas;\n}\n\nexport async function getReplicaState(\n sql: PostgresDB,\n shard: ShardID,\n id: string,\n): Promise<ReplicaState | null> {\n const schema = sql(upstreamSchema(shard));\n const results = await sql`\n SELECT\n replicas.\"id\",\n replicas.\"rank\",\n replicas.\"slot\",\n replicas.\"version\",\n replicas.\"generation\",\n replicas.\"backupPath\",\n replicas.\"backupV5\",\n slots.\"active\",\n slots.\"confirmed_flush_lsn\" as \"confirmedFlushLsn\"\n FROM ${schema}.replicas JOIN pg_replication_slots slots ON slot = slot_name\n WHERE \"id\" = ${id};\n `;\n return results.length\n ? v.parse(results[0], replicaStateSchema, 'passthrough')\n : null;\n}\n\nexport async function getInternalShardConfig(\n sql: PostgresDB,\n shard: ShardID,\n): Promise<InternalShardConfig> {\n const result = await sql`\n SELECT \"publications\", \"ddlDetection\"\n FROM ${sql(upstreamSchema(shard))}.\"shardConfig\";\n `;\n assert(\n result.length === 1,\n () => `Expected exactly one shardConfig row, got ${result.length}`,\n );\n return v.parse(result[0], internalShardConfigSchema, 'passthrough');\n}\n\n/**\n * Sets up and returns all publications (including internal ones) for\n * the given shard.\n */\nexport async function setupTablesAndReplication(\n lc: LogContext,\n sql: PostgresTransaction,\n requested: ShardConfig,\n includePartialIndexes = true,\n) {\n const {publications} = requested;\n // Validate requested publications.\n for (const pub of publications) {\n validatePublicationName(pub);\n if (pub.startsWith('_')) {\n throw new Error(\n `Publication names starting with \"_\" are reserved for internal use.\\n` +\n `Please use a different name for publication \"${pub}\".`,\n );\n }\n }\n const allPublications: string[] = [];\n\n // Setup application publications.\n if (publications.length) {\n const results = await sql<{pubname: string}[]>`\n SELECT pubname from pg_publication WHERE pubname IN ${sql(\n publications,\n )}`.values();\n\n if (results.length !== publications.length) {\n throw new Error(\n `Unknown or invalid publications. Specified: [${publications}]. Found: [${results.flat()}]`,\n );\n }\n allPublications.push(...publications);\n } else {\n const defaultPublication = defaultPublicationName(\n requested.appID,\n requested.shardNum,\n );\n await sql`\n DROP PUBLICATION IF EXISTS ${sql(defaultPublication)}`;\n await sql`\n CREATE PUBLICATION ${sql(defaultPublication)} \n FOR TABLES IN SCHEMA public\n WITH (publish_via_partition_root = true)`;\n allPublications.push(defaultPublication);\n }\n\n const metadataPublication = metadataPublicationName(\n requested.appID,\n requested.shardNum,\n );\n allPublications.push(metadataPublication);\n\n const shard = {...requested, publications: allPublications};\n\n // Setup the global tables and shard tables / publications.\n await sql.unsafe(globalSetup(shard) + shardSetup(shard, metadataPublication));\n\n const pubs = await getPublicationInfo(sql, allPublications);\n await replicaIdentitiesForTablesWithoutPrimaryKeys(pubs)?.apply(lc, sql);\n\n await setupTriggers(lc, sql, shard, includePartialIndexes);\n}\n\nexport async function setupTriggers(\n lc: LogContext,\n tx: PostgresTransaction,\n shard: ShardConfig,\n includePartialIndexes = true,\n) {\n const schema = upstreamSchema(shard);\n const [{ddlDetection}] = await tx<InternalShardConfig[]> /*sql*/ `\n SELECT \"ddlDetection\" FROM ${tx(schema)}.\"shardConfig\"`;\n\n // The functions invoked by event triggers are installed even if\n // event triggers are not supported/allowed by the db provider.\n // This allows users to manually invoke the update_schemas() function\n // as a workaround.\n await tx.unsafe(createEventFunctionStatements(shard, includePartialIndexes));\n try {\n await tx.savepoint(sub => sub.unsafe(triggerSetup(shard)));\n } catch (e) {\n if (ddlDetection) {\n // If ddlDetection has already been enabled, subsequent failures to\n // upgrade the trigger should be propagated rather than swallowed.\n throw e;\n }\n if (\n !(\n e instanceof postgres.PostgresError &&\n e.code === PG_INSUFFICIENT_PRIVILEGE\n )\n ) {\n throw e;\n }\n // If triggerSetup() fails, replication continues in ddlDetection=false mode.\n lc.warn?.(\n `Unable to create event triggers for schema change detection:\\n\\n` +\n `\"${e.hint ?? e.message}\"\\n\\n` +\n `Proceeding in degraded mode: schema changes will halt replication,\\n` +\n `requiring the replica to be reset (manually or with --auto-reset).`,\n );\n }\n}\n\nexport function validatePublications(\n lc: LogContext,\n published: PublicationInfo,\n) {\n // Verify that all publications export the proper events.\n published.publications.forEach(pub => {\n if (\n !pub.pubinsert ||\n !pub.pubupdate ||\n !pub.pubdelete ||\n !pub.pubtruncate\n ) {\n // TODO: Make APIError?\n throw new Error(\n `PUBLICATION ${pub.pubname} must publish insert, update, delete, and truncate`,\n );\n }\n });\n\n published.tables.forEach(table => validate(lc, table));\n}\n\ntype ReplicaIdentities = {\n apply(lc: LogContext, db: PostgresDB): Promise<void>;\n};\n\nexport function replicaIdentitiesForTablesWithoutPrimaryKeys(\n pubs: PublishedSchema,\n): ReplicaIdentities | undefined {\n const replicaIdentities: {\n schema: string;\n tableName: string;\n indexName: string;\n }[] = [];\n for (const table of pubs.tables) {\n if (!table.primaryKey?.length && table.replicaIdentity === Default) {\n // Look for an index that can serve as the REPLICA IDENTITY USING INDEX. It must be:\n // - UNIQUE\n // - NOT NULL columns\n // - not deferrable (i.e. isImmediate)\n // - not partial\n //\n // https://www.postgresql.org/docs/current/sql-altertable.html#SQL-ALTERTABLE-REPLICA-IDENTITY\n const {schema, name: tableName} = table;\n for (const {columns, name: indexName} of pubs.indexes.filter(\n idx =>\n idx.schema === schema &&\n idx.tableName === tableName &&\n idx.unique &&\n idx.isImmediate &&\n idx.predicate === undefined,\n )) {\n if (Object.keys(columns).some(col => !table.columns[col].notNull)) {\n continue; // Only indexes with all NOT NULL columns are suitable.\n }\n replicaIdentities.push({schema, tableName, indexName});\n break;\n }\n }\n }\n\n if (replicaIdentities.length === 0) {\n return undefined;\n }\n return {\n apply: async (lc: LogContext, sql: PostgresDB) => {\n for (const {schema, tableName, indexName} of replicaIdentities) {\n lc.info?.(\n `setting \"${indexName}\" as the REPLICA IDENTITY for \"${tableName}\"`,\n );\n await sql`\n ALTER TABLE ${sql(schema)}.${sql(tableName)} \n REPLICA IDENTITY USING INDEX ${sql(indexName)}`;\n }\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"shard.js","names":[],"sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/shard.ts"],"sourcesContent":["import {PG_INSUFFICIENT_PRIVILEGE} from '@drdgvhbh/postgres-error-codes';\nimport type {LogContext} from '@rocicorp/logger';\nimport {literal} from 'pg-format';\nimport postgres from 'postgres';\nimport {assert} from '../../../../../../shared/src/asserts.ts';\nimport {\n jsonObjectSchema,\n stringify,\n type JSONObject,\n} from '../../../../../../shared/src/bigint-json.ts';\nimport * as v from '../../../../../../shared/src/valita.ts';\nimport {Default} from '../../../../db/postgres-replica-identity-enum.ts';\nimport type {PostgresDB, PostgresTransaction} from '../../../../types/pg.ts';\nimport type {AppID, ShardConfig, ShardID} from '../../../../types/shards.ts';\nimport {appSchema, check, upstreamSchema} from '../../../../types/shards.ts';\nimport {id} from '../../../../types/sql.ts';\nimport {\n createEventFunctionStatements,\n createEventTriggerStatements,\n} from './ddl.ts';\nimport {\n getPublicationInfo,\n publishedSchema,\n type PublicationInfo,\n type PublishedSchema,\n} from './published.ts';\nimport {\n InitialSync,\n replicaStageSchema,\n type ReplicaStage,\n} from './replica-stage-enum.ts';\nimport {validate} from './validation.ts';\n\n/**\n * PostgreSQL unquoted identifiers must start with a letter or underscore\n * and contain only letters, digits, and underscores.\n */\nconst VALID_PUBLICATION_NAME = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\n\n/**\n * Validates that a publication name is a valid PostgreSQL identifier.\n * This provides defense-in-depth against SQL injection when publication\n * names are used in replication commands.\n */\nexport function validatePublicationName(name: string): void {\n if (!VALID_PUBLICATION_NAME.test(name)) {\n throw new Error(\n `Invalid publication name \"${name}\". Publication names must start with a letter or underscore ` +\n `and contain only letters, digits, and underscores.`,\n );\n }\n if (name.length > 63) {\n throw new Error(\n `Publication name \"${name}\" exceeds PostgreSQL's 63-character identifier limit.`,\n );\n }\n}\n\nexport function internalPublicationPrefix({appID}: AppID) {\n return `_${appID}_`;\n}\n\nexport function legacyReplicationSlot({appID, shardNum}: ShardID) {\n return `${appID}_${shardNum}`;\n}\n\nexport function replicationSlotPrefix(shard: ShardID) {\n const {appID, shardNum} = check(shard);\n return `${appID}_${shardNum}_`;\n}\n\n/**\n * An expression used to match replication slots in the shard\n * in a Postgres `LIKE` operator.\n */\nexport function replicationSlotExpression(shard: ShardID) {\n // Underscores have a special meaning in LIKE values\n // so they have to be escaped.\n return `${replicationSlotPrefix(shard)}%`.replaceAll('_', '\\\\_');\n}\n\nfunction defaultPublicationName(appID: string, shardID: string | number) {\n return `_${appID}_public_${shardID}`;\n}\n\nexport function metadataPublicationName(\n appID: string,\n shardID: string | number,\n) {\n return `_${appID}_metadata_${shardID}`;\n}\n\n// The GLOBAL_SETUP must be idempotent as it can be run multiple times for different shards.\nfunction globalSetup(appID: AppID): string {\n const app = id(appSchema(appID));\n\n return /*sql*/ `\n CREATE SCHEMA IF NOT EXISTS ${app};\n\n CREATE TABLE IF NOT EXISTS ${app}.permissions (\n \"permissions\" JSONB,\n \"hash\" TEXT,\n\n -- Ensure that there is only a single row in the table.\n -- Application code can be agnostic to this column, and\n -- simply invoke UPDATE statements on the version columns.\n \"lock\" BOOL PRIMARY KEY DEFAULT true CHECK (lock)\n );\n\n CREATE OR REPLACE FUNCTION ${app}.set_permissions_hash()\n RETURNS TRIGGER AS $$\n BEGIN\n NEW.hash = md5(NEW.permissions::text);\n RETURN NEW;\n END;\n $$ LANGUAGE plpgsql;\n\n CREATE OR REPLACE TRIGGER on_set_permissions \n BEFORE INSERT OR UPDATE ON ${app}.permissions\n FOR EACH ROW\n EXECUTE FUNCTION ${app}.set_permissions_hash();\n\n INSERT INTO ${app}.permissions (permissions) VALUES (NULL) ON CONFLICT DO NOTHING;\n`;\n}\n\nexport async function ensureGlobalTables(db: PostgresDB, appID: AppID) {\n await db.unsafe(globalSetup(appID));\n}\n\nexport function getClientsTableDefinition(schema: string) {\n return /*sql*/ `\n CREATE TABLE ${schema}.\"clients\" (\n \"clientGroupID\" TEXT NOT NULL,\n \"clientID\" TEXT NOT NULL,\n \"lastMutationID\" BIGINT NOT NULL,\n \"userID\" TEXT,\n PRIMARY KEY(\"clientGroupID\", \"clientID\")\n );`;\n}\n\n/**\n * Tracks the results of mutations.\n * 1. It is an error for the same mutation ID to be used twice.\n * 2. The result is JSONB to allow for arbitrary results.\n *\n * The tables must be cleaned up as the clients\n * receive the mutation responses and as clients are removed.\n */\nexport function getMutationsTableDefinition(schema: string) {\n return /*sql*/ `\n CREATE TABLE ${schema}.\"mutations\" (\n \"clientGroupID\" TEXT NOT NULL,\n \"clientID\" TEXT NOT NULL,\n \"mutationID\" BIGINT NOT NULL,\n \"result\" JSON NOT NULL,\n PRIMARY KEY(\"clientGroupID\", \"clientID\", \"mutationID\")\n );`;\n}\n\nexport const SHARD_CONFIG_TABLE = 'shardConfig';\n\nexport function shardSetup(\n shardConfig: ShardConfig,\n metadataPublication: string,\n): string {\n const app = id(appSchema(shardConfig));\n const shard = id(upstreamSchema(shardConfig));\n\n const pubs = shardConfig.publications.toSorted();\n assert(\n pubs.includes(metadataPublication),\n () => `Publications must include ${metadataPublication}`,\n );\n\n return /*sql*/ `\n CREATE SCHEMA IF NOT EXISTS ${shard};\n\n ${getClientsTableDefinition(shard)}\n ${getMutationsTableDefinition(shard)}\n\n CREATE TABLE ${shard}.\"${SHARD_CONFIG_TABLE}\" (\n \"publications\" TEXT[] NOT NULL,\n \"ddlDetection\" BOOL NOT NULL,\n\n -- Ensure that there is only a single row in the table.\n \"lock\" BOOL PRIMARY KEY DEFAULT true CHECK (lock)\n );\n\n INSERT INTO ${shard}.\"${SHARD_CONFIG_TABLE}\" (\n \"publications\",\n \"ddlDetection\" \n ) VALUES (\n ARRAY[${literal(pubs)}], \n false -- set in SAVEPOINT with triggerSetup() statements\n );\n\n CREATE TABLE ${shard}.replicas (\n -- The DEFAULT exists purely for backwards compatibility support.\n -- New code always specifies a value based on Date.now().\n \"id\" TEXT PRIMARY KEY DEFAULT replace(gen_random_uuid()::text, '-', ''),\n \"rank\" BIGSERIAL,\n \"slot\" TEXT NOT NULL,\n \"version\" TEXT, -- replaced by generation, slated for deletion\n \"epoch\" INT4 DEFAULT 0,\n \"generation\" TEXT NOT NULL,\n \"stage\" INT4 DEFAULT ${InitialSync},\n \"backupPath\" TEXT, -- subpath within the litestream backup URL\n \"backupV5\" BOOL DEFAULT false,\n \"initialSchema\" JSON, -- set after initial sync\n \"initialSyncContext\" JSON,\n \"subscriberContext\" JSON\n );\n\n DROP PUBLICATION IF EXISTS ${id(metadataPublication)};\n CREATE PUBLICATION ${id(metadataPublication)}\n FOR TABLE ${app}.\"permissions\", \n TABLE ${shard}.\"clients\", \n ${shard}.\"mutations\", \n ${shard}.\"replicas\" (id); -- advance the LSN/version with new replicas\n `;\n}\n\nexport function dropShard(appID: string, shardID: string | number): string {\n const schema = `${appID}_${shardID}`;\n const metadataPublication = metadataPublicationName(appID, shardID);\n const defaultPublication = defaultPublicationName(appID, shardID);\n\n // DROP SCHEMA ... CASCADE does not drop dependent PUBLICATIONS,\n // so PUBLICATIONs must be dropped explicitly.\n return /*sql*/ `\n DROP PUBLICATION IF EXISTS ${id(defaultPublication)};\n DROP PUBLICATION IF EXISTS ${id(metadataPublication)};\n DROP SCHEMA IF EXISTS ${id(schema)} CASCADE;\n `;\n}\n\nconst internalShardConfigSchema = v.object({\n publications: v.array(v.string()),\n ddlDetection: v.boolean(),\n});\n\nexport type InternalShardConfig = v.Infer<typeof internalShardConfigSchema>;\n\nconst replicaInfoSchema = v.object({\n id: v.string(),\n rank: v.bigint(),\n slot: v.string(),\n epoch: v.number(),\n generation: v.string(),\n stage: replicaStageSchema,\n backupPath: v.string().nullable(),\n backupV5: v.boolean(),\n});\n\nconst fullReplicaRowSchema = replicaInfoSchema.extend({\n initialSchema: publishedSchema,\n initialSyncContext: jsonObjectSchema.nullable(),\n subscriberContext: jsonObjectSchema.nullable(),\n});\n\nconst replicaSchema = internalShardConfigSchema.extend(\n fullReplicaRowSchema.shape,\n);\n\nexport type Replica = v.Infer<typeof replicaSchema>;\n\nconst replicationSlotStateSchema = v.object({\n active: v.boolean(),\n confirmedFlushLsn: v.string(),\n});\n\nconst replicaStateSchema = replicaInfoSchema.extend(\n replicationSlotStateSchema.shape,\n);\n\nexport type ReplicaState = v.Infer<typeof replicaStateSchema>;\n\n// triggerSetup is run separately in a sub-transaction (i.e. SAVEPOINT) so\n// that a failure (e.g. due to lack of superuser permissions) can be handled\n// by continuing in a degraded mode (ddlDetection = false).\nfunction triggerSetup(shard: ShardConfig): string {\n const schema = id(upstreamSchema(shard));\n return (\n createEventTriggerStatements(shard) +\n /*sql*/ `UPDATE ${schema}.\"shardConfig\" SET \"ddlDetection\" = true;`\n );\n}\n\nexport type BackupOptions = {\n backupPath: string | null;\n backupV5: boolean;\n};\n\n/**\n * Creates a new replica to mark it as the owner of a specified `slot`.\n * This should be done with an advisory lock for replica/slot management\n * to ensure that the slot does not get dropped by concurrent cleanup\n * logic.\n *\n * Once initial sync is complete, {@link initInitialSyncReplica} should be called to\n * make the replica usable for incremental sync.\n */\nexport async function createReplica(\n sql: PostgresDB,\n shard: ShardID,\n id: string,\n slot: string,\n epoch: number,\n replicaVersion: string,\n {backupPath, backupV5}: BackupOptions,\n stage: ReplicaStage,\n) {\n const schema = upstreamSchema(shard);\n const values: Partial<v.Infer<typeof fullReplicaRowSchema>> = {\n id,\n slot,\n epoch,\n generation: replicaVersion,\n backupPath,\n backupV5,\n stage,\n };\n await sql`INSERT INTO ${sql(schema)}.replicas ${sql(values)}`;\n}\n\n// Called in initial-sync to store the exact schema that was initially synced.\nexport async function initInitialSyncReplica(\n sql: PostgresDB,\n shard: ShardID,\n id: string,\n {tables, indexes}: PublishedSchema,\n initialSyncContext: JSONObject,\n) {\n const schema = upstreamSchema(shard);\n const synced: PublishedSchema = {tables, indexes};\n const values = {initialSchema: synced, initialSyncContext};\n await sql`UPDATE ${sql(schema)}.replicas SET ${sql(values)} WHERE id = ${id}`;\n}\n\n// Called to initialize a Restore replica (`destID`) with the generation\n// and initial sync context of the `sourceID` replica.\nexport async function initRestoreReplica(\n sql: PostgresDB,\n shard: ShardID,\n {sourceID, destID}: {sourceID: string; destID: string},\n): Promise<ReplicaState | null> {\n const schema = upstreamSchema(shard);\n await sql`\n UPDATE ${sql(schema)}.replicas dest \n SET \"generation\" = source.\"generation\",\n \"initialSchema\" = source.\"initialSchema\",\n \"initialSyncContext\" = source.\"initialSyncContext\"\n FROM ${sql(schema)}.replicas source\n WHERE source.id = ${sourceID} AND dest.id = ${destID};\n `;\n return getReplicaState(sql, shard, destID);\n}\n\n/**\n * Gets at the given version and optional ID. If no ID is specified, gets\n * the latest (i.e. highest rank) replica with that version.\n */\nexport async function getReplicaAtVersion(\n lc: LogContext,\n sql: PostgresDB,\n shard: ShardID,\n replicaVersion: string,\n id: string | null = null,\n context?: JSONObject,\n): Promise<Replica | null> {\n const schema = sql(upstreamSchema(shard));\n const result = await sql`\n SELECT\n replicas.\"id\",\n replicas.\"rank\",\n replicas.\"slot\",\n replicas.\"epoch\",\n replicas.\"generation\",\n replicas.\"stage\",\n replicas.\"backupPath\",\n replicas.\"backupV5\",\n replicas.\"initialSchema\",\n replicas.\"initialSyncContext\",\n replicas.\"subscriberContext\",\n \"shardConfig\".\"publications\",\n \"shardConfig\".\"ddlDetection\"\n FROM ${schema}.replicas JOIN ${schema}.\"shardConfig\" ON true\n WHERE generation = ${replicaVersion} AND \"initialSyncContext\" IS NOT NULL\n AND id = COALESCE(${id}, id)\n ORDER BY rank DESC LIMIT 1;\n `;\n if (result.length === 0) {\n // log out all the replicas and the joined shardConfig\n const allReplicas = await sql`\n SELECT id, slot, generation, \"initialSyncContext\", \"subscriberContext\" \n FROM ${schema}.replicas`;\n lc.info?.(\n `Replica ${id}@${replicaVersion}` +\n (context ? `(context: ${stringify(context)}) ` : '') +\n `not found in: ${stringify(allReplicas)}`,\n );\n return null;\n }\n return v.parse(result[0], replicaSchema, 'passthrough');\n}\n\nexport async function getActiveReplicas(\n lc: LogContext,\n sql: PostgresDB,\n shard: ShardID,\n): Promise<ReplicaState[]> {\n const schema = sql(upstreamSchema(shard));\n const results = await sql`\n SELECT\n replicas.\"id\",\n replicas.\"rank\",\n replicas.\"slot\",\n replicas.\"epoch\",\n replicas.\"generation\",\n replicas.\"stage\",\n replicas.\"backupPath\",\n replicas.\"backupV5\",\n slots.\"active\",\n slots.\"confirmed_flush_lsn\" as \"confirmedFlushLsn\"\n FROM ${schema}.replicas JOIN pg_replication_slots slots ON slot = slot_name\n WHERE \"initialSyncContext\" IS NOT NULL\n -- Exclude replicas whose slot has been invalidated (e.g. for exceeding\n -- max_slot_wal_keep_size). Such a replica can never be resumed, and\n -- restoring it just results in an AutoResetSignal on every attempt.\n AND slots.restart_lsn IS NOT NULL\n AND slots.wal_status IS DISTINCT FROM 'lost'\n ORDER BY generation DESC, confirmed_flush_lsn DESC;\n `;\n const replicas = v.parse(results, v.array(replicaStateSchema), 'passthrough');\n lc.info?.(`current replicas`, {replicas});\n return replicas;\n}\n\n/**\n * Returns candidates for restoring a new replica from. These include\n * all replicas in the newest `generation` of the given `epoch`, in order\n * of earliest available backup:\n * - An active InitialSync replica, which will be the only replica in\n * its generation by definition. Orphaned/inactive initial syncs are skipped.\n * - Replicas in the Replicate stage, active first.\n * - Replicas in the Restore stage, potentially available for resumption\n * as a last resort.\n */\nexport async function getRestoreCandidates(\n lc: LogContext,\n sql: PostgresDB,\n shard: ShardID,\n epoch: number,\n): Promise<ReplicaState[]> {\n const schema = sql(upstreamSchema(shard));\n const results = await sql`\n SELECT\n replicas.\"id\",\n replicas.\"rank\",\n replicas.\"slot\",\n replicas.\"epoch\",\n replicas.\"generation\",\n replicas.\"stage\",\n replicas.\"backupPath\",\n replicas.\"backupV5\",\n slots.\"active\",\n slots.\"confirmed_flush_lsn\" as \"confirmedFlushLsn\"\n FROM ${schema}.replicas JOIN pg_replication_slots slots ON slot = slot_name\n\n -- coalesce on the newest generation in the epoch\n WHERE generation = (\n SELECT MAX(r.generation)\n FROM ${schema}.replicas r\n JOIN pg_replication_slots s ON r.slot = s.slot_name\n WHERE r.\"epoch\" = ${epoch} AND r.\"backupV5\"\n -- ignore orphaned InitialSyncs, which cannot be restored from\n AND (s.\"active\" OR r.\"stage\" != ${InitialSync})\n AND s.restart_lsn IS NOT NULL\n AND s.wal_status IS DISTINCT FROM 'lost'\n )\n AND \"epoch\" = ${epoch} AND \"backupV5\"\n AND (slots.active OR \"stage\" != ${InitialSync})\n AND slots.restart_lsn IS NOT NULL\n AND slots.wal_status IS DISTINCT FROM 'lost'\n\n ORDER BY \n stage ASC, -- ordered by backup availalibility\n active DESC, -- prefer active slots (fork over resume)\n confirmed_flush_lsn DESC; -- prefer replicas further ahead\n `;\n const replicas = v.parse(results, v.array(replicaStateSchema), 'passthrough');\n lc.info?.(`current replicas at epoch ${epoch}`, {replicas});\n return replicas;\n}\n\nexport async function getReplicaState(\n sql: PostgresDB,\n shard: ShardID,\n id: string,\n): Promise<ReplicaState | null> {\n const schema = sql(upstreamSchema(shard));\n const results = await sql`\n SELECT\n replicas.\"id\",\n replicas.\"rank\",\n replicas.\"slot\",\n replicas.\"epoch\",\n replicas.\"generation\",\n replicas.\"stage\",\n replicas.\"backupPath\",\n replicas.\"backupV5\",\n slots.\"active\",\n slots.\"confirmed_flush_lsn\" as \"confirmedFlushLsn\"\n FROM ${schema}.replicas JOIN pg_replication_slots slots ON slot = slot_name\n WHERE \"id\" = ${id};\n `;\n return results.length\n ? v.parse(results[0], replicaStateSchema, 'passthrough')\n : null;\n}\n\nexport async function getInternalShardConfig(\n sql: PostgresDB,\n shard: ShardID,\n): Promise<InternalShardConfig> {\n const result = await sql`\n SELECT \"publications\", \"ddlDetection\"\n FROM ${sql(upstreamSchema(shard))}.\"shardConfig\";\n `;\n assert(\n result.length === 1,\n () => `Expected exactly one shardConfig row, got ${result.length}`,\n );\n return v.parse(result[0], internalShardConfigSchema, 'passthrough');\n}\n\n/**\n * Sets up and returns all publications (including internal ones) for\n * the given shard.\n */\nexport async function setupTablesAndReplication(\n lc: LogContext,\n sql: PostgresTransaction,\n requested: ShardConfig,\n includePartialIndexes = true,\n) {\n const {publications} = requested;\n // Validate requested publications.\n for (const pub of publications) {\n validatePublicationName(pub);\n if (pub.startsWith('_')) {\n throw new Error(\n `Publication names starting with \"_\" are reserved for internal use.\\n` +\n `Please use a different name for publication \"${pub}\".`,\n );\n }\n }\n const allPublications: string[] = [];\n\n // Setup application publications.\n if (publications.length) {\n const results = await sql<{pubname: string}[]>`\n SELECT pubname from pg_publication WHERE pubname IN ${sql(\n publications,\n )}`.values();\n\n if (results.length !== publications.length) {\n throw new Error(\n `Unknown or invalid publications. Specified: [${publications}]. Found: [${results.flat()}]`,\n );\n }\n allPublications.push(...publications);\n } else {\n const defaultPublication = defaultPublicationName(\n requested.appID,\n requested.shardNum,\n );\n await sql`\n DROP PUBLICATION IF EXISTS ${sql(defaultPublication)}`;\n await sql`\n CREATE PUBLICATION ${sql(defaultPublication)} \n FOR TABLES IN SCHEMA public\n WITH (publish_via_partition_root = true)`;\n allPublications.push(defaultPublication);\n }\n\n const metadataPublication = metadataPublicationName(\n requested.appID,\n requested.shardNum,\n );\n allPublications.push(metadataPublication);\n\n const shard = {...requested, publications: allPublications};\n\n // Setup the global tables and shard tables / publications.\n await sql.unsafe(globalSetup(shard) + shardSetup(shard, metadataPublication));\n\n const pubs = await getPublicationInfo(sql, allPublications);\n await replicaIdentitiesForTablesWithoutPrimaryKeys(pubs)?.apply(lc, sql);\n\n await setupTriggers(lc, sql, shard, includePartialIndexes);\n}\n\nexport async function setupTriggers(\n lc: LogContext,\n tx: PostgresTransaction,\n shard: ShardConfig,\n includePartialIndexes = true,\n) {\n const schema = upstreamSchema(shard);\n const [{ddlDetection}] = await tx<InternalShardConfig[]> /*sql*/ `\n SELECT \"ddlDetection\" FROM ${tx(schema)}.\"shardConfig\"`;\n\n // The functions invoked by event triggers are installed even if\n // event triggers are not supported/allowed by the db provider.\n // This allows users to manually invoke the update_schemas() function\n // as a workaround.\n await tx.unsafe(createEventFunctionStatements(shard, includePartialIndexes));\n try {\n await tx.savepoint(sub => sub.unsafe(triggerSetup(shard)));\n } catch (e) {\n if (ddlDetection) {\n // If ddlDetection has already been enabled, subsequent failures to\n // upgrade the trigger should be propagated rather than swallowed.\n throw e;\n }\n if (\n !(\n e instanceof postgres.PostgresError &&\n e.code === PG_INSUFFICIENT_PRIVILEGE\n )\n ) {\n throw e;\n }\n // If triggerSetup() fails, replication continues in ddlDetection=false mode.\n lc.warn?.(\n `Unable to create event triggers for schema change detection:\\n\\n` +\n `\"${e.hint ?? e.message}\"\\n\\n` +\n `Proceeding in degraded mode: schema changes will halt replication,\\n` +\n `requiring the replica to be reset (manually or with --auto-reset).`,\n );\n }\n}\n\nexport function validatePublications(\n lc: LogContext,\n published: PublicationInfo,\n) {\n // Verify that all publications export the proper events.\n published.publications.forEach(pub => {\n if (\n !pub.pubinsert ||\n !pub.pubupdate ||\n !pub.pubdelete ||\n !pub.pubtruncate\n ) {\n // TODO: Make APIError?\n throw new Error(\n `PUBLICATION ${pub.pubname} must publish insert, update, delete, and truncate`,\n );\n }\n });\n\n published.tables.forEach(table => validate(lc, table));\n}\n\ntype ReplicaIdentities = {\n apply(lc: LogContext, db: PostgresDB): Promise<void>;\n};\n\nexport function replicaIdentitiesForTablesWithoutPrimaryKeys(\n pubs: PublishedSchema,\n): ReplicaIdentities | undefined {\n const replicaIdentities: {\n schema: string;\n tableName: string;\n indexName: string;\n }[] = [];\n for (const table of pubs.tables) {\n if (!table.primaryKey?.length && table.replicaIdentity === Default) {\n // Look for an index that can serve as the REPLICA IDENTITY USING INDEX. It must be:\n // - UNIQUE\n // - NOT NULL columns\n // - not deferrable (i.e. isImmediate)\n // - not partial\n //\n // https://www.postgresql.org/docs/current/sql-altertable.html#SQL-ALTERTABLE-REPLICA-IDENTITY\n const {schema, name: tableName} = table;\n for (const {columns, name: indexName} of pubs.indexes.filter(\n idx =>\n idx.schema === schema &&\n idx.tableName === tableName &&\n idx.unique &&\n idx.isImmediate &&\n idx.predicate === undefined,\n )) {\n if (Object.keys(columns).some(col => !table.columns[col].notNull)) {\n continue; // Only indexes with all NOT NULL columns are suitable.\n }\n replicaIdentities.push({schema, tableName, indexName});\n break;\n }\n }\n }\n\n if (replicaIdentities.length === 0) {\n return undefined;\n }\n return {\n apply: async (lc: LogContext, sql: PostgresDB) => {\n for (const {schema, tableName, indexName} of replicaIdentities) {\n lc.info?.(\n `setting \"${indexName}\" as the REPLICA IDENTITY for \"${tableName}\"`,\n );\n await sql`\n ALTER TABLE ${sql(schema)}.${sql(tableName)} \n REPLICA IDENTITY USING INDEX ${sql(indexName)}`;\n }\n },\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ import type { BackfillProgressMark, BackfillRequest } from './current.ts';
2
+ /**
3
+ * The result of applying a backfill message to a column's tracked progress.
4
+ * If `accept` is true, `progress` is the column's new progress.
5
+ */
6
+ export type AcceptResult = {
7
+ accept: true;
8
+ progress: BackfillProgressMark | undefined;
9
+ } | {
10
+ accept: false;
11
+ };
12
+ /**
13
+ * Applies the continuity policy of `backfill` (and `backfill-completed`)
14
+ * messages to a column's `own` tracked progress, where `undefined` means that
15
+ * the column has not received any backfill data (i.e. it needs a backfill
16
+ * from scratch).
17
+ *
18
+ * * Messages without `progressMarks` are from pre-v8 replication-managers
19
+ * and are accepted unconditionally (i.e. the legacy behavior), with no
20
+ * change to the tracked progress.
21
+ * * A message without a `previous` mark starts a backfill from scratch (or on
22
+ * a new timeline), and is always accepted.
23
+ * * Otherwise, the message is only accepted if it is on the same timeline as
24
+ * `own`, and continues from a position at or before `own` (i.e. it does not
25
+ * create a gap).
26
+ *
27
+ * `current` is unset for `backfill-completed` messages, which do not advance
28
+ * the progress.
29
+ */
30
+ export declare function acceptBackfill(own: BackfillProgressMark | undefined, marks: {
31
+ previous?: BackfillProgressMark | undefined;
32
+ current?: BackfillProgressMark;
33
+ } | undefined): AcceptResult;
34
+ /**
35
+ * Returns whether a stream whose progress (for a column) is at `stream`
36
+ * will deliver all of the data needed by a subscriber whose progress (for the
37
+ * same column) is at `subscriber`. A `stream` progress of `undefined` indicates
38
+ * a backfill that will start from scratch, which covers all subscribers.
39
+ */
40
+ export declare function covers(stream: BackfillProgressMark | undefined, subscriber: BackfillProgressMark | undefined): boolean;
41
+ /**
42
+ * Returns the position from which a backfill for the `req` must start
43
+ * in order to satisfy the progress of all of its columns, i.e. the earliest
44
+ * progress mark, or `undefined` (i.e. from scratch) if any column has no
45
+ * progress or if the columns are on different timelines.
46
+ */
47
+ export declare function resumePoint(req: BackfillRequest): BackfillProgressMark | undefined;
48
+ /**
49
+ * Returns a copy of the `req` with the progress of every column set to
50
+ * the request's {@link resumePoint}, i.e. the position from which the
51
+ * backfill of the request will actually start.
52
+ */
53
+ export declare function withResumePoint(req: BackfillRequest): BackfillRequest;
54
+ /**
55
+ * Merges two BackfillRequests for the same table into one that satisfies
56
+ * both. Both requests are expected to reflect the same (current) upstream
57
+ * schema. For columns in both requests, the earlier progress mark is chosen,
58
+ * or no progress mark (i.e. from scratch) if they are on different timelines.
59
+ * If a column's backfill IDs differ (which is unexpected), the ID of `req1`
60
+ * is chosen, and the column is backfilled from scratch.
61
+ */
62
+ export declare function getTableSuperset(req1: BackfillRequest, req2: BackfillRequest): BackfillRequest;
63
+ /**
64
+ * Merges the BackfillRequests of multiple parties (e.g. subscribers) into
65
+ * one request per table, using {@link getTableSuperset}.
66
+ */
67
+ export declare function getSuperset(...requestSets: Iterable<BackfillRequest>[]): BackfillRequest[];
68
+ //# sourceMappingURL=backfill-progress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backfill-progress.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/change-source/protocol/backfill-progress.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,oBAAoB,EACpB,eAAe,EAEhB,MAAM,cAAc,CAAC;AAyBtB;;;GAGG;AACH,MAAM,MAAM,YAAY,GACpB;IAAC,MAAM,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,oBAAoB,GAAG,SAAS,CAAA;CAAC,GAC1D;IAAC,MAAM,EAAE,KAAK,CAAA;CAAC,CAAC;AAEpB;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,oBAAoB,GAAG,SAAS,EACrC,KAAK,EACD;IACE,QAAQ,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;IAC5C,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC,GACD,SAAS,GACZ,YAAY,CAyBd;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CACpB,MAAM,EAAE,oBAAoB,GAAG,SAAS,EACxC,UAAU,EAAE,oBAAoB,GAAG,SAAS,GAC3C,OAAO,CAOT;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,eAAe,GACnB,oBAAoB,GAAG,SAAS,CAelC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,eAAe,GAAG,eAAe,CAWrE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,eAAe,EACrB,IAAI,EAAE,eAAe,GACpB,eAAe,CA4BjB;AAMD;;;GAGG;AACH,wBAAgB,WAAW,CACzB,GAAG,WAAW,EAAE,QAAQ,CAAC,eAAe,CAAC,EAAE,GAC1C,eAAe,EAAE,CAUnB"}
@@ -0,0 +1,141 @@
1
+ import { deepEqual } from "../../../../../shared/src/json.js";
2
+ //#region ../zero-cache/src/services/change-source/protocol/backfill-progress.ts
3
+ /**
4
+ * Whether two progress marks are on the same timeline, and are thus comparable.
5
+ */
6
+ function sameTimeline(a, b) {
7
+ return a.timeline === b.timeline;
8
+ }
9
+ /** Returns the earlier of two marks on the same timeline. */
10
+ function earlier(a, b) {
11
+ return b.progressMark < a.progressMark ? b : a;
12
+ }
13
+ /** Returns the later of two marks on the same timeline. */
14
+ function later(a, b) {
15
+ return b.progressMark > a.progressMark ? b : a;
16
+ }
17
+ /**
18
+ * Applies the continuity policy of `backfill` (and `backfill-completed`)
19
+ * messages to a column's `own` tracked progress, where `undefined` means that
20
+ * the column has not received any backfill data (i.e. it needs a backfill
21
+ * from scratch).
22
+ *
23
+ * * Messages without `progressMarks` are from pre-v8 replication-managers
24
+ * and are accepted unconditionally (i.e. the legacy behavior), with no
25
+ * change to the tracked progress.
26
+ * * A message without a `previous` mark starts a backfill from scratch (or on
27
+ * a new timeline), and is always accepted.
28
+ * * Otherwise, the message is only accepted if it is on the same timeline as
29
+ * `own`, and continues from a position at or before `own` (i.e. it does not
30
+ * create a gap).
31
+ *
32
+ * `current` is unset for `backfill-completed` messages, which do not advance
33
+ * the progress.
34
+ */
35
+ function acceptBackfill(own, marks) {
36
+ if (marks === void 0) return {
37
+ accept: true,
38
+ progress: own
39
+ };
40
+ const { previous, current } = marks;
41
+ if (previous === void 0) return {
42
+ accept: true,
43
+ progress: current
44
+ };
45
+ if (own === void 0 || !sameTimeline(own, previous) || previous.progressMark > own.progressMark) return { accept: false };
46
+ return {
47
+ accept: true,
48
+ progress: current === void 0 ? own : sameTimeline(own, current) ? later(own, current) : current
49
+ };
50
+ }
51
+ /**
52
+ * Returns whether a stream whose progress (for a column) is at `stream`
53
+ * will deliver all of the data needed by a subscriber whose progress (for the
54
+ * same column) is at `subscriber`. A `stream` progress of `undefined` indicates
55
+ * a backfill that will start from scratch, which covers all subscribers.
56
+ */
57
+ function covers(stream, subscriber) {
58
+ return stream === void 0 || subscriber !== void 0 && sameTimeline(stream, subscriber) && stream.progressMark <= subscriber.progressMark;
59
+ }
60
+ /**
61
+ * Returns the position from which a backfill for the `req` must start
62
+ * in order to satisfy the progress of all of its columns, i.e. the earliest
63
+ * progress mark, or `undefined` (i.e. from scratch) if any column has no
64
+ * progress or if the columns are on different timelines.
65
+ */
66
+ function resumePoint(req) {
67
+ let point;
68
+ for (const { progress } of Object.values(req.columns)) {
69
+ if (progress === void 0) return;
70
+ if (point === void 0) point = progress;
71
+ else if (!sameTimeline(point, progress)) return;
72
+ else point = earlier(point, progress);
73
+ }
74
+ return point;
75
+ }
76
+ /**
77
+ * Returns a copy of the `req` with the progress of every column set to
78
+ * the request's {@link resumePoint}, i.e. the position from which the
79
+ * backfill of the request will actually start.
80
+ */
81
+ function withResumePoint(req) {
82
+ const progress = resumePoint(req);
83
+ return {
84
+ table: req.table,
85
+ columns: Object.fromEntries(Object.entries(req.columns).map(([col, { id }]) => [col, progress === void 0 ? { id } : {
86
+ id,
87
+ progress
88
+ }]))
89
+ };
90
+ }
91
+ /**
92
+ * Merges two BackfillRequests for the same table into one that satisfies
93
+ * both. Both requests are expected to reflect the same (current) upstream
94
+ * schema. For columns in both requests, the earlier progress mark is chosen,
95
+ * or no progress mark (i.e. from scratch) if they are on different timelines.
96
+ * If a column's backfill IDs differ (which is unexpected), the ID of `req1`
97
+ * is chosen, and the column is backfilled from scratch.
98
+ */
99
+ function getTableSuperset(req1, req2) {
100
+ const columns = { ...req1.columns };
101
+ for (const [name, col] of Object.entries(req2.columns)) {
102
+ const exists = columns[name];
103
+ if (!exists) {
104
+ columns[name] = col;
105
+ continue;
106
+ }
107
+ const { id, progress } = exists;
108
+ if (progress === void 0 || col.progress === void 0 || !sameTimeline(progress, col.progress) || !deepEqual(id, col.id)) columns[name] = { id };
109
+ else columns[name] = {
110
+ id,
111
+ progress: earlier(progress, col.progress)
112
+ };
113
+ }
114
+ return {
115
+ table: {
116
+ ...req1.table,
117
+ metadata: req1.table.metadata ?? req2.table.metadata
118
+ },
119
+ columns
120
+ };
121
+ }
122
+ function tableKey({ schema, name }) {
123
+ return JSON.stringify([schema, name]);
124
+ }
125
+ /**
126
+ * Merges the BackfillRequests of multiple parties (e.g. subscribers) into
127
+ * one request per table, using {@link getTableSuperset}.
128
+ */
129
+ function getSuperset(...requestSets) {
130
+ const merged = /* @__PURE__ */ new Map();
131
+ for (const requests of requestSets) for (const req of requests) {
132
+ const key = tableKey(req.table);
133
+ const existing = merged.get(key);
134
+ merged.set(key, existing ? getTableSuperset(existing, req) : req);
135
+ }
136
+ return [...merged.values()];
137
+ }
138
+ //#endregion
139
+ export { acceptBackfill, covers, getSuperset, resumePoint, withResumePoint };
140
+
141
+ //# sourceMappingURL=backfill-progress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backfill-progress.js","names":[],"sources":["../../../../../../../zero-cache/src/services/change-source/protocol/backfill-progress.ts"],"sourcesContent":["import {deepEqual} from '../../../../../shared/src/json.ts';\nimport type {\n BackfillProgressMark,\n BackfillRequest,\n Identifier,\n} from './current.ts';\n\n/**\n * Whether two progress marks are on the same timeline, and are thus comparable.\n */\nfunction sameTimeline(a: BackfillProgressMark, b: BackfillProgressMark) {\n return a.timeline === b.timeline;\n}\n\n/** Returns the earlier of two marks on the same timeline. */\nfunction earlier(\n a: BackfillProgressMark,\n b: BackfillProgressMark,\n): BackfillProgressMark {\n return b.progressMark < a.progressMark ? b : a;\n}\n\n/** Returns the later of two marks on the same timeline. */\nfunction later(\n a: BackfillProgressMark,\n b: BackfillProgressMark,\n): BackfillProgressMark {\n return b.progressMark > a.progressMark ? b : a;\n}\n\n/**\n * The result of applying a backfill message to a column's tracked progress.\n * If `accept` is true, `progress` is the column's new progress.\n */\nexport type AcceptResult =\n | {accept: true; progress: BackfillProgressMark | undefined}\n | {accept: false};\n\n/**\n * Applies the continuity policy of `backfill` (and `backfill-completed`)\n * messages to a column's `own` tracked progress, where `undefined` means that\n * the column has not received any backfill data (i.e. it needs a backfill\n * from scratch).\n *\n * * Messages without `progressMarks` are from pre-v8 replication-managers\n * and are accepted unconditionally (i.e. the legacy behavior), with no\n * change to the tracked progress.\n * * A message without a `previous` mark starts a backfill from scratch (or on\n * a new timeline), and is always accepted.\n * * Otherwise, the message is only accepted if it is on the same timeline as\n * `own`, and continues from a position at or before `own` (i.e. it does not\n * create a gap).\n *\n * `current` is unset for `backfill-completed` messages, which do not advance\n * the progress.\n */\nexport function acceptBackfill(\n own: BackfillProgressMark | undefined,\n marks:\n | {\n previous?: BackfillProgressMark | undefined;\n current?: BackfillProgressMark;\n }\n | undefined,\n): AcceptResult {\n if (marks === undefined) {\n return {accept: true, progress: own}; // legacy (pre-v8)\n }\n const {previous, current} = marks;\n // Starting from scratch is always accepted, as there can be no gap.\n if (previous === undefined) {\n return {accept: true, progress: current};\n }\n if (\n own === undefined ||\n !sameTimeline(own, previous) ||\n previous.progressMark > own.progressMark\n ) {\n return {accept: false};\n }\n return {\n accept: true,\n progress:\n current === undefined\n ? own\n : sameTimeline(own, current)\n ? later(own, current)\n : current,\n };\n}\n\n/**\n * Returns whether a stream whose progress (for a column) is at `stream`\n * will deliver all of the data needed by a subscriber whose progress (for the\n * same column) is at `subscriber`. A `stream` progress of `undefined` indicates\n * a backfill that will start from scratch, which covers all subscribers.\n */\nexport function covers(\n stream: BackfillProgressMark | undefined,\n subscriber: BackfillProgressMark | undefined,\n): boolean {\n return (\n stream === undefined ||\n (subscriber !== undefined &&\n sameTimeline(stream, subscriber) &&\n stream.progressMark <= subscriber.progressMark)\n );\n}\n\n/**\n * Returns the position from which a backfill for the `req` must start\n * in order to satisfy the progress of all of its columns, i.e. the earliest\n * progress mark, or `undefined` (i.e. from scratch) if any column has no\n * progress or if the columns are on different timelines.\n */\nexport function resumePoint(\n req: BackfillRequest,\n): BackfillProgressMark | undefined {\n let point: BackfillProgressMark | undefined;\n for (const {progress} of Object.values(req.columns)) {\n if (progress === undefined) {\n return undefined;\n }\n if (point === undefined) {\n point = progress;\n } else if (!sameTimeline(point, progress)) {\n return undefined;\n } else {\n point = earlier(point, progress);\n }\n }\n return point;\n}\n\n/**\n * Returns a copy of the `req` with the progress of every column set to\n * the request's {@link resumePoint}, i.e. the position from which the\n * backfill of the request will actually start.\n */\nexport function withResumePoint(req: BackfillRequest): BackfillRequest {\n const progress = resumePoint(req);\n return {\n table: req.table,\n columns: Object.fromEntries(\n Object.entries(req.columns).map(([col, {id}]) => [\n col,\n progress === undefined ? {id} : {id, progress},\n ]),\n ),\n };\n}\n\n/**\n * Merges two BackfillRequests for the same table into one that satisfies\n * both. Both requests are expected to reflect the same (current) upstream\n * schema. For columns in both requests, the earlier progress mark is chosen,\n * or no progress mark (i.e. from scratch) if they are on different timelines.\n * If a column's backfill IDs differ (which is unexpected), the ID of `req1`\n * is chosen, and the column is backfilled from scratch.\n */\nexport function getTableSuperset(\n req1: BackfillRequest,\n req2: BackfillRequest,\n): BackfillRequest {\n const columns = {...req1.columns};\n for (const [name, col] of Object.entries(req2.columns)) {\n const exists = columns[name];\n if (!exists) {\n columns[name] = col;\n continue;\n }\n const {id, progress} = exists;\n if (\n progress === undefined ||\n col.progress === undefined ||\n !sameTimeline(progress, col.progress) ||\n !deepEqual(id, col.id)\n ) {\n // Different timelines (or IDs). Start from scratch.\n columns[name] = {id};\n } else {\n columns[name] = {id, progress: earlier(progress, col.progress)};\n }\n }\n return {\n table: {\n ...req1.table,\n metadata: req1.table.metadata ?? req2.table.metadata,\n },\n columns,\n };\n}\n\nfunction tableKey({schema, name}: Identifier) {\n return JSON.stringify([schema, name]);\n}\n\n/**\n * Merges the BackfillRequests of multiple parties (e.g. subscribers) into\n * one request per table, using {@link getTableSuperset}.\n */\nexport function getSuperset(\n ...requestSets: Iterable<BackfillRequest>[]\n): BackfillRequest[] {\n const merged = new Map<string, BackfillRequest>();\n for (const requests of requestSets) {\n for (const req of requests) {\n const key = tableKey(req.table);\n const existing = merged.get(key);\n merged.set(key, existing ? getTableSuperset(existing, req) : req);\n }\n }\n return [...merged.values()];\n}\n"],"mappings":"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"}