@rocicorp/zero 1.6.0-canary.1 → 1.6.0-canary.11

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 (728) hide show
  1. package/README.md +28 -3
  2. package/out/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.130.0}/helpers/usingCtx.js +1 -1
  3. package/out/_virtual/__vite-optional-peer-dep_pg-native_pg.js +13 -0
  4. package/out/_virtual/__vite-optional-peer-dep_pg-native_pg.js.map +1 -0
  5. package/out/_virtual/_rolldown/runtime.js +12 -1
  6. package/out/analyze-query/src/analyze-cli.js.map +1 -1
  7. package/out/analyze-query/src/bin-analyze.js +6 -1
  8. package/out/analyze-query/src/bin-analyze.js.map +1 -1
  9. package/out/analyze-query/src/bin-transform.js.map +1 -1
  10. package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
  11. package/out/ast-to-zql/src/bin.js.map +1 -1
  12. package/out/ast-to-zql/src/format.js.map +1 -1
  13. package/out/datadog/src/datadog-log-sink.js.map +1 -1
  14. package/out/node_modules/.pnpm/@opentelemetry_semantic-conventions@1.41.1/node_modules/@opentelemetry/semantic-conventions/build/esm/stable_attributes.js +12 -0
  15. package/out/node_modules/.pnpm/@opentelemetry_semantic-conventions@1.41.1/node_modules/@opentelemetry/semantic-conventions/build/esm/stable_attributes.js.map +1 -0
  16. package/out/node_modules/.pnpm/pg-cloudflare@1.3.0/node_modules/pg-cloudflare/dist/empty.js +11 -0
  17. package/out/node_modules/.pnpm/pg-cloudflare@1.3.0/node_modules/pg-cloudflare/dist/empty.js.map +1 -0
  18. package/out/node_modules/.pnpm/pg-connection-string@2.12.0/node_modules/pg-connection-string/index.js +130 -0
  19. package/out/node_modules/.pnpm/pg-connection-string@2.12.0/node_modules/pg-connection-string/index.js.map +1 -0
  20. package/out/node_modules/.pnpm/pg-int8@1.0.1/node_modules/pg-int8/index.js +62 -0
  21. package/out/node_modules/.pnpm/pg-int8@1.0.1/node_modules/pg-int8/index.js.map +1 -0
  22. package/out/node_modules/.pnpm/pg-pool@3.13.0_pg@8.20.0/node_modules/pg-pool/index.js +353 -0
  23. package/out/node_modules/.pnpm/pg-pool@3.13.0_pg@8.20.0/node_modules/pg-pool/index.js.map +1 -0
  24. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/buffer-reader.js +60 -0
  25. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/buffer-reader.js.map +1 -0
  26. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/buffer-writer.js +81 -0
  27. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/buffer-writer.js.map +1 -0
  28. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/index.js +35 -0
  29. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/index.js.map +1 -0
  30. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/messages.js +167 -0
  31. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/messages.js.map +1 -0
  32. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/parser.js +288 -0
  33. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/parser.js.map +1 -0
  34. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/serializer.js +177 -0
  35. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/serializer.js.map +1 -0
  36. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/index.js +46 -0
  37. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/index.js.map +1 -0
  38. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/arrayParser.js +16 -0
  39. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/arrayParser.js.map +1 -0
  40. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/binaryParsers.js +165 -0
  41. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/binaryParsers.js.map +1 -0
  42. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/builtins.js +81 -0
  43. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/builtins.js.map +1 -0
  44. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/textParsers.js +167 -0
  45. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/textParsers.js.map +1 -0
  46. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/esm/index.js +19 -0
  47. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/esm/index.js.map +1 -0
  48. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/client.js +508 -0
  49. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/client.js.map +1 -0
  50. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/connection-parameters.js +104 -0
  51. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/connection-parameters.js.map +1 -0
  52. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/connection.js +160 -0
  53. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/connection.js.map +1 -0
  54. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/cert-signatures.js +97 -0
  55. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/cert-signatures.js.map +1 -0
  56. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/sasl.js +131 -0
  57. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/sasl.js.map +1 -0
  58. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils-legacy.js +39 -0
  59. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils-legacy.js.map +1 -0
  60. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils-webcrypto.js +89 -0
  61. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils-webcrypto.js.map +1 -0
  62. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils.js +13 -0
  63. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils.js.map +1 -0
  64. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/defaults.js +46 -0
  65. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/defaults.js.map +1 -0
  66. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/index.js +71 -0
  67. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/index.js.map +1 -0
  68. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/client.js +226 -0
  69. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/client.js.map +1 -0
  70. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/index.js +11 -0
  71. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/index.js.map +1 -0
  72. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/query.js +117 -0
  73. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/query.js.map +1 -0
  74. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/query.js +151 -0
  75. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/query.js.map +1 -0
  76. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/result.js +76 -0
  77. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/result.js.map +1 -0
  78. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/stream.js +73 -0
  79. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/stream.js.map +1 -0
  80. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/type-overrides.js +35 -0
  81. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/type-overrides.js.map +1 -0
  82. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/utils.js +118 -0
  83. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/utils.js.map +1 -0
  84. package/out/node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/helper.js +147 -0
  85. package/out/node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/helper.js.map +1 -0
  86. package/out/node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/index.js +21 -0
  87. package/out/node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/index.js.map +1 -0
  88. package/out/node_modules/.pnpm/postgres-array@2.0.0/node_modules/postgres-array/index.js +84 -0
  89. package/out/node_modules/.pnpm/postgres-array@2.0.0/node_modules/postgres-array/index.js.map +1 -0
  90. package/out/node_modules/.pnpm/postgres-bytea@1.0.1/node_modules/postgres-bytea/index.js +28 -0
  91. package/out/node_modules/.pnpm/postgres-bytea@1.0.1/node_modules/postgres-bytea/index.js.map +1 -0
  92. package/out/node_modules/.pnpm/postgres-date@1.0.7/node_modules/postgres-date/index.js +65 -0
  93. package/out/node_modules/.pnpm/postgres-date@1.0.7/node_modules/postgres-date/index.js.map +1 -0
  94. package/out/node_modules/.pnpm/postgres-interval@1.2.0/node_modules/postgres-interval/index.js +107 -0
  95. package/out/node_modules/.pnpm/postgres-interval@1.2.0/node_modules/postgres-interval/index.js.map +1 -0
  96. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.development.js +696 -0
  97. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.development.js.map +1 -0
  98. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.js +44 -0
  99. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.js.map +1 -0
  100. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.development.js +1585 -0
  101. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.development.js.map +1 -0
  102. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js +329 -0
  103. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js.map +1 -0
  104. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/index.js +13 -0
  105. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/index.js.map +1 -0
  106. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/jsx-runtime.js +13 -0
  107. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/jsx-runtime.js.map +1 -0
  108. package/out/node_modules/.pnpm/solid-js@1.9.13/node_modules/solid-js/dist/server.js +131 -0
  109. package/out/node_modules/.pnpm/solid-js@1.9.13/node_modules/solid-js/dist/server.js.map +1 -0
  110. package/out/node_modules/.pnpm/solid-js@1.9.13/node_modules/solid-js/store/dist/server.js +96 -0
  111. package/out/node_modules/.pnpm/solid-js@1.9.13/node_modules/solid-js/store/dist/server.js.map +1 -0
  112. package/out/node_modules/.pnpm/split2@4.2.0/node_modules/split2/index.js +95 -0
  113. package/out/node_modules/.pnpm/split2@4.2.0/node_modules/split2/index.js.map +1 -0
  114. package/out/node_modules/.pnpm/xtend@4.0.2/node_modules/xtend/mutable.js +18 -0
  115. package/out/node_modules/.pnpm/xtend@4.0.2/node_modules/xtend/mutable.js.map +1 -0
  116. package/out/otel/src/enabled.js.map +1 -1
  117. package/out/otel/src/log-options.js.map +1 -1
  118. package/out/otel/src/maybe-time.js.map +1 -1
  119. package/out/otel/src/span.js.map +1 -1
  120. package/out/replicache/src/async-iterable-to-array.js.map +1 -1
  121. package/out/replicache/src/bg-interval.js.map +1 -1
  122. package/out/replicache/src/btree/diff.js.map +1 -1
  123. package/out/replicache/src/btree/node.js.map +1 -1
  124. package/out/replicache/src/btree/read.js.map +1 -1
  125. package/out/replicache/src/btree/splice.js.map +1 -1
  126. package/out/replicache/src/btree/write.js +6 -3
  127. package/out/replicache/src/btree/write.js.map +1 -1
  128. package/out/replicache/src/call-default-fetch.js.map +1 -1
  129. package/out/replicache/src/connection-loop-delegates.js.map +1 -1
  130. package/out/replicache/src/connection-loop.js.map +1 -1
  131. package/out/replicache/src/cookies.js.map +1 -1
  132. package/out/replicache/src/dag/chunk.js.map +1 -1
  133. package/out/replicache/src/dag/gc.js.map +1 -1
  134. package/out/replicache/src/dag/key.js.map +1 -1
  135. package/out/replicache/src/dag/lazy-store.js.map +1 -1
  136. package/out/replicache/src/dag/store-impl.d.ts.map +1 -1
  137. package/out/replicache/src/dag/store-impl.js +8 -3
  138. package/out/replicache/src/dag/store-impl.js.map +1 -1
  139. package/out/replicache/src/dag/store.js.map +1 -1
  140. package/out/replicache/src/dag/visitor.js.map +1 -1
  141. package/out/replicache/src/db/commit.js.map +1 -1
  142. package/out/replicache/src/db/index.js.map +1 -1
  143. package/out/replicache/src/db/read.js.map +1 -1
  144. package/out/replicache/src/db/rebase.js.map +1 -1
  145. package/out/replicache/src/db/write.js.map +1 -1
  146. package/out/replicache/src/deleted-clients.js.map +1 -1
  147. package/out/replicache/src/error-responses.js.map +1 -1
  148. package/out/replicache/src/frozen-json.js.map +1 -1
  149. package/out/replicache/src/get-default-puller.js.map +1 -1
  150. package/out/replicache/src/get-default-pusher.js.map +1 -1
  151. package/out/replicache/src/get-kv-store-provider.js.map +1 -1
  152. package/out/replicache/src/hash.js.map +1 -1
  153. package/out/replicache/src/http-request-info.js.map +1 -1
  154. package/out/replicache/src/index-defs.js.map +1 -1
  155. package/out/replicache/src/kv/expo-sqlite/store.d.ts +1 -1
  156. package/out/replicache/src/kv/expo-sqlite/store.d.ts.map +1 -1
  157. package/out/replicache/src/kv/expo-sqlite/store.js +6 -7
  158. package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
  159. package/out/replicache/src/kv/idb-store-with-mem-fallback.js.map +1 -1
  160. package/out/replicache/src/kv/idb-store.js.map +1 -1
  161. package/out/replicache/src/kv/mem-store.js.map +1 -1
  162. package/out/replicache/src/kv/op-sqlite/store.d.ts.map +1 -1
  163. package/out/replicache/src/kv/op-sqlite/store.js +6 -6
  164. package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
  165. package/out/replicache/src/kv/read-impl.js.map +1 -1
  166. package/out/replicache/src/kv/sqlite-store.d.ts +6 -6
  167. package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
  168. package/out/replicache/src/kv/sqlite-store.js +107 -22
  169. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  170. package/out/replicache/src/kv/throw-if-closed.d.ts +1 -0
  171. package/out/replicache/src/kv/throw-if-closed.d.ts.map +1 -1
  172. package/out/replicache/src/kv/throw-if-closed.js +1 -4
  173. package/out/replicache/src/kv/throw-if-closed.js.map +1 -1
  174. package/out/replicache/src/kv/write-impl-base.js.map +1 -1
  175. package/out/replicache/src/kv/write-impl.js.map +1 -1
  176. package/out/replicache/src/lazy.js.map +1 -1
  177. package/out/replicache/src/log-options.js.map +1 -1
  178. package/out/replicache/src/make-idb-name.js.map +1 -1
  179. package/out/replicache/src/new-client-channel.js.map +1 -1
  180. package/out/replicache/src/on-persist-channel.js.map +1 -1
  181. package/out/replicache/src/patch-operation.js.map +1 -1
  182. package/out/replicache/src/pending-mutations.js.map +1 -1
  183. package/out/replicache/src/persist/client-gc.js.map +1 -1
  184. package/out/replicache/src/persist/client-group-gc.js.map +1 -1
  185. package/out/replicache/src/persist/client-groups.js +40 -0
  186. package/out/replicache/src/persist/client-groups.js.map +1 -1
  187. package/out/replicache/src/persist/clients.js +28 -0
  188. package/out/replicache/src/persist/clients.js.map +1 -1
  189. package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
  190. package/out/replicache/src/persist/gather-mem-only-visitor.js.map +1 -1
  191. package/out/replicache/src/persist/gather-not-cached-visitor.js.map +1 -1
  192. package/out/replicache/src/persist/heartbeat.js.map +1 -1
  193. package/out/replicache/src/persist/idb-databases-store-db-name.js.map +1 -1
  194. package/out/replicache/src/persist/idb-databases-store.js.map +1 -1
  195. package/out/replicache/src/persist/make-client-id.js.map +1 -1
  196. package/out/replicache/src/persist/persist.d.ts.map +1 -1
  197. package/out/replicache/src/persist/persist.js +4 -2
  198. package/out/replicache/src/persist/persist.js.map +1 -1
  199. package/out/replicache/src/persist/refresh.js.map +1 -1
  200. package/out/replicache/src/process-scheduler.js.map +1 -1
  201. package/out/replicache/src/pusher.js.map +1 -1
  202. package/out/replicache/src/replicache-impl.js.map +1 -1
  203. package/out/replicache/src/report-error.js.map +1 -1
  204. package/out/replicache/src/request-idle.js.map +1 -1
  205. package/out/replicache/src/scan-iterator.js.map +1 -1
  206. package/out/replicache/src/scan-options.js.map +1 -1
  207. package/out/replicache/src/set-interval-with-signal.js.map +1 -1
  208. package/out/replicache/src/subscriptions.js.map +1 -1
  209. package/out/replicache/src/sync/diff.js.map +1 -1
  210. package/out/replicache/src/sync/ids.js.map +1 -1
  211. package/out/replicache/src/sync/patch.js.map +1 -1
  212. package/out/replicache/src/sync/pull-error.js.map +1 -1
  213. package/out/replicache/src/sync/pull.d.ts.map +1 -1
  214. package/out/replicache/src/sync/pull.js +9 -6
  215. package/out/replicache/src/sync/pull.js.map +1 -1
  216. package/out/replicache/src/sync/push.js.map +1 -1
  217. package/out/replicache/src/sync/request-id.js.map +1 -1
  218. package/out/replicache/src/to-error.js.map +1 -1
  219. package/out/replicache/src/transaction-closed-error.js.map +1 -1
  220. package/out/replicache/src/transactions.js.map +1 -1
  221. package/out/replicache/src/with-transactions.js.map +1 -1
  222. package/out/shared/src/abort-error.js.map +1 -1
  223. package/out/shared/src/arrays.js.map +1 -1
  224. package/out/shared/src/asserts.js.map +1 -1
  225. package/out/shared/src/bigint-json.js.map +1 -1
  226. package/out/shared/src/binary-search.js.map +1 -1
  227. package/out/shared/src/broadcast-channel.js.map +1 -1
  228. package/out/shared/src/browser-env.js.map +1 -1
  229. package/out/shared/src/btree-set.js.map +1 -1
  230. package/out/shared/src/cache.js.map +1 -1
  231. package/out/shared/src/centroid.js.map +1 -1
  232. package/out/shared/src/custom-key-map.js.map +1 -1
  233. package/out/shared/src/custom-key-set.js.map +1 -1
  234. package/out/shared/src/deep-clone.js.map +1 -1
  235. package/out/shared/src/deep-merge.js.map +1 -1
  236. package/out/shared/src/document-visible.js.map +1 -1
  237. package/out/shared/src/dotenv.js.map +1 -1
  238. package/out/shared/src/error.js.map +1 -1
  239. package/out/shared/src/hash.js.map +1 -1
  240. package/out/shared/src/iterables.js.map +1 -1
  241. package/out/shared/src/json-schema.js.map +1 -1
  242. package/out/shared/src/json.js.map +1 -1
  243. package/out/shared/src/logging-test-utils.js.map +1 -1
  244. package/out/shared/src/logging.js.map +1 -1
  245. package/out/shared/src/map.d.ts +6 -0
  246. package/out/shared/src/map.d.ts.map +1 -0
  247. package/out/shared/src/map.js +39 -0
  248. package/out/shared/src/map.js.map +1 -0
  249. package/out/shared/src/must.js.map +1 -1
  250. package/out/shared/src/object-traversal.js.map +1 -1
  251. package/out/shared/src/objects.js.map +1 -1
  252. package/out/shared/src/options.js.map +1 -1
  253. package/out/shared/src/parse-big-int.js.map +1 -1
  254. package/out/shared/src/promise-race.js.map +1 -1
  255. package/out/shared/src/queue.d.ts.map +1 -1
  256. package/out/shared/src/queue.js +15 -21
  257. package/out/shared/src/queue.js.map +1 -1
  258. package/out/shared/src/rand.js.map +1 -1
  259. package/out/shared/src/random-uint64.js.map +1 -1
  260. package/out/shared/src/random-values.js.map +1 -1
  261. package/out/shared/src/record-proxy.js.map +1 -1
  262. package/out/shared/src/resolved-promises.js.map +1 -1
  263. package/out/shared/src/ring-buffer.d.ts +32 -0
  264. package/out/shared/src/ring-buffer.d.ts.map +1 -0
  265. package/out/shared/src/ring-buffer.js +109 -0
  266. package/out/shared/src/ring-buffer.js.map +1 -0
  267. package/out/shared/src/sentinels.js.map +1 -1
  268. package/out/shared/src/set-utils.js.map +1 -1
  269. package/out/shared/src/size-of-value.js.map +1 -1
  270. package/out/shared/src/sleep.js.map +1 -1
  271. package/out/shared/src/sorted-entries.js.map +1 -1
  272. package/out/shared/src/string-compare.js.map +1 -1
  273. package/out/shared/src/subscribable.js.map +1 -1
  274. package/out/shared/src/tdigest-schema.js.map +1 -1
  275. package/out/shared/src/tdigest.js.map +1 -1
  276. package/out/shared/src/valita.js.map +1 -1
  277. package/out/z2s/src/compiler.js.map +1 -1
  278. package/out/z2s/src/sql.js.map +1 -1
  279. package/out/zero/package.js +23 -23
  280. package/out/zero/package.js.map +1 -1
  281. package/out/zero/src/build-schema.js.map +1 -1
  282. package/out/zero/src/zero-cache-dev.js.map +1 -1
  283. package/out/zero/src/zero-out.js.map +1 -1
  284. package/out/zero-cache/src/auth/auth.js.map +1 -1
  285. package/out/zero-cache/src/auth/jwt.js.map +1 -1
  286. package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
  287. package/out/zero-cache/src/auth/read-authorizer.js.map +1 -1
  288. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  289. package/out/zero-cache/src/config/network.js.map +1 -1
  290. package/out/zero-cache/src/config/normalize.js.map +1 -1
  291. package/out/zero-cache/src/config/server-context.js.map +1 -1
  292. package/out/zero-cache/src/config/zero-config.js +5 -0
  293. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  294. package/out/zero-cache/src/custom/fetch.js.map +1 -1
  295. package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
  296. package/out/zero-cache/src/db/create.js.map +1 -1
  297. package/out/zero-cache/src/db/delete-lite-db.js.map +1 -1
  298. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  299. package/out/zero-cache/src/db/migration-lite.js +19 -0
  300. package/out/zero-cache/src/db/migration-lite.js.map +1 -1
  301. package/out/zero-cache/src/db/migration.js +19 -0
  302. package/out/zero-cache/src/db/migration.js.map +1 -1
  303. package/out/zero-cache/src/db/pg-copy-binary.js.map +1 -1
  304. package/out/zero-cache/src/db/pg-copy.js.map +1 -1
  305. package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
  306. package/out/zero-cache/src/db/pg-type-parser.js.map +1 -1
  307. package/out/zero-cache/src/db/run-transaction.js.map +1 -1
  308. package/out/zero-cache/src/db/specs.js.map +1 -1
  309. package/out/zero-cache/src/db/statements.js.map +1 -1
  310. package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
  311. package/out/zero-cache/src/db/warmup.js.map +1 -1
  312. package/out/zero-cache/src/observability/events.js.map +1 -1
  313. package/out/zero-cache/src/observability/metrics.js.map +1 -1
  314. package/out/zero-cache/src/scripts/decommission.js.map +1 -1
  315. package/out/zero-cache/src/scripts/deploy-permissions.js.map +1 -1
  316. package/out/zero-cache/src/scripts/permissions.js.map +1 -1
  317. package/out/zero-cache/src/server/anonymous-otel-start.js +11 -10
  318. package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
  319. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  320. package/out/zero-cache/src/server/change-streamer.js +13 -7
  321. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  322. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  323. package/out/zero-cache/src/server/logging.js.map +1 -1
  324. package/out/zero-cache/src/server/main.d.ts.map +1 -1
  325. package/out/zero-cache/src/server/main.js +4 -2
  326. package/out/zero-cache/src/server/main.js.map +1 -1
  327. package/out/zero-cache/src/server/mutator.js +4 -2
  328. package/out/zero-cache/src/server/mutator.js.map +1 -1
  329. package/out/zero-cache/src/server/otel-diag-logger.js.map +1 -1
  330. package/out/zero-cache/src/server/otel-log-sink.js.map +1 -1
  331. package/out/zero-cache/src/server/otel-start.js +1 -1
  332. package/out/zero-cache/src/server/otel-start.js.map +1 -1
  333. package/out/zero-cache/src/server/priority-op.js.map +1 -1
  334. package/out/zero-cache/src/server/reaper.d.ts.map +1 -1
  335. package/out/zero-cache/src/server/reaper.js +6 -4
  336. package/out/zero-cache/src/server/reaper.js.map +1 -1
  337. package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
  338. package/out/zero-cache/src/server/replicator.js +4 -2
  339. package/out/zero-cache/src/server/replicator.js.map +1 -1
  340. package/out/zero-cache/src/server/runner/main.d.ts.map +1 -1
  341. package/out/zero-cache/src/server/runner/main.js +2 -1
  342. package/out/zero-cache/src/server/runner/main.js.map +1 -1
  343. package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
  344. package/out/zero-cache/src/server/runner/runtime.js.map +1 -1
  345. package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
  346. package/out/zero-cache/src/server/shadow-syncer.js +6 -3
  347. package/out/zero-cache/src/server/shadow-syncer.js.map +1 -1
  348. package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
  349. package/out/zero-cache/src/server/syncer.js +8 -6
  350. package/out/zero-cache/src/server/syncer.js.map +1 -1
  351. package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
  352. package/out/zero-cache/src/server/worker-urls.js.map +1 -1
  353. package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
  354. package/out/zero-cache/src/services/analyze.js +5 -2
  355. package/out/zero-cache/src/services/analyze.js.map +1 -1
  356. package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
  357. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
  358. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  359. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  360. package/out/zero-cache/src/services/change-source/pg/backfill-metadata.js.map +1 -1
  361. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +2 -2
  362. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  363. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  364. package/out/zero-cache/src/services/change-source/pg/change-source.js +2 -2
  365. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  366. package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
  367. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +8 -2
  368. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  369. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +13 -12
  370. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  371. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.js.map +1 -1
  372. package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput-parser.js.map +1 -1
  373. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
  374. package/out/zero-cache/src/services/change-source/pg/lsn.js.map +1 -1
  375. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +1 -1
  376. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -1
  377. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +33 -33
  378. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  379. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
  380. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  381. package/out/zero-cache/src/services/change-source/pg/schema/published.js.map +1 -1
  382. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +1 -1
  383. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  384. package/out/zero-cache/src/services/change-source/pg/schema/validation.js.map +1 -1
  385. package/out/zero-cache/src/services/change-source/protocol/current/control.js.map +1 -1
  386. package/out/zero-cache/src/services/change-source/protocol/current/data.js +2 -0
  387. package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
  388. package/out/zero-cache/src/services/change-source/protocol/current/downstream.js.map +1 -1
  389. package/out/zero-cache/src/services/change-source/protocol/current/json.js.map +1 -1
  390. package/out/zero-cache/src/services/change-source/protocol/current/status.js.map +1 -1
  391. package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
  392. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
  393. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  394. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  395. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  396. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  397. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  398. package/out/zero-cache/src/services/change-streamer/replica-monitor.js.map +1 -1
  399. package/out/zero-cache/src/services/change-streamer/schema/init.js +21 -25
  400. package/out/zero-cache/src/services/change-streamer/schema/init.js.map +1 -1
  401. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts.map +1 -1
  402. package/out/zero-cache/src/services/change-streamer/schema/tables.js +1 -1
  403. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  404. package/out/zero-cache/src/services/change-streamer/snapshot.js +15 -0
  405. package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
  406. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  407. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  408. package/out/zero-cache/src/services/heapz.js.map +1 -1
  409. package/out/zero-cache/src/services/http-service.js.map +1 -1
  410. package/out/zero-cache/src/services/life-cycle.d.ts +1 -1
  411. package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
  412. package/out/zero-cache/src/services/life-cycle.js +8 -3
  413. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  414. package/out/zero-cache/src/services/limiter/sliding-window-limiter.js.map +1 -1
  415. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  416. package/out/zero-cache/src/services/mutagen/error.js.map +1 -1
  417. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  418. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  419. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  420. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  421. package/out/zero-cache/src/services/replicator/notifier.js.map +1 -1
  422. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  423. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  424. package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
  425. package/out/zero-cache/src/services/replicator/reporter/report-schema.js.map +1 -1
  426. package/out/zero-cache/src/services/replicator/schema/change-log.js.map +1 -1
  427. package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
  428. package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
  429. package/out/zero-cache/src/services/replicator/schema/table-metadata.js.map +1 -1
  430. package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
  431. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  432. package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
  433. package/out/zero-cache/src/services/run-ast.js +1 -0
  434. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  435. package/out/zero-cache/src/services/runner.js.map +1 -1
  436. package/out/zero-cache/src/services/running-state.d.ts.map +1 -1
  437. package/out/zero-cache/src/services/running-state.js +3 -0
  438. package/out/zero-cache/src/services/running-state.js.map +1 -1
  439. package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js.map +1 -1
  440. package/out/zero-cache/src/services/statz.js.map +1 -1
  441. package/out/zero-cache/src/services/view-syncer/active-users-gauge.js.map +1 -1
  442. package/out/zero-cache/src/services/view-syncer/client-handler.js +1 -1
  443. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  444. package/out/zero-cache/src/services/view-syncer/client-schema.js.map +1 -1
  445. package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
  446. package/out/zero-cache/src/services/view-syncer/cvr-purger.d.ts.map +1 -1
  447. package/out/zero-cache/src/services/view-syncer/cvr-purger.js +2 -1
  448. package/out/zero-cache/src/services/view-syncer/cvr-purger.js.map +1 -1
  449. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  450. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  451. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
  452. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +1 -1
  453. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  454. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +1 -1
  455. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  456. package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
  457. package/out/zero-cache/src/services/view-syncer/row-set-signature.js.map +1 -1
  458. package/out/zero-cache/src/services/view-syncer/schema/cvr.js.map +1 -1
  459. package/out/zero-cache/src/services/view-syncer/schema/init.js +97 -113
  460. package/out/zero-cache/src/services/view-syncer/schema/init.js.map +1 -1
  461. package/out/zero-cache/src/services/view-syncer/schema/types.js +103 -1
  462. package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
  463. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  464. package/out/zero-cache/src/services/view-syncer/tracer.js.map +1 -1
  465. package/out/zero-cache/src/services/view-syncer/ttl-clock.js.map +1 -1
  466. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  467. package/out/zero-cache/src/services/view-syncer/view-syncer.js +15 -5
  468. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  469. package/out/zero-cache/src/types/configuration-error.d.ts +4 -0
  470. package/out/zero-cache/src/types/configuration-error.d.ts.map +1 -0
  471. package/out/zero-cache/src/types/configuration-error.js +11 -0
  472. package/out/zero-cache/src/types/configuration-error.js.map +1 -0
  473. package/out/zero-cache/src/types/error-with-level.js.map +1 -1
  474. package/out/zero-cache/src/types/http.js.map +1 -1
  475. package/out/zero-cache/src/types/lexi-version.js.map +1 -1
  476. package/out/zero-cache/src/types/lite.js.map +1 -1
  477. package/out/zero-cache/src/types/names.js.map +1 -1
  478. package/out/zero-cache/src/types/pg-data-type.js.map +1 -1
  479. package/out/zero-cache/src/types/pg.d.ts +2 -0
  480. package/out/zero-cache/src/types/pg.d.ts.map +1 -1
  481. package/out/zero-cache/src/types/pg.js +34 -1
  482. package/out/zero-cache/src/types/pg.js.map +1 -1
  483. package/out/zero-cache/src/types/processes.js.map +1 -1
  484. package/out/zero-cache/src/types/profiler.js.map +1 -1
  485. package/out/zero-cache/src/types/row-key.js.map +1 -1
  486. package/out/zero-cache/src/types/shards.js.map +1 -1
  487. package/out/zero-cache/src/types/sql.js.map +1 -1
  488. package/out/zero-cache/src/types/state-version.js.map +1 -1
  489. package/out/zero-cache/src/types/streams.js.map +1 -1
  490. package/out/zero-cache/src/types/strings.js.map +1 -1
  491. package/out/zero-cache/src/types/subscription.js.map +1 -1
  492. package/out/zero-cache/src/types/timeout.js.map +1 -1
  493. package/out/zero-cache/src/types/url-params.js.map +1 -1
  494. package/out/zero-cache/src/types/websocket-handoff.js.map +1 -1
  495. package/out/zero-cache/src/types/ws.js.map +1 -1
  496. package/out/zero-cache/src/workers/connect-params.js.map +1 -1
  497. package/out/zero-cache/src/workers/connection.js.map +1 -1
  498. package/out/zero-cache/src/workers/mutator.js.map +1 -1
  499. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  500. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  501. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  502. package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
  503. package/out/zero-client/src/client/connection-manager.js +2 -1
  504. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  505. package/out/zero-client/src/client/connection.js.map +1 -1
  506. package/out/zero-client/src/client/context.js.map +1 -1
  507. package/out/zero-client/src/client/crud-impl.js.map +1 -1
  508. package/out/zero-client/src/client/crud.js.map +1 -1
  509. package/out/zero-client/src/client/custom.js +2 -1
  510. package/out/zero-client/src/client/custom.js.map +1 -1
  511. package/out/zero-client/src/client/delete-clients-manager.js.map +1 -1
  512. package/out/zero-client/src/client/enable-analytics.js.map +1 -1
  513. package/out/zero-client/src/client/error.js.map +1 -1
  514. package/out/zero-client/src/client/http-string.js.map +1 -1
  515. package/out/zero-client/src/client/inspector/client-group.js.map +1 -1
  516. package/out/zero-client/src/client/inspector/client.js.map +1 -1
  517. package/out/zero-client/src/client/inspector/html-dialog-prompt.js.map +1 -1
  518. package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
  519. package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
  520. package/out/zero-client/src/client/inspector/query.js.map +1 -1
  521. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  522. package/out/zero-client/src/client/keys.js.map +1 -1
  523. package/out/zero-client/src/client/log-options.js.map +1 -1
  524. package/out/zero-client/src/client/make-mutate-property.js.map +1 -1
  525. package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
  526. package/out/zero-client/src/client/metrics.js.map +1 -1
  527. package/out/zero-client/src/client/mutation-tracker.js.map +1 -1
  528. package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
  529. package/out/zero-client/src/client/options.js.map +1 -1
  530. package/out/zero-client/src/client/query-manager.js.map +1 -1
  531. package/out/zero-client/src/client/reload-error-handler.js.map +1 -1
  532. package/out/zero-client/src/client/server-option.js.map +1 -1
  533. package/out/zero-client/src/client/version.js +1 -1
  534. package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
  535. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  536. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  537. package/out/zero-client/src/client/zero.js +58 -32
  538. package/out/zero-client/src/client/zero.js.map +1 -1
  539. package/out/zero-client/src/util/nanoid.js.map +1 -1
  540. package/out/zero-protocol/src/analyze-query-result.js +3 -0
  541. package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
  542. package/out/zero-protocol/src/application-error.js.map +1 -1
  543. package/out/zero-protocol/src/ast.js.map +1 -1
  544. package/out/zero-protocol/src/change-desired-queries.js +1 -0
  545. package/out/zero-protocol/src/change-desired-queries.js.map +1 -1
  546. package/out/zero-protocol/src/client-schema.js.map +1 -1
  547. package/out/zero-protocol/src/close-connection.js.map +1 -1
  548. package/out/zero-protocol/src/connect.js +7 -0
  549. package/out/zero-protocol/src/connect.js.map +1 -1
  550. package/out/zero-protocol/src/custom-queries.js.map +1 -1
  551. package/out/zero-protocol/src/data.js.map +1 -1
  552. package/out/zero-protocol/src/delete-clients.js.map +1 -1
  553. package/out/zero-protocol/src/down.js.map +1 -1
  554. package/out/zero-protocol/src/error.js +7 -0
  555. package/out/zero-protocol/src/error.js.map +1 -1
  556. package/out/zero-protocol/src/inspect-down.js.map +1 -1
  557. package/out/zero-protocol/src/inspect-up.js +1 -0
  558. package/out/zero-protocol/src/inspect-up.js.map +1 -1
  559. package/out/zero-protocol/src/mutate-server.js.map +1 -1
  560. package/out/zero-protocol/src/mutation-id.js.map +1 -1
  561. package/out/zero-protocol/src/mutation.js.map +1 -1
  562. package/out/zero-protocol/src/mutations-patch.js.map +1 -1
  563. package/out/zero-protocol/src/ping.js.map +1 -1
  564. package/out/zero-protocol/src/poke.js +4 -0
  565. package/out/zero-protocol/src/poke.js.map +1 -1
  566. package/out/zero-protocol/src/pong.js.map +1 -1
  567. package/out/zero-protocol/src/primary-key.js.map +1 -1
  568. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  569. package/out/zero-protocol/src/pull.js.map +1 -1
  570. package/out/zero-protocol/src/push.js +16 -0
  571. package/out/zero-protocol/src/push.js.map +1 -1
  572. package/out/zero-protocol/src/queries-patch.js.map +1 -1
  573. package/out/zero-protocol/src/query-hash.js.map +1 -1
  574. package/out/zero-protocol/src/query-server.js.map +1 -1
  575. package/out/zero-protocol/src/row-patch.js.map +1 -1
  576. package/out/zero-protocol/src/up.js.map +1 -1
  577. package/out/zero-protocol/src/update-auth.js.map +1 -1
  578. package/out/zero-protocol/src/version.js.map +1 -1
  579. package/out/zero-react/src/use-connection-state.js +4 -2
  580. package/out/zero-react/src/use-connection-state.js.map +1 -1
  581. package/out/zero-react/src/use-query.js +6 -4
  582. package/out/zero-react/src/use-query.js.map +1 -1
  583. package/out/zero-react/src/use-zero-online.js +4 -2
  584. package/out/zero-react/src/use-zero-online.js.map +1 -1
  585. package/out/zero-react/src/zero-provider.js +15 -12
  586. package/out/zero-react/src/zero-provider.js.map +1 -1
  587. package/out/zero-schema/src/builder/relationship-builder.js.map +1 -1
  588. package/out/zero-schema/src/builder/schema-builder.js.map +1 -1
  589. package/out/zero-schema/src/builder/table-builder.js.map +1 -1
  590. package/out/zero-schema/src/compiled-permissions.js.map +1 -1
  591. package/out/zero-schema/src/name-mapper.js.map +1 -1
  592. package/out/zero-schema/src/permissions.js.map +1 -1
  593. package/out/zero-schema/src/schema-config.js.map +1 -1
  594. package/out/zero-server/src/adapters/drizzle.js.map +1 -1
  595. package/out/zero-server/src/adapters/kysely.js.map +1 -1
  596. package/out/zero-server/src/adapters/pg.js +1 -1
  597. package/out/zero-server/src/adapters/pg.js.map +1 -1
  598. package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
  599. package/out/zero-server/src/adapters/prisma.js.map +1 -1
  600. package/out/zero-server/src/custom.js +2 -1
  601. package/out/zero-server/src/custom.js.map +1 -1
  602. package/out/zero-server/src/logging.js.map +1 -1
  603. package/out/zero-server/src/pg-query-executor.js.map +1 -1
  604. package/out/zero-server/src/process-mutations.js.map +1 -1
  605. package/out/zero-server/src/push-processor.js.map +1 -1
  606. package/out/zero-server/src/queries/process-queries.js.map +1 -1
  607. package/out/zero-server/src/schema.js.map +1 -1
  608. package/out/zero-server/src/zql-database.js.map +1 -1
  609. package/out/zero-solid/src/solid-view.js +1 -1
  610. package/out/zero-solid/src/solid-view.js.map +1 -1
  611. package/out/zero-solid/src/use-connection-state.js +1 -1
  612. package/out/zero-solid/src/use-connection-state.js.map +1 -1
  613. package/out/zero-solid/src/use-query.js +2 -2
  614. package/out/zero-solid/src/use-query.js.map +1 -1
  615. package/out/zero-solid/src/use-zero-online.js +1 -1
  616. package/out/zero-solid/src/use-zero-online.js.map +1 -1
  617. package/out/zero-solid/src/use-zero.js +1 -1
  618. package/out/zero-solid/src/use-zero.js.map +1 -1
  619. package/out/zero-types/src/format.js.map +1 -1
  620. package/out/zero-types/src/name-mapper.js.map +1 -1
  621. package/out/zql/src/builder/builder.d.ts.map +1 -1
  622. package/out/zql/src/builder/builder.js +18 -7
  623. package/out/zql/src/builder/builder.js.map +1 -1
  624. package/out/zql/src/builder/debug-delegate.d.ts +5 -0
  625. package/out/zql/src/builder/debug-delegate.d.ts.map +1 -1
  626. package/out/zql/src/builder/debug-delegate.js +10 -1
  627. package/out/zql/src/builder/debug-delegate.js.map +1 -1
  628. package/out/zql/src/builder/filter.js.map +1 -1
  629. package/out/zql/src/builder/like.js.map +1 -1
  630. package/out/zql/src/error.js.map +1 -1
  631. package/out/zql/src/ivm/array-view.js.map +1 -1
  632. package/out/zql/src/ivm/cap.d.ts +32 -0
  633. package/out/zql/src/ivm/cap.d.ts.map +1 -0
  634. package/out/zql/src/ivm/cap.js +205 -0
  635. package/out/zql/src/ivm/cap.js.map +1 -0
  636. package/out/zql/src/ivm/change.js.map +1 -1
  637. package/out/zql/src/ivm/constraint.js +1 -1
  638. package/out/zql/src/ivm/constraint.js.map +1 -1
  639. package/out/zql/src/ivm/data.js.map +1 -1
  640. package/out/zql/src/ivm/exists.js.map +1 -1
  641. package/out/zql/src/ivm/fan-in.js.map +1 -1
  642. package/out/zql/src/ivm/fan-out.js.map +1 -1
  643. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  644. package/out/zql/src/ivm/filter-push.js.map +1 -1
  645. package/out/zql/src/ivm/filter.js.map +1 -1
  646. package/out/zql/src/ivm/flipped-join.d.ts +4 -8
  647. package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
  648. package/out/zql/src/ivm/flipped-join.js +59 -63
  649. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  650. package/out/zql/src/ivm/join-utils.js.map +1 -1
  651. package/out/zql/src/ivm/join.js.map +1 -1
  652. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
  653. package/out/zql/src/ivm/memory-source.js +1 -1
  654. package/out/zql/src/ivm/memory-source.js.map +1 -1
  655. package/out/zql/src/ivm/memory-storage.js.map +1 -1
  656. package/out/zql/src/ivm/operator.d.ts +1 -1
  657. package/out/zql/src/ivm/operator.js.map +1 -1
  658. package/out/zql/src/ivm/push-accumulated.js.map +1 -1
  659. package/out/zql/src/ivm/schema.d.ts +0 -8
  660. package/out/zql/src/ivm/schema.d.ts.map +1 -1
  661. package/out/zql/src/ivm/skip-yields.js.map +1 -1
  662. package/out/zql/src/ivm/skip.js.map +1 -1
  663. package/out/zql/src/ivm/source.js.map +1 -1
  664. package/out/zql/src/ivm/stream.js.map +1 -1
  665. package/out/zql/src/ivm/take.js +2 -2
  666. package/out/zql/src/ivm/take.js.map +1 -1
  667. package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
  668. package/out/zql/src/ivm/union-fan-in.js +3 -1
  669. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  670. package/out/zql/src/ivm/union-fan-out.js.map +1 -1
  671. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  672. package/out/zql/src/mutate/crud.js.map +1 -1
  673. package/out/zql/src/mutate/custom.js.map +1 -1
  674. package/out/zql/src/mutate/mutator-registry.js.map +1 -1
  675. package/out/zql/src/mutate/mutator.d.ts +12 -3
  676. package/out/zql/src/mutate/mutator.d.ts.map +1 -1
  677. package/out/zql/src/mutate/mutator.js.map +1 -1
  678. package/out/zql/src/planner/planner-builder.js.map +1 -1
  679. package/out/zql/src/planner/planner-connection.js.map +1 -1
  680. package/out/zql/src/planner/planner-constraint.js.map +1 -1
  681. package/out/zql/src/planner/planner-debug.js.map +1 -1
  682. package/out/zql/src/planner/planner-fan-in.js.map +1 -1
  683. package/out/zql/src/planner/planner-fan-out.js.map +1 -1
  684. package/out/zql/src/planner/planner-graph.js.map +1 -1
  685. package/out/zql/src/planner/planner-join.d.ts.map +1 -1
  686. package/out/zql/src/planner/planner-join.js +2 -1
  687. package/out/zql/src/planner/planner-join.js.map +1 -1
  688. package/out/zql/src/planner/planner-node.js.map +1 -1
  689. package/out/zql/src/planner/planner-source.js.map +1 -1
  690. package/out/zql/src/planner/planner-terminus.js.map +1 -1
  691. package/out/zql/src/query/complete-ordering.js.map +1 -1
  692. package/out/zql/src/query/create-builder.js.map +1 -1
  693. package/out/zql/src/query/error.js.map +1 -1
  694. package/out/zql/src/query/escape-like.js.map +1 -1
  695. package/out/zql/src/query/expression.js.map +1 -1
  696. package/out/zql/src/query/measure-push-operator.js.map +1 -1
  697. package/out/zql/src/query/metrics-delegate.js.map +1 -1
  698. package/out/zql/src/query/named.js.map +1 -1
  699. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  700. package/out/zql/src/query/query-impl.js +1 -1
  701. package/out/zql/src/query/query-impl.js.map +1 -1
  702. package/out/zql/src/query/query-internals.js.map +1 -1
  703. package/out/zql/src/query/query-registry.d.ts +10 -3
  704. package/out/zql/src/query/query-registry.d.ts.map +1 -1
  705. package/out/zql/src/query/query-registry.js.map +1 -1
  706. package/out/zql/src/query/runnable-query-impl.js.map +1 -1
  707. package/out/zql/src/query/static-query.js.map +1 -1
  708. package/out/zql/src/query/ttl.js.map +1 -1
  709. package/out/zql/src/query/validate-input.js.map +1 -1
  710. package/out/zqlite/src/database-storage.js.map +1 -1
  711. package/out/zqlite/src/db.js.map +1 -1
  712. package/out/zqlite/src/explain-queries.js.map +1 -1
  713. package/out/zqlite/src/internal/sql-inline.js.map +1 -1
  714. package/out/zqlite/src/internal/sql.js.map +1 -1
  715. package/out/zqlite/src/internal/statement-cache.js.map +1 -1
  716. package/out/zqlite/src/query-builder.js.map +1 -1
  717. package/out/zqlite/src/query-delegate.js.map +1 -1
  718. package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
  719. package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
  720. package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
  721. package/out/zqlite/src/table-source.d.ts.map +1 -1
  722. package/out/zqlite/src/table-source.js +7 -7
  723. package/out/zqlite/src/table-source.js.map +1 -1
  724. package/package.json +23 -23
  725. package/out/zero-client/src/util/socket.d.ts +0 -3
  726. package/out/zero-client/src/util/socket.d.ts.map +0 -1
  727. package/out/zero-client/src/util/socket.js +0 -8
  728. package/out/zero-client/src/util/socket.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","names":[],"sources":["../../../../../../zero-cache/src/server/runner/main.ts"],"sourcesContent":["import '../../../../shared/src/dotenv.ts';\n\nimport {exitAfter} from '../../services/life-cycle.ts';\nimport {parentWorker, singleProcessMode} from '../../types/processes.ts';\nimport {runWorker} from './run-worker.ts';\n\nif (!singleProcessMode()) {\n void exitAfter(() => runWorker(parentWorker, process.env));\n}\n\nexport default runWorker;\n"],"mappings":";;;;;AAMA,IAAI,CAAC,mBAAmB,CACjB,iBAAgB,UAAU,cAAc,QAAQ,IAAI,CAAC"}
1
+ {"version":3,"file":"main.js","names":[],"sources":["../../../../../../zero-cache/src/server/runner/main.ts"],"sourcesContent":["import {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport '../../../../shared/src/dotenv.ts';\n\nimport {exitAfter} from '../../services/life-cycle.ts';\nimport {parentWorker, singleProcessMode} from '../../types/processes.ts';\nimport {runWorker} from './run-worker.ts';\n\nif (!singleProcessMode()) {\n void exitAfter(new LogContext('info', {}, consoleLogSink), () =>\n runWorker(parentWorker, process.env),\n );\n}\n\nexport default runWorker;\n"],"mappings":";;;;;;AAOA,IAAI,CAAC,kBAAkB,GACrB,UAAe,IAAI,WAAW,QAAQ,CAAC,GAAG,cAAc,SACtD,UAAU,cAAc,QAAQ,GAAG,CACrC"}
@@ -1 +1 @@
1
- {"version":3,"file":"run-worker.js","names":[],"sources":["../../../../../../zero-cache/src/server/runner/run-worker.ts"],"sourcesContent":["import '../../../../shared/src/dotenv.ts';\n\nimport {styleText} from 'node:util';\nimport {resolver, type Resolver} from '@rocicorp/resolver';\nimport {colorConsole} from '../../../../shared/src/logging.ts';\nimport {PROTOCOL_VERSION} from '../../../../zero-protocol/src/protocol-version.ts';\nimport {normalizeZeroConfig} from '../../config/normalize.ts';\nimport {getServerVersion, getZeroConfig} from '../../config/zero-config.ts';\nimport {ProcessManager, runUntilKilled} from '../../services/life-cycle.ts';\nimport {childWorker, type Worker} from '../../types/processes.ts';\nimport {createLogContext} from '../logging.ts';\nimport {MAIN_URL} from '../worker-urls.ts';\nimport {getTaskID} from './runtime.ts';\nimport {ZeroDispatcher} from './zero-dispatcher.ts';\n\nconst startupMessageEnv = 'ZERO_ENABLE_STARTUP_MESSAGE';\n\nfunction printStartupMessage(env: NodeJS.ProcessEnv) {\n if (env[startupMessageEnv] !== '1') {\n return;\n }\n\n colorConsole.log(\n `\\nBTW, ${styleText(['bold', 'cyan'], 'Cloud Zero')} ` +\n 'is now available - professional Zero hosting from the team that built it.\\n' +\n `Get started now: ${styleText(['blue', 'underline'], 'https://zero.rocicorp.dev/cloud')}\\n\\n` +\n styleText('dim', `Disable this message with ${startupMessageEnv}=0`) +\n '\\n',\n );\n}\n\n/**\n * Top-level `runner` entry point to the zero-cache. This layer is responsible for:\n * * runtime-based config normalization\n * * lazy startup\n * * serving /statsz\n * * auto-reset restarts (TODO)\n */\nexport async function runWorker(\n parent: Worker | null,\n env: NodeJS.ProcessEnv,\n): Promise<void> {\n // Note: Deprecation warnings are only emitted at this top-level parse;\n // they are suppressed when parsed in subprocesses.\n const cfg = getZeroConfig({env, emitDeprecationWarnings: true});\n const lc = createLogContext(cfg, 'runner');\n\n const defaultTaskID = await getTaskID(lc);\n const config = normalizeZeroConfig(lc, cfg, env, defaultTaskID);\n const processes = new ProcessManager(lc, parent ?? process);\n\n const {port, keepaliveTimeoutMs, lazyStartup} = config;\n const serverVersion = getServerVersion(config);\n lc.info?.(`starting server${!serverVersion ? '' : `@${serverVersion}`} `, {\n protocolVersion: PROTOCOL_VERSION,\n taskID: config.taskID,\n app: config.app,\n shard: config.shard,\n port: config.port,\n });\n\n let zeroCache: Resolver<Worker> | undefined;\n function startZeroCache(): Promise<Worker> {\n if (zeroCache === undefined) {\n const startMs = performance.now();\n lc.info?.('starting zero-cache');\n\n const r = (zeroCache = resolver<Worker>());\n const w = childWorker(MAIN_URL, env)\n .once('message', () => {\n r.resolve(w);\n lc.info?.(`zero-cache ready (${performance.now() - startMs} ms)`);\n })\n .once('error', r.reject);\n\n processes.addWorker(w, 'user-facing', 'zero-cache');\n }\n return zeroCache.promise;\n }\n\n // Eagerly start the zero-cache if it was not configured with --lazy-startup.\n if (!lazyStartup) {\n void startZeroCache();\n }\n\n await processes.allWorkersReady();\n parent?.send(['ready', {ready: true}]);\n\n try {\n await runUntilKilled(\n lc,\n parent ?? process,\n new ZeroDispatcher(\n config,\n lc,\n {port, keepaliveTimeoutMs},\n startZeroCache,\n () => printStartupMessage(env),\n ),\n );\n } catch (err) {\n processes.logErrorAndExit(err, 'main');\n }\n\n await processes.done();\n}\n"],"mappings":";;;;;;;;;;;;;;AAeA,IAAM,oBAAoB;AAE1B,SAAS,oBAAoB,KAAwB;AACnD,KAAI,IAAI,uBAAuB,IAC7B;AAGF,cAAa,IACX,UAAU,UAAU,CAAC,QAAQ,OAAO,EAAE,aAAa,CAAC;mBAE9B,UAAU,CAAC,QAAQ,YAAY,EAAE,kCAAkC,CAAC,QACxF,UAAU,OAAO,6BAA6B,kBAAkB,IAAI,GACpE,KACH;;;;;;;;;AAUH,eAAsB,UACpB,QACA,KACe;CAGf,MAAM,MAAM,cAAc;EAAC;EAAK,yBAAyB;EAAK,CAAC;CAC/D,MAAM,KAAK,iBAAiB,KAAK,SAAS;CAG1C,MAAM,SAAS,oBAAoB,IAAI,KAAK,KADtB,MAAM,UAAU,GAAG,CACsB;CAC/D,MAAM,YAAY,IAAI,eAAe,IAAI,UAAU,QAAQ;CAE3D,MAAM,EAAC,MAAM,oBAAoB,gBAAe;CAChD,MAAM,gBAAgB,iBAAiB,OAAO;AAC9C,IAAG,OAAO,kBAAkB,CAAC,gBAAgB,KAAK,IAAI,gBAAgB,IAAI;EACxE,iBAAA;EACA,QAAQ,OAAO;EACf,KAAK,OAAO;EACZ,OAAO,OAAO;EACd,MAAM,OAAO;EACd,CAAC;CAEF,IAAI;CACJ,SAAS,iBAAkC;AACzC,MAAI,cAAc,KAAA,GAAW;GAC3B,MAAM,UAAU,YAAY,KAAK;AACjC,MAAG,OAAO,sBAAsB;GAEhC,MAAM,IAAK,YAAY,UAAkB;GACzC,MAAM,IAAI,YAAY,UAAU,IAAI,CACjC,KAAK,iBAAiB;AACrB,MAAE,QAAQ,EAAE;AACZ,OAAG,OAAO,qBAAqB,YAAY,KAAK,GAAG,QAAQ,MAAM;KACjE,CACD,KAAK,SAAS,EAAE,OAAO;AAE1B,aAAU,UAAU,GAAG,eAAe,aAAa;;AAErD,SAAO,UAAU;;AAInB,KAAI,CAAC,YACE,iBAAgB;AAGvB,OAAM,UAAU,iBAAiB;AACjC,SAAQ,KAAK,CAAC,SAAS,EAAC,OAAO,MAAK,CAAC,CAAC;AAEtC,KAAI;AACF,QAAM,eACJ,IACA,UAAU,SACV,IAAI,eACF,QACA,IACA;GAAC;GAAM;GAAmB,EAC1B,sBACM,oBAAoB,IAAI,CAC/B,CACF;UACM,KAAK;AACZ,YAAU,gBAAgB,KAAK,OAAO;;AAGxC,OAAM,UAAU,MAAM"}
1
+ {"version":3,"file":"run-worker.js","names":[],"sources":["../../../../../../zero-cache/src/server/runner/run-worker.ts"],"sourcesContent":["import '../../../../shared/src/dotenv.ts';\n\nimport {styleText} from 'node:util';\nimport {resolver, type Resolver} from '@rocicorp/resolver';\nimport {colorConsole} from '../../../../shared/src/logging.ts';\nimport {PROTOCOL_VERSION} from '../../../../zero-protocol/src/protocol-version.ts';\nimport {normalizeZeroConfig} from '../../config/normalize.ts';\nimport {getServerVersion, getZeroConfig} from '../../config/zero-config.ts';\nimport {ProcessManager, runUntilKilled} from '../../services/life-cycle.ts';\nimport {childWorker, type Worker} from '../../types/processes.ts';\nimport {createLogContext} from '../logging.ts';\nimport {MAIN_URL} from '../worker-urls.ts';\nimport {getTaskID} from './runtime.ts';\nimport {ZeroDispatcher} from './zero-dispatcher.ts';\n\nconst startupMessageEnv = 'ZERO_ENABLE_STARTUP_MESSAGE';\n\nfunction printStartupMessage(env: NodeJS.ProcessEnv) {\n if (env[startupMessageEnv] !== '1') {\n return;\n }\n\n colorConsole.log(\n `\\nBTW, ${styleText(['bold', 'cyan'], 'Cloud Zero')} ` +\n 'is now available - professional Zero hosting from the team that built it.\\n' +\n `Get started now: ${styleText(['blue', 'underline'], 'https://zero.rocicorp.dev/cloud')}\\n\\n` +\n styleText('dim', `Disable this message with ${startupMessageEnv}=0`) +\n '\\n',\n );\n}\n\n/**\n * Top-level `runner` entry point to the zero-cache. This layer is responsible for:\n * * runtime-based config normalization\n * * lazy startup\n * * serving /statsz\n * * auto-reset restarts (TODO)\n */\nexport async function runWorker(\n parent: Worker | null,\n env: NodeJS.ProcessEnv,\n): Promise<void> {\n // Note: Deprecation warnings are only emitted at this top-level parse;\n // they are suppressed when parsed in subprocesses.\n const cfg = getZeroConfig({env, emitDeprecationWarnings: true});\n const lc = createLogContext(cfg, 'runner');\n\n const defaultTaskID = await getTaskID(lc);\n const config = normalizeZeroConfig(lc, cfg, env, defaultTaskID);\n const processes = new ProcessManager(lc, parent ?? process);\n\n const {port, keepaliveTimeoutMs, lazyStartup} = config;\n const serverVersion = getServerVersion(config);\n lc.info?.(`starting server${!serverVersion ? '' : `@${serverVersion}`} `, {\n protocolVersion: PROTOCOL_VERSION,\n taskID: config.taskID,\n app: config.app,\n shard: config.shard,\n port: config.port,\n });\n\n let zeroCache: Resolver<Worker> | undefined;\n function startZeroCache(): Promise<Worker> {\n if (zeroCache === undefined) {\n const startMs = performance.now();\n lc.info?.('starting zero-cache');\n\n const r = (zeroCache = resolver<Worker>());\n const w = childWorker(MAIN_URL, env)\n .once('message', () => {\n r.resolve(w);\n lc.info?.(`zero-cache ready (${performance.now() - startMs} ms)`);\n })\n .once('error', r.reject);\n\n processes.addWorker(w, 'user-facing', 'zero-cache');\n }\n return zeroCache.promise;\n }\n\n // Eagerly start the zero-cache if it was not configured with --lazy-startup.\n if (!lazyStartup) {\n void startZeroCache();\n }\n\n await processes.allWorkersReady();\n parent?.send(['ready', {ready: true}]);\n\n try {\n await runUntilKilled(\n lc,\n parent ?? process,\n new ZeroDispatcher(\n config,\n lc,\n {port, keepaliveTimeoutMs},\n startZeroCache,\n () => printStartupMessage(env),\n ),\n );\n } catch (err) {\n processes.logErrorAndExit(err, 'main');\n }\n\n await processes.done();\n}\n"],"mappings":";;;;;;;;;;;;;;AAeA,IAAM,oBAAoB;AAE1B,SAAS,oBAAoB,KAAwB;CACnD,IAAI,IAAI,uBAAuB,KAC7B;CAGF,aAAa,IACX,UAAU,UAAU,CAAC,QAAQ,MAAM,GAAG,YAAY,EAAE;mBAE9B,UAAU,CAAC,QAAQ,WAAW,GAAG,iCAAiC,EAAE,QACxF,UAAU,OAAO,6BAA6B,kBAAkB,GAAG,IACnE,IACJ;AACF;;;;;;;;AASA,eAAsB,UACpB,QACA,KACe;CAGf,MAAM,MAAM,cAAc;EAAC;EAAK,yBAAyB;CAAI,CAAC;CAC9D,MAAM,KAAK,iBAAiB,KAAK,QAAQ;CAGzC,MAAM,SAAS,oBAAoB,IAAI,KAAK,KAAK,MADrB,UAAU,EAAE,CACsB;CAC9D,MAAM,YAAY,IAAI,eAAe,IAAI,UAAU,OAAO;CAE1D,MAAM,EAAC,MAAM,oBAAoB,gBAAe;CAChD,MAAM,gBAAgB,iBAAiB,MAAM;CAC7C,GAAG,OAAO,kBAAkB,CAAC,gBAAgB,KAAK,IAAI,gBAAgB,IAAI;EACxE,iBAAA;EACA,QAAQ,OAAO;EACf,KAAK,OAAO;EACZ,OAAO,OAAO;EACd,MAAM,OAAO;CACf,CAAC;CAED,IAAI;CACJ,SAAS,iBAAkC;EACzC,IAAI,cAAc,KAAA,GAAW;GAC3B,MAAM,UAAU,YAAY,IAAI;GAChC,GAAG,OAAO,qBAAqB;GAE/B,MAAM,IAAK,YAAY,SAAiB;GACxC,MAAM,IAAI,YAAY,UAAU,GAAG,EAChC,KAAK,iBAAiB;IACrB,EAAE,QAAQ,CAAC;IACX,GAAG,OAAO,qBAAqB,YAAY,IAAI,IAAI,QAAQ,KAAK;GAClE,CAAC,EACA,KAAK,SAAS,EAAE,MAAM;GAEzB,UAAU,UAAU,GAAG,eAAe,YAAY;EACpD;EACA,OAAO,UAAU;CACnB;CAGA,IAAI,CAAC,aACH,eAAoB;CAGtB,MAAM,UAAU,gBAAgB;CAChC,QAAQ,KAAK,CAAC,SAAS,EAAC,OAAO,KAAI,CAAC,CAAC;CAErC,IAAI;EACF,MAAM,eACJ,IACA,UAAU,SACV,IAAI,eACF,QACA,IACA;GAAC;GAAM;EAAkB,GACzB,sBACM,oBAAoB,GAAG,CAC/B,CACF;CACF,SAAS,KAAK;EACZ,UAAU,gBAAgB,KAAK,MAAM;CACvC;CAEA,MAAM,UAAU,KAAK;AACvB"}
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.js","names":[],"sources":["../../../../../../zero-cache/src/server/runner/runtime.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {nanoid} from 'nanoid';\nimport * as v from '../../../../shared/src/valita.ts';\n\n// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint-v4-response.html\nconst containerMetadataSchema = v.object({['TaskARN']: v.string()});\n\nexport async function getTaskID(lc: LogContext) {\n const containerURI = process.env['ECS_CONTAINER_METADATA_URI_V4'];\n if (containerURI) {\n try {\n const resp = await fetch(`${containerURI}`);\n const metadata = await resp.json();\n // Logged purely for debugging.\n lc.info?.(`Container metadata`, {metadata});\n } catch (e) {\n lc.warn?.('unable to lookup container metadata', e);\n }\n\n try {\n const resp = await fetch(`${containerURI}/task`);\n const metadata = v.parse(\n await resp.json(),\n containerMetadataSchema,\n 'passthrough',\n );\n lc.info?.(`Task metadata`, {metadata});\n const {TaskARN: taskID} = metadata;\n // Task ARN's are long, e.g.\n // \"arn:aws:ecs:us-east-1:712907626835:task/zbugs-prod-Cluster-vvNFcPUVpGHr/0042ea25bf534dc19975e26f61441737\"\n // We only care about the unique ID, i.e. the last path component.\n const lastSlash = taskID.lastIndexOf('/');\n return taskID.substring(lastSlash + 1); // works for lastSlash === -1 too\n } catch (e) {\n lc.warn?.('unable to determine task ID. falling back to random ID', e);\n }\n }\n return nanoid();\n}\n"],"mappings":";;;AAKA,IAAM,0BAA0B,eAAE,OAAO,GAAE,YAAY,eAAE,QAAQ,EAAC,CAAC;AAEnE,eAAsB,UAAU,IAAgB;CAC9C,MAAM,eAAe,QAAQ,IAAI;AACjC,KAAI,cAAc;AAChB,MAAI;GAEF,MAAM,WAAW,OADJ,MAAM,MAAM,GAAG,eAAe,EACf,MAAM;AAElC,MAAG,OAAO,sBAAsB,EAAC,UAAS,CAAC;WACpC,GAAG;AACV,MAAG,OAAO,uCAAuC,EAAE;;AAGrD,MAAI;GAEF,MAAM,WAAW,MACf,OAFW,MAAM,MAAM,GAAG,aAAa,OAAO,EAEnC,MAAM,EACjB,yBACA,cACD;AACD,MAAG,OAAO,iBAAiB,EAAC,UAAS,CAAC;GACtC,MAAM,EAAC,SAAS,WAAU;GAI1B,MAAM,YAAY,OAAO,YAAY,IAAI;AACzC,UAAO,OAAO,UAAU,YAAY,EAAE;WAC/B,GAAG;AACV,MAAG,OAAO,0DAA0D,EAAE;;;AAG1E,QAAO,QAAQ"}
1
+ {"version":3,"file":"runtime.js","names":[],"sources":["../../../../../../zero-cache/src/server/runner/runtime.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {nanoid} from 'nanoid';\nimport * as v from '../../../../shared/src/valita.ts';\n\n// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint-v4-response.html\nconst containerMetadataSchema = v.object({['TaskARN']: v.string()});\n\nexport async function getTaskID(lc: LogContext) {\n const containerURI = process.env['ECS_CONTAINER_METADATA_URI_V4'];\n if (containerURI) {\n try {\n const resp = await fetch(`${containerURI}`);\n const metadata = await resp.json();\n // Logged purely for debugging.\n lc.info?.(`Container metadata`, {metadata});\n } catch (e) {\n lc.warn?.('unable to lookup container metadata', e);\n }\n\n try {\n const resp = await fetch(`${containerURI}/task`);\n const metadata = v.parse(\n await resp.json(),\n containerMetadataSchema,\n 'passthrough',\n );\n lc.info?.(`Task metadata`, {metadata});\n const {TaskARN: taskID} = metadata;\n // Task ARN's are long, e.g.\n // \"arn:aws:ecs:us-east-1:712907626835:task/zbugs-prod-Cluster-vvNFcPUVpGHr/0042ea25bf534dc19975e26f61441737\"\n // We only care about the unique ID, i.e. the last path component.\n const lastSlash = taskID.lastIndexOf('/');\n return taskID.substring(lastSlash + 1); // works for lastSlash === -1 too\n } catch (e) {\n lc.warn?.('unable to determine task ID. falling back to random ID', e);\n }\n }\n return nanoid();\n}\n"],"mappings":";;;AAKA,IAAM,0BAA0B,eAAE,OAAO,GAAE,YAAY,eAAE,OAAO,EAAC,CAAC;AAElE,eAAsB,UAAU,IAAgB;CAC9C,MAAM,eAAe,QAAQ,IAAI;CACjC,IAAI,cAAc;EAChB,IAAI;GAEF,MAAM,WAAW,OAAM,MADJ,MAAM,GAAG,cAAc,GACd,KAAK;GAEjC,GAAG,OAAO,sBAAsB,EAAC,SAAQ,CAAC;EAC5C,SAAS,GAAG;GACV,GAAG,OAAO,uCAAuC,CAAC;EACpD;EAEA,IAAI;GAEF,MAAM,WAAW,MACf,OAAM,MAFW,MAAM,GAAG,aAAa,MAAM,GAElC,KAAK,GAChB,yBACA,aACF;GACA,GAAG,OAAO,iBAAiB,EAAC,SAAQ,CAAC;GACrC,MAAM,EAAC,SAAS,WAAU;GAI1B,MAAM,YAAY,OAAO,YAAY,GAAG;GACxC,OAAO,OAAO,UAAU,YAAY,CAAC;EACvC,SAAS,GAAG;GACV,GAAG,OAAO,0DAA0D,CAAC;EACvE;CACF;CACA,OAAO,OAAO;AAChB"}
@@ -1 +1 @@
1
- {"version":3,"file":"zero-dispatcher.js","names":["#getWorker","#onStart","#handoff"],"sources":["../../../../../../zero-cache/src/server/runner/zero-dispatcher.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {NormalizedZeroConfig} from '../../config/normalize.ts';\nimport {handleHeapzRequest} from '../../services/heapz.ts';\nimport {HttpService, type Options} from '../../services/http-service.ts';\nimport {handleStatzRequest} from '../../services/statz.ts';\nimport type {IncomingMessageSubset} from '../../types/http.ts';\nimport type {Worker} from '../../types/processes.ts';\nimport {\n installWebSocketHandoff,\n type HandoffSpec,\n} from '../../types/websocket-handoff.ts';\n\nexport class ZeroDispatcher extends HttpService {\n readonly id = 'zero-dispatcher';\n readonly #getWorker: () => Promise<Worker>;\n readonly #onStart: (() => void) | undefined;\n\n constructor(\n config: NormalizedZeroConfig,\n lc: LogContext,\n opts: Options,\n getWorker: () => Promise<Worker>,\n onStart?: () => void,\n ) {\n super(`zero-dispatcher`, lc, opts, fastify => {\n fastify.get('/statz', (req, res) =>\n handleStatzRequest(lc, config, req, res),\n );\n fastify.get('/heapz', (req, res) =>\n handleHeapzRequest(lc, config, req, res),\n );\n installWebSocketHandoff(lc, this.#handoff, fastify.server);\n });\n this.#getWorker = getWorker;\n this.#onStart = onStart;\n }\n\n protected override _onStart() {\n this.#onStart?.();\n }\n\n readonly #handoff = (\n _req: IncomingMessageSubset,\n dispatch: (h: HandoffSpec<string>) => void,\n onError: (error: unknown) => void,\n ) => {\n void this.#getWorker().then(\n sender => dispatch({payload: 'unused', sender}),\n onError,\n );\n };\n}\n"],"mappings":";;;;;AAYA,IAAa,iBAAb,cAAoC,YAAY;CAC9C,KAAc;CACd;CACA;CAEA,YACE,QACA,IACA,MACA,WACA,SACA;AACA,QAAM,mBAAmB,IAAI,OAAM,YAAW;AAC5C,WAAQ,IAAI,WAAW,KAAK,QAC1B,mBAAmB,IAAI,QAAQ,KAAK,IAAI,CACzC;AACD,WAAQ,IAAI,WAAW,KAAK,QAC1B,mBAAmB,IAAI,QAAQ,KAAK,IAAI,CACzC;AACD,2BAAwB,IAAI,MAAA,SAAe,QAAQ,OAAO;IAC1D;AACF,QAAA,YAAkB;AAClB,QAAA,UAAgB;;CAGlB,WAA8B;AAC5B,QAAA,WAAiB;;CAGnB,YACE,MACA,UACA,YACG;AACE,QAAA,WAAiB,CAAC,MACrB,WAAU,SAAS;GAAC,SAAS;GAAU;GAAO,CAAC,EAC/C,QACD"}
1
+ {"version":3,"file":"zero-dispatcher.js","names":["#getWorker","#onStart","#handoff"],"sources":["../../../../../../zero-cache/src/server/runner/zero-dispatcher.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {NormalizedZeroConfig} from '../../config/normalize.ts';\nimport {handleHeapzRequest} from '../../services/heapz.ts';\nimport {HttpService, type Options} from '../../services/http-service.ts';\nimport {handleStatzRequest} from '../../services/statz.ts';\nimport type {IncomingMessageSubset} from '../../types/http.ts';\nimport type {Worker} from '../../types/processes.ts';\nimport {\n installWebSocketHandoff,\n type HandoffSpec,\n} from '../../types/websocket-handoff.ts';\n\nexport class ZeroDispatcher extends HttpService {\n readonly id = 'zero-dispatcher';\n readonly #getWorker: () => Promise<Worker>;\n readonly #onStart: (() => void) | undefined;\n\n constructor(\n config: NormalizedZeroConfig,\n lc: LogContext,\n opts: Options,\n getWorker: () => Promise<Worker>,\n onStart?: () => void,\n ) {\n super(`zero-dispatcher`, lc, opts, fastify => {\n fastify.get('/statz', (req, res) =>\n handleStatzRequest(lc, config, req, res),\n );\n fastify.get('/heapz', (req, res) =>\n handleHeapzRequest(lc, config, req, res),\n );\n installWebSocketHandoff(lc, this.#handoff, fastify.server);\n });\n this.#getWorker = getWorker;\n this.#onStart = onStart;\n }\n\n protected override _onStart() {\n this.#onStart?.();\n }\n\n readonly #handoff = (\n _req: IncomingMessageSubset,\n dispatch: (h: HandoffSpec<string>) => void,\n onError: (error: unknown) => void,\n ) => {\n void this.#getWorker().then(\n sender => dispatch({payload: 'unused', sender}),\n onError,\n );\n };\n}\n"],"mappings":";;;;;AAYA,IAAa,iBAAb,cAAoC,YAAY;CAC9C,KAAc;CACd;CACA;CAEA,YACE,QACA,IACA,MACA,WACA,SACA;EACA,MAAM,mBAAmB,IAAI,OAAM,YAAW;GAC5C,QAAQ,IAAI,WAAW,KAAK,QAC1B,mBAAmB,IAAI,QAAQ,KAAK,GAAG,CACzC;GACA,QAAQ,IAAI,WAAW,KAAK,QAC1B,mBAAmB,IAAI,QAAQ,KAAK,GAAG,CACzC;GACA,wBAAwB,IAAI,KAAKE,UAAU,QAAQ,MAAM;EAC3D,CAAC;EACD,KAAKF,aAAa;EAClB,KAAKC,WAAW;CAClB;CAEA,WAA8B;EAC5B,KAAKA,WAAW;CAClB;CAEA,YACE,MACA,UACA,YACG;EACH,KAAUD,WAAW,EAAE,MACrB,WAAU,SAAS;GAAC,SAAS;GAAU;EAAM,CAAC,GAC9C,OACF;CACF;AACF"}
@@ -8,18 +8,21 @@ import { getServerContext } from "../config/server-context.js";
8
8
  import { initEventSink } from "../observability/events.js";
9
9
  import { startOtelAuto } from "./otel-start.js";
10
10
  import { ShadowSyncService } from "../services/shadow-sync/shadow-sync-service.js";
11
+ import { LogContext, consoleLogSink } from "@rocicorp/logger";
11
12
  //#region ../zero-cache/src/server/shadow-syncer.ts
12
13
  var MS_PER_HOUR = 1e3 * 60 * 60;
14
+ var lc = new LogContext("info", {}, consoleLogSink);
13
15
  function runWorker(parent, env, ...argv) {
14
16
  const config = getNormalizedZeroConfig({
15
17
  env,
16
18
  argv
17
19
  });
18
20
  startOtelAuto(createLogContext(config, "shadow-syncer", 0, false), "shadow-syncer", 0);
19
- const lc = createLogContext(config, "shadow-syncer");
21
+ lc = createLogContext(config, "shadow-syncer");
20
22
  initEventSink(lc, config);
21
23
  const { shadowSync, upstream, initialSync } = config;
22
- const service = new ShadowSyncService(lc, getShardConfig(config), upstream.db, getServerContext(config), {
24
+ const shard = getShardConfig(config);
25
+ const service = new ShadowSyncService(lc, shard, upstream.db, getServerContext(config), {
23
26
  intervalMs: shadowSync.intervalHours * MS_PER_HOUR,
24
27
  sampleRate: shadowSync.sampleRate,
25
28
  maxRowsPerTable: shadowSync.maxRowsPerTable,
@@ -28,7 +31,7 @@ function runWorker(parent, env, ...argv) {
28
31
  parent.send(["ready", { ready: true }]);
29
32
  return runUntilKilled(lc, parent, service);
30
33
  }
31
- if (!singleProcessMode()) exitAfter(() => runWorker(must(parentWorker), process.env, ...process.argv.slice(2)));
34
+ if (!singleProcessMode()) exitAfter(lc, () => runWorker(must(parentWorker), process.env, ...process.argv.slice(2)));
32
35
  //#endregion
33
36
  export { runWorker as default };
34
37
 
@@ -1 +1 @@
1
- {"version":3,"file":"shadow-syncer.js","names":[],"sources":["../../../../../zero-cache/src/server/shadow-syncer.ts"],"sourcesContent":["import {must} from '../../../shared/src/must.ts';\nimport {getServerContext} from '../config/server-context.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {exitAfter, runUntilKilled} from '../services/life-cycle.ts';\nimport {ShadowSyncService} from '../services/shadow-sync/shadow-sync-service.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {getShardConfig} from '../types/shards.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\n\nconst MS_PER_HOUR = 1000 * 60 * 60;\n\nexport default function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...argv: string[]\n): Promise<void> {\n const config = getNormalizedZeroConfig({env, argv});\n\n startOtelAuto(\n createLogContext(config, 'shadow-syncer', 0, false),\n 'shadow-syncer',\n 0,\n );\n const lc = createLogContext(config, 'shadow-syncer');\n initEventSink(lc, config);\n\n const {shadowSync, upstream, initialSync} = config;\n const shard = getShardConfig(config);\n const service = new ShadowSyncService(\n lc,\n shard,\n upstream.db,\n getServerContext(config),\n {\n intervalMs: shadowSync.intervalHours * MS_PER_HOUR,\n sampleRate: shadowSync.sampleRate,\n maxRowsPerTable: shadowSync.maxRowsPerTable,\n textCopy: initialSync.textCopy,\n },\n );\n\n parent.send(['ready', {ready: true}]);\n\n return runUntilKilled(lc, parent, service);\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(() =>\n runWorker(must(parentWorker), process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":";;;;;;;;;;;AAeA,IAAM,cAAc,MAAO,KAAK;AAEhC,SAAwB,UACtB,QACA,KACA,GAAG,MACY;CACf,MAAM,SAAS,wBAAwB;EAAC;EAAK;EAAK,CAAC;AAEnD,eACE,iBAAiB,QAAQ,iBAAiB,GAAG,MAAM,EACnD,iBACA,EACD;CACD,MAAM,KAAK,iBAAiB,QAAQ,gBAAgB;AACpD,eAAc,IAAI,OAAO;CAEzB,MAAM,EAAC,YAAY,UAAU,gBAAe;CAE5C,MAAM,UAAU,IAAI,kBAClB,IAFY,eAAe,OAAO,EAIlC,SAAS,IACT,iBAAiB,OAAO,EACxB;EACE,YAAY,WAAW,gBAAgB;EACvC,YAAY,WAAW;EACvB,iBAAiB,WAAW;EAC5B,UAAU,YAAY;EACvB,CACF;AAED,QAAO,KAAK,CAAC,SAAS,EAAC,OAAO,MAAK,CAAC,CAAC;AAErC,QAAO,eAAe,IAAI,QAAQ,QAAQ;;AAI5C,IAAI,CAAC,mBAAmB,CACjB,iBACH,UAAU,KAAK,aAAa,EAAE,QAAQ,KAAK,GAAG,QAAQ,KAAK,MAAM,EAAE,CAAC,CACrE"}
1
+ {"version":3,"file":"shadow-syncer.js","names":[],"sources":["../../../../../zero-cache/src/server/shadow-syncer.ts"],"sourcesContent":["import {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {must} from '../../../shared/src/must.ts';\nimport {getServerContext} from '../config/server-context.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {exitAfter, runUntilKilled} from '../services/life-cycle.ts';\nimport {ShadowSyncService} from '../services/shadow-sync/shadow-sync-service.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {getShardConfig} from '../types/shards.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\n\nconst MS_PER_HOUR = 1000 * 60 * 60;\n\n// Default LogContext, overridden in runWorker\nlet lc = new LogContext('info', {}, consoleLogSink);\n\nexport default function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...argv: string[]\n): Promise<void> {\n const config = getNormalizedZeroConfig({env, argv});\n\n startOtelAuto(\n createLogContext(config, 'shadow-syncer', 0, false),\n 'shadow-syncer',\n 0,\n );\n lc = createLogContext(config, 'shadow-syncer');\n initEventSink(lc, config);\n\n const {shadowSync, upstream, initialSync} = config;\n const shard = getShardConfig(config);\n const service = new ShadowSyncService(\n lc,\n shard,\n upstream.db,\n getServerContext(config),\n {\n intervalMs: shadowSync.intervalHours * MS_PER_HOUR,\n sampleRate: shadowSync.sampleRate,\n maxRowsPerTable: shadowSync.maxRowsPerTable,\n textCopy: initialSync.textCopy,\n },\n );\n\n parent.send(['ready', {ready: true}]);\n\n return runUntilKilled(lc, parent, service);\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(lc, () =>\n runWorker(must(parentWorker), process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":";;;;;;;;;;;;AAgBA,IAAM,cAAc,MAAO,KAAK;AAGhC,IAAI,KAAK,IAAI,WAAW,QAAQ,CAAC,GAAG,cAAc;AAElD,SAAwB,UACtB,QACA,KACA,GAAG,MACY;CACf,MAAM,SAAS,wBAAwB;EAAC;EAAK;CAAI,CAAC;CAElD,cACE,iBAAiB,QAAQ,iBAAiB,GAAG,KAAK,GAClD,iBACA,CACF;CACA,KAAK,iBAAiB,QAAQ,eAAe;CAC7C,cAAc,IAAI,MAAM;CAExB,MAAM,EAAC,YAAY,UAAU,gBAAe;CAC5C,MAAM,QAAQ,eAAe,MAAM;CACnC,MAAM,UAAU,IAAI,kBAClB,IACA,OACA,SAAS,IACT,iBAAiB,MAAM,GACvB;EACE,YAAY,WAAW,gBAAgB;EACvC,YAAY,WAAW;EACvB,iBAAiB,WAAW;EAC5B,UAAU,YAAY;CACxB,CACF;CAEA,OAAO,KAAK,CAAC,SAAS,EAAC,OAAO,KAAI,CAAC,CAAC;CAEpC,OAAO,eAAe,IAAI,QAAQ,OAAO;AAC3C;AAGA,IAAI,CAAC,kBAAkB,GACrB,UAAe,UACb,UAAU,KAAK,YAAY,GAAG,QAAQ,KAAK,GAAG,QAAQ,KAAK,MAAM,CAAC,CAAC,CACrE"}
@@ -1 +1 @@
1
- {"version":3,"file":"syncer.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/syncer.ts"],"names":[],"mappings":"AA+BA,OAAO,EAGL,KAAK,MAAM,EACZ,MAAM,uBAAuB,CAAC;AAgC/B,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,GAAG,IAAI,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,IAAI,CAAC,CAsMf"}
1
+ {"version":3,"file":"syncer.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/syncer.ts"],"names":[],"mappings":"AAgCA,OAAO,EAGL,KAAK,MAAM,EACZ,MAAM,uBAAuB,CAAC;AAmC/B,wBAA8B,SAAS,CACrC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,GAAG,IAAI,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,IAAI,CAAC,CAuMf"}
@@ -8,7 +8,7 @@ import { getNormalizedZeroConfig } from "../config/zero-config.js";
8
8
  import { Snapshotter } from "../services/view-syncer/snapshotter.js";
9
9
  import { PipelineDriver } from "../services/view-syncer/pipeline-driver.js";
10
10
  import { randInt } from "../../../shared/src/rand.js";
11
- import { pgClient } from "../types/pg.js";
11
+ import { connectPgClient } from "../types/pg.js";
12
12
  import { exitAfter, runUntilKilled } from "../services/life-cycle.js";
13
13
  import { createLogContext } from "./logging.js";
14
14
  import { warmupConnections } from "../db/warmup.js";
@@ -27,6 +27,7 @@ import { ViewSyncerService } from "../services/view-syncer/view-syncer.js";
27
27
  import { Syncer } from "../workers/syncer.js";
28
28
  import { InspectorDelegate } from "./inspector-delegate.js";
29
29
  import { isPriorityOpRunning, runPriorityOp } from "./priority-op.js";
30
+ import { LogContext, consoleLogSink } from "@rocicorp/logger";
30
31
  import { pid } from "node:process";
31
32
  import { randomUUID } from "node:crypto";
32
33
  import { tmpdir } from "node:os";
@@ -45,7 +46,8 @@ function getCustomQueryConfig(config) {
45
46
  forwardCookies: queryConfig.forwardCookies ?? false
46
47
  };
47
48
  }
48
- function runWorker(parent, env, ...args) {
49
+ var lc = new LogContext("info", {}, consoleLogSink);
50
+ async function runWorker(parent, env, ...args) {
49
51
  assert(args.length >= 2, `expected [fileMode, workerIndex, ...flags]`);
50
52
  const fileMode = parse(args[0], replicaFileModeSchema);
51
53
  const workerIndex = Number(args[1]);
@@ -54,13 +56,13 @@ function runWorker(parent, env, ...args) {
54
56
  argv: args.slice(2)
55
57
  });
56
58
  startOtelAuto(createLogContext(config, "syncer", workerIndex, false), "syncer", workerIndex);
57
- const lc = createLogContext(config, "syncer", workerIndex);
59
+ lc = createLogContext(config, "syncer", workerIndex);
58
60
  initEventSink(lc, config);
59
61
  const { cvr, upstream, enableCrudMutations } = config;
60
62
  const replicaFile = replicaFileName(config.replica.file, fileMode);
61
63
  lc.debug?.(`running view-syncer on ${replicaFile}`);
62
- const cvrDB = pgClient(lc, cvr.db, `sync-worker-${pid}-cvr`, { max: must(cvr.maxConnsPerWorker, "cvr.maxConnsPerWorker must be set") });
63
- const upstreamDB = enableCrudMutations ? pgClient(lc, upstream.db, `sync-worker-${pid}-upstream`, { max: must(upstream.maxConnsPerWorker, "upstream.maxConnsPerWorker must be set") }) : void 0;
64
+ const cvrDB = await connectPgClient(lc, cvr.db, `sync-worker-${pid}-cvr`, { max: must(cvr.maxConnsPerWorker, "cvr.maxConnsPerWorker must be set") });
65
+ const upstreamDB = enableCrudMutations && upstream.type === "pg" ? await connectPgClient(lc, upstream.db, `sync-worker-${pid}-upstream`, { max: must(upstream.maxConnsPerWorker, "upstream.maxConnsPerWorker must be set") }) : void 0;
64
66
  const dbWarmup = Promise.allSettled([warmupConnections(lc, cvrDB, "cvr"), upstreamDB ? warmupConnections(lc, upstreamDB, "upstream") : promiseVoid]);
65
67
  const tmpDir = config.storageDBTmpDir ?? tmpdir();
66
68
  const operatorStorage = DatabaseStorage.create(lc, path.join(tmpDir, `sync-worker-${randomUUID()}`));
@@ -105,7 +107,7 @@ function runWorker(parent, env, ...args) {
105
107
  dbWarmup.then(() => parent.send(["ready", { ready: true }]));
106
108
  return runUntilKilled(lc, parent, syncer);
107
109
  }
108
- if (!singleProcessMode()) exitAfter(() => runWorker(must(parentWorker), process.env, ...process.argv.slice(2)));
110
+ if (!singleProcessMode()) exitAfter(lc, () => runWorker(must(parentWorker), process.env, ...process.argv.slice(2)));
109
111
  //#endregion
110
112
  export { runWorker as default };
111
113
 
@@ -1 +1 @@
1
- {"version":3,"file":"syncer.js","names":[],"sources":["../../../../../zero-cache/src/server/syncer.ts"],"sourcesContent":["import {randomUUID} from 'node:crypto';\nimport {tmpdir} from 'node:os';\nimport path from 'node:path';\nimport {pid} from 'node:process';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {randInt} from '../../../shared/src/rand.ts';\nimport {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {DatabaseStorage} from '../../../zqlite/src/database-storage.ts';\nimport type {ValidateLegacyJWT} from '../auth/auth.ts';\nimport {tokenConfigOptions, verifyToken} from '../auth/jwt.ts';\nimport type {NormalizedZeroConfig} from '../config/normalize.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {CustomQueryTransformer} from '../custom-queries/transform-query.ts';\nimport {warmupConnections} from '../db/warmup.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {exitAfter, runUntilKilled} from '../services/life-cycle.ts';\nimport {MutagenService} from '../services/mutagen/mutagen.ts';\nimport {PusherService} from '../services/mutagen/pusher.ts';\nimport type {ReplicaState} from '../services/replicator/replicator.ts';\nimport {\n type ConnectionContextManager,\n ConnectionContextManagerImpl,\n} from '../services/view-syncer/connection-context-manager.ts';\nimport type {DrainCoordinator} from '../services/view-syncer/drain-coordinator.ts';\nimport {PipelineDriver} from '../services/view-syncer/pipeline-driver.ts';\nimport {Snapshotter} from '../services/view-syncer/snapshotter.ts';\nimport {ViewSyncerService} from '../services/view-syncer/view-syncer.ts';\nimport {ProtocolErrorWithLevel} from '../types/error-with-level.ts';\nimport {pgClient} from '../types/pg.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {getShardID} from '../types/shards.ts';\nimport type {Subscription} from '../types/subscription.ts';\nimport {replicaFileModeSchema, replicaFileName} from '../workers/replicator.ts';\nimport {Syncer} from '../workers/syncer.ts';\nimport {startAnonymousTelemetry} from './anonymous-otel-start.ts';\nimport {InspectorDelegate} from './inspector-delegate.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\nimport {isPriorityOpRunning, runPriorityOp} from './priority-op.ts';\n\nfunction randomID() {\n return randInt(1, Number.MAX_SAFE_INTEGER).toString(36);\n}\n\nfunction getCustomQueryConfig(\n config: Pick<NormalizedZeroConfig, 'query' | 'getQueries'>,\n) {\n const queryConfig = config.query?.url ? config.query : config.getQueries;\n\n if (!queryConfig?.url) {\n return undefined;\n }\n\n return {\n url: queryConfig.url,\n apiKey: queryConfig.apiKey,\n allowedClientHeaders: queryConfig.allowedClientHeaders,\n forwardCookies: queryConfig.forwardCookies ?? false,\n };\n}\n\nexport default function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...args: string[]\n): Promise<void> {\n assert(args.length >= 2, `expected [fileMode, workerIndex, ...flags]`);\n const fileMode = v.parse(args[0], replicaFileModeSchema);\n const workerIndex = Number(args[1]);\n const config = getNormalizedZeroConfig({env, argv: args.slice(2)});\n\n startOtelAuto(\n createLogContext(config, 'syncer', workerIndex, false),\n 'syncer',\n workerIndex,\n );\n const lc = createLogContext(config, 'syncer', workerIndex);\n initEventSink(lc, config);\n\n const {cvr, upstream, enableCrudMutations} = config;\n\n const replicaFile = replicaFileName(config.replica.file, fileMode);\n lc.debug?.(`running view-syncer on ${replicaFile}`);\n\n const cvrDB = pgClient(lc, cvr.db, `sync-worker-${pid}-cvr`, {\n max: must(cvr.maxConnsPerWorker, 'cvr.maxConnsPerWorker must be set'),\n });\n\n const upstreamDB = enableCrudMutations\n ? pgClient(lc, upstream.db, `sync-worker-${pid}-upstream`, {\n max: must(\n upstream.maxConnsPerWorker,\n 'upstream.maxConnsPerWorker must be set',\n ),\n })\n : undefined;\n\n const dbWarmup = Promise.allSettled([\n warmupConnections(lc, cvrDB, 'cvr'),\n upstreamDB ? warmupConnections(lc, upstreamDB, 'upstream') : promiseVoid,\n ]);\n\n const tmpDir = config.storageDBTmpDir ?? tmpdir();\n const operatorStorage = DatabaseStorage.create(\n lc,\n path.join(tmpDir, `sync-worker-${randomUUID()}`),\n );\n const writeAuthzStorage = DatabaseStorage.create(\n lc,\n path.join(tmpDir, `mutagen-${randomUUID()}`),\n );\n\n const shard = getShardID(config);\n const customQueryConfig = getCustomQueryConfig(config);\n const pushConfig =\n config.push.url === undefined && config.mutate.url === undefined\n ? undefined\n : {\n ...config.push,\n ...config.mutate,\n url: must(\n config.push.url ?? config.mutate.url,\n 'No push or mutate URL configured',\n ),\n };\n\n /** @deprecated used in JWT validation */\n let validateLegacyJWT: ValidateLegacyJWT | undefined = undefined;\n\n const tokenOptions = tokenConfigOptions(config.auth ?? {});\n if (tokenOptions.length === 1) {\n validateLegacyJWT = async (token, {userID}) => {\n if (!userID) {\n throw new ProtocolErrorWithLevel(\n {\n kind: 'Unauthorized',\n message: 'UserID is required for JWT validation.',\n origin: 'zeroCache',\n },\n 'warn',\n );\n }\n\n const decoded = await verifyToken(config.auth, token, {\n subject: userID,\n ...(config.auth?.issuer && {issuer: config.auth.issuer}),\n ...(config.auth?.audience && {\n audience: config.auth.audience,\n }),\n });\n return {\n type: 'jwt',\n raw: token,\n decoded,\n };\n };\n }\n\n const viewSyncerFactory = (\n id: string,\n sub: Subscription<ReplicaState>,\n drainCoordinator: DrainCoordinator,\n ) => {\n const logger = lc\n .withContext('component', 'view-syncer')\n .withContext('clientGroupID', id)\n .withContext('instance', randomID());\n\n const customQueryTransformer =\n customQueryConfig && new CustomQueryTransformer(logger, shard);\n const connContextManager = new ConnectionContextManagerImpl(\n logger,\n config.auth.revalidateIntervalSeconds,\n config.auth.retransformIntervalSeconds,\n customQueryConfig,\n pushConfig,\n validateLegacyJWT,\n );\n\n lc.debug?.(\n `creating view syncer. Query Planner Enabled: ${config.enableQueryPlanner}`,\n );\n\n const inspectorDelegate = new InspectorDelegate(customQueryTransformer);\n\n const priorityOpRunningYieldThresholdMs = Math.max(\n config.yieldThresholdMs / 4,\n 2,\n );\n const normalYieldThresholdMs = Math.max(config.yieldThresholdMs, 2);\n\n return new ViewSyncerService(\n config,\n logger,\n shard,\n config.taskID,\n id,\n cvrDB,\n new PipelineDriver(\n logger,\n config.log,\n new Snapshotter(logger, replicaFile, shard),\n shard,\n operatorStorage.createClientGroupStorage(id),\n id,\n inspectorDelegate,\n () =>\n isPriorityOpRunning()\n ? priorityOpRunningYieldThresholdMs\n : normalYieldThresholdMs,\n config.enableQueryPlanner,\n config,\n ),\n sub,\n drainCoordinator,\n config.log.slowHydrateThreshold,\n inspectorDelegate,\n connContextManager,\n customQueryTransformer,\n runPriorityOp,\n );\n };\n\n const mutagenFactory = upstreamDB\n ? (id: string) =>\n new MutagenService(\n lc\n .withContext('component', 'mutagen')\n .withContext('clientGroupID', id),\n shard,\n id,\n upstreamDB,\n config,\n writeAuthzStorage,\n )\n : undefined;\n\n const pusherFactory =\n pushConfig === undefined\n ? undefined\n : (id: string, connContextManager: ConnectionContextManager) =>\n new PusherService(\n config,\n lc.withContext('clientGroupID', id),\n id,\n connContextManager,\n );\n\n const syncer = new Syncer(\n lc,\n config,\n viewSyncerFactory,\n mutagenFactory,\n pusherFactory,\n parent,\n validateLegacyJWT,\n );\n\n startAnonymousTelemetry(lc, config);\n\n void dbWarmup.then(() => parent.send(['ready', {ready: true}]));\n\n return runUntilKilled(lc, parent, syncer);\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(() =>\n runWorker(must(parentWorker), process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"syncer.js","names":[],"sources":["../../../../../zero-cache/src/server/syncer.ts"],"sourcesContent":["import {randomUUID} from 'node:crypto';\nimport {tmpdir} from 'node:os';\nimport path from 'node:path';\nimport {pid} from 'node:process';\nimport {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {randInt} from '../../../shared/src/rand.ts';\nimport {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {DatabaseStorage} from '../../../zqlite/src/database-storage.ts';\nimport type {ValidateLegacyJWT} from '../auth/auth.ts';\nimport {tokenConfigOptions, verifyToken} from '../auth/jwt.ts';\nimport type {NormalizedZeroConfig} from '../config/normalize.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {CustomQueryTransformer} from '../custom-queries/transform-query.ts';\nimport {warmupConnections} from '../db/warmup.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {exitAfter, runUntilKilled} from '../services/life-cycle.ts';\nimport {MutagenService} from '../services/mutagen/mutagen.ts';\nimport {PusherService} from '../services/mutagen/pusher.ts';\nimport type {ReplicaState} from '../services/replicator/replicator.ts';\nimport {\n type ConnectionContextManager,\n ConnectionContextManagerImpl,\n} from '../services/view-syncer/connection-context-manager.ts';\nimport type {DrainCoordinator} from '../services/view-syncer/drain-coordinator.ts';\nimport {PipelineDriver} from '../services/view-syncer/pipeline-driver.ts';\nimport {Snapshotter} from '../services/view-syncer/snapshotter.ts';\nimport {ViewSyncerService} from '../services/view-syncer/view-syncer.ts';\nimport {ProtocolErrorWithLevel} from '../types/error-with-level.ts';\nimport {connectPgClient} from '../types/pg.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {getShardID} from '../types/shards.ts';\nimport type {Subscription} from '../types/subscription.ts';\nimport {replicaFileModeSchema, replicaFileName} from '../workers/replicator.ts';\nimport {Syncer} from '../workers/syncer.ts';\nimport {startAnonymousTelemetry} from './anonymous-otel-start.ts';\nimport {InspectorDelegate} from './inspector-delegate.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\nimport {isPriorityOpRunning, runPriorityOp} from './priority-op.ts';\n\nfunction randomID() {\n return randInt(1, Number.MAX_SAFE_INTEGER).toString(36);\n}\n\nfunction getCustomQueryConfig(\n config: Pick<NormalizedZeroConfig, 'query' | 'getQueries'>,\n) {\n const queryConfig = config.query?.url ? config.query : config.getQueries;\n\n if (!queryConfig?.url) {\n return undefined;\n }\n\n return {\n url: queryConfig.url,\n apiKey: queryConfig.apiKey,\n allowedClientHeaders: queryConfig.allowedClientHeaders,\n forwardCookies: queryConfig.forwardCookies ?? false,\n };\n}\n\n// Default LogContext, overridden in runWorker\nlet lc = new LogContext('info', {}, consoleLogSink);\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...args: string[]\n): Promise<void> {\n assert(args.length >= 2, `expected [fileMode, workerIndex, ...flags]`);\n const fileMode = v.parse(args[0], replicaFileModeSchema);\n const workerIndex = Number(args[1]);\n const config = getNormalizedZeroConfig({env, argv: args.slice(2)});\n\n startOtelAuto(\n createLogContext(config, 'syncer', workerIndex, false),\n 'syncer',\n workerIndex,\n );\n lc = createLogContext(config, 'syncer', workerIndex);\n initEventSink(lc, config);\n\n const {cvr, upstream, enableCrudMutations} = config;\n\n const replicaFile = replicaFileName(config.replica.file, fileMode);\n lc.debug?.(`running view-syncer on ${replicaFile}`);\n\n const cvrDB = await connectPgClient(lc, cvr.db, `sync-worker-${pid}-cvr`, {\n max: must(cvr.maxConnsPerWorker, 'cvr.maxConnsPerWorker must be set'),\n });\n\n const upstreamDB =\n enableCrudMutations && upstream.type === 'pg'\n ? await connectPgClient(lc, upstream.db, `sync-worker-${pid}-upstream`, {\n max: must(\n upstream.maxConnsPerWorker,\n 'upstream.maxConnsPerWorker must be set',\n ),\n })\n : undefined;\n\n const dbWarmup = Promise.allSettled([\n warmupConnections(lc, cvrDB, 'cvr'),\n upstreamDB ? warmupConnections(lc, upstreamDB, 'upstream') : promiseVoid,\n ]);\n\n const tmpDir = config.storageDBTmpDir ?? tmpdir();\n const operatorStorage = DatabaseStorage.create(\n lc,\n path.join(tmpDir, `sync-worker-${randomUUID()}`),\n );\n const writeAuthzStorage = DatabaseStorage.create(\n lc,\n path.join(tmpDir, `mutagen-${randomUUID()}`),\n );\n\n const shard = getShardID(config);\n const customQueryConfig = getCustomQueryConfig(config);\n const pushConfig =\n config.push.url === undefined && config.mutate.url === undefined\n ? undefined\n : {\n ...config.push,\n ...config.mutate,\n url: must(\n config.push.url ?? config.mutate.url,\n 'No push or mutate URL configured',\n ),\n };\n\n /** @deprecated used in JWT validation */\n let validateLegacyJWT: ValidateLegacyJWT | undefined = undefined;\n\n const tokenOptions = tokenConfigOptions(config.auth ?? {});\n if (tokenOptions.length === 1) {\n validateLegacyJWT = async (token, {userID}) => {\n if (!userID) {\n throw new ProtocolErrorWithLevel(\n {\n kind: 'Unauthorized',\n message: 'UserID is required for JWT validation.',\n origin: 'zeroCache',\n },\n 'warn',\n );\n }\n\n const decoded = await verifyToken(config.auth, token, {\n subject: userID,\n ...(config.auth?.issuer && {issuer: config.auth.issuer}),\n ...(config.auth?.audience && {\n audience: config.auth.audience,\n }),\n });\n return {\n type: 'jwt',\n raw: token,\n decoded,\n };\n };\n }\n\n const viewSyncerFactory = (\n id: string,\n sub: Subscription<ReplicaState>,\n drainCoordinator: DrainCoordinator,\n ) => {\n const logger = lc\n .withContext('component', 'view-syncer')\n .withContext('clientGroupID', id)\n .withContext('instance', randomID());\n\n const customQueryTransformer =\n customQueryConfig && new CustomQueryTransformer(logger, shard);\n const connContextManager = new ConnectionContextManagerImpl(\n logger,\n config.auth.revalidateIntervalSeconds,\n config.auth.retransformIntervalSeconds,\n customQueryConfig,\n pushConfig,\n validateLegacyJWT,\n );\n\n lc.debug?.(\n `creating view syncer. Query Planner Enabled: ${config.enableQueryPlanner}`,\n );\n\n const inspectorDelegate = new InspectorDelegate(customQueryTransformer);\n\n const priorityOpRunningYieldThresholdMs = Math.max(\n config.yieldThresholdMs / 4,\n 2,\n );\n const normalYieldThresholdMs = Math.max(config.yieldThresholdMs, 2);\n\n return new ViewSyncerService(\n config,\n logger,\n shard,\n config.taskID,\n id,\n cvrDB,\n new PipelineDriver(\n logger,\n config.log,\n new Snapshotter(logger, replicaFile, shard),\n shard,\n operatorStorage.createClientGroupStorage(id),\n id,\n inspectorDelegate,\n () =>\n isPriorityOpRunning()\n ? priorityOpRunningYieldThresholdMs\n : normalYieldThresholdMs,\n config.enableQueryPlanner,\n config,\n ),\n sub,\n drainCoordinator,\n config.log.slowHydrateThreshold,\n inspectorDelegate,\n connContextManager,\n customQueryTransformer,\n runPriorityOp,\n );\n };\n\n const mutagenFactory = upstreamDB\n ? (id: string) =>\n new MutagenService(\n lc\n .withContext('component', 'mutagen')\n .withContext('clientGroupID', id),\n shard,\n id,\n upstreamDB,\n config,\n writeAuthzStorage,\n )\n : undefined;\n\n const pusherFactory =\n pushConfig === undefined\n ? undefined\n : (id: string, connContextManager: ConnectionContextManager) =>\n new PusherService(\n config,\n lc.withContext('clientGroupID', id),\n id,\n connContextManager,\n );\n\n const syncer = new Syncer(\n lc,\n config,\n viewSyncerFactory,\n mutagenFactory,\n pusherFactory,\n parent,\n validateLegacyJWT,\n );\n\n startAnonymousTelemetry(lc, config);\n\n void dbWarmup.then(() => parent.send(['ready', {ready: true}]));\n\n return runUntilKilled(lc, parent, syncer);\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(lc, () =>\n runWorker(must(parentWorker), process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"worker-dispatcher.js","names":["#lc","#state"],"sources":["../../../../../zero-cache/src/server/worker-dispatcher.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport UrlPattern from 'url-pattern';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {h32} from '../../../shared/src/hash.ts';\nimport {getOrCreateGauge} from '../observability/metrics.ts';\nimport {RunningState} from '../services/running-state.ts';\nimport type {Service} from '../services/service.ts';\nimport type {IncomingMessageSubset} from '../types/http.ts';\nimport type {Worker} from '../types/processes.ts';\nimport {installWebSocketHandoff} from '../types/websocket-handoff.ts';\nimport {getConnectParams} from '../workers/connect-params.ts';\n\nexport class WorkerDispatcher implements Service {\n readonly id = 'worker-dispatcher';\n readonly #lc: LogContext;\n\n readonly #state = new RunningState(this.id);\n\n constructor(\n lc: LogContext,\n taskID: string,\n parent: Worker,\n syncers: Worker[],\n mutator: Worker | undefined,\n changeStreamer: Worker | undefined,\n ) {\n this.#lc = lc;\n\n function connectParams(req: IncomingMessageSubset) {\n const {headers, url: u} = req;\n const url = new URL(u ?? '', 'http://unused/');\n const path = parsePath(url);\n if (!path) {\n throw new Error(`Invalid URL: ${u}`);\n }\n const version = Number(path.version);\n if (Number.isNaN(version)) {\n throw new Error(`Invalid version: ${u}`);\n }\n const {params, error} = getConnectParams(version, url, headers);\n if (error !== null) {\n throw new Error(error);\n }\n return params;\n }\n\n const handlePush = (req: IncomingMessageSubset) => {\n assert(\n mutator !== undefined,\n 'Received a push for a custom mutation but no `push.url` was configured.',\n );\n return {payload: connectParams(req), sender: mutator};\n };\n\n let maxProtocolVersion = 0;\n getOrCreateGauge(\n 'sync',\n 'max-protocol-version',\n 'Latest sync protocol version from a connecting client',\n ).addCallback(result => {\n if (maxProtocolVersion) {\n result.observe(maxProtocolVersion);\n }\n });\n\n const handleSync = (req: IncomingMessageSubset) => {\n assert(syncers.length, 'Received a sync request with no sync workers.');\n const params = connectParams(req);\n const {clientGroupID, protocolVersion} = params;\n maxProtocolVersion = Math.max(maxProtocolVersion, protocolVersion);\n\n // Include the TaskID when hash-bucketting the client group to the sync\n // worker. This diversifies the distribution of client groups (across\n // workers) for different tasks, so that if one task sheds connections\n // from its most heavily loaded sync worker(s), those client groups will\n // be distributed uniformly across workers on the receiving task(s).\n const syncer = h32(taskID + '/' + clientGroupID) % syncers.length;\n\n lc.debug?.(`connecting ${clientGroupID} to syncer ${syncer}`);\n return {payload: params, sender: syncers[syncer]};\n };\n\n const handleChangeStream = (req: IncomingMessageSubset) => {\n // Note: The change-streamer is generally not dispatched via the main\n // port, and in particular, should *not* be accessible via that\n // port in single-node mode. However, this plumbing is maintained\n // for the purpose of allowing --lazy-startup of the\n // replication-manager as a possible future feature.\n assert(\n syncers.length === 0 && mutator === undefined,\n 'Dispatch to the change-streamer via the main port ' +\n 'is only allowed in multi-node mode',\n );\n assert(\n changeStreamer,\n 'Received a change-streamer request without a change-streamer worker',\n );\n const url = new URL(req.url ?? '', 'http://unused/');\n const path = parsePath(url);\n if (!path) {\n throw new Error(`Invalid URL: ${req.url}`);\n }\n\n return {\n payload: path.action,\n sender: changeStreamer,\n };\n };\n\n // handoff messages from this ZeroDispatcher to the appropriate worker (pool).\n installWebSocketHandoff<unknown>(\n lc,\n request => {\n const {url: u} = request;\n const url = new URL(u ?? '', 'http://unused/');\n const path = parsePath(url);\n if (!path) {\n throw new Error(`Invalid URL: ${u}`);\n }\n switch (path.worker) {\n case 'sync':\n return handleSync(request);\n case 'replication':\n return handleChangeStream(request);\n case 'mutate':\n return handlePush(request);\n default:\n throw new Error(`Invalid URL: ${u}`);\n }\n },\n parent,\n );\n }\n\n run() {\n const readyStart = Date.now();\n getOrCreateGauge('server', 'uptime', {\n description: 'Cumulative uptime, starting from when requests are served',\n unit: 's',\n }).addCallback(result => result.observe((Date.now() - readyStart) / 1000));\n\n return this.#state.stopped();\n }\n\n stop() {\n this.#state.stop(this.#lc);\n return this.#state.stopped();\n }\n}\n\nconst URL_PATTERN = new UrlPattern('(/:base)/:worker/v:version/:action');\n\nexport function parsePath(url: URL):\n | {\n base?: string;\n worker: 'sync' | 'mutate' | 'replication';\n version: string;\n action: string;\n }\n | undefined {\n // The match() returns both null and undefined.\n return URL_PATTERN.match(url.pathname) || undefined;\n} // The server allows the client to use any /:base/ path to facilitate\n// servicing requests on the same domain as the application.\n"],"mappings":";;;;;;;;AAYA,IAAa,mBAAb,MAAiD;CAC/C,KAAc;CACd;CAEA,SAAkB,IAAI,aAAa,KAAK,GAAG;CAE3C,YACE,IACA,QACA,QACA,SACA,SACA,gBACA;AACA,QAAA,KAAW;EAEX,SAAS,cAAc,KAA4B;GACjD,MAAM,EAAC,SAAS,KAAK,MAAK;GAC1B,MAAM,MAAM,IAAI,IAAI,KAAK,IAAI,iBAAiB;GAC9C,MAAM,OAAO,UAAU,IAAI;AAC3B,OAAI,CAAC,KACH,OAAM,IAAI,MAAM,gBAAgB,IAAI;GAEtC,MAAM,UAAU,OAAO,KAAK,QAAQ;AACpC,OAAI,OAAO,MAAM,QAAQ,CACvB,OAAM,IAAI,MAAM,oBAAoB,IAAI;GAE1C,MAAM,EAAC,QAAQ,UAAS,iBAAiB,SAAS,KAAK,QAAQ;AAC/D,OAAI,UAAU,KACZ,OAAM,IAAI,MAAM,MAAM;AAExB,UAAO;;EAGT,MAAM,cAAc,QAA+B;AACjD,UACE,YAAY,KAAA,GACZ,0EACD;AACD,UAAO;IAAC,SAAS,cAAc,IAAI;IAAE,QAAQ;IAAQ;;EAGvD,IAAI,qBAAqB;AACzB,mBACE,QACA,wBACA,wDACD,CAAC,aAAY,WAAU;AACtB,OAAI,mBACF,QAAO,QAAQ,mBAAmB;IAEpC;EAEF,MAAM,cAAc,QAA+B;AACjD,UAAO,QAAQ,QAAQ,gDAAgD;GACvE,MAAM,SAAS,cAAc,IAAI;GACjC,MAAM,EAAC,eAAe,oBAAmB;AACzC,wBAAqB,KAAK,IAAI,oBAAoB,gBAAgB;GAOlE,MAAM,SAAS,IAAI,SAAS,MAAM,cAAc,GAAG,QAAQ;AAE3D,MAAG,QAAQ,cAAc,cAAc,aAAa,SAAS;AAC7D,UAAO;IAAC,SAAS;IAAQ,QAAQ,QAAQ;IAAQ;;EAGnD,MAAM,sBAAsB,QAA+B;AAMzD,UACE,QAAQ,WAAW,KAAK,YAAY,KAAA,GACpC,uFAED;AACD,UACE,gBACA,sEACD;GAED,MAAM,OAAO,UADD,IAAI,IAAI,IAAI,OAAO,IAAI,iBAAiB,CACzB;AAC3B,OAAI,CAAC,KACH,OAAM,IAAI,MAAM,gBAAgB,IAAI,MAAM;AAG5C,UAAO;IACL,SAAS,KAAK;IACd,QAAQ;IACT;;AAIH,0BACE,KACA,YAAW;GACT,MAAM,EAAC,KAAK,MAAK;GAEjB,MAAM,OAAO,UADD,IAAI,IAAI,KAAK,IAAI,iBAAiB,CACnB;AAC3B,OAAI,CAAC,KACH,OAAM,IAAI,MAAM,gBAAgB,IAAI;AAEtC,WAAQ,KAAK,QAAb;IACE,KAAK,OACH,QAAO,WAAW,QAAQ;IAC5B,KAAK,cACH,QAAO,mBAAmB,QAAQ;IACpC,KAAK,SACH,QAAO,WAAW,QAAQ;IAC5B,QACE,OAAM,IAAI,MAAM,gBAAgB,IAAI;;KAG1C,OACD;;CAGH,MAAM;EACJ,MAAM,aAAa,KAAK,KAAK;AAC7B,mBAAiB,UAAU,UAAU;GACnC,aAAa;GACb,MAAM;GACP,CAAC,CAAC,aAAY,WAAU,OAAO,SAAS,KAAK,KAAK,GAAG,cAAc,IAAK,CAAC;AAE1E,SAAO,MAAA,MAAY,SAAS;;CAG9B,OAAO;AACL,QAAA,MAAY,KAAK,MAAA,GAAS;AAC1B,SAAO,MAAA,MAAY,SAAS;;;AAIhC,IAAM,cAAc,IAAI,WAAW,qCAAqC;AAExE,SAAgB,UAAU,KAOZ;AAEZ,QAAO,YAAY,MAAM,IAAI,SAAS,IAAI,KAAA"}
1
+ {"version":3,"file":"worker-dispatcher.js","names":["#lc","#state"],"sources":["../../../../../zero-cache/src/server/worker-dispatcher.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport UrlPattern from 'url-pattern';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {h32} from '../../../shared/src/hash.ts';\nimport {getOrCreateGauge} from '../observability/metrics.ts';\nimport {RunningState} from '../services/running-state.ts';\nimport type {Service} from '../services/service.ts';\nimport type {IncomingMessageSubset} from '../types/http.ts';\nimport type {Worker} from '../types/processes.ts';\nimport {installWebSocketHandoff} from '../types/websocket-handoff.ts';\nimport {getConnectParams} from '../workers/connect-params.ts';\n\nexport class WorkerDispatcher implements Service {\n readonly id = 'worker-dispatcher';\n readonly #lc: LogContext;\n\n readonly #state = new RunningState(this.id);\n\n constructor(\n lc: LogContext,\n taskID: string,\n parent: Worker,\n syncers: Worker[],\n mutator: Worker | undefined,\n changeStreamer: Worker | undefined,\n ) {\n this.#lc = lc;\n\n function connectParams(req: IncomingMessageSubset) {\n const {headers, url: u} = req;\n const url = new URL(u ?? '', 'http://unused/');\n const path = parsePath(url);\n if (!path) {\n throw new Error(`Invalid URL: ${u}`);\n }\n const version = Number(path.version);\n if (Number.isNaN(version)) {\n throw new Error(`Invalid version: ${u}`);\n }\n const {params, error} = getConnectParams(version, url, headers);\n if (error !== null) {\n throw new Error(error);\n }\n return params;\n }\n\n const handlePush = (req: IncomingMessageSubset) => {\n assert(\n mutator !== undefined,\n 'Received a push for a custom mutation but no `push.url` was configured.',\n );\n return {payload: connectParams(req), sender: mutator};\n };\n\n let maxProtocolVersion = 0;\n getOrCreateGauge(\n 'sync',\n 'max-protocol-version',\n 'Latest sync protocol version from a connecting client',\n ).addCallback(result => {\n if (maxProtocolVersion) {\n result.observe(maxProtocolVersion);\n }\n });\n\n const handleSync = (req: IncomingMessageSubset) => {\n assert(syncers.length, 'Received a sync request with no sync workers.');\n const params = connectParams(req);\n const {clientGroupID, protocolVersion} = params;\n maxProtocolVersion = Math.max(maxProtocolVersion, protocolVersion);\n\n // Include the TaskID when hash-bucketting the client group to the sync\n // worker. This diversifies the distribution of client groups (across\n // workers) for different tasks, so that if one task sheds connections\n // from its most heavily loaded sync worker(s), those client groups will\n // be distributed uniformly across workers on the receiving task(s).\n const syncer = h32(taskID + '/' + clientGroupID) % syncers.length;\n\n lc.debug?.(`connecting ${clientGroupID} to syncer ${syncer}`);\n return {payload: params, sender: syncers[syncer]};\n };\n\n const handleChangeStream = (req: IncomingMessageSubset) => {\n // Note: The change-streamer is generally not dispatched via the main\n // port, and in particular, should *not* be accessible via that\n // port in single-node mode. However, this plumbing is maintained\n // for the purpose of allowing --lazy-startup of the\n // replication-manager as a possible future feature.\n assert(\n syncers.length === 0 && mutator === undefined,\n 'Dispatch to the change-streamer via the main port ' +\n 'is only allowed in multi-node mode',\n );\n assert(\n changeStreamer,\n 'Received a change-streamer request without a change-streamer worker',\n );\n const url = new URL(req.url ?? '', 'http://unused/');\n const path = parsePath(url);\n if (!path) {\n throw new Error(`Invalid URL: ${req.url}`);\n }\n\n return {\n payload: path.action,\n sender: changeStreamer,\n };\n };\n\n // handoff messages from this ZeroDispatcher to the appropriate worker (pool).\n installWebSocketHandoff<unknown>(\n lc,\n request => {\n const {url: u} = request;\n const url = new URL(u ?? '', 'http://unused/');\n const path = parsePath(url);\n if (!path) {\n throw new Error(`Invalid URL: ${u}`);\n }\n switch (path.worker) {\n case 'sync':\n return handleSync(request);\n case 'replication':\n return handleChangeStream(request);\n case 'mutate':\n return handlePush(request);\n default:\n throw new Error(`Invalid URL: ${u}`);\n }\n },\n parent,\n );\n }\n\n run() {\n const readyStart = Date.now();\n getOrCreateGauge('server', 'uptime', {\n description: 'Cumulative uptime, starting from when requests are served',\n unit: 's',\n }).addCallback(result => result.observe((Date.now() - readyStart) / 1000));\n\n return this.#state.stopped();\n }\n\n stop() {\n this.#state.stop(this.#lc);\n return this.#state.stopped();\n }\n}\n\nconst URL_PATTERN = new UrlPattern('(/:base)/:worker/v:version/:action');\n\nexport function parsePath(url: URL):\n | {\n base?: string;\n worker: 'sync' | 'mutate' | 'replication';\n version: string;\n action: string;\n }\n | undefined {\n // The match() returns both null and undefined.\n return URL_PATTERN.match(url.pathname) || undefined;\n} // The server allows the client to use any /:base/ path to facilitate\n// servicing requests on the same domain as the application.\n"],"mappings":";;;;;;;;AAYA,IAAa,mBAAb,MAAiD;CAC/C,KAAc;CACd;CAEA,SAAkB,IAAI,aAAa,KAAK,EAAE;CAE1C,YACE,IACA,QACA,QACA,SACA,SACA,gBACA;EACA,KAAKA,MAAM;EAEX,SAAS,cAAc,KAA4B;GACjD,MAAM,EAAC,SAAS,KAAK,MAAK;GAC1B,MAAM,MAAM,IAAI,IAAI,KAAK,IAAI,gBAAgB;GAC7C,MAAM,OAAO,UAAU,GAAG;GAC1B,IAAI,CAAC,MACH,MAAM,IAAI,MAAM,gBAAgB,GAAG;GAErC,MAAM,UAAU,OAAO,KAAK,OAAO;GACnC,IAAI,OAAO,MAAM,OAAO,GACtB,MAAM,IAAI,MAAM,oBAAoB,GAAG;GAEzC,MAAM,EAAC,QAAQ,UAAS,iBAAiB,SAAS,KAAK,OAAO;GAC9D,IAAI,UAAU,MACZ,MAAM,IAAI,MAAM,KAAK;GAEvB,OAAO;EACT;EAEA,MAAM,cAAc,QAA+B;GACjD,OACE,YAAY,KAAA,GACZ,yEACF;GACA,OAAO;IAAC,SAAS,cAAc,GAAG;IAAG,QAAQ;GAAO;EACtD;EAEA,IAAI,qBAAqB;EACzB,iBACE,QACA,wBACA,uDACF,EAAE,aAAY,WAAU;GACtB,IAAI,oBACF,OAAO,QAAQ,kBAAkB;EAErC,CAAC;EAED,MAAM,cAAc,QAA+B;GACjD,OAAO,QAAQ,QAAQ,+CAA+C;GACtE,MAAM,SAAS,cAAc,GAAG;GAChC,MAAM,EAAC,eAAe,oBAAmB;GACzC,qBAAqB,KAAK,IAAI,oBAAoB,eAAe;GAOjE,MAAM,SAAS,IAAI,SAAS,MAAM,aAAa,IAAI,QAAQ;GAE3D,GAAG,QAAQ,cAAc,cAAc,aAAa,QAAQ;GAC5D,OAAO;IAAC,SAAS;IAAQ,QAAQ,QAAQ;GAAO;EAClD;EAEA,MAAM,sBAAsB,QAA+B;GAMzD,OACE,QAAQ,WAAW,KAAK,YAAY,KAAA,GACpC,sFAEF;GACA,OACE,gBACA,qEACF;GAEA,MAAM,OAAO,UAAU,IADP,IAAI,IAAI,OAAO,IAAI,gBACZ,CAAG;GAC1B,IAAI,CAAC,MACH,MAAM,IAAI,MAAM,gBAAgB,IAAI,KAAK;GAG3C,OAAO;IACL,SAAS,KAAK;IACd,QAAQ;GACV;EACF;EAGA,wBACE,KACA,YAAW;GACT,MAAM,EAAC,KAAK,MAAK;GAEjB,MAAM,OAAO,UAAU,IADP,IAAI,KAAK,IAAI,gBACN,CAAG;GAC1B,IAAI,CAAC,MACH,MAAM,IAAI,MAAM,gBAAgB,GAAG;GAErC,QAAQ,KAAK,QAAb;IACE,KAAK,QACH,OAAO,WAAW,OAAO;IAC3B,KAAK,eACH,OAAO,mBAAmB,OAAO;IACnC,KAAK,UACH,OAAO,WAAW,OAAO;IAC3B,SACE,MAAM,IAAI,MAAM,gBAAgB,GAAG;GACvC;EACF,GACA,MACF;CACF;CAEA,MAAM;EACJ,MAAM,aAAa,KAAK,IAAI;EAC5B,iBAAiB,UAAU,UAAU;GACnC,aAAa;GACb,MAAM;EACR,CAAC,EAAE,aAAY,WAAU,OAAO,SAAS,KAAK,IAAI,IAAI,cAAc,GAAI,CAAC;EAEzE,OAAO,KAAKC,OAAO,QAAQ;CAC7B;CAEA,OAAO;EACL,KAAKA,OAAO,KAAK,KAAKD,GAAG;EACzB,OAAO,KAAKC,OAAO,QAAQ;CAC7B;AACF;AAEA,IAAM,cAAc,IAAI,WAAW,oCAAoC;AAEvE,SAAgB,UAAU,KAOZ;CAEZ,OAAO,YAAY,MAAM,IAAI,QAAQ,KAAK,KAAA;AAC5C"}
@@ -1 +1 @@
1
- {"version":3,"file":"worker-urls.js","names":[],"sources":["../../../../../zero-cache/src/server/worker-urls.ts"],"sourcesContent":["// This module provides URLs for worker files.\n\nconst tsRe = /\\.ts$/;\n\nfunction resolve(path: string): URL {\n const {url} = import.meta;\n if (url.endsWith('.js')) {\n // When compiled, change .ts to .js\n path = path.replace(tsRe, '.js');\n }\n return new URL(path, url);\n}\n\n// These URLs are part of the build process. See ../../zero/tool/build.ts\n// All these urls must be relative to this file and be located in the same directory.\n\nexport const CHANGE_STREAMER_URL = resolve('./change-streamer.ts');\nexport const MAIN_URL = resolve('./main.ts');\nexport const MUTATOR_URL = resolve('./mutator.ts');\nexport const REAPER_URL = resolve('./reaper.ts');\nexport const REPLICATOR_URL = resolve('./replicator.ts');\nexport const SHADOW_SYNCER_URL = resolve('./shadow-syncer.ts');\nexport const SYNCER_URL = resolve('./syncer.ts');\nexport const WRITE_WORKER_URL = resolve('./write-worker.ts');\n"],"mappings":";AAEA,IAAM,OAAO;AAEb,SAAS,QAAQ,MAAmB;CAClC,MAAM,EAAC,QAAO,OAAO;AACrB,KAAI,IAAI,SAAS,MAAM,CAErB,QAAO,KAAK,QAAQ,MAAM,MAAM;AAElC,QAAO,IAAI,IAAI,MAAM,IAAI;;AAM3B,IAAa,sBAAsB,QAAQ,uBAAuB;AAClE,IAAa,WAAW,QAAQ,YAAY;AACjB,QAAQ,eAAe;AAClD,IAAa,aAAa,QAAQ,cAAc;AAChD,IAAa,iBAAiB,QAAQ,kBAAkB;AACxD,IAAa,oBAAoB,QAAQ,qBAAqB;AAC9D,IAAa,aAAa,QAAQ,cAAc;AAChD,IAAa,mBAAmB,QAAQ,oBAAoB"}
1
+ {"version":3,"file":"worker-urls.js","names":[],"sources":["../../../../../zero-cache/src/server/worker-urls.ts"],"sourcesContent":["// This module provides URLs for worker files.\n\nconst tsRe = /\\.ts$/;\n\nfunction resolve(path: string): URL {\n const {url} = import.meta;\n if (url.endsWith('.js')) {\n // When compiled, change .ts to .js\n path = path.replace(tsRe, '.js');\n }\n return new URL(path, url);\n}\n\n// These URLs are part of the build process. See ../../zero/tool/build.ts\n// All these urls must be relative to this file and be located in the same directory.\n\nexport const CHANGE_STREAMER_URL = resolve('./change-streamer.ts');\nexport const MAIN_URL = resolve('./main.ts');\nexport const MUTATOR_URL = resolve('./mutator.ts');\nexport const REAPER_URL = resolve('./reaper.ts');\nexport const REPLICATOR_URL = resolve('./replicator.ts');\nexport const SHADOW_SYNCER_URL = resolve('./shadow-syncer.ts');\nexport const SYNCER_URL = resolve('./syncer.ts');\nexport const WRITE_WORKER_URL = resolve('./write-worker.ts');\n"],"mappings":";AAEA,IAAM,OAAO;AAEb,SAAS,QAAQ,MAAmB;CAClC,MAAM,EAAC,QAAO,OAAO;CACrB,IAAI,IAAI,SAAS,KAAK,GAEpB,OAAO,KAAK,QAAQ,MAAM,KAAK;CAEjC,OAAO,IAAI,IAAI,MAAM,GAAG;AAC1B;AAKA,IAAa,sBAAsB,QAAQ,sBAAsB;AACjE,IAAa,WAAW,QAAQ,WAAW;AAChB,QAAQ,cAAc;AACjD,IAAa,aAAa,QAAQ,aAAa;AAC/C,IAAa,iBAAiB,QAAQ,iBAAiB;AACvD,IAAa,oBAAoB,QAAQ,oBAAoB;AAC7D,IAAa,aAAa,QAAQ,aAAa;AAC/C,IAAa,mBAAmB,QAAQ,mBAAmB"}
@@ -1 +1 @@
1
- {"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/services/analyze.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,oDAAoD,CAAC;AAC3F,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,mCAAmC,CAAC;AAC3D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,6CAA6C,CAAC;AAC9E,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,kDAAkD,CAAC;AAWxF,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AAQjE,wBAAsB,YAAY,CAChC,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,oBAAoB,EAC5B,YAAY,EAAE,YAAY,EAC1B,GAAG,EAAE,GAAG,EACR,UAAU,UAAO,EACjB,UAAU,UAAQ,EAClB,WAAW,CAAC,EAAE,iBAAiB,EAC/B,IAAI,CAAC,EAAE,OAAO,EACd,SAAS,UAAQ,GAChB,OAAO,CAAC,kBAAkB,CAAC,CAiF7B"}
1
+ {"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/services/analyze.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,oDAAoD,CAAC;AAC3F,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,mCAAmC,CAAC;AAC3D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,6CAA6C,CAAC;AAC9E,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,kDAAkD,CAAC;AAWxF,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AAQjE,wBAAsB,YAAY,CAChC,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,oBAAoB,EAC5B,YAAY,EAAE,YAAY,EAC1B,GAAG,EAAE,GAAG,EACR,UAAU,UAAO,EACjB,UAAU,UAAQ,EAClB,WAAW,CAAC,EAAE,iBAAiB,EAC/B,IAAI,CAAC,EAAE,OAAO,EACd,SAAS,UAAQ,GAChB,OAAO,CAAC,kBAAkB,CAAC,CA2F7B"}
@@ -7,7 +7,7 @@ import { computeZqlSpecs, mustGetTableSpec } from "../db/lite-tables.js";
7
7
  import { createSQLiteCostModel } from "../../../zqlite/src/sqlite-cost-model.js";
8
8
  import { runAst } from "./run-ast.js";
9
9
  import { explainQueries } from "../../../zqlite/src/explain-queries.js";
10
- import { _usingCtx } from "../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/usingCtx.js";
10
+ import { _usingCtx } from "../../../_virtual/_@oxc-project_runtime@0.130.0/helpers/usingCtx.js";
11
11
  import { TimeSliceTimer } from "./view-syncer/view-syncer.js";
12
12
  //#region ../zero-cache/src/services/analyze.ts
13
13
  var TIME_SLICE_LAP_THRESHOLD_MS = 200;
@@ -54,7 +54,10 @@ async function analyzeQuery(lc, config, clientSchema, ast, syncedRows = true, ve
54
54
  decorateFilterInput: (input) => input
55
55
  }
56
56
  }, yieldProcess);
57
- result.sqlitePlans = explainQueries(result.readRowCountsByQuery ?? {}, db);
57
+ const fallback = explainQueries(result.readRowCountsByQuery ?? {}, db);
58
+ const captured = result.sqlitePlans ?? {};
59
+ for (const [query, plan] of Object.entries(fallback)) if (!captured[query]) captured[query] = plan;
60
+ result.sqlitePlans = captured;
58
61
  if (planDebugger) result.joinPlans = serializePlanDebugEvents(planDebugger.events);
59
62
  return result;
60
63
  } catch (_) {
@@ -1 +1 @@
1
- {"version":3,"file":"analyze.js","names":[],"sources":["../../../../../zero-cache/src/services/analyze.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {AnalyzeQueryResult} from '../../../zero-protocol/src/analyze-query-result.ts';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport type {ClientSchema} from '../../../zero-protocol/src/client-schema.ts';\nimport type {PermissionsConfig} from '../../../zero-schema/src/compiled-permissions.ts';\nimport {Debug} from '../../../zql/src/builder/debug-delegate.ts';\nimport {MemoryStorage} from '../../../zql/src/ivm/memory-storage.ts';\nimport {\n AccumulatorDebugger,\n serializePlanDebugEvents,\n} from '../../../zql/src/planner/planner-debug.ts';\nimport {Database} from '../../../zqlite/src/db.ts';\nimport {explainQueries} from '../../../zqlite/src/explain-queries.ts';\nimport {createSQLiteCostModel} from '../../../zqlite/src/sqlite-cost-model.ts';\nimport {TableSource} from '../../../zqlite/src/table-source.ts';\nimport type {JWTAuth} from '../auth/auth.ts';\nimport type {NormalizedZeroConfig} from '../config/normalize.ts';\nimport {computeZqlSpecs, mustGetTableSpec} from '../db/lite-tables.ts';\nimport type {LiteAndZqlSpec, LiteTableSpec} from '../db/specs.ts';\nimport {runAst} from './run-ast.ts';\nimport {TimeSliceTimer} from './view-syncer/view-syncer.ts';\n\nconst TIME_SLICE_LAP_THRESHOLD_MS = 200;\n\nexport async function analyzeQuery(\n lc: LogContext,\n config: NormalizedZeroConfig,\n clientSchema: ClientSchema,\n ast: AST,\n syncedRows = true,\n vendedRows = false,\n permissions?: PermissionsConfig,\n auth?: JWTAuth,\n joinPlans = false,\n): Promise<AnalyzeQueryResult> {\n using db = new Database(lc, config.replica.file);\n const fullTables = new Map<string, LiteTableSpec>();\n const tableSpecs = new Map<string, LiteAndZqlSpec>();\n const tables = new Map<string, TableSource>();\n\n computeZqlSpecs(\n lc,\n db,\n {includeBackfillingColumns: false},\n tableSpecs,\n fullTables,\n );\n\n // Mirror production: the planner runs iff ZERO_ENABLE_QUERY_PLANNER is set\n // on the server, so the analysis reflects what actually executes. Diagnostic\n // event collection is orthogonal and opt-in via `joinPlans`.\n const costModel = config.enableQueryPlanner\n ? createSQLiteCostModel(db, tableSpecs)\n : undefined;\n const planDebugger = joinPlans ? new AccumulatorDebugger() : undefined;\n const timer = await new TimeSliceTimer(lc).start();\n const shouldYield = () => timer.elapsedLap() > TIME_SLICE_LAP_THRESHOLD_MS;\n const yieldProcess = () => timer.yieldProcess();\n const result = await runAst(\n lc,\n clientSchema,\n ast,\n true,\n {\n applyPermissions: permissions !== undefined,\n syncedRows,\n vendedRows,\n auth,\n db,\n tableSpecs,\n permissions,\n costModel,\n planDebugger,\n host: {\n debug: new Debug(),\n getSource(tableName: string) {\n let source = tables.get(tableName);\n if (source) {\n return source;\n }\n\n const tableSpec = mustGetTableSpec(tableSpecs, tableName);\n const {primaryKey} = tableSpec.tableSpec;\n\n source = new TableSource(\n lc,\n config.log,\n db,\n tableName,\n tableSpec.zqlSpec,\n primaryKey,\n shouldYield,\n );\n tables.set(tableName, source);\n return source;\n },\n createStorage() {\n return new MemoryStorage();\n },\n decorateSourceInput: input => input,\n decorateInput: input => input,\n addEdge() {},\n decorateFilterInput: input => input,\n },\n },\n yieldProcess,\n );\n\n result.sqlitePlans = explainQueries(result.readRowCountsByQuery ?? {}, db);\n\n if (planDebugger) {\n result.joinPlans = serializePlanDebugEvents(planDebugger.events);\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;;;AAsBA,IAAM,8BAA8B;AAEpC,eAAsB,aACpB,IACA,QACA,cACA,KACA,aAAa,MACb,aAAa,OACb,aACA,MACA,YAAY,OACiB;;;EAC7B,MAAM,KAAA,YAAA,EAAK,IAAI,SAAS,IAAI,OAAO,QAAQ,KAAK,CAAA;EAChD,MAAM,6BAAa,IAAI,KAA4B;EACnD,MAAM,6BAAa,IAAI,KAA6B;EACpD,MAAM,yBAAS,IAAI,KAA0B;AAE7C,kBACE,IACA,IACA,EAAC,2BAA2B,OAAM,EAClC,YACA,WACD;EAKD,MAAM,YAAY,OAAO,qBACrB,sBAAsB,IAAI,WAAW,GACrC,KAAA;EACJ,MAAM,eAAe,YAAY,IAAI,qBAAqB,GAAG,KAAA;EAC7D,MAAM,QAAQ,MAAM,IAAI,eAAe,GAAG,CAAC,OAAO;EAClD,MAAM,oBAAoB,MAAM,YAAY,GAAG;EAC/C,MAAM,qBAAqB,MAAM,cAAc;EAC/C,MAAM,SAAS,MAAM,OACnB,IACA,cACA,KACA,MACA;GACE,kBAAkB,gBAAgB,KAAA;GAClC;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,MAAM;IACJ,OAAO,IAAI,OAAO;IAClB,UAAU,WAAmB;KAC3B,IAAI,SAAS,OAAO,IAAI,UAAU;AAClC,SAAI,OACF,QAAO;KAGT,MAAM,YAAY,iBAAiB,YAAY,UAAU;KACzD,MAAM,EAAC,eAAc,UAAU;AAE/B,cAAS,IAAI,YACX,IACA,OAAO,KACP,IACA,WACA,UAAU,SACV,YACA,YACD;AACD,YAAO,IAAI,WAAW,OAAO;AAC7B,YAAO;;IAET,gBAAgB;AACd,YAAO,IAAI,eAAe;;IAE5B,sBAAqB,UAAS;IAC9B,gBAAe,UAAS;IACxB,UAAU;IACV,sBAAqB,UAAS;IAC/B;GACF,EACD,aACD;AAED,SAAO,cAAc,eAAe,OAAO,wBAAwB,EAAE,EAAE,GAAG;AAE1E,MAAI,aACF,QAAO,YAAY,yBAAyB,aAAa,OAAO;AAGlE,SAAO"}
1
+ {"version":3,"file":"analyze.js","names":[],"sources":["../../../../../zero-cache/src/services/analyze.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {AnalyzeQueryResult} from '../../../zero-protocol/src/analyze-query-result.ts';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport type {ClientSchema} from '../../../zero-protocol/src/client-schema.ts';\nimport type {PermissionsConfig} from '../../../zero-schema/src/compiled-permissions.ts';\nimport {Debug} from '../../../zql/src/builder/debug-delegate.ts';\nimport {MemoryStorage} from '../../../zql/src/ivm/memory-storage.ts';\nimport {\n AccumulatorDebugger,\n serializePlanDebugEvents,\n} from '../../../zql/src/planner/planner-debug.ts';\nimport {Database} from '../../../zqlite/src/db.ts';\nimport {explainQueries} from '../../../zqlite/src/explain-queries.ts';\nimport {createSQLiteCostModel} from '../../../zqlite/src/sqlite-cost-model.ts';\nimport {TableSource} from '../../../zqlite/src/table-source.ts';\nimport type {JWTAuth} from '../auth/auth.ts';\nimport type {NormalizedZeroConfig} from '../config/normalize.ts';\nimport {computeZqlSpecs, mustGetTableSpec} from '../db/lite-tables.ts';\nimport type {LiteAndZqlSpec, LiteTableSpec} from '../db/specs.ts';\nimport {runAst} from './run-ast.ts';\nimport {TimeSliceTimer} from './view-syncer/view-syncer.ts';\n\nconst TIME_SLICE_LAP_THRESHOLD_MS = 200;\n\nexport async function analyzeQuery(\n lc: LogContext,\n config: NormalizedZeroConfig,\n clientSchema: ClientSchema,\n ast: AST,\n syncedRows = true,\n vendedRows = false,\n permissions?: PermissionsConfig,\n auth?: JWTAuth,\n joinPlans = false,\n): Promise<AnalyzeQueryResult> {\n using db = new Database(lc, config.replica.file);\n const fullTables = new Map<string, LiteTableSpec>();\n const tableSpecs = new Map<string, LiteAndZqlSpec>();\n const tables = new Map<string, TableSource>();\n\n computeZqlSpecs(\n lc,\n db,\n {includeBackfillingColumns: false},\n tableSpecs,\n fullTables,\n );\n\n // Mirror production: the planner runs iff ZERO_ENABLE_QUERY_PLANNER is set\n // on the server, so the analysis reflects what actually executes. Diagnostic\n // event collection is orthogonal and opt-in via `joinPlans`.\n const costModel = config.enableQueryPlanner\n ? createSQLiteCostModel(db, tableSpecs)\n : undefined;\n const planDebugger = joinPlans ? new AccumulatorDebugger() : undefined;\n const timer = await new TimeSliceTimer(lc).start();\n const shouldYield = () => timer.elapsedLap() > TIME_SLICE_LAP_THRESHOLD_MS;\n const yieldProcess = () => timer.yieldProcess();\n const result = await runAst(\n lc,\n clientSchema,\n ast,\n true,\n {\n applyPermissions: permissions !== undefined,\n syncedRows,\n vendedRows,\n auth,\n db,\n tableSpecs,\n permissions,\n costModel,\n planDebugger,\n host: {\n debug: new Debug(),\n getSource(tableName: string) {\n let source = tables.get(tableName);\n if (source) {\n return source;\n }\n\n const tableSpec = mustGetTableSpec(tableSpecs, tableName);\n const {primaryKey} = tableSpec.tableSpec;\n\n source = new TableSource(\n lc,\n config.log,\n db,\n tableName,\n tableSpec.zqlSpec,\n primaryKey,\n shouldYield,\n );\n tables.set(tableName, source);\n return source;\n },\n createStorage() {\n return new MemoryStorage();\n },\n decorateSourceInput: input => input,\n decorateInput: input => input,\n addEdge() {},\n decorateFilterInput: input => input,\n },\n },\n yieldProcess,\n );\n\n // Fill in plans for any queries SQLite did not actually execute (and thus\n // did not populate scanStatus EXPLAIN for) using the substituted-binding\n // fallback. Plans captured at execution time use the real bindings and win.\n const fallback = explainQueries(result.readRowCountsByQuery ?? {}, db);\n const captured = result.sqlitePlans ?? {};\n for (const [query, plan] of Object.entries(fallback)) {\n if (!captured[query]) {\n captured[query] = plan;\n }\n }\n result.sqlitePlans = captured;\n\n if (planDebugger) {\n result.joinPlans = serializePlanDebugEvents(planDebugger.events);\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;;;AAsBA,IAAM,8BAA8B;AAEpC,eAAsB,aACpB,IACA,QACA,cACA,KACA,aAAa,MACb,aAAa,OACb,aACA,MACA,YAAY,OACiB;;;EAC7B,MAAM,KAAA,YAAA,EAAK,IAAI,SAAS,IAAI,OAAO,QAAQ,IAAI,CAAA;EAC/C,MAAM,6BAAa,IAAI,IAA2B;EAClD,MAAM,6BAAa,IAAI,IAA4B;EACnD,MAAM,yBAAS,IAAI,IAAyB;EAE5C,gBACE,IACA,IACA,EAAC,2BAA2B,MAAK,GACjC,YACA,UACF;EAKA,MAAM,YAAY,OAAO,qBACrB,sBAAsB,IAAI,UAAU,IACpC,KAAA;EACJ,MAAM,eAAe,YAAY,IAAI,oBAAoB,IAAI,KAAA;EAC7D,MAAM,QAAQ,MAAM,IAAI,eAAe,EAAE,EAAE,MAAM;EACjD,MAAM,oBAAoB,MAAM,WAAW,IAAI;EAC/C,MAAM,qBAAqB,MAAM,aAAa;EAC9C,MAAM,SAAS,MAAM,OACnB,IACA,cACA,KACA,MACA;GACE,kBAAkB,gBAAgB,KAAA;GAClC;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,MAAM;IACJ,OAAO,IAAI,MAAM;IACjB,UAAU,WAAmB;KAC3B,IAAI,SAAS,OAAO,IAAI,SAAS;KACjC,IAAI,QACF,OAAO;KAGT,MAAM,YAAY,iBAAiB,YAAY,SAAS;KACxD,MAAM,EAAC,eAAc,UAAU;KAE/B,SAAS,IAAI,YACX,IACA,OAAO,KACP,IACA,WACA,UAAU,SACV,YACA,WACF;KACA,OAAO,IAAI,WAAW,MAAM;KAC5B,OAAO;IACT;IACA,gBAAgB;KACd,OAAO,IAAI,cAAc;IAC3B;IACA,sBAAqB,UAAS;IAC9B,gBAAe,UAAS;IACxB,UAAU,CAAC;IACX,sBAAqB,UAAS;GAChC;EACF,GACA,YACF;EAKA,MAAM,WAAW,eAAe,OAAO,wBAAwB,CAAC,GAAG,EAAE;EACrE,MAAM,WAAW,OAAO,eAAe,CAAC;EACxC,KAAK,MAAM,CAAC,OAAO,SAAS,OAAO,QAAQ,QAAQ,GACjD,IAAI,CAAC,SAAS,QACZ,SAAS,SAAS;EAGtB,OAAO,cAAc;EAErB,IAAI,cACF,OAAO,YAAY,yBAAyB,aAAa,MAAM;EAGjE,OAAO;;;;;;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"backfill-manager.js","names":["#lc","#requiredBackfills","#changeStreamer","#backfillStreamer","#jsonFormat","#awaitingStatusWatermarks","#minBackoffMs","#maxBackoffMs","#retryDelayMs","#lastStatusWatermark","#setRequiredBackfill","#checkAndStartBackfill","#backfillRetryTimer","#runningBackfill","#runBackfill","#stopRunningBackfill","#retryBackfillWithBackoff","#changeStreamReached","#currentTxWatermark","#setLastStatusWatermark","#backfillRunningFor","#deleteRequiredBackfill"],"sources":["../../../../../../../zero-cache/src/services/change-source/common/backfill-manager.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {assert} from '../../../../../shared/src/asserts.ts';\nimport {stringify} from '../../../../../shared/src/bigint-json.ts';\nimport {CustomKeyMap} from '../../../../../shared/src/custom-key-map.ts';\nimport {must} from '../../../../../shared/src/must.ts';\nimport {randInt} from '../../../../../shared/src/rand.ts';\nimport {JSON_STRINGIFIED, type JSONFormat} from '../../../types/lite.ts';\nimport {\n stateVersionFromString,\n stateVersionToString,\n} from '../../../types/state-version.ts';\nimport type {\n BackfillCompleted,\n BackfillRequest,\n ChangeStreamMessage,\n Identifier,\n MessageBackfill,\n} from '../protocol/current.ts';\nimport type {\n Cancelable,\n ChangeStreamMultiplexer,\n Listener,\n} from './change-stream-multiplexer.ts';\n\nfunction tableKey({schema, name}: Identifier) {\n return `${schema}.${name}`;\n}\n\ntype BackfillStreamer = (\n req: BackfillRequest,\n) => AsyncGenerator<MessageBackfill | BackfillCompleted>;\n\ntype RunningBackfillState = {\n request: BackfillRequest;\n canceledReason?: string | undefined;\n minWatermark: string;\n};\n\nconst MIN_BACKOFF_INTERVAL_MS = 2_000;\nconst MAX_BACKOFF_INTERVAL_MS = 60_000;\n\ntype AwaitingStatusWatermark = {\n watermark: string;\n reached: () => void;\n};\n\n/**\n * The BackfillManager initiates backfills for BackfillRequests from the\n * change-streamer (i.e. unfinished backfills from previous sessions)\n * or for new backfills signaled by `create-table` or `add-column` messages\n * from the change-source.\n *\n * The BackfillManager registers itself as a change stream listener in order\n * to track necessary backfills, and potentially invalidate the in-progress\n * backfill (e.g. due to a schema change) so that it can be retried at a\n * new snapshot.\n *\n * The manager also handles low priority streaming of the backfill messages\n * using the {@link ChangeStreamMultiplexer}, implementing a policy of always\n * releasing its reservation if another producer (i.e. the main change stream)\n * has messages to stream.\n */\nexport class BackfillManager implements Cancelable, Listener {\n readonly #lc: LogContext;\n\n /**\n * Tracks the metadata of required backfills based on schema changes\n * and initial backfill requests.\n */\n readonly #requiredBackfills = new CustomKeyMap<Identifier, BackfillRequest>(\n tableKey,\n );\n readonly #changeStreamer: ChangeStreamMultiplexer;\n readonly #backfillStreamer: BackfillStreamer;\n readonly #jsonFormat: JSONFormat;\n\n /**\n * The current running backfill. The backfill request is always also in\n * `#requiredBackfills` (technically, it can be a subset of what's in\n * `#requiredBackfills`); the request is removed from `#requiredBackfills`\n * upon completion.\n */\n #runningBackfill: RunningBackfillState | null = null;\n\n /** The last seen watermark in the change stream. */\n #lastStatusWatermark: string | null = null;\n\n readonly #awaitingStatusWatermarks: AwaitingStatusWatermark[] = [];\n\n /** The watermark of the current transaction in the change stream. */\n #currentTxWatermark: string | null = null;\n\n constructor(\n lc: LogContext,\n changeStreamer: ChangeStreamMultiplexer,\n backfillStreamer: BackfillStreamer,\n jsonFormat: JSONFormat = JSON_STRINGIFIED,\n minBackoffMs = MIN_BACKOFF_INTERVAL_MS,\n maxBackoffMs = MAX_BACKOFF_INTERVAL_MS,\n ) {\n this.#lc = lc.withContext('component', 'backfill-manager');\n this.#changeStreamer = changeStreamer;\n this.#backfillStreamer = backfillStreamer;\n this.#jsonFormat = jsonFormat;\n this.#minBackoffMs = minBackoffMs;\n this.#maxBackoffMs = maxBackoffMs;\n this.#retryDelayMs = minBackoffMs;\n }\n\n run(lastWatermark: string, initialRequests: BackfillRequest[]) {\n this.#lc.info?.(\n `starting backfill manager with ${initialRequests.length} initial requests`,\n {requests: initialRequests},\n );\n this.#lastStatusWatermark = lastWatermark;\n initialRequests.forEach(req =>\n this.#setRequiredBackfill('initial-request', req),\n );\n this.#checkAndStartBackfill();\n }\n\n #setLastStatusWatermark({watermark}: {watermark: string}) {\n // Only allow the watermark to move forward. This prevents a backfill\n // transaction (whose watermark is unrelated to change-stream state)\n // from moving the watermark backwards.\n if ((this.#lastStatusWatermark ?? '') < watermark) {\n this.#lastStatusWatermark = watermark;\n for (let i = this.#awaitingStatusWatermarks.length - 1; i >= 0; i--) {\n const awaiting = this.#awaitingStatusWatermarks[i];\n if (watermark >= awaiting.watermark) {\n awaiting.reached();\n this.#awaitingStatusWatermarks.splice(i, 1);\n }\n }\n }\n }\n\n #changeStreamReached(\n lc: LogContext,\n watermark: string,\n ): Promise<void> | null {\n if ((this.#lastStatusWatermark ?? '') < watermark) {\n const {promise, resolve: reached} = resolver();\n this.#awaitingStatusWatermarks.push({watermark, reached});\n lc.info?.(\n `waiting for change stream (at ${this.#lastStatusWatermark}) to reach ${watermark}`,\n );\n return promise;\n }\n return null;\n }\n\n readonly #minBackoffMs: number;\n readonly #maxBackoffMs: number;\n #retryDelayMs: number;\n #backfillRetryTimer: NodeJS.Timeout | undefined;\n\n #checkAndStartBackfill() {\n if (\n !this.#backfillRetryTimer &&\n !this.#runningBackfill &&\n this.#requiredBackfills.size\n ) {\n // Pick a random backfill to avoid head-of-line blocking by a\n // problematic backfill (e.g. awaiting a primary key). This is\n // simpler that adding logic to classify (and declassify)\n // problematic backfills.\n const candidates = [...this.#requiredBackfills.values()];\n const request = candidates[randInt(0, candidates.length - 1)];\n const state = {request, minWatermark: ''};\n const lc = this.#lc.withContext('table', request.table.name);\n\n this.#runningBackfill = state;\n void this.#runBackfill(lc, state)\n .then(() => {\n this.#stopRunningBackfill('backfill exited', state);\n this.#retryDelayMs = this.#minBackoffMs; // reset on success\n })\n // For unexpected errors (e.g. upstream replication slot\n // unavailability), retry with exponential backoff.\n .catch(e => {\n this.#stopRunningBackfill(String(e), state);\n this.#retryBackfillWithBackoff(e);\n });\n }\n }\n\n #retryBackfillWithBackoff(e: unknown) {\n const log = this.#retryDelayMs === this.#maxBackoffMs ? 'error' : 'warn';\n this.#lc[log]?.(\n `Error running backfill. Retrying in ${this.#retryDelayMs} ms`,\n e,\n );\n this.#backfillRetryTimer = setTimeout(() => {\n this.#backfillRetryTimer = undefined;\n this.#checkAndStartBackfill();\n }, this.#retryDelayMs);\n\n this.#retryDelayMs = Math.min(this.#retryDelayMs * 2, this.#maxBackoffMs);\n }\n\n async #runBackfill(lc: LogContext, state: RunningBackfillState) {\n const changeStream = this.#changeStreamer; // Purely for readability\n\n // backfillTx is set if and only if a changeStreamer reservation has been\n // acquired and the backfill stream is inside a transaction.\n let backfillTx: string | null = null;\n\n /**\n * @returns the new tx watermark, or null if backfill was cancelled\n */\n const beginTxFor = async (\n msg: MessageBackfill | BackfillCompleted,\n ): Promise<string | null> => {\n assert(backfillTx === null, 'Expected no active backfill transaction');\n const lastWatermark = await changeStream.reserve('backfill');\n\n // After obtaining the changeStream reservation, check if the stream\n // had changes that resulted in invalidating / canceling this backfill.\n if (\n state.canceledReason ||\n (msg.tag === 'backfill' && msg.watermark < state.minWatermark)\n ) {\n if (state.canceledReason === undefined) {\n assert(msg.tag === 'backfill', 'Expected backfill message tag'); // TypeScript should have figured this out.\n this.#stopRunningBackfill(\n `row key change at ${state.minWatermark} ` +\n `postdates backfill watermark at ${msg.watermark}`,\n state,\n );\n }\n changeStream.release(lastWatermark);\n return null;\n }\n\n const {major, minor = 0n} = stateVersionFromString(lastWatermark);\n let tx = stateVersionToString({\n major,\n minor: BigInt(minor) + 1n,\n });\n\n if (msg.tag === 'backfill-completed' && tx < msg.watermark) {\n // At this point it must be the case that the #changeStreamReached() the\n // backfill watermark. Given that guarantee, ensure that the version of the\n // transaction containing the backfill-completed message is at least up\n // to the backfill watermark, so that the final database state version is\n // never earlier than the version of any backfilled rows.\n tx = msg.watermark;\n }\n\n void changeStream.push([\n 'begin',\n {tag: 'begin', json: this.#jsonFormat, skipAck: true},\n {commitWatermark: tx},\n ]);\n return (backfillTx = tx);\n };\n\n const commitTx = () => {\n if (backfillTx) {\n void changeStream.push([\n 'commit',\n {tag: 'commit'},\n {watermark: backfillTx},\n ]);\n changeStream.release(backfillTx);\n }\n backfillTx = null;\n };\n\n for await (const msg of this.#backfillStreamer(state.request)) {\n // Before sending `backfill-completed`, the main replication stream\n // may need to catch up, and/or the current transaction may need to be\n // committed to open a new transaction that's up to backfill watermark.\n const mustWaitBeforeFlush =\n msg.tag === 'backfill-completed' &&\n (this.#changeStreamReached(lc, msg.watermark) ||\n (backfillTx !== null && backfillTx < msg.watermark));\n\n // If necessary, yield the reservation to the main stream.\n if (\n backfillTx &&\n (changeStream.waiterDelay() > 0 || mustWaitBeforeFlush)\n ) {\n commitTx();\n }\n\n mustWaitBeforeFlush && (await mustWaitBeforeFlush);\n\n if (\n msg.tag === 'backfill' &&\n msg.rowValues.length > 0 &&\n msg.relation.rowKey.columns.length === 0\n ) {\n throw new MissingRowKeyError(state.request);\n }\n\n // Reserve the changeStreamer if not in a transaction.\n if ((backfillTx ??= await beginTxFor(msg)) === null) {\n lc.info?.(\n `backfill stream canceled: ${state.canceledReason}`,\n state.request,\n );\n this.#checkAndStartBackfill(); // start the next backfill if present\n return; // this backfill is canceled\n }\n\n // `await` to allow the change streamer to exert back pressure\n // on backfills.\n await changeStream.push(['data', msg]);\n }\n\n // Flush any final tx and release the stream.\n backfillTx && commitTx();\n lc.debug?.(`backfill stream exited`, state.canceledReason ?? '');\n }\n\n #backfillRunningFor(table: Identifier): RunningBackfillState | null {\n const state = this.#runningBackfill;\n return state?.request.table.schema === table.schema &&\n state.request.table.name === table.name\n ? state\n : null;\n }\n\n /**\n * Stops the running backfill for the specified `reason`. If `instance` is\n * specified, the running backfill is stopped only if it is that instance.\n * This allows the running backfill itself to clear backfill state without\n * accidentally stopping a different (e.g. subsequent) backfill.\n */\n #stopRunningBackfill(reason?: string, instance?: RunningBackfillState) {\n const backfill = this.#runningBackfill;\n if (backfill && backfill === (instance ?? backfill)) {\n backfill.canceledReason = reason;\n this.#runningBackfill = null;\n reason && this.#lc.info?.(`canceling backfill:`, reason);\n }\n }\n\n #setRequiredBackfill(source: string, req: BackfillRequest) {\n const action = this.#requiredBackfills.has(req.table) ? 'updated' : 'added';\n this.#lc.info?.(`Backfill ${action}: ${source}`, {backfill: req});\n this.#requiredBackfills.set(req.table, req);\n }\n\n #deleteRequiredBackfill(source: string, id: Identifier) {\n const req = this.#requiredBackfills.get(id);\n if (req) {\n const action = source === 'backfill-completed' ? 'completed' : 'dropped';\n this.#lc.info?.(`Backfill ${action}: ${source}`, {backfill: req});\n this.#requiredBackfills.delete(id);\n }\n }\n\n /**\n * Implements {@link Listener.onChange()}, invoked by the\n * {@link ChangeStreamMultiplexer}.\n */\n onChange(message: ChangeStreamMessage): void {\n if (message[0] === 'begin') {\n this.#currentTxWatermark = message[2].commitWatermark;\n return;\n }\n if (message[0] === 'commit') {\n this.#currentTxWatermark = null;\n this.#setLastStatusWatermark(message[2]);\n // Every commit is a candidate for starting the next backfill\n // (if one is not currently running).\n this.#checkAndStartBackfill();\n return;\n }\n if (message[0] === 'status') {\n this.#setLastStatusWatermark(message[2]);\n return;\n }\n if (message[0] !== 'data') {\n return;\n }\n const change = message[1];\n const {tag} = change;\n switch (tag) {\n case 'update-table-metadata': {\n const {table, new: metadata} = change;\n const backfillRequest = this.#requiredBackfills.get(table);\n if (backfillRequest) {\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n table: {...backfillRequest.table, metadata},\n });\n if (this.#backfillRunningFor(table)) {\n this.#stopRunningBackfill(`TableMetadata updated`);\n }\n }\n break;\n }\n case 'create-table': {\n const {\n spec: {schema, name},\n metadata = null,\n backfill,\n } = change;\n\n if (backfill) {\n this.#setRequiredBackfill(tag, {\n table: {schema, name, metadata},\n columns: backfill,\n });\n }\n break;\n }\n case 'rename-table': {\n const {old, new: newTable} = change;\n const backfillRequest = this.#requiredBackfills.get(old);\n if (backfillRequest) {\n const {schema, name} = newTable;\n this.#deleteRequiredBackfill(tag, old);\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n table: {...backfillRequest.table, schema, name},\n });\n if (this.#backfillRunningFor(old)) {\n this.#stopRunningBackfill(`table renamed`);\n }\n }\n break;\n }\n case 'drop-table': {\n const {id} = change;\n const backfillRequest = this.#requiredBackfills.get(id);\n if (backfillRequest) {\n this.#deleteRequiredBackfill(tag, id);\n if (this.#backfillRunningFor(id)) {\n this.#stopRunningBackfill(`table dropped`);\n }\n }\n break;\n }\n case 'add-column': {\n const {\n table,\n tableMetadata: metadata = null,\n column,\n backfill,\n } = change;\n if (backfill) {\n const backfillRequest = this.#requiredBackfills.get(table);\n if (!backfillRequest) {\n this.#setRequiredBackfill(tag, {\n table: {...table, metadata},\n columns: {[column.name]: backfill},\n });\n } else {\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n table: {...backfillRequest.table, metadata},\n columns: {\n ...backfillRequest.columns,\n [column.name]: backfill,\n },\n });\n // Note: The running backfill need not be canceled if a\n // new column is added. The new column will be backfilled\n // by its own stream after the current backfill completes.\n }\n }\n break;\n }\n case 'update-column': {\n const {\n table,\n old: {name: oldName},\n new: {name: newName},\n } = change;\n if (oldName !== newName) {\n const backfillRequest = this.#requiredBackfills.get(table);\n if (backfillRequest && oldName in backfillRequest.columns) {\n const {[oldName]: colSpec, ...otherCols} = backfillRequest.columns;\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n columns: {...otherCols, [newName]: colSpec},\n });\n const backfill = this.#backfillRunningFor(table);\n if (backfill && oldName in backfill.request.columns) {\n this.#stopRunningBackfill(`column renamed`);\n }\n }\n }\n break;\n }\n case 'drop-column': {\n const {table, column} = change;\n const backfillRequest = this.#requiredBackfills.get(table);\n if (backfillRequest && column in backfillRequest.columns) {\n const {[column]: _excluded, ...remaining} = backfillRequest.columns;\n if (Object.keys(remaining).length === 0) {\n this.#deleteRequiredBackfill(tag, table);\n } else {\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n columns: remaining,\n });\n }\n const backfill = this.#backfillRunningFor(table);\n if (backfill && column in backfill.request.columns) {\n this.#stopRunningBackfill(`column dropped`);\n }\n }\n break;\n }\n case 'update': {\n const {relation, key, new: row} = change;\n const backfill = this.#backfillRunningFor(relation);\n const txWatermark = must(this.#currentTxWatermark, `not in a tx`);\n if (backfill?.request.table.metadata && key !== null) {\n // A corner case that backfill is unable to correctly handle is\n // when a row's key changes; this is decomposed into a delete\n // of the old key and a set of the new key in the replica change\n // log, at which point the backfill algorithm assumes that the\n // (old) row is deleted but does not know to backfill the new row.\n // In these corner cases, the current backfill is canceled and\n // retried if its version precedes this update.\n for (const col of Object.keys(\n backfill.request.table.metadata.rowKey,\n )) {\n if (key[col] !== row[col]) {\n backfill.minWatermark = txWatermark;\n this.#lc.info?.(\n `key for row as changed (col: ${col}). ` +\n `backfill data must not predate ${backfill.minWatermark}`,\n );\n break;\n }\n }\n }\n break;\n }\n case 'backfill-completed': {\n const {relation, columns} = change;\n const backfillRequest = this.#requiredBackfills.get(relation);\n assert(\n backfillRequest,\n () => `No BackfillRequest completed backfill ${stringify(change)}`,\n );\n const remaining = Object.entries(backfillRequest.columns).filter(\n ([col]) =>\n !(columns.includes(col) || relation.rowKey.columns.includes(col)),\n );\n if (remaining.length === 0) {\n this.#deleteRequiredBackfill(tag, relation);\n } else {\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n columns: Object.fromEntries(remaining),\n });\n }\n // Technically the backfill is already stopping, but this method\n // cleans up the state that tracks it.\n this.#stopRunningBackfill();\n break;\n }\n }\n }\n\n cancel(): void {\n this.#stopRunningBackfill(`change stream canceled`);\n clearTimeout(this.#backfillRetryTimer);\n }\n}\n\nabstract class BackfillStreamError extends Error {\n constructor(bf: BackfillRequest, msg: string, cause?: unknown) {\n super(\n `Cannot backfill ${bf.table.schema}.${bf.table.name}` +\n `[${Object.keys(bf.columns).join(',')}]: ${msg}`,\n {cause},\n );\n }\n}\n\n/**\n * Background: The zero-cache supports replication of tables without a\n * PRIMARY KEY to facilitate the onboarding process. These rows can be\n * INSERT'ed, but postgres will rightfully prohibit UPDATEs and DELETEs\n * on such tables because the rows cannot be identified by a key. Supporting\n * this mode of replication allows the user to \"fix\" the setup by adding the\n * primary key, after which the table can be published downstream without\n * requiring a resync of the data.\n *\n * In terms of backfill, however, non-empty tables without a row key **cannot**\n * be backfilled, because backfill retries would result in writing duplicating\n * rows. (Empty tables, on the other hand, are fine because there is no data\n * to be deduped.)\n *\n * The MissingRowKeyError is used to signal that the table cannot be backfilled\n * in its current state. For simplicity, it is handled like runtime errors and\n * retried with backoff, with which it can eventually succeed if (1) a primary\n * key is added or (2) the table is emptied, e.g. via a TRUNCATE.\n */\nclass MissingRowKeyError extends BackfillStreamError {\n readonly name = 'MissingRowKeyError';\n\n constructor(bf: BackfillRequest, cause?: unknown) {\n super(bf, `\"${bf.table.name}\" is missing a PRIMARY KEY`, cause);\n }\n}\n\n/**\n * Error type for backfill stream implementations to throw indicating that\n * the backfill request failed due to a schema incompatibility error. This\n * type of error does not need exponential backoff, as the retry happens\n * naturally once the invalidating schema change is processed and committed.\n */\nexport class SchemaIncompatibilityError extends BackfillStreamError {\n readonly name = 'SchemaIncompatibilityError';\n\n constructor(bf: BackfillRequest, msg: string, cause?: unknown) {\n super(bf, msg, cause);\n }\n}\n"],"mappings":";;;;;;;;;AAyBA,SAAS,SAAS,EAAC,QAAQ,QAAmB;AAC5C,QAAO,GAAG,OAAO,GAAG;;AAatB,IAAM,0BAA0B;AAChC,IAAM,0BAA0B;;;;;;;;;;;;;;;;;AAuBhC,IAAa,kBAAb,MAA6D;CAC3D;;;;;CAMA,qBAA8B,IAAI,aAChC,SACD;CACD;CACA;CACA;;;;;;;CAQA,mBAAgD;;CAGhD,uBAAsC;CAEtC,4BAAgE,EAAE;;CAGlE,sBAAqC;CAErC,YACE,IACA,gBACA,kBACA,aAAA,KACA,eAAe,yBACf,eAAe,yBACf;AACA,QAAA,KAAW,GAAG,YAAY,aAAa,mBAAmB;AAC1D,QAAA,iBAAuB;AACvB,QAAA,mBAAyB;AACzB,QAAA,aAAmB;AACnB,QAAA,eAAqB;AACrB,QAAA,eAAqB;AACrB,QAAA,eAAqB;;CAGvB,IAAI,eAAuB,iBAAoC;AAC7D,QAAA,GAAS,OACP,kCAAkC,gBAAgB,OAAO,oBACzD,EAAC,UAAU,iBAAgB,CAC5B;AACD,QAAA,sBAA4B;AAC5B,kBAAgB,SAAQ,QACtB,MAAA,oBAA0B,mBAAmB,IAAI,CAClD;AACD,QAAA,uBAA6B;;CAG/B,wBAAwB,EAAC,aAAiC;AAIxD,OAAK,MAAA,uBAA6B,MAAM,WAAW;AACjD,SAAA,sBAA4B;AAC5B,QAAK,IAAI,IAAI,MAAA,yBAA+B,SAAS,GAAG,KAAK,GAAG,KAAK;IACnE,MAAM,WAAW,MAAA,yBAA+B;AAChD,QAAI,aAAa,SAAS,WAAW;AACnC,cAAS,SAAS;AAClB,WAAA,yBAA+B,OAAO,GAAG,EAAE;;;;;CAMnD,qBACE,IACA,WACsB;AACtB,OAAK,MAAA,uBAA6B,MAAM,WAAW;GACjD,MAAM,EAAC,SAAS,SAAS,YAAW,UAAU;AAC9C,SAAA,yBAA+B,KAAK;IAAC;IAAW;IAAQ,CAAC;AACzD,MAAG,OACD,iCAAiC,MAAA,oBAA0B,aAAa,YACzE;AACD,UAAO;;AAET,SAAO;;CAGT;CACA;CACA;CACA;CAEA,yBAAyB;AACvB,MACE,CAAC,MAAA,sBACD,CAAC,MAAA,mBACD,MAAA,kBAAwB,MACxB;GAKA,MAAM,aAAa,CAAC,GAAG,MAAA,kBAAwB,QAAQ,CAAC;GACxD,MAAM,UAAU,WAAW,QAAQ,GAAG,WAAW,SAAS,EAAE;GAC5D,MAAM,QAAQ;IAAC;IAAS,cAAc;IAAG;GACzC,MAAM,KAAK,MAAA,GAAS,YAAY,SAAS,QAAQ,MAAM,KAAK;AAE5D,SAAA,kBAAwB;AACnB,SAAA,YAAkB,IAAI,MAAM,CAC9B,WAAW;AACV,UAAA,oBAA0B,mBAAmB,MAAM;AACnD,UAAA,eAAqB,MAAA;KACrB,CAGD,OAAM,MAAK;AACV,UAAA,oBAA0B,OAAO,EAAE,EAAE,MAAM;AAC3C,UAAA,yBAA+B,EAAE;KACjC;;;CAIR,0BAA0B,GAAY;EACpC,MAAM,MAAM,MAAA,iBAAuB,MAAA,eAAqB,UAAU;AAClE,QAAA,GAAS,OACP,uCAAuC,MAAA,aAAmB,MAC1D,EACD;AACD,QAAA,qBAA2B,iBAAiB;AAC1C,SAAA,qBAA2B,KAAA;AAC3B,SAAA,uBAA6B;KAC5B,MAAA,aAAmB;AAEtB,QAAA,eAAqB,KAAK,IAAI,MAAA,eAAqB,GAAG,MAAA,aAAmB;;CAG3E,OAAA,YAAmB,IAAgB,OAA6B;EAC9D,MAAM,eAAe,MAAA;EAIrB,IAAI,aAA4B;;;;EAKhC,MAAM,aAAa,OACjB,QAC2B;AAC3B,UAAO,eAAe,MAAM,0CAA0C;GACtE,MAAM,gBAAgB,MAAM,aAAa,QAAQ,WAAW;AAI5D,OACE,MAAM,kBACL,IAAI,QAAQ,cAAc,IAAI,YAAY,MAAM,cACjD;AACA,QAAI,MAAM,mBAAmB,KAAA,GAAW;AACtC,YAAO,IAAI,QAAQ,YAAY,gCAAgC;AAC/D,WAAA,oBACE,qBAAqB,MAAM,aAAa,mCACH,IAAI,aACzC,MACD;;AAEH,iBAAa,QAAQ,cAAc;AACnC,WAAO;;GAGT,MAAM,EAAC,OAAO,QAAQ,OAAM,uBAAuB,cAAc;GACjE,IAAI,KAAK,qBAAqB;IAC5B;IACA,OAAO,OAAO,MAAM,GAAG;IACxB,CAAC;AAEF,OAAI,IAAI,QAAQ,wBAAwB,KAAK,IAAI,UAM/C,MAAK,IAAI;AAGN,gBAAa,KAAK;IACrB;IACA;KAAC,KAAK;KAAS,MAAM,MAAA;KAAkB,SAAS;KAAK;IACrD,EAAC,iBAAiB,IAAG;IACtB,CAAC;AACF,UAAQ,aAAa;;EAGvB,MAAM,iBAAiB;AACrB,OAAI,YAAY;AACT,iBAAa,KAAK;KACrB;KACA,EAAC,KAAK,UAAS;KACf,EAAC,WAAW,YAAW;KACxB,CAAC;AACF,iBAAa,QAAQ,WAAW;;AAElC,gBAAa;;AAGf,aAAW,MAAM,OAAO,MAAA,iBAAuB,MAAM,QAAQ,EAAE;GAI7D,MAAM,sBACJ,IAAI,QAAQ,yBACX,MAAA,oBAA0B,IAAI,IAAI,UAAU,IAC1C,eAAe,QAAQ,aAAa,IAAI;AAG7C,OACE,eACC,aAAa,aAAa,GAAG,KAAK,qBAEnC,WAAU;AAGZ,0BAAwB,MAAM;AAE9B,OACE,IAAI,QAAQ,cACZ,IAAI,UAAU,SAAS,KACvB,IAAI,SAAS,OAAO,QAAQ,WAAW,EAEvC,OAAM,IAAI,mBAAmB,MAAM,QAAQ;AAI7C,QAAK,eAAe,MAAM,WAAW,IAAI,MAAM,MAAM;AACnD,OAAG,OACD,6BAA6B,MAAM,kBACnC,MAAM,QACP;AACD,UAAA,uBAA6B;AAC7B;;AAKF,SAAM,aAAa,KAAK,CAAC,QAAQ,IAAI,CAAC;;AAIxC,gBAAc,UAAU;AACxB,KAAG,QAAQ,0BAA0B,MAAM,kBAAkB,GAAG;;CAGlE,oBAAoB,OAAgD;EAClE,MAAM,QAAQ,MAAA;AACd,SAAO,OAAO,QAAQ,MAAM,WAAW,MAAM,UAC3C,MAAM,QAAQ,MAAM,SAAS,MAAM,OACjC,QACA;;;;;;;;CASN,qBAAqB,QAAiB,UAAiC;EACrE,MAAM,WAAW,MAAA;AACjB,MAAI,YAAY,cAAc,YAAY,WAAW;AACnD,YAAS,iBAAiB;AAC1B,SAAA,kBAAwB;AACxB,aAAU,MAAA,GAAS,OAAO,uBAAuB,OAAO;;;CAI5D,qBAAqB,QAAgB,KAAsB;EACzD,MAAM,SAAS,MAAA,kBAAwB,IAAI,IAAI,MAAM,GAAG,YAAY;AACpE,QAAA,GAAS,OAAO,YAAY,OAAO,IAAI,UAAU,EAAC,UAAU,KAAI,CAAC;AACjE,QAAA,kBAAwB,IAAI,IAAI,OAAO,IAAI;;CAG7C,wBAAwB,QAAgB,IAAgB;EACtD,MAAM,MAAM,MAAA,kBAAwB,IAAI,GAAG;AAC3C,MAAI,KAAK;GACP,MAAM,SAAS,WAAW,uBAAuB,cAAc;AAC/D,SAAA,GAAS,OAAO,YAAY,OAAO,IAAI,UAAU,EAAC,UAAU,KAAI,CAAC;AACjE,SAAA,kBAAwB,OAAO,GAAG;;;;;;;CAQtC,SAAS,SAAoC;AAC3C,MAAI,QAAQ,OAAO,SAAS;AAC1B,SAAA,qBAA2B,QAAQ,GAAG;AACtC;;AAEF,MAAI,QAAQ,OAAO,UAAU;AAC3B,SAAA,qBAA2B;AAC3B,SAAA,uBAA6B,QAAQ,GAAG;AAGxC,SAAA,uBAA6B;AAC7B;;AAEF,MAAI,QAAQ,OAAO,UAAU;AAC3B,SAAA,uBAA6B,QAAQ,GAAG;AACxC;;AAEF,MAAI,QAAQ,OAAO,OACjB;EAEF,MAAM,SAAS,QAAQ;EACvB,MAAM,EAAC,QAAO;AACd,UAAQ,KAAR;GACE,KAAK,yBAAyB;IAC5B,MAAM,EAAC,OAAO,KAAK,aAAY;IAC/B,MAAM,kBAAkB,MAAA,kBAAwB,IAAI,MAAM;AAC1D,QAAI,iBAAiB;AACnB,WAAA,oBAA0B,KAAK;MAC7B,GAAG;MACH,OAAO;OAAC,GAAG,gBAAgB;OAAO;OAAS;MAC5C,CAAC;AACF,SAAI,MAAA,mBAAyB,MAAM,CACjC,OAAA,oBAA0B,wBAAwB;;AAGtD;;GAEF,KAAK,gBAAgB;IACnB,MAAM,EACJ,MAAM,EAAC,QAAQ,QACf,WAAW,MACX,aACE;AAEJ,QAAI,SACF,OAAA,oBAA0B,KAAK;KAC7B,OAAO;MAAC;MAAQ;MAAM;MAAS;KAC/B,SAAS;KACV,CAAC;AAEJ;;GAEF,KAAK,gBAAgB;IACnB,MAAM,EAAC,KAAK,KAAK,aAAY;IAC7B,MAAM,kBAAkB,MAAA,kBAAwB,IAAI,IAAI;AACxD,QAAI,iBAAiB;KACnB,MAAM,EAAC,QAAQ,SAAQ;AACvB,WAAA,uBAA6B,KAAK,IAAI;AACtC,WAAA,oBAA0B,KAAK;MAC7B,GAAG;MACH,OAAO;OAAC,GAAG,gBAAgB;OAAO;OAAQ;OAAK;MAChD,CAAC;AACF,SAAI,MAAA,mBAAyB,IAAI,CAC/B,OAAA,oBAA0B,gBAAgB;;AAG9C;;GAEF,KAAK,cAAc;IACjB,MAAM,EAAC,OAAM;AAEb,QADwB,MAAA,kBAAwB,IAAI,GAAG,EAClC;AACnB,WAAA,uBAA6B,KAAK,GAAG;AACrC,SAAI,MAAA,mBAAyB,GAAG,CAC9B,OAAA,oBAA0B,gBAAgB;;AAG9C;;GAEF,KAAK,cAAc;IACjB,MAAM,EACJ,OACA,eAAe,WAAW,MAC1B,QACA,aACE;AACJ,QAAI,UAAU;KACZ,MAAM,kBAAkB,MAAA,kBAAwB,IAAI,MAAM;AAC1D,SAAI,CAAC,gBACH,OAAA,oBAA0B,KAAK;MAC7B,OAAO;OAAC,GAAG;OAAO;OAAS;MAC3B,SAAS,GAAE,OAAO,OAAO,UAAS;MACnC,CAAC;SAEF,OAAA,oBAA0B,KAAK;MAC7B,GAAG;MACH,OAAO;OAAC,GAAG,gBAAgB;OAAO;OAAS;MAC3C,SAAS;OACP,GAAG,gBAAgB;QAClB,OAAO,OAAO;OAChB;MACF,CAAC;;AAMN;;GAEF,KAAK,iBAAiB;IACpB,MAAM,EACJ,OACA,KAAK,EAAC,MAAM,WACZ,KAAK,EAAC,MAAM,cACV;AACJ,QAAI,YAAY,SAAS;KACvB,MAAM,kBAAkB,MAAA,kBAAwB,IAAI,MAAM;AAC1D,SAAI,mBAAmB,WAAW,gBAAgB,SAAS;MACzD,MAAM,GAAE,UAAU,SAAS,GAAG,cAAa,gBAAgB;AAC3D,YAAA,oBAA0B,KAAK;OAC7B,GAAG;OACH,SAAS;QAAC,GAAG;SAAY,UAAU;QAAQ;OAC5C,CAAC;MACF,MAAM,WAAW,MAAA,mBAAyB,MAAM;AAChD,UAAI,YAAY,WAAW,SAAS,QAAQ,QAC1C,OAAA,oBAA0B,iBAAiB;;;AAIjD;;GAEF,KAAK,eAAe;IAClB,MAAM,EAAC,OAAO,WAAU;IACxB,MAAM,kBAAkB,MAAA,kBAAwB,IAAI,MAAM;AAC1D,QAAI,mBAAmB,UAAU,gBAAgB,SAAS;KACxD,MAAM,GAAE,SAAS,WAAW,GAAG,cAAa,gBAAgB;AAC5D,SAAI,OAAO,KAAK,UAAU,CAAC,WAAW,EACpC,OAAA,uBAA6B,KAAK,MAAM;SAExC,OAAA,oBAA0B,KAAK;MAC7B,GAAG;MACH,SAAS;MACV,CAAC;KAEJ,MAAM,WAAW,MAAA,mBAAyB,MAAM;AAChD,SAAI,YAAY,UAAU,SAAS,QAAQ,QACzC,OAAA,oBAA0B,iBAAiB;;AAG/C;;GAEF,KAAK,UAAU;IACb,MAAM,EAAC,UAAU,KAAK,KAAK,QAAO;IAClC,MAAM,WAAW,MAAA,mBAAyB,SAAS;IACnD,MAAM,cAAc,KAAK,MAAA,oBAA0B,cAAc;AACjE,QAAI,UAAU,QAAQ,MAAM,YAAY,QAAQ;UAQzC,MAAM,OAAO,OAAO,KACvB,SAAS,QAAQ,MAAM,SAAS,OACjC,CACC,KAAI,IAAI,SAAS,IAAI,MAAM;AACzB,eAAS,eAAe;AACxB,YAAA,GAAS,OACP,gCAAgC,IAAI,oCACA,SAAS,eAC9C;AACD;;;AAIN;;GAEF,KAAK,sBAAsB;IACzB,MAAM,EAAC,UAAU,YAAW;IAC5B,MAAM,kBAAkB,MAAA,kBAAwB,IAAI,SAAS;AAC7D,WACE,uBACM,yCAAyC,UAAU,OAAO,GACjE;IACD,MAAM,YAAY,OAAO,QAAQ,gBAAgB,QAAQ,CAAC,QACvD,CAAC,SACA,EAAE,QAAQ,SAAS,IAAI,IAAI,SAAS,OAAO,QAAQ,SAAS,IAAI,EACnE;AACD,QAAI,UAAU,WAAW,EACvB,OAAA,uBAA6B,KAAK,SAAS;QAE3C,OAAA,oBAA0B,KAAK;KAC7B,GAAG;KACH,SAAS,OAAO,YAAY,UAAU;KACvC,CAAC;AAIJ,UAAA,qBAA2B;AAC3B;;;;CAKN,SAAe;AACb,QAAA,oBAA0B,yBAAyB;AACnD,eAAa,MAAA,mBAAyB;;;AAI1C,IAAe,sBAAf,cAA2C,MAAM;CAC/C,YAAY,IAAqB,KAAa,OAAiB;AAC7D,QACE,mBAAmB,GAAG,MAAM,OAAO,GAAG,GAAG,MAAM,KAAA,GACzC,OAAO,KAAK,GAAG,QAAQ,CAAC,KAAK,IAAI,CAAC,KAAK,OAC7C,EAAC,OAAM,CACR;;;;;;;;;;;;;;;;;;;;;;AAuBL,IAAM,qBAAN,cAAiC,oBAAoB;CACnD,OAAgB;CAEhB,YAAY,IAAqB,OAAiB;AAChD,QAAM,IAAI,IAAI,GAAG,MAAM,KAAK,6BAA6B,MAAM;;;;;;;;;AAUnE,IAAa,6BAAb,cAAgD,oBAAoB;CAClE,OAAgB;CAEhB,YAAY,IAAqB,KAAa,OAAiB;AAC7D,QAAM,IAAI,KAAK,MAAM"}
1
+ {"version":3,"file":"backfill-manager.js","names":["#lc","#requiredBackfills","#changeStreamer","#backfillStreamer","#jsonFormat","#awaitingStatusWatermarks","#minBackoffMs","#maxBackoffMs","#retryDelayMs","#lastStatusWatermark","#setRequiredBackfill","#checkAndStartBackfill","#backfillRetryTimer","#runningBackfill","#runBackfill","#stopRunningBackfill","#retryBackfillWithBackoff","#changeStreamReached","#currentTxWatermark","#setLastStatusWatermark","#backfillRunningFor","#deleteRequiredBackfill"],"sources":["../../../../../../../zero-cache/src/services/change-source/common/backfill-manager.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {assert} from '../../../../../shared/src/asserts.ts';\nimport {stringify} from '../../../../../shared/src/bigint-json.ts';\nimport {CustomKeyMap} from '../../../../../shared/src/custom-key-map.ts';\nimport {must} from '../../../../../shared/src/must.ts';\nimport {randInt} from '../../../../../shared/src/rand.ts';\nimport {JSON_STRINGIFIED, type JSONFormat} from '../../../types/lite.ts';\nimport {\n stateVersionFromString,\n stateVersionToString,\n} from '../../../types/state-version.ts';\nimport type {\n BackfillCompleted,\n BackfillRequest,\n ChangeStreamMessage,\n Identifier,\n MessageBackfill,\n} from '../protocol/current.ts';\nimport type {\n Cancelable,\n ChangeStreamMultiplexer,\n Listener,\n} from './change-stream-multiplexer.ts';\n\nfunction tableKey({schema, name}: Identifier) {\n return `${schema}.${name}`;\n}\n\ntype BackfillStreamer = (\n req: BackfillRequest,\n) => AsyncGenerator<MessageBackfill | BackfillCompleted>;\n\ntype RunningBackfillState = {\n request: BackfillRequest;\n canceledReason?: string | undefined;\n minWatermark: string;\n};\n\nconst MIN_BACKOFF_INTERVAL_MS = 2_000;\nconst MAX_BACKOFF_INTERVAL_MS = 60_000;\n\ntype AwaitingStatusWatermark = {\n watermark: string;\n reached: () => void;\n};\n\n/**\n * The BackfillManager initiates backfills for BackfillRequests from the\n * change-streamer (i.e. unfinished backfills from previous sessions)\n * or for new backfills signaled by `create-table` or `add-column` messages\n * from the change-source.\n *\n * The BackfillManager registers itself as a change stream listener in order\n * to track necessary backfills, and potentially invalidate the in-progress\n * backfill (e.g. due to a schema change) so that it can be retried at a\n * new snapshot.\n *\n * The manager also handles low priority streaming of the backfill messages\n * using the {@link ChangeStreamMultiplexer}, implementing a policy of always\n * releasing its reservation if another producer (i.e. the main change stream)\n * has messages to stream.\n */\nexport class BackfillManager implements Cancelable, Listener {\n readonly #lc: LogContext;\n\n /**\n * Tracks the metadata of required backfills based on schema changes\n * and initial backfill requests.\n */\n readonly #requiredBackfills = new CustomKeyMap<Identifier, BackfillRequest>(\n tableKey,\n );\n readonly #changeStreamer: ChangeStreamMultiplexer;\n readonly #backfillStreamer: BackfillStreamer;\n readonly #jsonFormat: JSONFormat;\n\n /**\n * The current running backfill. The backfill request is always also in\n * `#requiredBackfills` (technically, it can be a subset of what's in\n * `#requiredBackfills`); the request is removed from `#requiredBackfills`\n * upon completion.\n */\n #runningBackfill: RunningBackfillState | null = null;\n\n /** The last seen watermark in the change stream. */\n #lastStatusWatermark: string | null = null;\n\n readonly #awaitingStatusWatermarks: AwaitingStatusWatermark[] = [];\n\n /** The watermark of the current transaction in the change stream. */\n #currentTxWatermark: string | null = null;\n\n constructor(\n lc: LogContext,\n changeStreamer: ChangeStreamMultiplexer,\n backfillStreamer: BackfillStreamer,\n jsonFormat: JSONFormat = JSON_STRINGIFIED,\n minBackoffMs = MIN_BACKOFF_INTERVAL_MS,\n maxBackoffMs = MAX_BACKOFF_INTERVAL_MS,\n ) {\n this.#lc = lc.withContext('component', 'backfill-manager');\n this.#changeStreamer = changeStreamer;\n this.#backfillStreamer = backfillStreamer;\n this.#jsonFormat = jsonFormat;\n this.#minBackoffMs = minBackoffMs;\n this.#maxBackoffMs = maxBackoffMs;\n this.#retryDelayMs = minBackoffMs;\n }\n\n run(lastWatermark: string, initialRequests: BackfillRequest[]) {\n this.#lc.info?.(\n `starting backfill manager with ${initialRequests.length} initial requests`,\n {requests: initialRequests},\n );\n this.#lastStatusWatermark = lastWatermark;\n initialRequests.forEach(req =>\n this.#setRequiredBackfill('initial-request', req),\n );\n this.#checkAndStartBackfill();\n }\n\n #setLastStatusWatermark({watermark}: {watermark: string}) {\n // Only allow the watermark to move forward. This prevents a backfill\n // transaction (whose watermark is unrelated to change-stream state)\n // from moving the watermark backwards.\n if ((this.#lastStatusWatermark ?? '') < watermark) {\n this.#lastStatusWatermark = watermark;\n for (let i = this.#awaitingStatusWatermarks.length - 1; i >= 0; i--) {\n const awaiting = this.#awaitingStatusWatermarks[i];\n if (watermark >= awaiting.watermark) {\n awaiting.reached();\n this.#awaitingStatusWatermarks.splice(i, 1);\n }\n }\n }\n }\n\n #changeStreamReached(\n lc: LogContext,\n watermark: string,\n ): Promise<void> | null {\n if ((this.#lastStatusWatermark ?? '') < watermark) {\n const {promise, resolve: reached} = resolver();\n this.#awaitingStatusWatermarks.push({watermark, reached});\n lc.info?.(\n `waiting for change stream (at ${this.#lastStatusWatermark}) to reach ${watermark}`,\n );\n return promise;\n }\n return null;\n }\n\n readonly #minBackoffMs: number;\n readonly #maxBackoffMs: number;\n #retryDelayMs: number;\n #backfillRetryTimer: NodeJS.Timeout | undefined;\n\n #checkAndStartBackfill() {\n if (\n !this.#backfillRetryTimer &&\n !this.#runningBackfill &&\n this.#requiredBackfills.size\n ) {\n // Pick a random backfill to avoid head-of-line blocking by a\n // problematic backfill (e.g. awaiting a primary key). This is\n // simpler that adding logic to classify (and declassify)\n // problematic backfills.\n const candidates = [...this.#requiredBackfills.values()];\n const request = candidates[randInt(0, candidates.length - 1)];\n const state = {request, minWatermark: ''};\n const lc = this.#lc.withContext('table', request.table.name);\n\n this.#runningBackfill = state;\n void this.#runBackfill(lc, state)\n .then(() => {\n this.#stopRunningBackfill('backfill exited', state);\n this.#retryDelayMs = this.#minBackoffMs; // reset on success\n })\n // For unexpected errors (e.g. upstream replication slot\n // unavailability), retry with exponential backoff.\n .catch(e => {\n this.#stopRunningBackfill(String(e), state);\n this.#retryBackfillWithBackoff(e);\n });\n }\n }\n\n #retryBackfillWithBackoff(e: unknown) {\n const log = this.#retryDelayMs === this.#maxBackoffMs ? 'error' : 'warn';\n this.#lc[log]?.(\n `Error running backfill. Retrying in ${this.#retryDelayMs} ms`,\n e,\n );\n this.#backfillRetryTimer = setTimeout(() => {\n this.#backfillRetryTimer = undefined;\n this.#checkAndStartBackfill();\n }, this.#retryDelayMs);\n\n this.#retryDelayMs = Math.min(this.#retryDelayMs * 2, this.#maxBackoffMs);\n }\n\n async #runBackfill(lc: LogContext, state: RunningBackfillState) {\n const changeStream = this.#changeStreamer; // Purely for readability\n\n // backfillTx is set if and only if a changeStreamer reservation has been\n // acquired and the backfill stream is inside a transaction.\n let backfillTx: string | null = null;\n\n /**\n * @returns the new tx watermark, or null if backfill was cancelled\n */\n const beginTxFor = async (\n msg: MessageBackfill | BackfillCompleted,\n ): Promise<string | null> => {\n assert(backfillTx === null, 'Expected no active backfill transaction');\n const lastWatermark = await changeStream.reserve('backfill');\n\n // After obtaining the changeStream reservation, check if the stream\n // had changes that resulted in invalidating / canceling this backfill.\n if (\n state.canceledReason ||\n (msg.tag === 'backfill' && msg.watermark < state.minWatermark)\n ) {\n if (state.canceledReason === undefined) {\n assert(msg.tag === 'backfill', 'Expected backfill message tag'); // TypeScript should have figured this out.\n this.#stopRunningBackfill(\n `row key change at ${state.minWatermark} ` +\n `postdates backfill watermark at ${msg.watermark}`,\n state,\n );\n }\n changeStream.release(lastWatermark);\n return null;\n }\n\n const {major, minor = 0n} = stateVersionFromString(lastWatermark);\n let tx = stateVersionToString({\n major,\n minor: BigInt(minor) + 1n,\n });\n\n if (msg.tag === 'backfill-completed' && tx < msg.watermark) {\n // At this point it must be the case that the #changeStreamReached() the\n // backfill watermark. Given that guarantee, ensure that the version of the\n // transaction containing the backfill-completed message is at least up\n // to the backfill watermark, so that the final database state version is\n // never earlier than the version of any backfilled rows.\n tx = msg.watermark;\n }\n\n void changeStream.push([\n 'begin',\n {tag: 'begin', json: this.#jsonFormat, skipAck: true},\n {commitWatermark: tx},\n ]);\n return (backfillTx = tx);\n };\n\n const commitTx = () => {\n if (backfillTx) {\n void changeStream.push([\n 'commit',\n {tag: 'commit'},\n {watermark: backfillTx},\n ]);\n changeStream.release(backfillTx);\n }\n backfillTx = null;\n };\n\n for await (const msg of this.#backfillStreamer(state.request)) {\n // Before sending `backfill-completed`, the main replication stream\n // may need to catch up, and/or the current transaction may need to be\n // committed to open a new transaction that's up to backfill watermark.\n const mustWaitBeforeFlush =\n msg.tag === 'backfill-completed' &&\n (this.#changeStreamReached(lc, msg.watermark) ||\n (backfillTx !== null && backfillTx < msg.watermark));\n\n // If necessary, yield the reservation to the main stream.\n if (\n backfillTx &&\n (changeStream.waiterDelay() > 0 || mustWaitBeforeFlush)\n ) {\n commitTx();\n }\n\n mustWaitBeforeFlush && (await mustWaitBeforeFlush);\n\n if (\n msg.tag === 'backfill' &&\n msg.rowValues.length > 0 &&\n msg.relation.rowKey.columns.length === 0\n ) {\n throw new MissingRowKeyError(state.request);\n }\n\n // Reserve the changeStreamer if not in a transaction.\n if ((backfillTx ??= await beginTxFor(msg)) === null) {\n lc.info?.(\n `backfill stream canceled: ${state.canceledReason}`,\n state.request,\n );\n this.#checkAndStartBackfill(); // start the next backfill if present\n return; // this backfill is canceled\n }\n\n // `await` to allow the change streamer to exert back pressure\n // on backfills.\n await changeStream.push(['data', msg]);\n }\n\n // Flush any final tx and release the stream.\n backfillTx && commitTx();\n lc.debug?.(`backfill stream exited`, state.canceledReason ?? '');\n }\n\n #backfillRunningFor(table: Identifier): RunningBackfillState | null {\n const state = this.#runningBackfill;\n return state?.request.table.schema === table.schema &&\n state.request.table.name === table.name\n ? state\n : null;\n }\n\n /**\n * Stops the running backfill for the specified `reason`. If `instance` is\n * specified, the running backfill is stopped only if it is that instance.\n * This allows the running backfill itself to clear backfill state without\n * accidentally stopping a different (e.g. subsequent) backfill.\n */\n #stopRunningBackfill(reason?: string, instance?: RunningBackfillState) {\n const backfill = this.#runningBackfill;\n if (backfill && backfill === (instance ?? backfill)) {\n backfill.canceledReason = reason;\n this.#runningBackfill = null;\n reason && this.#lc.info?.(`canceling backfill:`, reason);\n }\n }\n\n #setRequiredBackfill(source: string, req: BackfillRequest) {\n const action = this.#requiredBackfills.has(req.table) ? 'updated' : 'added';\n this.#lc.info?.(`Backfill ${action}: ${source}`, {backfill: req});\n this.#requiredBackfills.set(req.table, req);\n }\n\n #deleteRequiredBackfill(source: string, id: Identifier) {\n const req = this.#requiredBackfills.get(id);\n if (req) {\n const action = source === 'backfill-completed' ? 'completed' : 'dropped';\n this.#lc.info?.(`Backfill ${action}: ${source}`, {backfill: req});\n this.#requiredBackfills.delete(id);\n }\n }\n\n /**\n * Implements {@link Listener.onChange()}, invoked by the\n * {@link ChangeStreamMultiplexer}.\n */\n onChange(message: ChangeStreamMessage): void {\n if (message[0] === 'begin') {\n this.#currentTxWatermark = message[2].commitWatermark;\n return;\n }\n if (message[0] === 'commit') {\n this.#currentTxWatermark = null;\n this.#setLastStatusWatermark(message[2]);\n // Every commit is a candidate for starting the next backfill\n // (if one is not currently running).\n this.#checkAndStartBackfill();\n return;\n }\n if (message[0] === 'status') {\n this.#setLastStatusWatermark(message[2]);\n return;\n }\n if (message[0] !== 'data') {\n return;\n }\n const change = message[1];\n const {tag} = change;\n switch (tag) {\n case 'update-table-metadata': {\n const {table, new: metadata} = change;\n const backfillRequest = this.#requiredBackfills.get(table);\n if (backfillRequest) {\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n table: {...backfillRequest.table, metadata},\n });\n if (this.#backfillRunningFor(table)) {\n this.#stopRunningBackfill(`TableMetadata updated`);\n }\n }\n break;\n }\n case 'create-table': {\n const {\n spec: {schema, name},\n metadata = null,\n backfill,\n } = change;\n\n if (backfill) {\n this.#setRequiredBackfill(tag, {\n table: {schema, name, metadata},\n columns: backfill,\n });\n }\n break;\n }\n case 'rename-table': {\n const {old, new: newTable} = change;\n const backfillRequest = this.#requiredBackfills.get(old);\n if (backfillRequest) {\n const {schema, name} = newTable;\n this.#deleteRequiredBackfill(tag, old);\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n table: {...backfillRequest.table, schema, name},\n });\n if (this.#backfillRunningFor(old)) {\n this.#stopRunningBackfill(`table renamed`);\n }\n }\n break;\n }\n case 'drop-table': {\n const {id} = change;\n const backfillRequest = this.#requiredBackfills.get(id);\n if (backfillRequest) {\n this.#deleteRequiredBackfill(tag, id);\n if (this.#backfillRunningFor(id)) {\n this.#stopRunningBackfill(`table dropped`);\n }\n }\n break;\n }\n case 'add-column': {\n const {\n table,\n tableMetadata: metadata = null,\n column,\n backfill,\n } = change;\n if (backfill) {\n const backfillRequest = this.#requiredBackfills.get(table);\n if (!backfillRequest) {\n this.#setRequiredBackfill(tag, {\n table: {...table, metadata},\n columns: {[column.name]: backfill},\n });\n } else {\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n table: {...backfillRequest.table, metadata},\n columns: {\n ...backfillRequest.columns,\n [column.name]: backfill,\n },\n });\n // Note: The running backfill need not be canceled if a\n // new column is added. The new column will be backfilled\n // by its own stream after the current backfill completes.\n }\n }\n break;\n }\n case 'update-column': {\n const {\n table,\n old: {name: oldName},\n new: {name: newName},\n } = change;\n if (oldName !== newName) {\n const backfillRequest = this.#requiredBackfills.get(table);\n if (backfillRequest && oldName in backfillRequest.columns) {\n const {[oldName]: colSpec, ...otherCols} = backfillRequest.columns;\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n columns: {...otherCols, [newName]: colSpec},\n });\n const backfill = this.#backfillRunningFor(table);\n if (backfill && oldName in backfill.request.columns) {\n this.#stopRunningBackfill(`column renamed`);\n }\n }\n }\n break;\n }\n case 'drop-column': {\n const {table, column} = change;\n const backfillRequest = this.#requiredBackfills.get(table);\n if (backfillRequest && column in backfillRequest.columns) {\n const {[column]: _excluded, ...remaining} = backfillRequest.columns;\n if (Object.keys(remaining).length === 0) {\n this.#deleteRequiredBackfill(tag, table);\n } else {\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n columns: remaining,\n });\n }\n const backfill = this.#backfillRunningFor(table);\n if (backfill && column in backfill.request.columns) {\n this.#stopRunningBackfill(`column dropped`);\n }\n }\n break;\n }\n case 'update': {\n const {relation, key, new: row} = change;\n const backfill = this.#backfillRunningFor(relation);\n const txWatermark = must(this.#currentTxWatermark, `not in a tx`);\n if (backfill?.request.table.metadata && key !== null) {\n // A corner case that backfill is unable to correctly handle is\n // when a row's key changes; this is decomposed into a delete\n // of the old key and a set of the new key in the replica change\n // log, at which point the backfill algorithm assumes that the\n // (old) row is deleted but does not know to backfill the new row.\n // In these corner cases, the current backfill is canceled and\n // retried if its version precedes this update.\n for (const col of Object.keys(\n backfill.request.table.metadata.rowKey,\n )) {\n if (key[col] !== row[col]) {\n backfill.minWatermark = txWatermark;\n this.#lc.info?.(\n `key for row as changed (col: ${col}). ` +\n `backfill data must not predate ${backfill.minWatermark}`,\n );\n break;\n }\n }\n }\n break;\n }\n case 'backfill-completed': {\n const {relation, columns} = change;\n const backfillRequest = this.#requiredBackfills.get(relation);\n assert(\n backfillRequest,\n () => `No BackfillRequest completed backfill ${stringify(change)}`,\n );\n const remaining = Object.entries(backfillRequest.columns).filter(\n ([col]) =>\n !(columns.includes(col) || relation.rowKey.columns.includes(col)),\n );\n if (remaining.length === 0) {\n this.#deleteRequiredBackfill(tag, relation);\n } else {\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n columns: Object.fromEntries(remaining),\n });\n }\n // Technically the backfill is already stopping, but this method\n // cleans up the state that tracks it.\n this.#stopRunningBackfill();\n break;\n }\n }\n }\n\n cancel(): void {\n this.#stopRunningBackfill(`change stream canceled`);\n clearTimeout(this.#backfillRetryTimer);\n }\n}\n\nabstract class BackfillStreamError extends Error {\n constructor(bf: BackfillRequest, msg: string, cause?: unknown) {\n super(\n `Cannot backfill ${bf.table.schema}.${bf.table.name}` +\n `[${Object.keys(bf.columns).join(',')}]: ${msg}`,\n {cause},\n );\n }\n}\n\n/**\n * Background: The zero-cache supports replication of tables without a\n * PRIMARY KEY to facilitate the onboarding process. These rows can be\n * INSERT'ed, but postgres will rightfully prohibit UPDATEs and DELETEs\n * on such tables because the rows cannot be identified by a key. Supporting\n * this mode of replication allows the user to \"fix\" the setup by adding the\n * primary key, after which the table can be published downstream without\n * requiring a resync of the data.\n *\n * In terms of backfill, however, non-empty tables without a row key **cannot**\n * be backfilled, because backfill retries would result in writing duplicating\n * rows. (Empty tables, on the other hand, are fine because there is no data\n * to be deduped.)\n *\n * The MissingRowKeyError is used to signal that the table cannot be backfilled\n * in its current state. For simplicity, it is handled like runtime errors and\n * retried with backoff, with which it can eventually succeed if (1) a primary\n * key is added or (2) the table is emptied, e.g. via a TRUNCATE.\n */\nclass MissingRowKeyError extends BackfillStreamError {\n readonly name = 'MissingRowKeyError';\n\n constructor(bf: BackfillRequest, cause?: unknown) {\n super(bf, `\"${bf.table.name}\" is missing a PRIMARY KEY`, cause);\n }\n}\n\n/**\n * Error type for backfill stream implementations to throw indicating that\n * the backfill request failed due to a schema incompatibility error. This\n * type of error does not need exponential backoff, as the retry happens\n * naturally once the invalidating schema change is processed and committed.\n */\nexport class SchemaIncompatibilityError extends BackfillStreamError {\n readonly name = 'SchemaIncompatibilityError';\n\n constructor(bf: BackfillRequest, msg: string, cause?: unknown) {\n super(bf, msg, cause);\n }\n}\n"],"mappings":";;;;;;;;;AAyBA,SAAS,SAAS,EAAC,QAAQ,QAAmB;CAC5C,OAAO,GAAG,OAAO,GAAG;AACtB;AAYA,IAAM,0BAA0B;AAChC,IAAM,0BAA0B;;;;;;;;;;;;;;;;;AAuBhC,IAAa,kBAAb,MAA6D;CAC3D;;;;;CAMA,qBAA8B,IAAI,aAChC,QACF;CACA;CACA;CACA;;;;;;;CAQA,mBAAgD;;CAGhD,uBAAsC;CAEtC,4BAAgE,CAAC;;CAGjE,sBAAqC;CAErC,YACE,IACA,gBACA,kBACA,aAAA,KACA,eAAe,yBACf,eAAe,yBACf;EACA,KAAKA,MAAM,GAAG,YAAY,aAAa,kBAAkB;EACzD,KAAKE,kBAAkB;EACvB,KAAKC,oBAAoB;EACzB,KAAKC,cAAc;EACnB,KAAKE,gBAAgB;EACrB,KAAKC,gBAAgB;EACrB,KAAKC,gBAAgB;CACvB;CAEA,IAAI,eAAuB,iBAAoC;EAC7D,KAAKR,IAAI,OACP,kCAAkC,gBAAgB,OAAO,oBACzD,EAAC,UAAU,gBAAe,CAC5B;EACA,KAAKS,uBAAuB;EAC5B,gBAAgB,SAAQ,QACtB,KAAKC,qBAAqB,mBAAmB,GAAG,CAClD;EACA,KAAKC,uBAAuB;CAC9B;CAEA,wBAAwB,EAAC,aAAiC;EAIxD,KAAK,KAAKF,wBAAwB,MAAM,WAAW;GACjD,KAAKA,uBAAuB;GAC5B,KAAK,IAAI,IAAI,KAAKJ,0BAA0B,SAAS,GAAG,KAAK,GAAG,KAAK;IACnE,MAAM,WAAW,KAAKA,0BAA0B;IAChD,IAAI,aAAa,SAAS,WAAW;KACnC,SAAS,QAAQ;KACjB,KAAKA,0BAA0B,OAAO,GAAG,CAAC;IAC5C;GACF;EACF;CACF;CAEA,qBACE,IACA,WACsB;EACtB,KAAK,KAAKI,wBAAwB,MAAM,WAAW;GACjD,MAAM,EAAC,SAAS,SAAS,YAAW,SAAS;GAC7C,KAAKJ,0BAA0B,KAAK;IAAC;IAAW;GAAO,CAAC;GACxD,GAAG,OACD,iCAAiC,KAAKI,qBAAqB,aAAa,WAC1E;GACA,OAAO;EACT;EACA,OAAO;CACT;CAEA;CACA;CACA;CACA;CAEA,yBAAyB;EACvB,IACE,CAAC,KAAKG,uBACN,CAAC,KAAKC,oBACN,KAAKZ,mBAAmB,MACxB;GAKA,MAAM,aAAa,CAAC,GAAG,KAAKA,mBAAmB,OAAO,CAAC;GACvD,MAAM,UAAU,WAAW,QAAQ,GAAG,WAAW,SAAS,CAAC;GAC3D,MAAM,QAAQ;IAAC;IAAS,cAAc;GAAE;GACxC,MAAM,KAAK,KAAKD,IAAI,YAAY,SAAS,QAAQ,MAAM,IAAI;GAE3D,KAAKa,mBAAmB;GACxB,KAAUC,aAAa,IAAI,KAAK,EAC7B,WAAW;IACV,KAAKC,qBAAqB,mBAAmB,KAAK;IAClD,KAAKP,gBAAgB,KAAKF;GAC5B,CAAC,EAGA,OAAM,MAAK;IACV,KAAKS,qBAAqB,OAAO,CAAC,GAAG,KAAK;IAC1C,KAAKC,0BAA0B,CAAC;GAClC,CAAC;EACL;CACF;CAEA,0BAA0B,GAAY;EACpC,MAAM,MAAM,KAAKR,kBAAkB,KAAKD,gBAAgB,UAAU;EAClE,KAAKP,IAAI,OACP,uCAAuC,KAAKQ,cAAc,MAC1D,CACF;EACA,KAAKI,sBAAsB,iBAAiB;GAC1C,KAAKA,sBAAsB,KAAA;GAC3B,KAAKD,uBAAuB;EAC9B,GAAG,KAAKH,aAAa;EAErB,KAAKA,gBAAgB,KAAK,IAAI,KAAKA,gBAAgB,GAAG,KAAKD,aAAa;CAC1E;CAEA,MAAMO,aAAa,IAAgB,OAA6B;EAC9D,MAAM,eAAe,KAAKZ;EAI1B,IAAI,aAA4B;;;;EAKhC,MAAM,aAAa,OACjB,QAC2B;GAC3B,OAAO,eAAe,MAAM,yCAAyC;GACrE,MAAM,gBAAgB,MAAM,aAAa,QAAQ,UAAU;GAI3D,IACE,MAAM,kBACL,IAAI,QAAQ,cAAc,IAAI,YAAY,MAAM,cACjD;IACA,IAAI,MAAM,mBAAmB,KAAA,GAAW;KACtC,OAAO,IAAI,QAAQ,YAAY,+BAA+B;KAC9D,KAAKa,qBACH,qBAAqB,MAAM,aAAa,mCACH,IAAI,aACzC,KACF;IACF;IACA,aAAa,QAAQ,aAAa;IAClC,OAAO;GACT;GAEA,MAAM,EAAC,OAAO,QAAQ,OAAM,uBAAuB,aAAa;GAChE,IAAI,KAAK,qBAAqB;IAC5B;IACA,OAAO,OAAO,KAAK,IAAI;GACzB,CAAC;GAED,IAAI,IAAI,QAAQ,wBAAwB,KAAK,IAAI,WAM/C,KAAK,IAAI;GAGX,aAAkB,KAAK;IACrB;IACA;KAAC,KAAK;KAAS,MAAM,KAAKX;KAAa,SAAS;IAAI;IACpD,EAAC,iBAAiB,GAAE;GACtB,CAAC;GACD,OAAQ,aAAa;EACvB;EAEA,MAAM,iBAAiB;GACrB,IAAI,YAAY;IACd,aAAkB,KAAK;KACrB;KACA,EAAC,KAAK,SAAQ;KACd,EAAC,WAAW,WAAU;IACxB,CAAC;IACD,aAAa,QAAQ,UAAU;GACjC;GACA,aAAa;EACf;EAEA,WAAW,MAAM,OAAO,KAAKD,kBAAkB,MAAM,OAAO,GAAG;GAI7D,MAAM,sBACJ,IAAI,QAAQ,yBACX,KAAKc,qBAAqB,IAAI,IAAI,SAAS,KACzC,eAAe,QAAQ,aAAa,IAAI;GAG7C,IACE,eACC,aAAa,YAAY,IAAI,KAAK,sBAEnC,SAAS;GAGX,uBAAwB,MAAM;GAE9B,IACE,IAAI,QAAQ,cACZ,IAAI,UAAU,SAAS,KACvB,IAAI,SAAS,OAAO,QAAQ,WAAW,GAEvC,MAAM,IAAI,mBAAmB,MAAM,OAAO;GAI5C,KAAK,eAAe,MAAM,WAAW,GAAG,OAAO,MAAM;IACnD,GAAG,OACD,6BAA6B,MAAM,kBACnC,MAAM,OACR;IACA,KAAKN,uBAAuB;IAC5B;GACF;GAIA,MAAM,aAAa,KAAK,CAAC,QAAQ,GAAG,CAAC;EACvC;EAGA,cAAc,SAAS;EACvB,GAAG,QAAQ,0BAA0B,MAAM,kBAAkB,EAAE;CACjE;CAEA,oBAAoB,OAAgD;EAClE,MAAM,QAAQ,KAAKE;EACnB,OAAO,OAAO,QAAQ,MAAM,WAAW,MAAM,UAC3C,MAAM,QAAQ,MAAM,SAAS,MAAM,OACjC,QACA;CACN;;;;;;;CAQA,qBAAqB,QAAiB,UAAiC;EACrE,MAAM,WAAW,KAAKA;EACtB,IAAI,YAAY,cAAc,YAAY,WAAW;GACnD,SAAS,iBAAiB;GAC1B,KAAKA,mBAAmB;GACxB,UAAU,KAAKb,IAAI,OAAO,uBAAuB,MAAM;EACzD;CACF;CAEA,qBAAqB,QAAgB,KAAsB;EACzD,MAAM,SAAS,KAAKC,mBAAmB,IAAI,IAAI,KAAK,IAAI,YAAY;EACpE,KAAKD,IAAI,OAAO,YAAY,OAAO,IAAI,UAAU,EAAC,UAAU,IAAG,CAAC;EAChE,KAAKC,mBAAmB,IAAI,IAAI,OAAO,GAAG;CAC5C;CAEA,wBAAwB,QAAgB,IAAgB;EACtD,MAAM,MAAM,KAAKA,mBAAmB,IAAI,EAAE;EAC1C,IAAI,KAAK;GACP,MAAM,SAAS,WAAW,uBAAuB,cAAc;GAC/D,KAAKD,IAAI,OAAO,YAAY,OAAO,IAAI,UAAU,EAAC,UAAU,IAAG,CAAC;GAChE,KAAKC,mBAAmB,OAAO,EAAE;EACnC;CACF;;;;;CAMA,SAAS,SAAoC;EAC3C,IAAI,QAAQ,OAAO,SAAS;GAC1B,KAAKiB,sBAAsB,QAAQ,GAAG;GACtC;EACF;EACA,IAAI,QAAQ,OAAO,UAAU;GAC3B,KAAKA,sBAAsB;GAC3B,KAAKC,wBAAwB,QAAQ,EAAE;GAGvC,KAAKR,uBAAuB;GAC5B;EACF;EACA,IAAI,QAAQ,OAAO,UAAU;GAC3B,KAAKQ,wBAAwB,QAAQ,EAAE;GACvC;EACF;EACA,IAAI,QAAQ,OAAO,QACjB;EAEF,MAAM,SAAS,QAAQ;EACvB,MAAM,EAAC,QAAO;EACd,QAAQ,KAAR;GACE,KAAK,yBAAyB;IAC5B,MAAM,EAAC,OAAO,KAAK,aAAY;IAC/B,MAAM,kBAAkB,KAAKlB,mBAAmB,IAAI,KAAK;IACzD,IAAI,iBAAiB;KACnB,KAAKS,qBAAqB,KAAK;MAC7B,GAAG;MACH,OAAO;OAAC,GAAG,gBAAgB;OAAO;MAAQ;KAC5C,CAAC;KACD,IAAI,KAAKU,oBAAoB,KAAK,GAChC,KAAKL,qBAAqB,uBAAuB;IAErD;IACA;GACF;GACA,KAAK,gBAAgB;IACnB,MAAM,EACJ,MAAM,EAAC,QAAQ,QACf,WAAW,MACX,aACE;IAEJ,IAAI,UACF,KAAKL,qBAAqB,KAAK;KAC7B,OAAO;MAAC;MAAQ;MAAM;KAAQ;KAC9B,SAAS;IACX,CAAC;IAEH;GACF;GACA,KAAK,gBAAgB;IACnB,MAAM,EAAC,KAAK,KAAK,aAAY;IAC7B,MAAM,kBAAkB,KAAKT,mBAAmB,IAAI,GAAG;IACvD,IAAI,iBAAiB;KACnB,MAAM,EAAC,QAAQ,SAAQ;KACvB,KAAKoB,wBAAwB,KAAK,GAAG;KACrC,KAAKX,qBAAqB,KAAK;MAC7B,GAAG;MACH,OAAO;OAAC,GAAG,gBAAgB;OAAO;OAAQ;MAAI;KAChD,CAAC;KACD,IAAI,KAAKU,oBAAoB,GAAG,GAC9B,KAAKL,qBAAqB,eAAe;IAE7C;IACA;GACF;GACA,KAAK,cAAc;IACjB,MAAM,EAAC,OAAM;IAEb,IADwB,KAAKd,mBAAmB,IAAI,EAChD,GAAiB;KACnB,KAAKoB,wBAAwB,KAAK,EAAE;KACpC,IAAI,KAAKD,oBAAoB,EAAE,GAC7B,KAAKL,qBAAqB,eAAe;IAE7C;IACA;GACF;GACA,KAAK,cAAc;IACjB,MAAM,EACJ,OACA,eAAe,WAAW,MAC1B,QACA,aACE;IACJ,IAAI,UAAU;KACZ,MAAM,kBAAkB,KAAKd,mBAAmB,IAAI,KAAK;KACzD,IAAI,CAAC,iBACH,KAAKS,qBAAqB,KAAK;MAC7B,OAAO;OAAC,GAAG;OAAO;MAAQ;MAC1B,SAAS,GAAE,OAAO,OAAO,SAAQ;KACnC,CAAC;UAED,KAAKA,qBAAqB,KAAK;MAC7B,GAAG;MACH,OAAO;OAAC,GAAG,gBAAgB;OAAO;MAAQ;MAC1C,SAAS;OACP,GAAG,gBAAgB;QAClB,OAAO,OAAO;MACjB;KACF,CAAC;IAKL;IACA;GACF;GACA,KAAK,iBAAiB;IACpB,MAAM,EACJ,OACA,KAAK,EAAC,MAAM,WACZ,KAAK,EAAC,MAAM,cACV;IACJ,IAAI,YAAY,SAAS;KACvB,MAAM,kBAAkB,KAAKT,mBAAmB,IAAI,KAAK;KACzD,IAAI,mBAAmB,WAAW,gBAAgB,SAAS;MACzD,MAAM,GAAE,UAAU,SAAS,GAAG,cAAa,gBAAgB;MAC3D,KAAKS,qBAAqB,KAAK;OAC7B,GAAG;OACH,SAAS;QAAC,GAAG;SAAY,UAAU;OAAO;MAC5C,CAAC;MACD,MAAM,WAAW,KAAKU,oBAAoB,KAAK;MAC/C,IAAI,YAAY,WAAW,SAAS,QAAQ,SAC1C,KAAKL,qBAAqB,gBAAgB;KAE9C;IACF;IACA;GACF;GACA,KAAK,eAAe;IAClB,MAAM,EAAC,OAAO,WAAU;IACxB,MAAM,kBAAkB,KAAKd,mBAAmB,IAAI,KAAK;IACzD,IAAI,mBAAmB,UAAU,gBAAgB,SAAS;KACxD,MAAM,GAAE,SAAS,WAAW,GAAG,cAAa,gBAAgB;KAC5D,IAAI,OAAO,KAAK,SAAS,EAAE,WAAW,GACpC,KAAKoB,wBAAwB,KAAK,KAAK;UAEvC,KAAKX,qBAAqB,KAAK;MAC7B,GAAG;MACH,SAAS;KACX,CAAC;KAEH,MAAM,WAAW,KAAKU,oBAAoB,KAAK;KAC/C,IAAI,YAAY,UAAU,SAAS,QAAQ,SACzC,KAAKL,qBAAqB,gBAAgB;IAE9C;IACA;GACF;GACA,KAAK,UAAU;IACb,MAAM,EAAC,UAAU,KAAK,KAAK,QAAO;IAClC,MAAM,WAAW,KAAKK,oBAAoB,QAAQ;IAClD,MAAM,cAAc,KAAK,KAAKF,qBAAqB,aAAa;IAChE,IAAI,UAAU,QAAQ,MAAM,YAAY,QAAQ;UAQzC,MAAM,OAAO,OAAO,KACvB,SAAS,QAAQ,MAAM,SAAS,MAClC,GACE,IAAI,IAAI,SAAS,IAAI,MAAM;MACzB,SAAS,eAAe;MACxB,KAAKlB,IAAI,OACP,gCAAgC,IAAI,oCACA,SAAS,cAC/C;MACA;KACF;;IAGJ;GACF;GACA,KAAK,sBAAsB;IACzB,MAAM,EAAC,UAAU,YAAW;IAC5B,MAAM,kBAAkB,KAAKC,mBAAmB,IAAI,QAAQ;IAC5D,OACE,uBACM,yCAAyC,UAAU,MAAM,GACjE;IACA,MAAM,YAAY,OAAO,QAAQ,gBAAgB,OAAO,EAAE,QACvD,CAAC,SACA,EAAE,QAAQ,SAAS,GAAG,KAAK,SAAS,OAAO,QAAQ,SAAS,GAAG,EACnE;IACA,IAAI,UAAU,WAAW,GACvB,KAAKoB,wBAAwB,KAAK,QAAQ;SAE1C,KAAKX,qBAAqB,KAAK;KAC7B,GAAG;KACH,SAAS,OAAO,YAAY,SAAS;IACvC,CAAC;IAIH,KAAKK,qBAAqB;IAC1B;GACF;EACF;CACF;CAEA,SAAe;EACb,KAAKA,qBAAqB,wBAAwB;EAClD,aAAa,KAAKH,mBAAmB;CACvC;AACF;AAEA,IAAe,sBAAf,cAA2C,MAAM;CAC/C,YAAY,IAAqB,KAAa,OAAiB;EAC7D,MACE,mBAAmB,GAAG,MAAM,OAAO,GAAG,GAAG,MAAM,KAAA,GACzC,OAAO,KAAK,GAAG,OAAO,EAAE,KAAK,GAAG,EAAE,KAAK,OAC7C,EAAC,MAAK,CACR;CACF;AACF;;;;;;;;;;;;;;;;;;;;AAqBA,IAAM,qBAAN,cAAiC,oBAAoB;CACnD,OAAgB;CAEhB,YAAY,IAAqB,OAAiB;EAChD,MAAM,IAAI,IAAI,GAAG,MAAM,KAAK,6BAA6B,KAAK;CAChE;AACF;;;;;;;AAQA,IAAa,6BAAb,cAAgD,oBAAoB;CAClE,OAAgB;CAEhB,YAAY,IAAqB,KAAa,OAAiB;EAC7D,MAAM,IAAI,KAAK,KAAK;CACtB;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"change-stream-multiplexer.js","names":["#lc","#sub","#producers","#listeners","#waiters","#lastWatermark"],"sources":["../../../../../../../zero-cache/src/services/change-source/common/change-stream-multiplexer.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {assert} from '../../../../../shared/src/asserts.ts';\nimport type {Source} from '../../../types/streams.ts';\nimport {Subscription} from '../../../types/subscription.ts';\nimport type {\n ChangeStreamMessage,\n DownstreamStatusMessage,\n} from '../protocol/current.ts';\n\nexport type Cancelable = {\n cancel(): void;\n};\n\nexport type Listener = {\n onChange(change: ChangeStreamMessage): void;\n};\n\ntype Waiter = {\n producer: string;\n startTime: number;\n grantReservation: (watermark: string) => void;\n};\n\n/**\n * Facilitates cooperative multiplexing of transactions from\n * multiple producers into a single change stream.\n */\nexport class ChangeStreamMultiplexer {\n readonly #lc: LogContext;\n readonly #sub: Subscription<ChangeStreamMessage>;\n readonly #producers: Cancelable[] = [];\n readonly #listeners: Listener[] = [];\n\n /**\n * The `#lastWatermark` tracks the watermark of the last transaction\n * committed to stream. It is set to null when a producer has reserved\n * the stream, and set to the new watermark when the producer releases\n * the reservation.\n */\n #lastWatermark: string | null;\n\n /**\n * Tracks the queue of producers waiting for a reservation.\n */\n readonly #waiters: Waiter[] = [];\n\n constructor(lc: LogContext, lastWatermark: string) {\n this.#lc = lc;\n this.#sub = Subscription.create<ChangeStreamMessage>({\n cleanup: () => this.#producers.forEach(p => p.cancel()),\n });\n this.#lastWatermark = lastWatermark;\n }\n\n addProducers(...p: Cancelable[]): this {\n this.#producers.push(...p);\n return this;\n }\n\n addListeners(...l: Listener[]): this {\n this.#listeners.push(...l);\n return this;\n }\n\n /**\n * Called by a producer to \"reserve\" the exclusive right to push data\n * changes to the stream. If the stream is currently reserved,\n * the resulting Promise must be awaited.\n *\n * The producer must then {@link release()} the reservation when it sees\n * fit.\n *\n * @param producer The name of the producer, purely for debugging output\n */\n reserve(producer: string): string | Promise<string> {\n if (this.#lastWatermark !== null) {\n // If the stream is not reserved, reserve it and return the\n // watermark.\n const lastWatermark = this.#lastWatermark;\n this.#lastWatermark = null;\n return lastWatermark;\n }\n\n // Otherwise, wait for the current reservation to be released.\n const startTime = performance.now();\n const {promise, resolve: grantReservation} = resolver<string>();\n this.#waiters.push({producer, startTime, grantReservation});\n\n return promise;\n }\n\n /**\n * If there are producers currently awaiting a reservation, returns\n * the duration (in milliseconds) of the oldest reservation request.\n * Returns a negative number if there are no waiters.\n */\n waiterDelay(): number {\n if (this.#waiters.length === 0) {\n return -1;\n }\n return performance.now() - this.#waiters[0].startTime;\n }\n\n /**\n * Called by a producer to release its reservation after committing its\n * last transaction.\n */\n release(newWatermark: string) {\n const waiter = this.#waiters.shift();\n if (!waiter) {\n this.#lastWatermark = newWatermark;\n } else {\n const {producer, startTime, grantReservation} = waiter;\n grantReservation(newWatermark);\n const elapsed = performance.now() - startTime;\n this.#lc.info?.(\n `${producer} waited ${elapsed.toFixed(3)} ms for stream reservation`,\n );\n }\n }\n\n /**\n * `pushStatus()` can be called without a reservation, as it\n * does not constitute a data change and can appear anywhere\n * in the stream.\n */\n pushStatus(message: DownstreamStatusMessage) {\n // Let listeners know about all status messages.\n this.#listeners.forEach(l => l.onChange(message));\n // The ChangeStreamer only cares about status messages requiring an ack\n // or containing a lagReport. To reduce churn, avoid sending other status\n // messages.\n if (message[1].ack || message[1].lagReport) {\n this.#sub.push(message);\n }\n }\n\n /**\n * `push()` must only be called by a producer after it has\n * {@link reserve}d the stream.\n */\n push(message: ChangeStreamMessage): Promise<unknown> {\n assert(\n this.#lastWatermark === null,\n `push() called without reserve()-ing the stream`,\n );\n this.#listeners.forEach(l => l.onChange(message));\n return this.#sub.push(message).result;\n }\n\n fail(err: Error) {\n this.#sub.fail(err);\n }\n\n asSource(): Source<ChangeStreamMessage> {\n return this.#sub;\n }\n}\n"],"mappings":";;;;;;;;AA4BA,IAAa,0BAAb,MAAqC;CACnC;CACA;CACA,aAAoC,EAAE;CACtC,aAAkC,EAAE;;;;;;;CAQpC;;;;CAKA,WAA8B,EAAE;CAEhC,YAAY,IAAgB,eAAuB;AACjD,QAAA,KAAW;AACX,QAAA,MAAY,aAAa,OAA4B,EACnD,eAAe,MAAA,UAAgB,SAAQ,MAAK,EAAE,QAAQ,CAAC,EACxD,CAAC;AACF,QAAA,gBAAsB;;CAGxB,aAAa,GAAG,GAAuB;AACrC,QAAA,UAAgB,KAAK,GAAG,EAAE;AAC1B,SAAO;;CAGT,aAAa,GAAG,GAAqB;AACnC,QAAA,UAAgB,KAAK,GAAG,EAAE;AAC1B,SAAO;;;;;;;;;;;;CAaT,QAAQ,UAA4C;AAClD,MAAI,MAAA,kBAAwB,MAAM;GAGhC,MAAM,gBAAgB,MAAA;AACtB,SAAA,gBAAsB;AACtB,UAAO;;EAIT,MAAM,YAAY,YAAY,KAAK;EACnC,MAAM,EAAC,SAAS,SAAS,qBAAoB,UAAkB;AAC/D,QAAA,QAAc,KAAK;GAAC;GAAU;GAAW;GAAiB,CAAC;AAE3D,SAAO;;;;;;;CAQT,cAAsB;AACpB,MAAI,MAAA,QAAc,WAAW,EAC3B,QAAO;AAET,SAAO,YAAY,KAAK,GAAG,MAAA,QAAc,GAAG;;;;;;CAO9C,QAAQ,cAAsB;EAC5B,MAAM,SAAS,MAAA,QAAc,OAAO;AACpC,MAAI,CAAC,OACH,OAAA,gBAAsB;OACjB;GACL,MAAM,EAAC,UAAU,WAAW,qBAAoB;AAChD,oBAAiB,aAAa;GAC9B,MAAM,UAAU,YAAY,KAAK,GAAG;AACpC,SAAA,GAAS,OACP,GAAG,SAAS,UAAU,QAAQ,QAAQ,EAAE,CAAC,4BAC1C;;;;;;;;CASL,WAAW,SAAkC;AAE3C,QAAA,UAAgB,SAAQ,MAAK,EAAE,SAAS,QAAQ,CAAC;AAIjD,MAAI,QAAQ,GAAG,OAAO,QAAQ,GAAG,UAC/B,OAAA,IAAU,KAAK,QAAQ;;;;;;CAQ3B,KAAK,SAAgD;AACnD,SACE,MAAA,kBAAwB,MACxB,iDACD;AACD,QAAA,UAAgB,SAAQ,MAAK,EAAE,SAAS,QAAQ,CAAC;AACjD,SAAO,MAAA,IAAU,KAAK,QAAQ,CAAC;;CAGjC,KAAK,KAAY;AACf,QAAA,IAAU,KAAK,IAAI;;CAGrB,WAAwC;AACtC,SAAO,MAAA"}
1
+ {"version":3,"file":"change-stream-multiplexer.js","names":["#lc","#sub","#producers","#listeners","#waiters","#lastWatermark"],"sources":["../../../../../../../zero-cache/src/services/change-source/common/change-stream-multiplexer.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {assert} from '../../../../../shared/src/asserts.ts';\nimport type {Source} from '../../../types/streams.ts';\nimport {Subscription} from '../../../types/subscription.ts';\nimport type {\n ChangeStreamMessage,\n DownstreamStatusMessage,\n} from '../protocol/current.ts';\n\nexport type Cancelable = {\n cancel(): void;\n};\n\nexport type Listener = {\n onChange(change: ChangeStreamMessage): void;\n};\n\ntype Waiter = {\n producer: string;\n startTime: number;\n grantReservation: (watermark: string) => void;\n};\n\n/**\n * Facilitates cooperative multiplexing of transactions from\n * multiple producers into a single change stream.\n */\nexport class ChangeStreamMultiplexer {\n readonly #lc: LogContext;\n readonly #sub: Subscription<ChangeStreamMessage>;\n readonly #producers: Cancelable[] = [];\n readonly #listeners: Listener[] = [];\n\n /**\n * The `#lastWatermark` tracks the watermark of the last transaction\n * committed to stream. It is set to null when a producer has reserved\n * the stream, and set to the new watermark when the producer releases\n * the reservation.\n */\n #lastWatermark: string | null;\n\n /**\n * Tracks the queue of producers waiting for a reservation.\n */\n readonly #waiters: Waiter[] = [];\n\n constructor(lc: LogContext, lastWatermark: string) {\n this.#lc = lc;\n this.#sub = Subscription.create<ChangeStreamMessage>({\n cleanup: () => this.#producers.forEach(p => p.cancel()),\n });\n this.#lastWatermark = lastWatermark;\n }\n\n addProducers(...p: Cancelable[]): this {\n this.#producers.push(...p);\n return this;\n }\n\n addListeners(...l: Listener[]): this {\n this.#listeners.push(...l);\n return this;\n }\n\n /**\n * Called by a producer to \"reserve\" the exclusive right to push data\n * changes to the stream. If the stream is currently reserved,\n * the resulting Promise must be awaited.\n *\n * The producer must then {@link release()} the reservation when it sees\n * fit.\n *\n * @param producer The name of the producer, purely for debugging output\n */\n reserve(producer: string): string | Promise<string> {\n if (this.#lastWatermark !== null) {\n // If the stream is not reserved, reserve it and return the\n // watermark.\n const lastWatermark = this.#lastWatermark;\n this.#lastWatermark = null;\n return lastWatermark;\n }\n\n // Otherwise, wait for the current reservation to be released.\n const startTime = performance.now();\n const {promise, resolve: grantReservation} = resolver<string>();\n this.#waiters.push({producer, startTime, grantReservation});\n\n return promise;\n }\n\n /**\n * If there are producers currently awaiting a reservation, returns\n * the duration (in milliseconds) of the oldest reservation request.\n * Returns a negative number if there are no waiters.\n */\n waiterDelay(): number {\n if (this.#waiters.length === 0) {\n return -1;\n }\n return performance.now() - this.#waiters[0].startTime;\n }\n\n /**\n * Called by a producer to release its reservation after committing its\n * last transaction.\n */\n release(newWatermark: string) {\n const waiter = this.#waiters.shift();\n if (!waiter) {\n this.#lastWatermark = newWatermark;\n } else {\n const {producer, startTime, grantReservation} = waiter;\n grantReservation(newWatermark);\n const elapsed = performance.now() - startTime;\n this.#lc.info?.(\n `${producer} waited ${elapsed.toFixed(3)} ms for stream reservation`,\n );\n }\n }\n\n /**\n * `pushStatus()` can be called without a reservation, as it\n * does not constitute a data change and can appear anywhere\n * in the stream.\n */\n pushStatus(message: DownstreamStatusMessage) {\n // Let listeners know about all status messages.\n this.#listeners.forEach(l => l.onChange(message));\n // The ChangeStreamer only cares about status messages requiring an ack\n // or containing a lagReport. To reduce churn, avoid sending other status\n // messages.\n if (message[1].ack || message[1].lagReport) {\n this.#sub.push(message);\n }\n }\n\n /**\n * `push()` must only be called by a producer after it has\n * {@link reserve}d the stream.\n */\n push(message: ChangeStreamMessage): Promise<unknown> {\n assert(\n this.#lastWatermark === null,\n `push() called without reserve()-ing the stream`,\n );\n this.#listeners.forEach(l => l.onChange(message));\n return this.#sub.push(message).result;\n }\n\n fail(err: Error) {\n this.#sub.fail(err);\n }\n\n asSource(): Source<ChangeStreamMessage> {\n return this.#sub;\n }\n}\n"],"mappings":";;;;;;;;AA4BA,IAAa,0BAAb,MAAqC;CACnC;CACA;CACA,aAAoC,CAAC;CACrC,aAAkC,CAAC;;;;;;;CAQnC;;;;CAKA,WAA8B,CAAC;CAE/B,YAAY,IAAgB,eAAuB;EACjD,KAAKA,MAAM;EACX,KAAKC,OAAO,aAAa,OAA4B,EACnD,eAAe,KAAKC,WAAW,SAAQ,MAAK,EAAE,OAAO,CAAC,EACxD,CAAC;EACD,KAAKG,iBAAiB;CACxB;CAEA,aAAa,GAAG,GAAuB;EACrC,KAAKH,WAAW,KAAK,GAAG,CAAC;EACzB,OAAO;CACT;CAEA,aAAa,GAAG,GAAqB;EACnC,KAAKC,WAAW,KAAK,GAAG,CAAC;EACzB,OAAO;CACT;;;;;;;;;;;CAYA,QAAQ,UAA4C;EAClD,IAAI,KAAKE,mBAAmB,MAAM;GAGhC,MAAM,gBAAgB,KAAKA;GAC3B,KAAKA,iBAAiB;GACtB,OAAO;EACT;EAGA,MAAM,YAAY,YAAY,IAAI;EAClC,MAAM,EAAC,SAAS,SAAS,qBAAoB,SAAiB;EAC9D,KAAKD,SAAS,KAAK;GAAC;GAAU;GAAW;EAAgB,CAAC;EAE1D,OAAO;CACT;;;;;;CAOA,cAAsB;EACpB,IAAI,KAAKA,SAAS,WAAW,GAC3B,OAAO;EAET,OAAO,YAAY,IAAI,IAAI,KAAKA,SAAS,GAAG;CAC9C;;;;;CAMA,QAAQ,cAAsB;EAC5B,MAAM,SAAS,KAAKA,SAAS,MAAM;EACnC,IAAI,CAAC,QACH,KAAKC,iBAAiB;OACjB;GACL,MAAM,EAAC,UAAU,WAAW,qBAAoB;GAChD,iBAAiB,YAAY;GAC7B,MAAM,UAAU,YAAY,IAAI,IAAI;GACpC,KAAKL,IAAI,OACP,GAAG,SAAS,UAAU,QAAQ,QAAQ,CAAC,EAAE,2BAC3C;EACF;CACF;;;;;;CAOA,WAAW,SAAkC;EAE3C,KAAKG,WAAW,SAAQ,MAAK,EAAE,SAAS,OAAO,CAAC;EAIhD,IAAI,QAAQ,GAAG,OAAO,QAAQ,GAAG,WAC/B,KAAKF,KAAK,KAAK,OAAO;CAE1B;;;;;CAMA,KAAK,SAAgD;EACnD,OACE,KAAKI,mBAAmB,MACxB,gDACF;EACA,KAAKF,WAAW,SAAQ,MAAK,EAAE,SAAS,OAAO,CAAC;EAChD,OAAO,KAAKF,KAAK,KAAK,OAAO,EAAE;CACjC;CAEA,KAAK,KAAY;EACf,KAAKA,KAAK,KAAK,GAAG;CACpB;CAEA,WAAwC;EACtC,OAAO,KAAKA;CACd;AACF"}