@rocicorp/zero 1.11.0-canary.3 → 1.11.0-canary.30
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.
- package/out/analyze-query/src/analyze-cli.js +4 -4
- package/out/analyze-query/src/analyze-cli.js.map +1 -1
- package/out/analyze-query/src/bin-analyze.js +13 -12
- package/out/analyze-query/src/bin-analyze.js.map +1 -1
- package/out/analyze-query/src/bin-transform.js +1 -1
- package/out/analyze-query/src/bin-transform.js.map +1 -1
- package/out/ast-to-zql/src/ast-to-zql.d.ts +8 -1
- package/out/ast-to-zql/src/ast-to-zql.d.ts.map +1 -1
- package/out/ast-to-zql/src/ast-to-zql.js +25 -25
- package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
- package/out/otel/src/log-options.d.ts +16 -0
- package/out/otel/src/log-options.d.ts.map +1 -1
- package/out/otel/src/log-options.js +60 -6
- package/out/otel/src/log-options.js.map +1 -1
- package/out/replicache/src/bg-interval.d.ts +2 -1
- package/out/replicache/src/bg-interval.d.ts.map +1 -1
- package/out/replicache/src/bg-interval.js +13 -4
- package/out/replicache/src/bg-interval.js.map +1 -1
- package/out/replicache/src/btree/node.d.ts.map +1 -1
- package/out/replicache/src/btree/node.js +3 -6
- package/out/replicache/src/btree/node.js.map +1 -1
- package/out/replicache/src/dag/gc.d.ts.map +1 -1
- package/out/replicache/src/dag/gc.js +8 -11
- package/out/replicache/src/dag/gc.js.map +1 -1
- package/out/replicache/src/dag/invalid-ref-count-error.d.ts +12 -0
- package/out/replicache/src/dag/invalid-ref-count-error.d.ts.map +1 -0
- package/out/replicache/src/dag/invalid-ref-count-error.js +19 -0
- package/out/replicache/src/dag/invalid-ref-count-error.js.map +1 -0
- package/out/replicache/src/dag/lazy-store.d.ts +17 -4
- package/out/replicache/src/dag/lazy-store.d.ts.map +1 -1
- package/out/replicache/src/dag/lazy-store.js +35 -9
- package/out/replicache/src/dag/lazy-store.js.map +1 -1
- package/out/replicache/src/dag/store-impl.d.ts +12 -2
- package/out/replicache/src/dag/store-impl.d.ts.map +1 -1
- package/out/replicache/src/dag/store-impl.js +29 -9
- package/out/replicache/src/dag/store-impl.js.map +1 -1
- package/out/replicache/src/db/rebase.d.ts +14 -2
- package/out/replicache/src/db/rebase.d.ts.map +1 -1
- package/out/replicache/src/db/rebase.js +27 -5
- package/out/replicache/src/db/rebase.js.map +1 -1
- package/out/replicache/src/get-kv-store-provider.d.ts +1 -1
- package/out/replicache/src/get-kv-store-provider.d.ts.map +1 -1
- package/out/replicache/src/get-kv-store-provider.js +4 -4
- package/out/replicache/src/get-kv-store-provider.js.map +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.d.ts.map +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.js +28 -5
- package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
- package/out/replicache/src/kv/idb-store.d.ts +18 -0
- package/out/replicache/src/kv/idb-store.d.ts.map +1 -1
- package/out/replicache/src/kv/idb-store.js +30 -2
- package/out/replicache/src/kv/idb-store.js.map +1 -1
- package/out/replicache/src/kv/mem-fallback-store.d.ts +54 -0
- package/out/replicache/src/kv/mem-fallback-store.d.ts.map +1 -0
- package/out/replicache/src/kv/mem-fallback-store.js +177 -0
- package/out/replicache/src/kv/mem-fallback-store.js.map +1 -0
- package/out/replicache/src/kv/mem-store.d.ts +1 -0
- package/out/replicache/src/kv/mem-store.d.ts.map +1 -1
- package/out/replicache/src/kv/mem-store.js +3 -0
- package/out/replicache/src/kv/mem-store.js.map +1 -1
- package/out/replicache/src/kv/op-sqlite/store.d.ts.map +1 -1
- package/out/replicache/src/kv/op-sqlite/store.js +4 -4
- package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
- package/out/replicache/src/kv/sqlite-store.d.ts +22 -1
- package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
- package/out/replicache/src/kv/sqlite-store.js +159 -14
- package/out/replicache/src/kv/sqlite-store.js.map +1 -1
- package/out/replicache/src/kv/store.d.ts +6 -0
- package/out/replicache/src/kv/store.d.ts.map +1 -1
- package/out/replicache/src/new-client-channel.js.map +1 -1
- package/out/replicache/src/persist/client-gc.d.ts +2 -1
- package/out/replicache/src/persist/client-gc.d.ts.map +1 -1
- package/out/replicache/src/persist/client-gc.js +2 -2
- package/out/replicache/src/persist/client-gc.js.map +1 -1
- package/out/replicache/src/persist/client-group-gc.d.ts +2 -1
- package/out/replicache/src/persist/client-group-gc.d.ts.map +1 -1
- package/out/replicache/src/persist/client-group-gc.js +2 -2
- package/out/replicache/src/persist/client-group-gc.js.map +1 -1
- package/out/replicache/src/persist/clients.d.ts +2 -2
- package/out/replicache/src/persist/collect-idb-databases.d.ts +28 -8
- package/out/replicache/src/persist/collect-idb-databases.d.ts.map +1 -1
- package/out/replicache/src/persist/collect-idb-databases.js +97 -20
- package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
- package/out/replicache/src/persist/heartbeat.d.ts +2 -1
- package/out/replicache/src/persist/heartbeat.d.ts.map +1 -1
- package/out/replicache/src/persist/heartbeat.js +2 -2
- package/out/replicache/src/persist/heartbeat.js.map +1 -1
- package/out/replicache/src/persist/idb-databases-store-db-name.js +1 -1
- package/out/replicache/src/persist/idb-databases-store-db-name.js.map +1 -1
- package/out/replicache/src/persist/persist.d.ts.map +1 -1
- package/out/replicache/src/persist/persist.js +18 -8
- package/out/replicache/src/persist/persist.js.map +1 -1
- package/out/replicache/src/persist/refresh.js +6 -2
- package/out/replicache/src/persist/refresh.js.map +1 -1
- package/out/replicache/src/replicache-impl.d.ts +85 -3
- package/out/replicache/src/replicache-impl.d.ts.map +1 -1
- package/out/replicache/src/replicache-impl.js +321 -29
- package/out/replicache/src/replicache-impl.js.map +1 -1
- package/out/replicache/src/replicache-options.d.ts +9 -0
- package/out/replicache/src/replicache-options.d.ts.map +1 -1
- package/out/replicache/src/storage-failure.d.ts +37 -0
- package/out/replicache/src/storage-failure.d.ts.map +1 -0
- package/out/replicache/src/storage-failure.js +31 -0
- package/out/replicache/src/storage-failure.js.map +1 -0
- package/out/shared/src/arrays.d.ts +5 -0
- package/out/shared/src/arrays.d.ts.map +1 -1
- package/out/shared/src/arrays.js +8 -10
- package/out/shared/src/arrays.js.map +1 -1
- package/out/shared/src/base64.d.ts +19 -0
- package/out/shared/src/base64.d.ts.map +1 -0
- package/out/shared/src/base64.js +40 -0
- package/out/shared/src/base64.js.map +1 -0
- package/out/shared/src/centroid.d.ts +0 -1
- package/out/shared/src/centroid.d.ts.map +1 -1
- package/out/shared/src/centroid.js +1 -4
- package/out/shared/src/centroid.js.map +1 -1
- package/out/shared/src/error.d.ts +11 -0
- package/out/shared/src/error.d.ts.map +1 -1
- package/out/shared/src/error.js +35 -1
- package/out/shared/src/error.js.map +1 -1
- package/out/shared/src/hash.d.ts.map +1 -1
- package/out/shared/src/hash.js +32 -7
- package/out/shared/src/hash.js.map +1 -1
- package/out/shared/src/iterables.d.ts +2 -0
- package/out/shared/src/iterables.d.ts.map +1 -1
- package/out/shared/src/iterables.js +18 -1
- package/out/shared/src/iterables.js.map +1 -1
- package/out/shared/src/map.d.ts +24 -2
- package/out/shared/src/map.d.ts.map +1 -1
- package/out/shared/src/map.js +18 -4
- package/out/shared/src/map.js.map +1 -1
- package/out/shared/src/options-types.d.ts +2 -2
- package/out/shared/src/options.d.ts.map +1 -1
- package/out/shared/src/options.js +10 -7
- package/out/shared/src/options.js.map +1 -1
- package/out/shared/src/packed-boolean-map.d.ts +7 -0
- package/out/shared/src/packed-boolean-map.d.ts.map +1 -0
- package/out/shared/src/packed-boolean-map.js +36 -0
- package/out/shared/src/packed-boolean-map.js.map +1 -0
- package/out/shared/src/random-values.js +12 -1
- package/out/shared/src/random-values.js.map +1 -1
- package/out/shared/src/ring-buffer.d.ts +10 -0
- package/out/shared/src/ring-buffer.d.ts.map +1 -1
- package/out/shared/src/ring-buffer.js +26 -1
- package/out/shared/src/ring-buffer.js.map +1 -1
- package/out/shared/src/tdigest.d.ts +10 -0
- package/out/shared/src/tdigest.d.ts.map +1 -1
- package/out/shared/src/tdigest.js +147 -63
- package/out/shared/src/tdigest.js.map +1 -1
- package/out/shared/src/valita.d.ts +13 -0
- package/out/shared/src/valita.d.ts.map +1 -1
- package/out/shared/src/valita.js +23 -23
- package/out/shared/src/valita.js.map +1 -1
- package/out/z2s/src/compiler.d.ts.map +1 -1
- package/out/z2s/src/compiler.js +10 -6
- package/out/z2s/src/compiler.js.map +1 -1
- package/out/z2s/src/sql.d.ts.map +1 -1
- package/out/z2s/src/sql.js +9 -5
- package/out/z2s/src/sql.js.map +1 -1
- package/out/zero/package.js +4 -5
- package/out/zero/package.js.map +1 -1
- package/out/zero/src/zero.js +2 -1
- package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
- package/out/zero-cache/src/auth/write-authorizer.js +1 -0
- package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
- package/out/zero-cache/src/config/normalize.d.ts +1 -0
- package/out/zero-cache/src/config/normalize.d.ts.map +1 -1
- package/out/zero-cache/src/config/normalize.js +46 -4
- package/out/zero-cache/src/config/normalize.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +116 -24
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +409 -157
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/custom/fetch.d.ts +1 -1
- package/out/zero-cache/src/custom/fetch.d.ts.map +1 -1
- package/out/zero-cache/src/custom/fetch.js.map +1 -1
- package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
- package/out/zero-cache/src/db/lite-tables.js +3 -4
- package/out/zero-cache/src/db/lite-tables.js.map +1 -1
- package/out/zero-cache/src/db/migration-lite.d.ts.map +1 -1
- package/out/zero-cache/src/db/migration-lite.js +5 -3
- package/out/zero-cache/src/db/migration-lite.js.map +1 -1
- package/out/zero-cache/src/observability/events.js +4 -1
- package/out/zero-cache/src/observability/events.js.map +1 -1
- package/out/zero-cache/src/observability/log-throttle.d.ts +44 -0
- package/out/zero-cache/src/observability/log-throttle.d.ts.map +1 -0
- package/out/zero-cache/src/observability/log-throttle.js +76 -0
- package/out/zero-cache/src/observability/log-throttle.js.map +1 -0
- package/out/zero-cache/src/observability/metrics.d.ts.map +1 -1
- package/out/zero-cache/src/observability/metrics.js +2 -7
- package/out/zero-cache/src/observability/metrics.js.map +1 -1
- package/out/zero-cache/src/scripts/permissions.d.ts +16 -0
- package/out/zero-cache/src/scripts/permissions.d.ts.map +1 -1
- package/out/zero-cache/src/scripts/permissions.js +3 -3
- package/out/zero-cache/src/scripts/permissions.js.map +1 -1
- package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +117 -77
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/inspector-delegate.d.ts +28 -2
- package/out/zero-cache/src/server/inspector-delegate.d.ts.map +1 -1
- package/out/zero-cache/src/server/inspector-delegate.js +37 -17
- package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
- package/out/zero-cache/src/server/main.d.ts.map +1 -1
- package/out/zero-cache/src/server/main.js +8 -1
- package/out/zero-cache/src/server/main.js.map +1 -1
- package/out/zero-cache/src/server/mutator.js +2 -0
- package/out/zero-cache/src/server/mutator.js.map +1 -1
- package/out/zero-cache/src/server/reaper.d.ts.map +1 -1
- package/out/zero-cache/src/server/reaper.js +2 -0
- package/out/zero-cache/src/server/reaper.js.map +1 -1
- package/out/zero-cache/src/server/replicator.d.ts +3 -0
- package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/server/replicator.js +95 -21
- package/out/zero-cache/src/server/replicator.js.map +1 -1
- package/out/zero-cache/src/server/runner/zero-dispatcher.d.ts.map +1 -1
- package/out/zero-cache/src/server/runner/zero-dispatcher.js +5 -0
- package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
- package/out/zero-cache/src/server/syncer-assigner.d.ts +14 -0
- package/out/zero-cache/src/server/syncer-assigner.d.ts.map +1 -0
- package/out/zero-cache/src/server/syncer-assigner.js +72 -0
- package/out/zero-cache/src/server/syncer-assigner.js.map +1 -0
- package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/server/syncer.js +24 -2
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/server/worker-dispatcher.d.ts.map +1 -1
- package/out/zero-cache/src/server/worker-dispatcher.js +21 -4
- package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
- package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
- package/out/zero-cache/src/services/analyze.js +11 -9
- package/out/zero-cache/src/services/analyze.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js +198 -24
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js +16 -4
- package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/init-cleanup.d.ts +24 -0
- package/out/zero-cache/src/services/change-source/common/init-cleanup.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-source/common/init-cleanup.js +44 -0
- package/out/zero-cache/src/services/change-source/common/init-cleanup.js.map +1 -0
- package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts +92 -2
- package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-restore.js +27 -10
- package/out/zero-cache/src/services/change-source/common/replica-restore.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts +1 -0
- package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.js +15 -3
- package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.d.ts +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +7 -3
- package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts +9 -0
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +206 -19
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source-init.d.ts +55 -0
- package/out/zero-cache/src/services/change-source/pg/change-source-init.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-source/pg/change-source-init.js +305 -0
- package/out/zero-cache/src/services/change-source/pg/change-source-init.js.map +1 -0
- package/out/zero-cache/src/services/change-source/pg/change-source.d.ts +31 -18
- package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.js +130 -157
- package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +38 -2
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js +55 -21
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts +31 -0
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +102 -3
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.d.ts +16 -0
- package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js +113 -0
- package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js.map +1 -0
- package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +40 -15
- package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slots.js +149 -27
- package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js +68 -8
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.js +32 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.d.ts +10 -0
- package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.js +6 -0
- package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.js.map +1 -0
- package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +50 -27
- package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js +98 -34
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/backfill-progress.d.ts +68 -0
- package/out/zero-cache/src/services/change-source/protocol/backfill-progress.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js +141 -0
- package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js.map +1 -0
- package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts +45 -2
- package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/data.js +10 -1
- package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +60 -3
- package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts +23 -10
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.js +17 -7
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current.js +2 -1
- package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.d.ts +72 -0
- package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js +162 -0
- package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/backfill-state.d.ts +50 -0
- package/out/zero-cache/src/services/change-streamer/backfill-state.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/backfill-state.js +268 -0
- package/out/zero-cache/src/services/change-streamer/backfill-state.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js +6 -17
- package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js +59 -8
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/broadcast.d.ts +8 -2
- package/out/zero-cache/src/services/change-streamer/broadcast.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/broadcast.js +24 -4
- package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts +19 -4
- package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-log-codec.js +28 -8
- package/out/zero-cache/src/services/change-streamer/change-log-codec.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-log-initializer.d.ts +17 -0
- package/out/zero-cache/src/services/change-streamer/change-log-initializer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-log-initializer.js +44 -1
- package/out/zero-cache/src/services/change-streamer/change-log-initializer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +48 -6
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +131 -34
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +22 -0
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +149 -78
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +38 -16
- package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/forwarder.d.ts +3 -3
- package/out/zero-cache/src/services/change-streamer/forwarder.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/forwarder.js +36 -5
- package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.d.ts +53 -0
- package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js +156 -0
- package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts +58 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.js +107 -16
- package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-message.d.ts +92 -0
- package/out/zero-cache/src/services/change-streamer/snapshot-message.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/snapshot-message.js +42 -0
- package/out/zero-cache/src/services/change-streamer/snapshot-message.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js +16 -4
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot.d.ts +7 -69
- package/out/zero-cache/src/services/change-streamer/snapshot.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot.js +21 -53
- package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts +6 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +27 -9
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.d.ts +95 -4
- package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js +293 -83
- package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscribe.d.ts +446 -0
- package/out/zero-cache/src/services/change-streamer/subscribe.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/subscribe.js +76 -0
- package/out/zero-cache/src/services/change-streamer/subscribe.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +58 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.js +123 -12
- package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts +19 -1
- package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/upstream-acker.js +23 -3
- package/out/zero-cache/src/services/change-streamer/upstream-acker.js.map +1 -1
- package/out/zero-cache/src/services/http-service.d.ts +1 -1
- package/out/zero-cache/src/services/http-service.d.ts.map +1 -1
- package/out/zero-cache/src/services/http-service.js.map +1 -1
- package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
- package/out/zero-cache/src/services/life-cycle.js +3 -1
- package/out/zero-cache/src/services/life-cycle.js.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.d.ts +34 -3
- package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.js +121 -35
- package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
- package/out/zero-cache/src/services/litestream/config-v5.yml +6 -7
- package/out/zero-cache/src/services/litestream/litestream-checkpointer.js +51 -24
- package/out/zero-cache/src/services/litestream/litestream-checkpointer.js.map +1 -1
- package/out/zero-cache/src/services/litestream/litestream-controller.d.ts +10 -2
- package/out/zero-cache/src/services/litestream/litestream-controller.d.ts.map +1 -1
- package/out/zero-cache/src/services/litestream/litestream-controller.js +3 -2
- package/out/zero-cache/src/services/litestream/litestream-controller.js.map +1 -1
- package/out/zero-cache/src/services/litestream/restore-progress.d.ts +24 -0
- package/out/zero-cache/src/services/litestream/restore-progress.d.ts.map +1 -0
- package/out/zero-cache/src/services/litestream/restore-progress.js +91 -0
- package/out/zero-cache/src/services/litestream/restore-progress.js.map +1 -0
- package/out/zero-cache/src/services/litestream/wal-keeper.d.ts +33 -0
- package/out/zero-cache/src/services/litestream/wal-keeper.d.ts.map +1 -0
- package/out/zero-cache/src/services/litestream/wal-keeper.js +49 -0
- package/out/zero-cache/src/services/litestream/wal-keeper.js.map +1 -0
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts +5 -0
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js +7 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.js +5 -6
- package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
- package/out/zero-cache/src/services/plannerz.d.ts +163 -0
- package/out/zero-cache/src/services/plannerz.d.ts.map +1 -0
- package/out/zero-cache/src/services/plannerz.js +267 -0
- package/out/zero-cache/src/services/plannerz.js.map +1 -0
- package/out/zero-cache/src/services/profz.d.ts +30 -0
- package/out/zero-cache/src/services/profz.d.ts.map +1 -0
- package/out/zero-cache/src/services/profz.js +163 -0
- package/out/zero-cache/src/services/profz.js.map +1 -0
- package/out/zero-cache/src/services/replicator/change-log-cookies.js +1 -1
- package/out/zero-cache/src/services/replicator/change-log-cookies.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts +5 -4
- package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/change-log-stream-writer.js +20 -22
- package/out/zero-cache/src/services/replicator/change-log-stream-writer.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.js +51 -5
- package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.d.ts +5 -2
- package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.js +54 -21
- package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
- package/out/zero-cache/src/services/replicator/replication-status.d.ts +35 -1
- package/out/zero-cache/src/services/replicator/replication-status.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/replication-status.js +99 -8
- package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
- package/out/zero-cache/src/services/replicator/replicator.d.ts +4 -1
- package/out/zero-cache/src/services/replicator/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/replicator.js +5 -2
- package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
- package/out/zero-cache/src/services/replicator/reporter/recorder.d.ts +16 -1
- package/out/zero-cache/src/services/replicator/reporter/recorder.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/reporter/recorder.js +70 -27
- package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts +23 -2
- package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/backfilling.js +54 -2
- package/out/zero-cache/src/services/replicator/schema/backfilling.js.map +1 -1
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts +6 -6
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +19 -14
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker-client.d.ts +11 -3
- package/out/zero-cache/src/services/replicator/write-worker-client.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker.js +5 -1
- package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
- package/out/zero-cache/src/services/run-ast.d.ts +12 -0
- package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
- package/out/zero-cache/src/services/run-ast.js +42 -22
- package/out/zero-cache/src/services/run-ast.js.map +1 -1
- package/out/zero-cache/src/services/runner.d.ts +2 -1
- package/out/zero-cache/src/services/runner.d.ts.map +1 -1
- package/out/zero-cache/src/services/runner.js +10 -2
- package/out/zero-cache/src/services/runner.js.map +1 -1
- package/out/zero-cache/src/services/statz.d.ts +4 -0
- package/out/zero-cache/src/services/statz.d.ts.map +1 -1
- package/out/zero-cache/src/services/statz.js +22 -5
- package/out/zero-cache/src/services/statz.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/advancement-metrics.d.ts +26 -0
- package/out/zero-cache/src/services/view-syncer/advancement-metrics.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/advancement-metrics.js +26 -0
- package/out/zero-cache/src/services/view-syncer/advancement-metrics.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/client-handler.d.ts +19 -2
- package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.js +42 -30
- package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.d.ts +2 -1
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.js +1 -0
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts +27 -2
- package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.js +85 -20
- package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts +76 -11
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js +214 -40
- package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.d.ts +81 -0
- package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js +128 -0
- package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts +14 -1
- package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/drain-coordinator.js +30 -4
- package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/hydration-budget.d.ts +33 -0
- package/out/zero-cache/src/services/view-syncer/hydration-budget.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/hydration-budget.js +62 -0
- package/out/zero-cache/src/services/view-syncer/hydration-budget.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts +51 -0
- package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js +78 -0
- package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts +10 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js +18 -4
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +80 -6
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +658 -195
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/plan-warnings.d.ts +7 -0
- package/out/zero-cache/src/services/view-syncer/plan-warnings.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/plan-warnings.js +35 -0
- package/out/zero-cache/src/services/view-syncer/plan-warnings.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/query-covering.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/query-covering.js +2 -6
- package/out/zero-cache/src/services/view-syncer/query-covering.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/query-shape.d.ts +25 -0
- package/out/zero-cache/src/services/view-syncer/query-shape.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/query-shape.js +28 -0
- package/out/zero-cache/src/services/view-syncer/query-shape.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/query-stats.d.ts +82 -0
- package/out/zero-cache/src/services/view-syncer/query-stats.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/query-stats.js +214 -0
- package/out/zero-cache/src/services/view-syncer/query-stats.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/schema/types.d.ts +2 -107
- package/out/zero-cache/src/services/view-syncer/schema/types.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/types.js +5 -20
- package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.d.ts +95 -0
- package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js +155 -0
- package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +96 -14
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js +214 -30
- package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +11 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +566 -107
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/pg-data-type.d.ts +2 -0
- package/out/zero-cache/src/types/pg-data-type.d.ts.map +1 -1
- package/out/zero-cache/src/types/pg-data-type.js +6 -1
- package/out/zero-cache/src/types/pg-data-type.js.map +1 -1
- package/out/zero-cache/src/types/pg.d.ts.map +1 -1
- package/out/zero-cache/src/types/pg.js +1 -1
- package/out/zero-cache/src/types/pg.js.map +1 -1
- package/out/zero-cache/src/types/processes.d.ts +39 -0
- package/out/zero-cache/src/types/processes.d.ts.map +1 -1
- package/out/zero-cache/src/types/processes.js +43 -4
- package/out/zero-cache/src/types/processes.js.map +1 -1
- package/out/zero-cache/src/types/profiler.d.ts +12 -0
- package/out/zero-cache/src/types/profiler.d.ts.map +1 -1
- package/out/zero-cache/src/types/profiler.js +46 -1
- package/out/zero-cache/src/types/profiler.js.map +1 -1
- package/out/zero-cache/src/types/row-key.d.ts +1 -15
- package/out/zero-cache/src/types/row-key.d.ts.map +1 -1
- package/out/zero-cache/src/types/row-key.js +6 -8
- package/out/zero-cache/src/types/row-key.js.map +1 -1
- package/out/zero-cache/src/types/streams.d.ts +49 -7
- package/out/zero-cache/src/types/streams.d.ts.map +1 -1
- package/out/zero-cache/src/types/streams.js +190 -58
- package/out/zero-cache/src/types/streams.js.map +1 -1
- package/out/zero-cache/src/types/subscription.d.ts +15 -0
- package/out/zero-cache/src/types/subscription.d.ts.map +1 -1
- package/out/zero-cache/src/types/subscription.js +123 -31
- package/out/zero-cache/src/types/subscription.js.map +1 -1
- package/out/zero-cache/src/types/websocket-handoff.d.ts +1 -1
- package/out/zero-cache/src/types/websocket-handoff.d.ts.map +1 -1
- package/out/zero-cache/src/types/websocket-handoff.js +18 -9
- package/out/zero-cache/src/types/websocket-handoff.js.map +1 -1
- package/out/zero-cache/src/types/ws.d.ts.map +1 -1
- package/out/zero-cache/src/types/ws.js +1 -16
- package/out/zero-cache/src/types/ws.js.map +1 -1
- package/out/zero-cache/src/workers/connect-params.d.ts +18 -1
- package/out/zero-cache/src/workers/connect-params.d.ts.map +1 -1
- package/out/zero-cache/src/workers/connect-params.js +25 -1
- package/out/zero-cache/src/workers/connect-params.js.map +1 -1
- package/out/zero-cache/src/workers/connection.d.ts +4 -4
- package/out/zero-cache/src/workers/connection.d.ts.map +1 -1
- package/out/zero-cache/src/workers/connection.js +8 -8
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/workers/replicator.js +15 -7
- package/out/zero-cache/src/workers/replicator.js.map +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js +21 -16
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/workers/syncer.js +55 -4
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/active-clients-manager.d.ts.map +1 -1
- package/out/zero-client/src/client/active-clients-manager.js +4 -1
- package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
- package/out/zero-client/src/client/connection-manager.d.ts +20 -0
- package/out/zero-client/src/client/connection-manager.d.ts.map +1 -1
- package/out/zero-client/src/client/connection-manager.js +77 -13
- package/out/zero-client/src/client/connection-manager.js.map +1 -1
- package/out/zero-client/src/client/connection-status-enum.d.ts +2 -0
- package/out/zero-client/src/client/connection-status-enum.d.ts.map +1 -1
- package/out/zero-client/src/client/connection-status-enum.js +3 -1
- package/out/zero-client/src/client/connection-status-enum.js.map +1 -1
- package/out/zero-client/src/client/connection.d.ts +6 -0
- package/out/zero-client/src/client/connection.d.ts.map +1 -1
- package/out/zero-client/src/client/connection.js +2 -1
- package/out/zero-client/src/client/connection.js.map +1 -1
- package/out/zero-client/src/client/context.d.ts +23 -13
- package/out/zero-client/src/client/context.d.ts.map +1 -1
- package/out/zero-client/src/client/context.js +94 -26
- package/out/zero-client/src/client/context.js.map +1 -1
- package/out/zero-client/src/client/crud-impl.d.ts.map +1 -1
- package/out/zero-client/src/client/crud-impl.js +1 -1
- package/out/zero-client/src/client/crud-impl.js.map +1 -1
- package/out/zero-client/src/client/custom.d.ts +5 -4
- package/out/zero-client/src/client/custom.d.ts.map +1 -1
- package/out/zero-client/src/client/custom.js +3 -3
- package/out/zero-client/src/client/custom.js.map +1 -1
- package/out/zero-client/src/client/inspector/lazy-inspector.js +1 -1
- package/out/zero-client/src/client/ivm-branch.d.ts.map +1 -1
- package/out/zero-client/src/client/ivm-branch.js +20 -3
- package/out/zero-client/src/client/ivm-branch.js.map +1 -1
- package/out/zero-client/src/client/keys.d.ts.map +1 -1
- package/out/zero-client/src/client/keys.js +19 -1
- package/out/zero-client/src/client/keys.js.map +1 -1
- package/out/zero-client/src/client/make-mutate-property.d.ts +1 -1
- package/out/zero-client/src/client/make-mutate-property.d.ts.map +1 -1
- package/out/zero-client/src/client/make-mutate-property.js.map +1 -1
- package/out/zero-client/src/client/make-replicache-mutators.d.ts.map +1 -1
- package/out/zero-client/src/client/make-replicache-mutators.js +1 -1
- package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
- package/out/zero-client/src/client/mutation-tracker.d.ts +2 -2
- package/out/zero-client/src/client/mutation-tracker.d.ts.map +1 -1
- package/out/zero-client/src/client/mutation-tracker.js +3 -7
- package/out/zero-client/src/client/mutation-tracker.js.map +1 -1
- package/out/zero-client/src/client/mutator-proxy.d.ts.map +1 -1
- package/out/zero-client/src/client/mutator-proxy.js +9 -4
- package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
- package/out/zero-client/src/client/options.d.ts +33 -0
- package/out/zero-client/src/client/options.d.ts.map +1 -1
- package/out/zero-client/src/client/options.js.map +1 -1
- package/out/zero-client/src/client/query-manager.d.ts +9 -0
- package/out/zero-client/src/client/query-manager.d.ts.map +1 -1
- package/out/zero-client/src/client/query-manager.js +47 -14
- package/out/zero-client/src/client/query-manager.js.map +1 -1
- package/out/zero-client/src/client/reload-error-handler.js +2 -2
- package/out/zero-client/src/client/reload-error-handler.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero-rep.d.ts +1 -0
- package/out/zero-client/src/client/zero-rep.d.ts.map +1 -1
- package/out/zero-client/src/client/zero-rep.js +8 -6
- package/out/zero-client/src/client/zero-rep.js.map +1 -1
- package/out/zero-client/src/client/zero.d.ts +18 -3
- package/out/zero-client/src/client/zero.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.js +89 -50
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-client/src/mod.d.ts +2 -1
- package/out/zero-client/src/mod.d.ts.map +1 -1
- package/out/zero-client/src/mod.js +1 -0
- package/out/zero-client/src/types/query-result.d.ts +2 -0
- package/out/zero-client/src/types/query-result.d.ts.map +1 -1
- package/out/zero-events/src/status.d.ts +64 -0
- package/out/zero-events/src/status.d.ts.map +1 -1
- package/out/zero-protocol/src/analyze-query-result.d.ts +5 -0
- package/out/zero-protocol/src/analyze-query-result.d.ts.map +1 -1
- package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
- package/out/zero-protocol/src/ast.d.ts.map +1 -1
- package/out/zero-protocol/src/ast.js +13 -14
- package/out/zero-protocol/src/ast.js.map +1 -1
- package/out/zero-protocol/src/connect.d.ts +16 -0
- package/out/zero-protocol/src/connect.d.ts.map +1 -1
- package/out/zero-protocol/src/connect.js +27 -1
- package/out/zero-protocol/src/connect.js.map +1 -1
- package/out/zero-protocol/src/down.d.ts +31 -31
- package/out/zero-protocol/src/feature-flag-enum.d.ts +14 -0
- package/out/zero-protocol/src/feature-flag-enum.d.ts.map +1 -0
- package/out/zero-protocol/src/feature-flag-enum.js +10 -0
- package/out/zero-protocol/src/feature-flag-enum.js.map +1 -0
- package/out/zero-protocol/src/feature-flag.d.ts +5 -0
- package/out/zero-protocol/src/feature-flag.d.ts.map +1 -0
- package/out/zero-protocol/src/inspect-down.d.ts +16 -16
- package/out/zero-protocol/src/poke.d.ts +17 -10
- package/out/zero-protocol/src/poke.d.ts.map +1 -1
- package/out/zero-protocol/src/poke.js +3 -2
- package/out/zero-protocol/src/poke.js.map +1 -1
- package/out/zero-protocol/src/protocol-version.d.ts +30 -6
- package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
- package/out/zero-protocol/src/protocol-version.js +1 -1
- package/out/zero-protocol/src/protocol-version.js.map +1 -1
- package/out/zero-protocol/src/query-hash.d.ts.map +1 -1
- package/out/zero-protocol/src/query-hash.js +2 -5
- package/out/zero-protocol/src/query-hash.js.map +1 -1
- package/out/zero-react/src/use-query.d.ts +5 -4
- package/out/zero-react/src/use-query.d.ts.map +1 -1
- package/out/zero-react/src/use-query.js +74 -7
- package/out/zero-react/src/use-query.js.map +1 -1
- package/out/zero-react/src/zero.js +1 -0
- package/out/zero-server/src/adapters/drizzle.d.ts +2 -1
- package/out/zero-server/src/adapters/drizzle.d.ts.map +1 -1
- package/out/zero-server/src/adapters/drizzle.js +12 -7
- package/out/zero-server/src/adapters/drizzle.js.map +1 -1
- package/out/zero-server/src/adapters/kysely.d.ts +2 -1
- package/out/zero-server/src/adapters/kysely.d.ts.map +1 -1
- package/out/zero-server/src/adapters/kysely.js +8 -2
- package/out/zero-server/src/adapters/kysely.js.map +1 -1
- package/out/zero-server/src/adapters/pg.d.ts +5 -1
- package/out/zero-server/src/adapters/pg.d.ts.map +1 -1
- package/out/zero-server/src/adapters/pg.js +28 -7
- package/out/zero-server/src/adapters/pg.js.map +1 -1
- package/out/zero-server/src/adapters/postgresjs.d.ts +1 -0
- package/out/zero-server/src/adapters/postgresjs.d.ts.map +1 -1
- package/out/zero-server/src/adapters/postgresjs.js +7 -1
- package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
- package/out/zero-server/src/adapters/prisma.d.ts +3 -2
- package/out/zero-server/src/adapters/prisma.d.ts.map +1 -1
- package/out/zero-server/src/adapters/prisma.js +8 -2
- package/out/zero-server/src/adapters/prisma.js.map +1 -1
- package/out/zero-server/src/closable-db-transaction.d.ts +16 -0
- package/out/zero-server/src/closable-db-transaction.d.ts.map +1 -0
- package/out/zero-server/src/closable-db-transaction.js +31 -0
- package/out/zero-server/src/closable-db-transaction.js.map +1 -0
- package/out/zero-server/src/custom.d.ts +10 -4
- package/out/zero-server/src/custom.d.ts.map +1 -1
- package/out/zero-server/src/custom.js +15 -6
- package/out/zero-server/src/custom.js.map +1 -1
- package/out/zero-server/src/logging.d.ts +2 -2
- package/out/zero-server/src/logging.d.ts.map +1 -1
- package/out/zero-server/src/logging.js +2 -2
- package/out/zero-server/src/logging.js.map +1 -1
- package/out/zero-server/src/mod.d.ts +1 -1
- package/out/zero-server/src/mod.d.ts.map +1 -1
- package/out/zero-server/src/pg-query-executor.d.ts +2 -2
- package/out/zero-server/src/pg-query-executor.d.ts.map +1 -1
- package/out/zero-server/src/pg-query-executor.js +2 -2
- package/out/zero-server/src/pg-query-executor.js.map +1 -1
- package/out/zero-server/src/process-mutations.d.ts +25 -4
- package/out/zero-server/src/process-mutations.d.ts.map +1 -1
- package/out/zero-server/src/process-mutations.js +103 -52
- package/out/zero-server/src/process-mutations.js.map +1 -1
- package/out/zero-server/src/push-processor.d.ts +8 -2
- package/out/zero-server/src/push-processor.d.ts.map +1 -1
- package/out/zero-server/src/push-processor.js +12 -4
- package/out/zero-server/src/push-processor.js.map +1 -1
- package/out/zero-server/src/schema.d.ts +2 -2
- package/out/zero-server/src/schema.d.ts.map +1 -1
- package/out/zero-server/src/schema.js +3 -2
- package/out/zero-server/src/schema.js.map +1 -1
- package/out/zero-server/src/zql-database.d.ts +21 -1
- package/out/zero-server/src/zql-database.d.ts.map +1 -1
- package/out/zero-server/src/zql-database.js +61 -24
- package/out/zero-server/src/zql-database.js.map +1 -1
- package/out/zero-solid/src/solid-view.d.ts +17 -0
- package/out/zero-solid/src/solid-view.d.ts.map +1 -1
- package/out/zero-solid/src/solid-view.js +39 -1
- package/out/zero-solid/src/solid-view.js.map +1 -1
- package/out/zero-solid/src/use-query.js +1 -2
- package/out/zero-solid/src/use-query.js.map +1 -1
- package/out/zero-solid/src/zero.js +1 -0
- package/out/zql/src/builder/builder.d.ts +25 -0
- package/out/zql/src/builder/builder.d.ts.map +1 -1
- package/out/zql/src/builder/builder.js +69 -19
- package/out/zql/src/builder/builder.js.map +1 -1
- package/out/zql/src/builder/correlated-predicate-pushdown.d.ts +43 -0
- package/out/zql/src/builder/correlated-predicate-pushdown.d.ts.map +1 -0
- package/out/zql/src/builder/correlated-predicate-pushdown.js +161 -0
- package/out/zql/src/builder/correlated-predicate-pushdown.js.map +1 -0
- package/out/zql/src/builder/debug-delegate.d.ts +6 -1
- package/out/zql/src/builder/debug-delegate.d.ts.map +1 -1
- package/out/zql/src/builder/debug-delegate.js +14 -2
- package/out/zql/src/builder/debug-delegate.js.map +1 -1
- package/out/zql/src/ivm/array-view.d.ts +29 -0
- package/out/zql/src/ivm/array-view.d.ts.map +1 -1
- package/out/zql/src/ivm/array-view.js +74 -16
- package/out/zql/src/ivm/array-view.js.map +1 -1
- package/out/zql/src/ivm/cap.d.ts +2 -1
- package/out/zql/src/ivm/cap.d.ts.map +1 -1
- package/out/zql/src/ivm/cap.js +14 -4
- package/out/zql/src/ivm/cap.js.map +1 -1
- package/out/zql/src/ivm/data.d.ts.map +1 -1
- package/out/zql/src/ivm/data.js +8 -3
- package/out/zql/src/ivm/data.js.map +1 -1
- package/out/zql/src/ivm/deferred-input.d.ts.map +1 -1
- package/out/zql/src/ivm/deferred-input.js +3 -2
- package/out/zql/src/ivm/deferred-input.js.map +1 -1
- package/out/zql/src/ivm/exists.d.ts +3 -1
- package/out/zql/src/ivm/exists.d.ts.map +1 -1
- package/out/zql/src/ivm/exists.js +4 -1
- package/out/zql/src/ivm/exists.js.map +1 -1
- package/out/zql/src/ivm/fan-in.d.ts +6 -2
- package/out/zql/src/ivm/fan-in.d.ts.map +1 -1
- package/out/zql/src/ivm/fan-in.js +13 -5
- package/out/zql/src/ivm/fan-in.js.map +1 -1
- package/out/zql/src/ivm/fan-out.d.ts +5 -2
- package/out/zql/src/ivm/fan-out.d.ts.map +1 -1
- package/out/zql/src/ivm/fan-out.js +4 -0
- package/out/zql/src/ivm/fan-out.js.map +1 -1
- package/out/zql/src/ivm/filter-operators.d.ts +8 -6
- package/out/zql/src/ivm/filter-operators.d.ts.map +1 -1
- package/out/zql/src/ivm/filter-operators.js +19 -8
- package/out/zql/src/ivm/filter-operators.js.map +1 -1
- package/out/zql/src/ivm/filter.d.ts +5 -2
- package/out/zql/src/ivm/filter.d.ts.map +1 -1
- package/out/zql/src/ivm/filter.js +10 -4
- package/out/zql/src/ivm/filter.js.map +1 -1
- package/out/zql/src/ivm/flipped-join.d.ts +9 -4
- package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
- package/out/zql/src/ivm/flipped-join.js +97 -46
- package/out/zql/src/ivm/flipped-join.js.map +1 -1
- package/out/zql/src/ivm/join-index-tap.d.ts +33 -0
- package/out/zql/src/ivm/join-index-tap.d.ts.map +1 -0
- package/out/zql/src/ivm/join-index-tap.js +70 -0
- package/out/zql/src/ivm/join-index-tap.js.map +1 -0
- package/out/zql/src/ivm/join-index.d.ts +35 -0
- package/out/zql/src/ivm/join-index.d.ts.map +1 -0
- package/out/zql/src/ivm/join-index.js +141 -0
- package/out/zql/src/ivm/join-index.js.map +1 -0
- package/out/zql/src/ivm/join-utils.d.ts +6 -0
- package/out/zql/src/ivm/join-utils.d.ts.map +1 -1
- package/out/zql/src/ivm/join-utils.js +22 -1
- package/out/zql/src/ivm/join-utils.js.map +1 -1
- package/out/zql/src/ivm/join.d.ts +19 -1
- package/out/zql/src/ivm/join.d.ts.map +1 -1
- package/out/zql/src/ivm/join.js +107 -15
- package/out/zql/src/ivm/join.js.map +1 -1
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts +1 -1
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts.map +1 -1
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.js +6 -4
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
- package/out/zql/src/ivm/memory-source.d.ts +29 -4
- package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
- package/out/zql/src/ivm/memory-source.js +70 -34
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/ivm/memory-storage.d.ts +1 -0
- package/out/zql/src/ivm/memory-storage.d.ts.map +1 -1
- package/out/zql/src/ivm/memory-storage.js +1 -0
- package/out/zql/src/ivm/memory-storage.js.map +1 -1
- package/out/zql/src/ivm/operator.d.ts +45 -10
- package/out/zql/src/ivm/operator.d.ts.map +1 -1
- package/out/zql/src/ivm/operator.js +9 -4
- package/out/zql/src/ivm/operator.js.map +1 -1
- package/out/zql/src/ivm/push-accumulated.d.ts +6 -1
- package/out/zql/src/ivm/push-accumulated.d.ts.map +1 -1
- package/out/zql/src/ivm/push-accumulated.js +47 -2
- package/out/zql/src/ivm/push-accumulated.js.map +1 -1
- package/out/zql/src/ivm/skip.d.ts +2 -1
- package/out/zql/src/ivm/skip.d.ts.map +1 -1
- package/out/zql/src/ivm/skip.js +3 -0
- package/out/zql/src/ivm/skip.js.map +1 -1
- package/out/zql/src/ivm/take-gate.d.ts +57 -0
- package/out/zql/src/ivm/take-gate.d.ts.map +1 -0
- package/out/zql/src/ivm/take-gate.js +106 -0
- package/out/zql/src/ivm/take-gate.js.map +1 -0
- package/out/zql/src/ivm/take.d.ts +8 -2
- package/out/zql/src/ivm/take.d.ts.map +1 -1
- package/out/zql/src/ivm/take.js +139 -103
- package/out/zql/src/ivm/take.js.map +1 -1
- package/out/zql/src/ivm/union-fan-in.d.ts +3 -0
- package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
- package/out/zql/src/ivm/union-fan-in.js +63 -10
- package/out/zql/src/ivm/union-fan-in.js.map +1 -1
- package/out/zql/src/ivm/union-fan-out.d.ts +2 -1
- package/out/zql/src/ivm/union-fan-out.d.ts.map +1 -1
- package/out/zql/src/ivm/union-fan-out.js +5 -0
- package/out/zql/src/ivm/union-fan-out.js.map +1 -1
- package/out/zql/src/ivm/view-apply-change.d.ts.map +1 -1
- package/out/zql/src/ivm/view-apply-change.js +9 -4
- package/out/zql/src/ivm/view-apply-change.js.map +1 -1
- package/out/zql/src/ivm/view.d.ts +15 -0
- package/out/zql/src/ivm/view.d.ts.map +1 -1
- package/out/zql/src/mutate/custom.d.ts +53 -1
- package/out/zql/src/mutate/custom.d.ts.map +1 -1
- package/out/zql/src/mutate/custom.js.map +1 -1
- package/out/zql/src/mutate/mutator-registry.d.ts.map +1 -1
- package/out/zql/src/mutate/mutator-registry.js +3 -3
- package/out/zql/src/mutate/mutator-registry.js.map +1 -1
- package/out/zql/src/mutate/mutator.d.ts +2 -2
- package/out/zql/src/mutate/mutator.d.ts.map +1 -1
- package/out/zql/src/mutate/mutator.js.map +1 -1
- package/out/zql/src/planner/planner-builder.d.ts +23 -3
- package/out/zql/src/planner/planner-builder.d.ts.map +1 -1
- package/out/zql/src/planner/planner-builder.js +34 -17
- package/out/zql/src/planner/planner-builder.js.map +1 -1
- package/out/zql/src/planner/planner-connection.d.ts +58 -2
- package/out/zql/src/planner/planner-connection.d.ts.map +1 -1
- package/out/zql/src/planner/planner-connection.js +103 -11
- package/out/zql/src/planner/planner-connection.js.map +1 -1
- package/out/zql/src/planner/planner-debug.d.ts.map +1 -1
- package/out/zql/src/planner/planner-debug.js +3 -8
- package/out/zql/src/planner/planner-debug.js.map +1 -1
- package/out/zql/src/planner/planner-graph.d.ts +10 -0
- package/out/zql/src/planner/planner-graph.d.ts.map +1 -1
- package/out/zql/src/planner/planner-graph.js +14 -0
- package/out/zql/src/planner/planner-graph.js.map +1 -1
- package/out/zql/src/planner/planner-source.d.ts +2 -2
- package/out/zql/src/planner/planner-source.d.ts.map +1 -1
- package/out/zql/src/planner/planner-source.js +2 -2
- package/out/zql/src/planner/planner-source.js.map +1 -1
- package/out/zql/src/planner/planner-warnings.d.ts +83 -0
- package/out/zql/src/planner/planner-warnings.d.ts.map +1 -0
- package/out/zql/src/planner/planner-warnings.js +78 -0
- package/out/zql/src/planner/planner-warnings.js.map +1 -0
- package/out/zql/src/query/measure-push-operator.d.ts +9 -2
- package/out/zql/src/query/measure-push-operator.d.ts.map +1 -1
- package/out/zql/src/query/measure-push-operator.js +52 -5
- package/out/zql/src/query/measure-push-operator.js.map +1 -1
- package/out/zql/src/query/query-delegate-base.d.ts +4 -2
- package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
- package/out/zql/src/query/query-delegate-base.js +66 -33
- package/out/zql/src/query/query-delegate-base.js.map +1 -1
- package/out/zql/src/query/query-delegate.d.ts +33 -6
- package/out/zql/src/query/query-delegate.d.ts.map +1 -1
- package/out/zql/src/query/query-impl.d.ts +1 -0
- package/out/zql/src/query/query-impl.d.ts.map +1 -1
- package/out/zql/src/query/query-impl.js +6 -23
- package/out/zql/src/query/query-impl.js.map +1 -1
- package/out/zql/src/query/query-transitions.d.ts.map +1 -1
- package/out/zql/src/query/query-transitions.js +2 -5
- package/out/zql/src/query/query-transitions.js.map +1 -1
- package/out/zql/src/query/query.d.ts +22 -5
- package/out/zql/src/query/query.d.ts.map +1 -1
- package/out/zql/src/query/runnable-query-impl.d.ts +1 -0
- package/out/zql/src/query/runnable-query-impl.d.ts.map +1 -1
- package/out/zql/src/query/runnable-query-impl.js +2 -10
- package/out/zql/src/query/runnable-query-impl.js.map +1 -1
- package/out/zql/src/query/typed-view.d.ts +1 -1
- package/out/zql/src/query/typed-view.d.ts.map +1 -1
- package/out/zqlite/src/database-storage.d.ts.map +1 -1
- package/out/zqlite/src/database-storage.js +9 -1
- package/out/zqlite/src/database-storage.js.map +1 -1
- package/out/zqlite/src/db.d.ts +9 -1
- package/out/zqlite/src/db.d.ts.map +1 -1
- package/out/zqlite/src/db.js +62 -30
- package/out/zqlite/src/db.js.map +1 -1
- package/out/zqlite/src/index-usage.d.ts +19 -0
- package/out/zqlite/src/index-usage.d.ts.map +1 -0
- package/out/zqlite/src/index-usage.js +49 -0
- package/out/zqlite/src/index-usage.js.map +1 -0
- package/out/zqlite/src/pending-delta.d.ts +110 -0
- package/out/zqlite/src/pending-delta.d.ts.map +1 -0
- package/out/zqlite/src/pending-delta.js +278 -0
- package/out/zqlite/src/pending-delta.js.map +1 -0
- package/out/zqlite/src/query-builder.d.ts +17 -1
- package/out/zqlite/src/query-builder.d.ts.map +1 -1
- package/out/zqlite/src/query-builder.js +111 -20
- package/out/zqlite/src/query-builder.js.map +1 -1
- package/out/zqlite/src/query-delegate.d.ts.map +1 -1
- package/out/zqlite/src/query-delegate.js +10 -7
- package/out/zqlite/src/query-delegate.js.map +1 -1
- package/out/zqlite/src/resolve-scalar-subqueries.d.ts +3 -1
- package/out/zqlite/src/resolve-scalar-subqueries.d.ts.map +1 -1
- package/out/zqlite/src/resolve-scalar-subqueries.js +1 -0
- package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
- package/out/zqlite/src/sqlite-cost-model.d.ts.map +1 -1
- package/out/zqlite/src/sqlite-cost-model.js +52 -1
- package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
- package/out/zqlite/src/sqlite-stat-fanout.d.ts.map +1 -1
- package/out/zqlite/src/sqlite-stat-fanout.js +5 -31
- package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
- package/out/zqlite/src/sqlite-stat4-sample.d.ts +37 -0
- package/out/zqlite/src/sqlite-stat4-sample.d.ts.map +1 -0
- package/out/zqlite/src/sqlite-stat4-sample.js +61 -0
- package/out/zqlite/src/sqlite-stat4-sample.js.map +1 -0
- package/out/zqlite/src/table-source.d.ts +87 -2
- package/out/zqlite/src/table-source.d.ts.map +1 -1
- package/out/zqlite/src/table-source.js +276 -42
- package/out/zqlite/src/table-source.js.map +1 -1
- package/package.json +4 -5
- package/out/replicache/src/kv/idb-store-with-mem-fallback.d.ts +0 -25
- package/out/replicache/src/kv/idb-store-with-mem-fallback.d.ts.map +0 -1
- package/out/replicache/src/kv/idb-store-with-mem-fallback.js +0 -83
- package/out/replicache/src/kv/idb-store-with-mem-fallback.js.map +0 -1
- package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts +0 -29
- package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js +0 -154
- package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"change-streamer-service.js","names":["#lc","#shard","#changeDBProvider","#replicaVersion","#source","#storer","#forwarder","#reservations","#replicationStatusPublisher","#sqliteCatchupOptions","#changeLogWriter","#purgeScheduler","#comparator","#acker","#initializer","#readRouter","#autoReset","#state","#serving","#txCounter","#changeCounter","#transactionForwardDuration","#catchupRoutes","#reservationDemotions","#reservationConfirmDelays","#sqliteCatchup","#closeSQLiteCatchup","#purgeLock","#latestStatus","#latestLagReportCommitTimeMs","#lastForwardedCommitWatermark","#stream","#handleControlMessage","#recordForwardedTransactionBoundary","#selectSQLiteCatchup","#captureRequiredHead","#recordCatchupRoute","#confirmReservations","#backupWatermark","#requestSQLitePurge","#maybeSchedulePGPurge","#maybeScheduleSQLitePurge","#getChangeLogState","#pgPurgeScheduled","#pgPurgeRunning","#pgPurgedWatermark","#purgePGChangeLog","#sqlitePurgeScheduled","#sqlitePurgeRunning","#sqlitePurgeContinuation","#purgeSQLiteChangeLog","#getCleanupFloor","#loggedBehindWatermark","#currentTransactionCompletion","#transactionForwardStartedAt","#recordTransactionForwardDuration","#declineSQLiteCatchup","#openSQLiteCatchup","#changeLogUnavailableSince"],"sources":["../../../../../../zero-cache/src/services/change-streamer/change-streamer-service.ts"],"sourcesContent":["import {getDefaultHighWaterMark} from 'node:stream';\nimport type {LogContext} from '@rocicorp/logger';\nimport {resolver, type Resolver} from '@rocicorp/resolver';\nimport {defu} from 'defu';\nimport postgres, {type Options, type PostgresType} from 'postgres';\nimport {assert, unreachable} from '../../../../shared/src/asserts.ts';\nimport {must} from '../../../../shared/src/must.ts';\nimport {promiseOrAbort} from '../../../../shared/src/promise-race.ts';\nimport {promiseVoid} from '../../../../shared/src/resolved-promises.ts';\nimport {publishCriticalEvent} from '../../observability/events.ts';\nimport {\n getOrCreateCounter,\n getOrCreateLatencyHistogram,\n} from '../../observability/metrics.ts';\nimport {min} from '../../types/lexi-version.ts';\nimport type {PostgresDB} from '../../types/pg.ts';\nimport type {ShardID} from '../../types/shards.ts';\nimport type {Source} from '../../types/streams.ts';\nimport {Subscription} from '../../types/subscription.ts';\nimport type {\n ChangeSource,\n ChangeStream,\n} from '../change-source/change-source.ts';\nimport {\n type ChangeStreamControl,\n type ChangeStreamData,\n type Rollback,\n} from '../change-source/protocol/current/downstream.ts';\nimport type {LitestreamVersion} from '../litestream/metrics.ts';\nimport {\n publishReplicationError,\n replicationStatusError,\n type ReplicationStatusPublisher,\n} from '../replicator/replication-status.ts';\nimport type {SubscriptionState} from '../replicator/schema/replication-state.ts';\nimport {\n DEFAULT_MAX_RETRY_DELAY_MS,\n RunningState,\n UnrecoverableError,\n} from '../running-state.ts';\nimport {\n ChangeLogInitializer,\n replicaInitializationSource,\n} from './change-log-initializer.ts';\nimport {\n type ChangeStreamerService,\n type Status,\n type SubscriberContext,\n type WatermarkedChange,\n} from './change-streamer.ts';\nimport * as ErrorType from './error-type-enum.ts';\nimport {Forwarder} from './forwarder.ts';\nimport {\n AutoResetSignal,\n ensureReplicationConfig,\n markResetRequired,\n} from './schema/tables.ts';\nimport {SnapshotReservations} from './snapshot-reservations.ts';\nimport type {SnapshotMessage} from './snapshot.ts';\nimport {\n SQLiteChangeLogCatchup,\n type SQLiteChangeLogCleanupGuard,\n} from './sqlite-change-log-catchup.ts';\nimport {\n SQLiteChangeLogComparator,\n type SQLiteChangeLogCompareOptions,\n} from './sqlite-change-log-comparator.ts';\nimport {\n SQLiteChangeLogPurgeScheduler,\n type PurgeContinuation,\n type SQLiteChangeLogPurgeSchedulerOptions,\n} from './sqlite-change-log-purge-scheduler.ts';\nimport {\n inspectSQLiteChangeLog,\n SQLiteChangeLogReadRouter,\n type ChangeLogReadRoute,\n type SQLiteChangeLogCoverage,\n} from './sqlite-change-log-read-router.ts';\nimport {SQLiteChangeLogReader} from './sqlite-change-log-reader.ts';\nimport {\n SQLiteChangeLogWriter,\n type SQLiteChangeLogWriterOptions,\n} from './sqlite-change-log-writer.ts';\nimport type {PostgresDBProvider} from './storer.ts';\nimport {\n Storer,\n type PurgeLock,\n type TuningOptions as StorerOptions,\n} from './storer.ts';\nimport {Subscriber} from './subscriber.ts';\nimport {UpstreamAcker} from './upstream-acker.ts';\n\nexport type BackupConfig = {\n backupURL: string;\n litestreamVersion: LitestreamVersion;\n};\n\nexport type SQLiteCatchupOptions = {\n /** The change-log database, i.e. `changeLogFileName(replicaFile)`. */\n changeLogFile: string;\n readBatchRows: number;\n barrierTimeoutMs: number;\n /**\n * Backstop for the change-log writer's ACK, which is what normally releases\n * the barrier. Defaults to the catchup coordinator's interval.\n */\n barrierPollIntervalMs?: number | undefined;\n /**\n * Optional policy hook used by focused tests. Production canary selection is\n * supplied by `sqliteChangeLogServe` and is stable by shard plus task ID.\n * Backup subscribers are rejected before either selector is invoked.\n */\n shouldUse?: ((ctx: SubscriberContext) => boolean) | undefined;\n /**\n * Overrides the purge scheduler's guard, for tests. When absent, the\n * service supplies the scheduler's real writer-serialized guard, or the\n * catchup's no-op default when purging is not configured.\n */\n cleanupGuard?: SQLiteChangeLogCleanupGuard | undefined;\n /**\n * How long the change log may be unavailable before declining to serve from\n * it is reported as a warning rather than a debug line. Defaults to\n * {@link DEFAULT_CHANGE_LOG_UNAVAILABLE_WARN_THRESHOLD_MS}.\n */\n notReadyWarnThresholdMs?: number | undefined;\n};\n\nexport type SQLiteChangeLogServeOptions = {\n /** Stable percentage of eligible serving tasks routed to SQLite. */\n readPercent: number;\n /**\n * Stable percentage of eligible serving tasks routed to a log that has not\n * yet aged through {@link retentionMs}. Zero keeps a reseeded log on PG for\n * that whole window; above zero it serves that share of tasks, and a\n * reservation it cannot cover is demoted rather than held pending.\n * Defaults to zero.\n */\n coldReadPercent?: number | undefined;\n /** The warm-up window a reseeded log ages through. */\n retentionMs: number;\n /** Injectable for deterministic breaker tests. */\n failureCooldownMs?: number | undefined;\n /** Injectable for deterministic warm-up and breaker tests. */\n now?: (() => number) | undefined;\n /** Injectable readiness inspection for service-level tests. */\n inspect?: (() => SQLiteChangeLogCoverage | undefined) | undefined;\n};\n\nexport type TuningOptions = StorerOptions & {\n flowControlConsensusTimeoutProportion: number;\n flowControlSlowSubscriberGracePeriodMs?: number | undefined;\n sqliteCatchup?: SQLiteCatchupOptions | undefined;\n /**\n * Supplied when `sqliteChangeLogMode != off`, i.e. this is the gate on the\n * change-log writer. Absent, nothing writes the log.\n */\n sqliteChangeLogWriter?:\n | Omit<\n SQLiteChangeLogWriterOptions,\n 'onCommit' | 'onDisabled' | 'onRebuilt'\n >\n | undefined;\n /**\n * Also supplied when `sqliteChangeLogMode != off`, and deliberately not\n * gated on the read path: in `write` mode no reader ever opens, and this is\n * the configuration the purge scheduler actually ships in. The scheduler\n * runs on the writer's own connection, so with the writer absent (mode\n * `off`) it is never constructed, and a leftover file on disk is left to\n * the replicator's cleanup.\n */\n sqliteChangeLogPurge?: SQLiteChangeLogPurgeSchedulerOptions | undefined;\n /**\n * Supplied in `compare` mode and later modes.\n * `replicaFile` enables the initialization comparison.\n * The other fields configure sampled catchup comparisons.\n * Both checks are advisory, and Postgres remains authoritative.\n * The comparator also requires the writer and catchup options.\n */\n sqliteChangeLogCompare?:\n | (SQLiteChangeLogCompareOptions & {replicaFile: string})\n | undefined;\n /** Supplied only in `serve` mode. A zero percentage keeps every read on PG. */\n sqliteChangeLogServe?: SQLiteChangeLogServeOptions | undefined;\n};\n\n/**\n * Performs initialization and schema migrations to initialize a ChangeStreamerImpl.\n */\nexport async function initializeStreamer(\n lc: LogContext,\n shard: ShardID,\n taskID: string,\n discoveryAddress: string,\n discoveryProtocol: string,\n changeDB: PostgresDB,\n changeSource: ChangeSource,\n replicationStatusPublisher: ReplicationStatusPublisher,\n subscriptionState: SubscriptionState,\n backupConfig: BackupConfig | null,\n purgeLock: PurgeLock | null,\n autoReset: boolean,\n opts: TuningOptions,\n setTimeoutFn = setTimeout,\n): Promise<ChangeStreamerService> {\n await ensureReplicationConfig(\n lc,\n changeDB,\n subscriptionState,\n shard,\n autoReset,\n purgeLock ?? undefined,\n setTimeoutFn,\n );\n\n // Dynamically creates connection pools that the implementation uses to\n // isolate concurrent, long-running transactions. This works around a bug in\n // the postgres.js client where connections get swapped in certain conditions.\n //\n // https://github.com/porsager/postgres/issues/1204\n const changeDBProvider: PostgresDBProvider = (\n applicationName: string,\n max: number,\n ) =>\n postgres(\n defu(\n {max, connection: {['application_name']: applicationName}},\n // ParsedOptions are technically compatible with Options, but happen\n // to not be typed that way. The postgres.js author does an equivalent\n // merge of ParsedOptions and Options here:\n // https://github.com/porsager/postgres/blob/089214e85c23c90cf142d47fb30bd03f42874984/src/subscribe.js#L13\n changeDB.options as unknown as Options<Record<string, PostgresType>>,\n ),\n ) as PostgresDB;\n\n const {replicaVersion} = subscriptionState;\n return new ChangeStreamerImpl(\n lc,\n shard,\n taskID,\n discoveryAddress,\n discoveryProtocol,\n changeDBProvider,\n replicaVersion,\n changeSource,\n replicationStatusPublisher,\n backupConfig,\n purgeLock,\n autoReset,\n opts,\n setTimeoutFn,\n );\n}\n\nconst REPLICATION_STATUS_ERROR_DELAY_THRESHOLD_MS = 5000;\n\n// How long the change log may be unavailable before declining to serve from it\n// stops looking like normal startup ordering. Subscriptions can arrive before\n// the stream loop's first reconcile has created and seeded the log, so early\n// declines are expected. Still declining a minute later means the log is not\n// being written at all -- `sqliteChangeLogMode=off`, a writer that has failed\n// soft, or a path mismatch -- which is worth surfacing.\nconst DEFAULT_CHANGE_LOG_UNAVAILABLE_WARN_THRESHOLD_MS = 60_000;\n\n/**\n * Upstream-agnostic dispatch of messages in a {@link ChangeStreamMessage} to a\n * {@link Forwarder} and {@link Storer} to execute the forward-store-ack\n * procedure described in {@link ChangeStreamer}.\n *\n * ### Subscriber Catchup\n *\n * Connecting clients first need to be \"caught up\" to the current watermark\n * (from stored change log entries) before new entries are forwarded to\n * them. This is non-trivial because the replication stream may be in the\n * middle of a pending streamed Transaction for which some entries have\n * already been forwarded but are not yet committed to the store.\n *\n *\n * ```\n * ------------------------------- - - - - - - - - - - - - - - - - - - -\n * | Historic changes in storage | Pending (streamed) tx | Next tx\n * ------------------------------- - - - - - - - - - - - - - - - - - - -\n * Replication stream\n * > > > > > > > > >\n * ^ ---> required catchup ---> ^\n * Subscriber watermark Subscription begins\n * ```\n *\n * Preemptively buffering the changes of every pending transaction\n * would be wasteful and consume too much memory for large transactions.\n *\n * Instead, the streamer synchronously dispatches changes and subscriptions\n * to the {@link Forwarder} and the {@link Storer} such that the two\n * components are aligned as to where in the stream the subscription started.\n * The two components then coordinate catchup and handoff via the\n * {@link Subscriber} object with the following algorithm:\n *\n * * If the streamer is in the middle of a pending Transaction, the\n * Subscriber is \"queued\" on both the Forwarder and the Storer. In this\n * state, new changes are *not* forwarded to the Subscriber, and catchup\n * is not yet executed.\n * * Once the commit message for the pending Transaction is processed\n * by the Storer, it begins catchup on the Subscriber (with a READONLY\n * snapshot so that it does not block subsequent storage operations).\n * This catchup is thus guaranteed to load the change log entries of\n * that last Transaction.\n * * When the Forwarder processes that same commit message, it moves the\n * Subscriber from the \"queued\" to the \"active\" set of clients such that\n * the Subscriber begins receiving new changes, starting from the next\n * Transaction.\n * * The Subscriber does not forward those changes, however, if its catchup\n * is not complete. Until then, it buffers the changes in memory.\n * * Once catchup is complete, the buffered changes are immediately sent\n * and the Subscriber henceforth forwards changes as they are received.\n *\n * In the (common) case where the streamer is not in the middle of a pending\n * transaction when a subscription begins, the Storer begins catchup\n * immediately and the Forwarder directly adds the Subscriber to its active\n * set. However, the Subscriber still buffers any forwarded messages until\n * its catchup is complete.\n *\n * ### Watermarks and ordering\n *\n * The ChangeStreamerService depends on its {@link ChangeSource} to send\n * changes in contiguous [`begin`, `data` ..., `data`, `commit`] sequences\n * in commit order. This follows Postgres's Logical Replication Protocol\n * Message Flow:\n *\n * https://www.postgresql.org/docs/16/protocol-logical-replication.html#PROTOCOL-LOGICAL-MESSAGES-FLOW\n *\n * > The logical replication protocol sends individual transactions one by one.\n * > This means that all messages between a pair of Begin and Commit messages belong to the same transaction.\n *\n * In order to correctly replay (new) and filter (old) messages to subscribers\n * at different points in the replication stream, these changes must be assigned\n * watermarks such that they preserve the order in which they were received\n * from the ChangeSource.\n *\n * A previous implementation incorrectly derived these watermarks from the Postgres\n * Log Sequence Numbers (LSN) of each message. However, LSNs from concurrent,\n * non-conflicting transactions can overlap, which can result in a `begin` message\n * with an earlier LSN arriving after a `commit` message. For example, the\n * changes for these transactions:\n *\n * ```\n * LSN: 1 2 3 4 5 6 7 8 9 10\n * tx1: begin data data data commit\n * tx2: begin data data data commit\n * ```\n *\n * will arrive as:\n *\n * ```\n * begin1, data2, data4, data6, commit8, begin3, data5, data7, data9, commit10\n * ```\n *\n * Thus, LSN of non-commit messages are not suitable for tracking the sorting\n * order of the replication stream.\n *\n * Instead, the ChangeStreamer uses the following algorithm for deterministic\n * catchup and filtering of changes:\n *\n * * A `commit` message is assigned to a watermark corresponding to its LSN.\n * These are guaranteed to be in commit order by definition.\n *\n * * `begin` and `data` messages are assigned to the watermark of the\n * preceding `commit` (the previous transaction, or the replication\n * slot's starting LSN) plus 1. This guarantees that they will be sorted\n * after the previously commit transaction even if their LSNs came before it.\n * This is referred to as the `preCommitWatermark`.\n *\n * * In the ChangeLog DB, messages have a secondary sort column `pos`, which is\n * the position of the message within its transaction, with the `begin` message\n * starting at `0`. This guarantees that `begin` and `data` messages will be\n * fetched in the original ChangeSource order during catchup.\n *\n * `begin` and `data` messages share the same watermark, but this is sufficient for\n * Subscriber filtering because subscribers only know about the `commit` watermarks\n * exposed in the `Downstream` `Commit` message. The Subscriber object thus compares\n * the internal watermarks of the incoming messages against the commit watermark of\n * the caller, updating the watermark at every `Commit` message that is forwarded.\n *\n * ### Cleanup\n *\n * As mentioned in the {@link ChangeStreamer} documentation: \"the ChangeStreamer\n * uses a combination of [the \"initial\", i.e. backup-derived watermark and] ACK\n * responses from connected subscribers to determine the watermark up\n * to which it is safe to purge old change log entries.\"\n *\n * More concretely:\n *\n * * The `initial`, backup-derived watermark is the earliest to which cleanup\n * should ever happen.\n *\n * * However, it is possible for the replica backup to be *ahead* of a connected\n * subscriber; and if a network error causes that subscriber to retry from its\n * last watermark, the change streamer must support it.\n *\n * Thus, before cleaning up to an `initial` backup-derived watermark, the change\n * streamer first confirms that all connected subscribers have also passed\n * that watermark.\n */\nclass ChangeStreamerImpl implements ChangeStreamerService {\n readonly id: string;\n readonly #lc: LogContext;\n readonly #shard: ShardID;\n readonly #changeDBProvider: PostgresDBProvider;\n readonly #replicaVersion: string;\n readonly #source: ChangeSource;\n readonly #storer: Storer;\n readonly #forwarder: Forwarder;\n readonly #reservations: SnapshotReservations | undefined;\n readonly #replicationStatusPublisher: ReplicationStatusPublisher;\n readonly #sqliteCatchupOptions: SQLiteCatchupOptions | undefined;\n readonly #changeLogWriter: SQLiteChangeLogWriter | undefined;\n readonly #purgeScheduler: SQLiteChangeLogPurgeScheduler | undefined;\n readonly #comparator: SQLiteChangeLogComparator | undefined;\n readonly #acker: UpstreamAcker;\n readonly #initializer: ChangeLogInitializer;\n readonly #readRouter: SQLiteChangeLogReadRouter | undefined;\n\n readonly #autoReset: boolean;\n readonly #state: RunningState;\n\n // Starting the (Postgres) ChangeStream results in killing the previous\n // Postgres subscriber, potentially creating a gap in which the old\n // change-streamer has shut down and the new change-streamer has not yet\n // been recognized as \"healthy\" (and thus does not get any requests).\n //\n // To minimize this gap, delay starting the ChangeStream until the first\n // request from a `serving` replicator, indicating that higher level\n // load-balancing / routing logic has begun routing requests to this task.\n readonly #serving = resolver();\n\n readonly #txCounter = getOrCreateCounter(\n 'replication',\n 'transactions',\n 'Count of replicated transactions',\n );\n readonly #changeCounter = getOrCreateCounter(\n 'replication',\n 'changes',\n 'Count of replicated changes (DML or DDL statements)',\n );\n // The number the SQLite change-log commit lands in. It is labeled by whether\n // the log is being written so that the cost of putting a commit on the\n // forward path is attributable rather than inferred.\n readonly #transactionForwardDuration = getOrCreateLatencyHistogram(\n 'replication',\n 'transaction_forward_duration',\n \"Time from receiving a transaction's `begin` to forwarding its `commit`, \" +\n 'i.e. the change-streamer half of forward-to-subscriber latency.',\n );\n readonly #catchupRoutes = getOrCreateCounter(\n 'replication',\n 'sqlite_change_log.catchup_routes',\n 'Catchup subscriptions by selected source and low-cardinality reason.',\n );\n readonly #reservationDemotions = getOrCreateCounter(\n 'replication',\n 'sqlite_change_log.reservation_demotions',\n 'Snapshot reservations demoted from SQLite to PG because the change ' +\n 'log could not cover the backup being restored. With PG retired ' +\n 'these followers have no fallback, so this is the rate at which one ' +\n 'would instead have to wait for a later backup.',\n );\n readonly #reservationConfirmDelays = getOrCreateCounter(\n 'replication',\n 'sqlite_change_log.reservation_confirm_delays',\n 'Snapshot reservations whose confirmation was deferred because the ' +\n \"selected source's change-log minimum was later than the backup \" +\n 'watermark. Counted once per reservation, by that source.',\n );\n\n #latestStatus: Status;\n #latestLagReportCommitTimeMs = 0;\n #backupWatermark: string | undefined;\n #pgPurgedWatermark: string = '';\n /**\n * The floor last logged as held back by a laggard. The level-triggered\n * retry re-evaluates every {@link CLEANUP_DELAY_MS}; logging only when the\n * blocking floor moves keeps a stuck subscriber from emitting the same\n * line indefinitely.\n */\n #loggedBehindWatermark: string | undefined;\n #sqlitePurgeContinuation: PurgeContinuation | undefined;\n #purgeLock: PurgeLock | null;\n // PG and SQLite intentionally own separate level-triggered loops. Neither\n // waits for, advances, or retries the other.\n #pgPurgeScheduled = false;\n #pgPurgeRunning = false;\n #sqlitePurgeScheduled = false;\n #sqlitePurgeRunning = false;\n #stream: ChangeStream | undefined;\n #sqliteCatchup: SQLiteChangeLogCatchup | undefined;\n #changeLogUnavailableSince: number | undefined;\n #lastForwardedCommitWatermark: string | undefined;\n #transactionForwardStartedAt: number | undefined;\n #currentTransactionCompletion:\n | Resolver<ForwardedTransactionCompletion>\n | undefined;\n\n constructor(\n lc: LogContext,\n shard: ShardID,\n taskID: string,\n discoveryAddress: string,\n discoveryProtocol: string,\n changeDBProvider: PostgresDBProvider,\n replicaVersion: string,\n source: ChangeSource,\n replicationStatusPublisher: ReplicationStatusPublisher,\n backupConfig: BackupConfig | null,\n initialPurgeLock: PurgeLock | null,\n autoReset: boolean,\n opts: TuningOptions,\n setTimeoutFn = setTimeout,\n ) {\n this.id = `change-streamer`;\n this.#lc = lc.withContext('component', 'change-streamer');\n this.#shard = shard;\n this.#changeDBProvider = changeDBProvider;\n this.#replicaVersion = replicaVersion;\n this.#source = source;\n this.#storer = new Storer(\n lc,\n shard,\n taskID,\n discoveryAddress,\n discoveryProtocol,\n changeDBProvider,\n replicaVersion,\n consumed => this.#acker.trackPgChangeLog(consumed[2].watermark),\n err => this.stop(err),\n opts,\n );\n this.#forwarder = new Forwarder(lc, {\n flowControlConsensusTimeoutProportion:\n opts.flowControlConsensusTimeoutProportion,\n flowControlSlowSubscriberGracePeriodMs:\n opts.flowControlSlowSubscriberGracePeriodMs,\n });\n const serveOptions = opts.sqliteChangeLogServe;\n const writerOptions = opts.sqliteChangeLogWriter;\n const catchupOptions = opts.sqliteCatchup;\n this.#readRouter =\n serveOptions && writerOptions && catchupOptions\n ? new SQLiteChangeLogReadRouter({\n shard,\n readPercent: serveOptions.readPercent,\n coldReadPercent: serveOptions.coldReadPercent,\n retentionMs: serveOptions.retentionMs,\n failureCooldownMs: serveOptions.failureCooldownMs,\n now: serveOptions.now,\n inspect:\n serveOptions.inspect ??\n (() =>\n inspectSQLiteChangeLog(\n lc,\n catchupOptions.changeLogFile,\n writerOptions.identity,\n )),\n })\n : undefined;\n this.#reservations = backupConfig\n ? new SnapshotReservations(lc, backupConfig, taskID => {\n this.#readRouter?.release(taskID);\n this.#purgeScheduler?.resume(taskID);\n })\n : undefined;\n this.#replicationStatusPublisher = replicationStatusPublisher;\n this.#changeLogWriter = opts.sqliteChangeLogWriter\n ? new SQLiteChangeLogWriter(lc, {\n ...opts.sqliteChangeLogWriter,\n onCommit: watermark => {\n this.#sqliteCatchup?.onChangeLogCommit(watermark);\n // The commit closed the log's transaction, i.e. opened a purge\n // window (§3.3).\n this.#purgeScheduler?.onWriterIdle();\n },\n // Fail-soft deletes the file, and the reader is cached here, so it\n // has to go with it: otherwise it serves an unlinked inode while\n // every new open sees nothing.\n onDisabled: () => {\n // The writer stays disabled and the file stays absent for the life\n // of this process, so unlike a transient read/barrier failure this\n // breaker never expires.\n this.#readRouter?.trip(true);\n this.#closeSQLiteCatchup();\n this.#comparator?.stop();\n },\n onRebuilt: () => {\n this.#closeSQLiteCatchup();\n // Invalidate cycles that can still read the replaced file.\n this.#comparator?.invalidate();\n },\n })\n : undefined;\n // The purge scheduler runs on the writer's own connection, which is also\n // its gate: no writer (mode `off`) means no scheduler, and a writer that\n // has not created the file yet -- or failed soft and deleted it -- makes\n // cycles skip rather than fail, so a late-appearing file is picked up\n // without a restart.\n this.#purgeScheduler =\n opts.sqliteChangeLogPurge && this.#changeLogWriter\n ? new SQLiteChangeLogPurgeScheduler(\n lc,\n () => this.#changeLogWriter?.connection,\n () => this.#forwarder.getAcks(),\n opts.sqliteChangeLogPurge,\n )\n : undefined;\n this.#acker = new UpstreamAcker({\n trackPgChangeLog: true, // TODO: set false when retiring PG\n trackBackup: backupConfig?.litestreamVersion === 'v5',\n });\n const replicaSource = opts.sqliteChangeLogCompare\n ? replicaInitializationSource(lc, opts.sqliteChangeLogCompare.replicaFile)\n : undefined;\n this.#initializer = new ChangeLogInitializer(\n lc,\n {\n initFromPgChangeLog: true, // TODO: set false when retiring PG\n initFromReplica: replicaSource !== undefined,\n },\n {\n pgChangeLog: () =>\n this.#storer.getStartStreamInitializationParameters(),\n // Only reached when `initFromReplica` is set, i.e. when the option\n // that supplies the file is present.\n replica: () => must(replicaSource)(),\n // Use the Postgres resume point when it is present. Otherwise, let the\n // SQLite change log supply its own resume point.\n reconcileChangeLog: (resumeFrom, seed) =>\n resumeFrom\n ? this.#changeLogWriter?.reconcile(resumeFrom)\n : this.#changeLogWriter?.reconcileFromLog(seed),\n changeLog: () => this.#changeLogWriter?.connection,\n },\n );\n this.#sqliteCatchupOptions = opts.sqliteCatchup\n ? {\n ...opts.sqliteCatchup,\n // The real cleanup guard, in place of the catchup's no-op default:\n // registration and purge batches now serialize on one mutex.\n cleanupGuard:\n opts.sqliteCatchup.cleanupGuard ??\n this.#purgeScheduler?.cleanupGuard,\n }\n : undefined;\n // Compare mode requires the writer and catchup configuration.\n this.#comparator =\n opts.sqliteChangeLogCompare &&\n opts.sqliteChangeLogWriter &&\n opts.sqliteCatchup\n ? new SQLiteChangeLogComparator(\n lc,\n shard,\n opts.sqliteCatchup.changeLogFile,\n opts.sqliteChangeLogWriter.identity,\n this.#storer,\n {setTimeoutFn, ...opts.sqliteChangeLogCompare},\n )\n : undefined;\n this.#purgeLock = initialPurgeLock;\n this.#autoReset = autoReset;\n this.#state = new RunningState(this.id, undefined, setTimeoutFn);\n this.#latestStatus = {tag: 'status'};\n }\n\n async run() {\n this.#lc.info?.('starting change stream');\n\n this.#forwarder.startProgressMonitor();\n\n const lagReportInit = await this.#source.startLagReporter();\n if (lagReportInit) {\n this.#latestStatus.lagReport = {\n nextSendTimeMs: lagReportInit.nextSendTimeMs,\n };\n // Record the commit time of the initiated lag report (i.e. \"head\")\n // for the purpose of skipping over any lag reports that are re-streamed\n // by the change-source in the case of a change-streamer starting from\n // an older watermark.\n this.#latestLagReportCommitTimeMs = lagReportInit.firstCommitTimeMs;\n }\n\n // Once this change-streamer acquires \"ownership\" of the change DB,\n // it is safe to start the storer.\n await this.#storer.assumeOwnership(this.#purgeLock);\n this.#purgeLock = null;\n\n // The threshold in (estimated number of) bytes to send() on subscriber\n // websockets before `await`-ing the I/O buffers to be ready for more.\n const flushBytesThreshold = getDefaultHighWaterMark(false);\n\n while (this.#state.shouldRun()) {\n let err: unknown;\n let watermark: string | null = null;\n let unflushedBytes = 0;\n try {\n // Initialization reconciles the change log for every stream\n // connection. It completes before `startStream`, so no change can\n // arrive during reconciliation.\n const {lastWatermark, backfillRequests} =\n await this.#initializer.initialize();\n // SQLite catchup must not be eligible until this has been initialized\n // from the durable PG head. Commits observed only since process startup\n // are insufficient after a change-streamer restart.\n this.#lastForwardedCommitWatermark = lastWatermark;\n const stream = await this.#source.startStream(\n lastWatermark,\n backfillRequests,\n );\n this.#storer.run().catch(e => stream.changes.cancel(e));\n\n this.#stream = stream;\n if (\n this.#state.resetBackoff() >\n REPLICATION_STATUS_ERROR_DELAY_THRESHOLD_MS\n ) {\n // After recovering from a backoff for which a replication status\n // error was published, publish an OK status\n this.#replicationStatusPublisher.publish(\n this.#lc,\n 'Replicating',\n `Replicating from ${lastWatermark}`,\n );\n }\n watermark = null;\n\n this.#acker.reset(stream.acks);\n for await (const change of stream.changes) {\n this.#acker.trackDownstream(change);\n\n const [type, msg] = change;\n switch (type) {\n case 'status':\n if (\n msg.lagReport &&\n msg.lagReport.lastTimings.commitTimeMs >=\n this.#latestLagReportCommitTimeMs\n ) {\n // Lag reports are not stored in the cdc change log, but rather\n // only forwarded on \"live\" connections. When a new subscriber\n // is catching up, it is initialized with the #latestStatus\n // from which it can measure lag while catching up.\n this.#latestStatus.lagReport = msg.lagReport;\n this.#latestLagReportCommitTimeMs =\n msg.lagReport.lastTimings.commitTimeMs;\n this.#forwarder.sendStatus(this.#latestStatus);\n }\n continue;\n case 'control':\n await this.#handleControlMessage(msg);\n continue; // control messages are not stored/forwarded\n case 'begin':\n watermark = change[2].commitWatermark;\n break;\n case 'commit':\n if (watermark !== change[2].watermark) {\n throw new UnrecoverableError(\n `commit watermark ${change[2].watermark} does not match 'begin' watermark ${watermark}`,\n );\n }\n this.#txCounter.add(1);\n break;\n default:\n if (type === 'data') {\n this.#changeCounter.add(1);\n }\n if (watermark === null) {\n throw new UnrecoverableError(\n `${type} change (${msg.tag}) received before 'begin' message`,\n );\n }\n break;\n }\n\n const json = this.#storer.store(watermark, change);\n // The SQLite change log commits at transaction boundaries, and its\n // commit for this transaction lands here -- before the forward of the\n // `commit` message, and before #recordForwardedTransactionBoundary\n // advances what #captureRequiredHead reads. No `await` separates it\n // from #storer.store() above, which is the assertable form of\n // invariant 1: the storer only enqueues, and its Postgres commit\n // cannot complete without yielding, so a synchronous SQLite commit in\n // the same loop iteration always precedes anything that can advance\n // the watermark this stream would resume from. Never throws; a write\n // failure disables the writer rather than stopping replication.\n this.#changeLogWriter?.write(change, json);\n const entry: WatermarkedChange = [watermark, change[1].tag, json];\n unflushedBytes += json.length;\n if (unflushedBytes < flushBytesThreshold) {\n // pipeline changes until flushBytesThreshold\n this.#forwarder.forward(entry);\n this.#recordForwardedTransactionBoundary(type, entry[0]);\n } else {\n // Wait for messages to clear socket buffers to ensure that they\n // make their way to subscribers. Without this `await`, the\n // messages end up being buffered in this process, which:\n // (1) results in memory pressure and increased GC activity\n // (2) prevents subscribers from processing the messages as they\n // arrive, instead getting them in a large batch after being\n // idle while they were queued (causing further delays).\n const forwarded = this.#forwarder.forwardWithFlowControl(entry);\n // forwardWithFlowControl synchronously sends the entry and updates\n // the Forwarder's transaction state before returning its flow-\n // control promise. Record the boundary before awaiting that promise\n // so registrations during the wait observe the forwarded state.\n this.#recordForwardedTransactionBoundary(type, entry[0]);\n await promiseOrAbort(\n forwarded,\n stream.changes.signal,\n this.#state.signal,\n );\n unflushedBytes = 0;\n }\n\n if (type === 'commit' || type === 'rollback') {\n watermark = null;\n }\n\n // Allow the storer to exert back pressure.\n const readyForMore = this.#storer.readyForMore();\n if (readyForMore) {\n await promiseOrAbort(\n readyForMore,\n stream.changes.signal,\n this.#state.signal,\n );\n }\n }\n } catch (e) {\n err = e;\n } finally {\n this.#stream?.changes.cancel();\n this.#stream = undefined;\n }\n\n // When the change stream is interrupted, abort any pending transaction.\n if (watermark) {\n this.#lc.warn?.(`aborting interrupted transaction ${watermark}`);\n this.#storer.abort();\n // Rolling back the log leaves no rows for the interrupted transaction,\n // so the next connection's reconciliation sees a head at or below its\n // resume watermark rather than a partial transaction.\n this.#changeLogWriter?.abort();\n // A rollback ends the log's open transaction without a commit\n // notification; wake any purge batch waiting for that window.\n this.#purgeScheduler?.onWriterIdle();\n this.#forwarder.forward([watermark, 'rollback', ROLLBACK_JSON]);\n this.#recordForwardedTransactionBoundary('rollback', watermark);\n }\n\n // Backoff and drain any pending entries in the storer before reconnecting.\n await Promise.all([\n this.#storer.stop(),\n this.#state.backoff(this.#lc, err),\n this.#state.retryDelay > REPLICATION_STATUS_ERROR_DELAY_THRESHOLD_MS\n ? publishCriticalEvent(\n this.#lc,\n replicationStatusError(this.#lc, 'Replicating', err),\n )\n : promiseVoid,\n ]);\n }\n\n this.#forwarder.stopProgressMonitor();\n this.#lc.info?.('ChangeStreamer stopped');\n }\n\n async #handleControlMessage(msg: ChangeStreamControl[1]) {\n this.#lc.info?.('received control message', msg);\n const {tag} = msg;\n\n switch (tag) {\n case 'reset-required':\n await markResetRequired(\n this.#changeDBProvider('change-streamer-reset', 1),\n this.#shard,\n );\n await publishReplicationError(\n this.#lc,\n 'Replicating',\n msg.message ?? 'Resync required',\n msg.errorDetails,\n );\n if (this.#autoReset) {\n this.#lc.warn?.('shutting down for auto-reset');\n await this.stop(new AutoResetSignal());\n }\n break;\n default:\n unreachable(tag);\n }\n }\n\n async subscribe(ctx: SubscriberContext): Promise<Source<string>> {\n const {protocolVersion, id, mode, replicaVersion, watermark} = ctx;\n if (mode === 'serving') {\n this.#serving.resolve();\n }\n let cleanupSubscriber = () => {};\n const downstream = Subscription.create<string>({\n cleanup: () => cleanupSubscriber(),\n });\n // No subscriber's ACK advances the SQLite change log's head any more: the\n // writer runs in this process, so the barrier is notified from the commit\n // itself (see #changeLogWriter's onCommit).\n const subscriber = new Subscriber(\n protocolVersion,\n id,\n mode,\n watermark,\n downstream,\n () => this.#latestStatus,\n {},\n );\n const lc = this.#lc.withContext('subscriber', subscriber.id);\n const removeFromForwarder = () => {\n lc.info?.(`removing subscriber ${subscriber.id}`);\n this.#forwarder.remove(subscriber);\n };\n cleanupSubscriber = removeFromForwarder;\n if (replicaVersion !== this.#replicaVersion) {\n lc.warn?.(`rejecting subscriber at replica version ${replicaVersion}`);\n subscriber.close(\n ErrorType.WrongReplicaVersion,\n `current replica version is ${\n this.#replicaVersion\n } (requested ${replicaVersion})`,\n );\n } else {\n lc.info?.(`adding subscriber ${subscriber.id}`);\n\n const catchupFromPG = () => {\n // Keep the existing PG registration/catchup lockstep unchanged when\n // SQLite was not selected before Forwarder.add().\n cleanupSubscriber = removeFromForwarder;\n this.#forwarder.add(subscriber);\n this.#storer.catchup(subscriber, mode);\n };\n const sqliteSelection = this.#selectSQLiteCatchup(lc, ctx);\n if (!sqliteSelection) {\n catchupFromPG();\n } else {\n const {catchup, reason, coverage, logWarm} = sqliteSelection;\n cleanupSubscriber = () => catchup.remove(subscriber);\n const registration = await catchup.catchup(\n subscriber,\n () => this.#captureRequiredHead(),\n {logWarm},\n );\n switch (registration.kind) {\n case 'registered':\n lc.debug?.(\n `serving ${ctx.id} from SQLite catchup`,\n ...(coverage ? [{sqliteChangeLogCoverage: coverage}] : []),\n );\n this.#recordCatchupRoute('sqlite', reason);\n break;\n case 'uncovered':\n lc.info?.(\n `serving ${ctx.id} from PG catchup: subscriber watermark ` +\n `${ctx.watermark} is below the SQLite change-log minimum ` +\n registration.minWatermark,\n );\n this.#recordCatchupRoute('pg', 'watermark-uncovered');\n catchupFromPG();\n break;\n case 'declined':\n // Registration failed before the subscriber was committed to\n // SQLite, so PG is still available. The coordinator has already\n // tripped the breaker, which keeps the retry off SQLite for the\n // cooldown; it is deliberately not closed here, since closing it\n // would abort the catchups of every other subscriber it is\n // serving.\n lc.error?.(\n `serving ${ctx.id} from PG catchup: SQLite catchup ` +\n `registration failed`,\n registration.error,\n );\n this.#recordCatchupRoute('pg', 'registration-failed');\n catchupFromPG();\n break;\n case 'handled':\n // The coordinator closed or failed the subscriber itself, so\n // there is nothing left to route. Still counted, so that the\n // route counter sums to the number of subscriptions.\n this.#recordCatchupRoute('sqlite', 'registration-handled');\n break;\n default:\n unreachable(registration);\n }\n }\n }\n // Any snapshot reservation held by this task can be closed now that\n // it is subscribed to the change stream.\n this.#reservations?.close(ctx.taskID);\n return downstream;\n }\n\n async startSnapshotReservation(\n taskID: string,\n ): Promise<Source<SnapshotMessage>> {\n if (!this.#reservations) {\n throw new Error('backups are not configured');\n }\n const downstream = this.#reservations.open(taskID);\n\n try {\n // Wait for an in-flight SQLite purge batch before reading and\n // advertising the reservation's bounds.\n await (this.#purgeScheduler?.pause(taskID) ?? promiseVoid);\n // A concurrent retry for this task may have superseded and cancelled\n // this reservation while its purge pause was settling. Only the current\n // owner may replace the task's source pin.\n if (!this.#reservations.isCurrent(taskID, downstream)) {\n return downstream;\n }\n // Pin after the purge pause has settled, so the SQLite minimum captured\n // by the router cannot move before it is advertised. #confirmReservations\n // skips an unpinned task while this await is in flight.\n this.#readRouter?.pin(taskID);\n // If a backup has been confirmed, immediately confirm the reservation.\n await this.#confirmReservations();\n return downstream;\n } catch (e) {\n // Cancel the reservation this call opened, not whatever currently\n // holds the task's slot: an overlapping retry for the same taskID may\n // have already replaced it, and a by-taskID close would tear down the\n // replacement and release its purge pause while it is advertising\n // snapshot bounds. cancel() routes through the subscription's cleanup,\n // which closes the reservation only if this instance still owns it.\n downstream.cancel();\n throw e;\n }\n }\n\n trackBackupWatermark(watermark: string) {\n this.#backupWatermark = watermark;\n this.#acker.trackBackup(watermark);\n // The durable backup floor is an independent input to each change-log\n // implementation. SQLite receives it even when the PG log has already\n // reached this watermark.\n this.#requestSQLitePurge('deferred');\n this.#maybeSchedulePGPurge();\n this.#maybeScheduleSQLitePurge();\n\n // Confirm any waiting reservations now that a backup has been confirmed.\n // Note that this is asynchronous and best effort; if it fails, the watermark\n // is still \"tracked\" and the confirmation will be retried on the next backup.\n void this.#confirmReservations().catch(e =>\n this.#lc.warn?.(`error confirming snapshot reservation`, e),\n );\n }\n\n async #confirmReservations() {\n const backupWatermark = this.#backupWatermark;\n const reservations = this.#reservations;\n if (\n backupWatermark === undefined ||\n !reservations?.confirmationsRequired()\n ) {\n return;\n }\n\n // Resolve PG bounds before touching any pin. Everything below runs to\n // completion without awaiting, which is what keeps a reservation's\n // advertised bounds and its pin in agreement: a concurrent /snapshot\n // retry for the same task replaces the reservation and re-pins it, and\n // confirming that replacement with the previous pin's bounds is exactly\n // the mismatch pinning exists to prevent. The cost is one PG round trip\n // per call even when every reservation is pinned to SQLite.\n const pgState = await this.#getChangeLogState();\n for (const taskID of reservations.unconfirmedTaskIDs()) {\n let route = this.#readRouter?.peek(taskID);\n // startSnapshotReservation pins only after an in-flight purge has\n // completed. A task with no pin yet is confirmed by that path instead.\n if (this.#readRouter && route === undefined) {\n continue;\n }\n\n if (route?.source === 'sqlite') {\n const coverage = must(\n route.coverage,\n 'a pinned SQLite route must carry its covered range',\n );\n if (coverage.minWatermark > backupWatermark) {\n // A log seeded after this backup, most often. Holding the\n // reservation until a backup reaches the log's minimum would stall\n // a follower that PG can serve now, so move it -- pin included.\n this.#lc.info?.(\n `demoting ${taskID} to PG catchup: SQLite change-log minimum ` +\n `${coverage.minWatermark} is later than backupWatermark ` +\n backupWatermark,\n );\n this.#reservationDemotions.add(1);\n route = must(this.#readRouter).demote(taskID);\n }\n }\n\n const source = route?.source ?? 'pg';\n let minWatermark: string;\n if (route?.source === 'sqlite') {\n // Demotion above already moved every SQLite route the backup is\n // outside of, so this one covers it and confirms below.\n minWatermark = must(\n route.coverage,\n 'a pinned SQLite route must carry its covered range',\n ).minWatermark;\n } else {\n ({minWatermark} = pgState);\n }\n\n if (minWatermark <= backupWatermark) {\n reservations.confirmFor(\n taskID,\n pgState.replicaVersion,\n backupWatermark,\n source,\n );\n } else {\n // PG cannot catch a restored replica up from this backup yet. Keep\n // the reservation pending until a later backup moves the durable\n // watermark into its covered range.\n if (reservations.noteConfirmationDelayed(taskID)) {\n this.#reservationConfirmDelays.add(1);\n }\n this.#lc.error?.(\n `pg change-log minWatermark ${minWatermark} is later than ` +\n `backupWatermark ${backupWatermark}. Delaying confirmation of ` +\n `snapshot reservation until next backup.`,\n );\n }\n }\n }\n\n #maybeSchedulePGPurge(): void {\n const backupWatermark = this.#backupWatermark;\n if (\n this.#pgPurgeScheduled ||\n this.#pgPurgeRunning ||\n backupWatermark === undefined ||\n this.#pgPurgedWatermark >= backupWatermark\n ) {\n return;\n }\n this.#pgPurgeScheduled = true;\n this.#state.setTimeout(() => {\n this.#pgPurgeScheduled = false;\n this.#pgPurgeRunning = true;\n return this.#purgePGChangeLog().finally(() => {\n this.#pgPurgeRunning = false;\n this.#maybeSchedulePGPurge();\n });\n }, CLEANUP_DELAY_MS);\n }\n\n #maybeScheduleSQLitePurge(): void {\n if (\n this.#sqlitePurgeScheduled ||\n this.#sqlitePurgeRunning ||\n this.#backupWatermark === undefined ||\n this.#sqlitePurgeContinuation === undefined\n ) {\n return;\n }\n const delay =\n this.#sqlitePurgeContinuation === 'immediate' ? 0 : CLEANUP_DELAY_MS;\n this.#sqlitePurgeScheduled = true;\n this.#state.setTimeout(() => {\n this.#sqlitePurgeScheduled = false;\n this.#sqlitePurgeRunning = true;\n return this.#purgeSQLiteChangeLog().finally(() => {\n this.#sqlitePurgeRunning = false;\n this.#maybeScheduleSQLitePurge();\n });\n }, delay);\n }\n\n async #getChangeLogState(): Promise<{\n replicaVersion: string;\n minWatermark: string;\n }> {\n const minWatermark = await this.#storer.getMinWatermarkForCatchup();\n if (!minWatermark) {\n this.#lc.warn?.(\n `Unexpected empty changeLog. Resync if \"Local replica watermark\" errors arise`,\n );\n }\n return {\n replicaVersion: this.#replicaVersion,\n minWatermark: minWatermark ?? this.#replicaVersion,\n };\n }\n\n #getCleanupFloor(): {\n backupWatermark: string;\n purgeWatermark: string;\n current: string[];\n } {\n const backupWatermark = this.#backupWatermark;\n assert(\n backupWatermark !== undefined,\n 'cleanup cannot run without a backup watermark',\n );\n const current = [\n ...this.#forwarder.getAcks(),\n ...(this.#reservations?.getReservedWatermarks() ?? []),\n ];\n // The cleanup delay above is the grace period for disconnected\n // subscribers to reconnect and expose their ACKs. Once it expires, an\n // empty set places no additional constraint on the confirmed backup\n // watermark and must not pin either change log indefinitely.\n return {\n backupWatermark,\n purgeWatermark: min(backupWatermark, ...current),\n current,\n };\n }\n\n async #purgePGChangeLog(): Promise<void> {\n try {\n const {backupWatermark, purgeWatermark, current} =\n this.#getCleanupFloor();\n if (purgeWatermark < backupWatermark) {\n if (this.#loggedBehindWatermark !== purgeWatermark) {\n this.#loggedBehindWatermark = purgeWatermark;\n this.#lc.info?.(\n `At least one client is behind backup ${backupWatermark}`,\n {watermarks: current},\n );\n }\n } else {\n this.#loggedBehindWatermark = undefined;\n }\n if (purgeWatermark <= this.#pgPurgedWatermark) {\n return;\n }\n this.#lc.info?.(`Purging PG changes before ${purgeWatermark} ...`);\n const start = performance.now();\n const deleted = await this.#storer.purgeRecordsBefore(purgeWatermark);\n const elapsed = (performance.now() - start).toFixed(2);\n this.#lc.info?.(\n `Purged ${deleted} PG changes before ${purgeWatermark} (${elapsed} ms)`,\n );\n this.#pgPurgedWatermark = purgeWatermark;\n } catch (e) {\n this.#lc.warn?.(`error purging the PG change log`, e);\n }\n }\n\n async #purgeSQLiteChangeLog(): Promise<void> {\n const scheduler = this.#purgeScheduler;\n if (!scheduler) {\n return;\n }\n try {\n const {backupWatermark, purgeWatermark} = this.#getCleanupFloor();\n // Consume the request before starting. A backup notification that\n // arrives during this pass records another request, which is merged with\n // the continuation returned by this pass rather than being overwritten.\n this.#sqlitePurgeContinuation = undefined;\n if (purgeWatermark < backupWatermark) {\n // Live constraint changes are not edge-triggered. Keep evaluating the\n // floor until it reaches the durable backup watermark, independently\n // of whether the PG implementation still exists.\n this.#requestSQLitePurge('deferred');\n }\n const result = await scheduler.purge(purgeWatermark);\n this.#requestSQLitePurge(result.continuation);\n } catch (e) {\n // purge() is fail-soft, but retain the coordinator's retry if an\n // unexpected caller-boundary failure escapes it.\n this.#requestSQLitePurge('deferred');\n this.#lc.warn?.(`error purging the SQLite change log`, e);\n }\n }\n\n #requestSQLitePurge(continuation: PurgeContinuation | undefined): void {\n if (!this.#purgeScheduler || continuation === undefined) {\n return;\n }\n if (\n continuation === 'immediate' ||\n this.#sqlitePurgeContinuation === undefined\n ) {\n this.#sqlitePurgeContinuation = continuation;\n }\n }\n\n async stop(err?: unknown) {\n this.#state.stop(this.#lc, err);\n this.#stream?.changes.cancel();\n this.#purgeScheduler?.stop();\n this.#comparator?.stop();\n this.#sqliteCatchup?.close();\n this.#changeLogWriter?.close();\n await Promise.allSettled([this.#storer.stop(), this.#source.stop()]);\n }\n\n #recordForwardedTransactionBoundary(\n type: ChangeStreamData[0],\n watermark: string,\n ) {\n switch (type) {\n case 'begin':\n assert(\n this.#currentTransactionCompletion === undefined,\n 'forwarded begin while a transaction is already in progress',\n );\n this.#currentTransactionCompletion =\n resolver<ForwardedTransactionCompletion>();\n this.#transactionForwardStartedAt = performance.now();\n break;\n case 'commit': {\n const completion = this.#currentTransactionCompletion;\n assert(completion, 'forwarded commit without a pending transaction');\n this.#recordTransactionForwardDuration();\n this.#lastForwardedCommitWatermark = watermark;\n this.#currentTransactionCompletion = undefined;\n completion.resolve({kind: 'committed', watermark});\n break;\n }\n case 'rollback': {\n const completion = this.#currentTransactionCompletion;\n assert(completion, 'forwarded rollback without a pending transaction');\n const committed = this.#lastForwardedCommitWatermark;\n assert(committed, 'last forwarded commit watermark is not initialized');\n this.#transactionForwardStartedAt = undefined;\n this.#currentTransactionCompletion = undefined;\n completion.resolve({kind: 'rolled-back', watermark: committed});\n break;\n }\n }\n }\n\n #recordTransactionForwardDuration() {\n const startedAt = this.#transactionForwardStartedAt;\n this.#transactionForwardStartedAt = undefined;\n if (startedAt !== undefined) {\n this.#transactionForwardDuration.recordMs(performance.now() - startedAt, {\n sqlite_change_log: this.#changeLogWriter?.enabled ? 'on' : 'off',\n });\n }\n }\n\n /**\n * Closes and forgets the cached catchup coordinator, so that a later\n * subscription re-opens the log from scratch rather than reading a handle\n * whose file is gone.\n */\n #closeSQLiteCatchup() {\n this.#sqliteCatchup?.close();\n this.#sqliteCatchup = undefined;\n }\n\n #captureRequiredHead(): string | Promise<string> {\n const committed = this.#lastForwardedCommitWatermark;\n assert(committed, 'last forwarded commit watermark is not initialized');\n return (\n this.#currentTransactionCompletion?.promise.then(\n completion => completion.watermark,\n ) ?? committed\n );\n }\n\n #selectSQLiteCatchup(\n lc: LogContext,\n ctx: SubscriberContext,\n ): SQLiteCatchupSelection | undefined {\n const opts = this.#sqliteCatchupOptions;\n if (this.#lastForwardedCommitWatermark === undefined || !opts) {\n this.#recordCatchupRoute('pg', 'not-ready');\n return undefined;\n }\n // SQLite catchup is only for disposable serving replicas. Backup\n // subscribers retain the existing PG recovery policy until RMv2 can\n // resume the canonical replica directly from replica/backup/slot state.\n // Enforce this before the selector so no canary policy can override it.\n if (ctx.mode !== 'serving') {\n lc.info?.(`not serving backup subscriber ${ctx.id} from SQLite catchup`);\n this.#recordCatchupRoute('pg', 'ineligible-mode');\n return undefined;\n }\n\n let route: ChangeLogReadRoute | undefined;\n if (this.#readRouter) {\n route = this.#readRouter.consume(ctx.taskID);\n if (route.source === 'pg') {\n if (route.reason === 'cold-log') {\n lc.info?.(\n `serving ${ctx.id} from PG catchup: SQLite change log is still warming`,\n {sqliteChangeLogCoverage: route.coverage},\n );\n } else if (route.reason === 'log-unavailable') {\n this.#declineSQLiteCatchup(\n lc,\n ctx,\n opts,\n `${opts.changeLogFile} is unavailable or incompatible`,\n );\n } else {\n lc.debug?.(\n `serving ${ctx.id} from PG catchup: SQLite route ${route.reason}`,\n ...(route.coverage\n ? [{sqliteChangeLogCoverage: route.coverage}]\n : []),\n );\n }\n this.#recordCatchupRoute('pg', route.reason);\n return undefined;\n }\n const coverage = route.coverage;\n assert(coverage, 'a SQLite route must carry its covered range');\n if (ctx.watermark < coverage.minWatermark) {\n lc.info?.(\n `serving ${ctx.id} from PG catchup: subscriber watermark ` +\n `${ctx.watermark} is below the SQLite change-log minimum ` +\n coverage.minWatermark,\n {sqliteChangeLogCoverage: coverage},\n );\n this.#recordCatchupRoute('pg', 'watermark-uncovered');\n return undefined;\n }\n }\n\n // `shouldUse` remains as a test hook and an optional extra policy gate.\n // Production selection comes from #readRouter.\n if (!this.#readRouter && !opts.shouldUse?.(ctx)) {\n this.#recordCatchupRoute('pg', 'selector');\n return undefined;\n }\n if (this.#readRouter && opts.shouldUse && !opts.shouldUse(ctx)) {\n this.#recordCatchupRoute('pg', 'selector');\n return undefined;\n }\n\n const catchup =\n this.#sqliteCatchup ?? this.#openSQLiteCatchup(lc, opts, ctx);\n if (catchup) {\n return {\n catchup,\n reason: route?.reason ?? 'selector',\n coverage: route?.coverage,\n // Legacy focused-test selection has no warm classification.\n logWarm:\n route === undefined ? undefined : route.reason !== 'selected-cold',\n };\n } else {\n this.#recordCatchupRoute('pg', 'log-unavailable');\n }\n return undefined;\n }\n\n #recordCatchupRoute(source: 'pg' | 'sqlite', reason: string): void {\n this.#catchupRoutes.add(1, {source, reason});\n }\n\n /**\n * Opens the change log and, if it can serve, wraps it in the coordinator that\n * subsequent subscriptions reuse.\n *\n * The log may not exist yet -- the writer creates it at the stream loop's\n * first reconcile, and deletes it when it fails soft -- or may exist without\n * content, or may be unreadable. None of those can be allowed to fail a\n * subscription:\n * this is the last point at which PG catchup is still available -- past\n * `Forwarder.add()` the subscriber is committed to SQLite -- so each declines\n * here instead. Neither the failure nor the reader is retained, so a later\n * subscription retries from scratch.\n */\n #openSQLiteCatchup(\n lc: LogContext,\n opts: SQLiteCatchupOptions,\n ctx: SubscriberContext,\n ): SQLiteChangeLogCatchup | undefined {\n let reader: SQLiteChangeLogReader | undefined;\n try {\n reader = new SQLiteChangeLogReader(this.#lc, opts.changeLogFile);\n // `plan()` doubles as the readiness check: it reports `not-ready` when\n // the writer has not created or seeded the stream table. The barrier\n // cannot wait its way out of that, since a log with no head can never\n // reach the required one.\n if (reader.plan(ctx.watermark).kind === 'not-ready') {\n reader.close();\n this.#declineSQLiteCatchup(\n lc,\n ctx,\n opts,\n `${opts.changeLogFile} has no changes to serve yet`,\n );\n return undefined;\n }\n } catch (e) {\n // An absent file is the common case, since a readonly handle cannot\n // create one. A corrupt or truncated file lands here too, and is equally\n // not a reason to fail the subscription.\n reader?.close();\n this.#declineSQLiteCatchup(\n lc,\n ctx,\n opts,\n `cannot read ${opts.changeLogFile}`,\n e,\n );\n return undefined;\n }\n this.#changeLogUnavailableSince = undefined;\n // Readiness is checked once, on the way to the cached coordinator: a log\n // with content keeps it. Purging preserves the latest transaction as a\n // catchup boundary, and `reconcileChangeLog` reseeds inside a single\n // transaction, so a reader never observes an emptied log.\n this.#sqliteCatchup = new SQLiteChangeLogCatchup(\n this.#lc,\n this.#forwarder,\n reader,\n {\n batchSize: opts.readBatchRows,\n barrierTimeoutMs: opts.barrierTimeoutMs,\n barrierPollIntervalMs: opts.barrierPollIntervalMs,\n cleanupGuard: opts.cleanupGuard,\n onFailure: failure => {\n this.#lc.warn?.(\n `temporarily disabling SQLite catchup after ${failure}`,\n );\n this.#readRouter?.trip();\n },\n },\n );\n return this.#sqliteCatchup;\n }\n\n /**\n * Reports falling back to PG catchup, tracking how long the log has been\n * unavailable so that a subscription that merely arrived before the writer's\n * first reconcile does not look like an incident.\n */\n #declineSQLiteCatchup(\n lc: LogContext,\n ctx: SubscriberContext,\n opts: SQLiteCatchupOptions,\n reason: string,\n error?: unknown,\n ): void {\n const now = Date.now();\n this.#changeLogUnavailableSince ??= now;\n const unavailableMs = now - this.#changeLogUnavailableSince;\n const threshold =\n opts.notReadyWarnThresholdMs ??\n DEFAULT_CHANGE_LOG_UNAVAILABLE_WARN_THRESHOLD_MS;\n lc[unavailableMs >= threshold ? 'warn' : 'debug']?.(\n `serving ${ctx.id} from PG catchup: ${reason} ` +\n `(unavailable for ${unavailableMs} ms)`,\n ...(error === undefined ? [] : [error]),\n );\n }\n}\n\ntype ForwardedTransactionCompletion =\n | {kind: 'committed'; watermark: string}\n | {kind: 'rolled-back'; watermark: string};\n\ntype SQLiteCatchupSelection = {\n readonly catchup: SQLiteChangeLogCatchup;\n readonly reason: string;\n readonly coverage: SQLiteChangeLogCoverage | undefined;\n /**\n * Whether the router classified the log as warm for this subscriber, or\n * undefined when selection did not run through the router (focused tests).\n */\n readonly logWarm: boolean | undefined;\n};\n\n// The delay between receiving an initial, backup-based watermark\n// and performing a check of whether to purge records before it.\n// This delay should be long enough to handle situations like the following:\n//\n// 1. `litestream restore` downloads a backup for the `replication-manager`\n// 2. `replication-manager` starts up and runs this `change-streamer`\n// 3. `zero-cache`s that are running on a different replica connect to this\n// `change-streamer` after exponential backoff retries.\n//\n// It is possible for a `zero-cache`[3] to be behind the backup restored [1].\n// This cleanup delay (30 seconds) is thus set to be a value comfortably\n// longer than the max delay for exponential backoff (10 seconds) in\n// `services/running-state.ts`. This allows the `zero-cache` [3] to reconnect\n// so that the `change-streamer` can track its progress and know when it has\n// surpassed the initial watermark of the backup [1].\nconst CLEANUP_DELAY_MS = DEFAULT_MAX_RETRY_DELAY_MS * 3;\n\nconst ROLLBACK_JSON = JSON.stringify([\n 'rollback',\n {tag: 'rollback'},\n] satisfies Rollback);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4LA,eAAsB,mBACpB,IACA,OACA,QACA,kBACA,mBACA,UACA,cACA,4BACA,mBACA,cACA,WACA,WACA,MACA,eAAe,YACiB;CAChC,MAAM,wBACJ,IACA,UACA,mBACA,OACA,WACA,aAAa,KAAA,GACb,YACF;CAOA,MAAM,oBACJ,iBACA,QAEA,SACE,KACE;EAAC;EAAK,YAAY,GAAE,qBAAqB,gBAAe;CAAC,GAKzD,SAAS,OACX,CACF;CAEF,MAAM,EAAC,mBAAkB;CACzB,OAAO,IAAI,mBACT,IACA,OACA,QACA,kBACA,mBACA,kBACA,gBACA,cACA,4BACA,cACA,WACA,WACA,MACA,YACF;AACF;AAEA,IAAM,8CAA8C;AAQpD,IAAM,mDAAmD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4IzD,IAAM,qBAAN,MAA0D;CACxD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CAUA,WAAoB,SAAS;CAE7B,aAAsB,mBACpB,eACA,gBACA,kCACF;CACA,iBAA0B,mBACxB,eACA,WACA,qDACF;CAIA,8BAAuC,4BACrC,eACA,gCACA,yIAEF;CACA,iBAA0B,mBACxB,eACA,oCACA,sEACF;CACA,wBAAiC,mBAC/B,eACA,2CACA,qPAIF;CACA,4BAAqC,mBACnC,eACA,gDACA,2LAGF;CAEA;CACA,+BAA+B;CAC/B;CACA,qBAA6B;;;;;;;CAO7B;CACA;CACA;CAGA,oBAAoB;CACpB,kBAAkB;CAClB,wBAAwB;CACxB,sBAAsB;CACtB;CACA;CACA;CACA;CACA;CACA;CAIA,YACE,IACA,OACA,QACA,kBACA,mBACA,kBACA,gBACA,QACA,4BACA,cACA,kBACA,WACA,MACA,eAAe,YACf;EACA,KAAK,KAAK;EACV,KAAKA,MAAM,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"}
|
|
1
|
+
{"version":3,"file":"change-streamer-service.js","names":["#lc","#shard","#changeDBProvider","#replicaVersion","#source","#storer","#pgChangeLogEnabled","#forwarder","#reservations","#replicationStatusPublisher","#sqliteCatchupOptions","#changeLogWriter","#purgeScheduler","#comparator","#acker","#initializer","#readRouter","#autoReset","#state","#backfills","#serving","#txCounter","#changeCounter","#transactionForwardDuration","#catchupRoutes","#reservationDemotions","#reservationConfirmDelays","#lagReportingDisabled","#sqliteCatchup","#closeSQLiteCatchup","#purgeLock","#latestLagReportCommitTimeMs","#lastForwardedCommitWatermark","#stream","#mayRestart","#handleControlMessage","#recordForwardedTransactionBoundary","#selectSQLiteCatchup","#captureRequiredHead","#recordCatchupRoute","#confirmReservations","#backupWatermark","#requestSQLitePurge","#maybeSchedulePGPurge","#maybeScheduleSQLitePurge","#getChangeLogState","#pgPurgeScheduled","#pgPurgeRunning","#pgPurgedWatermark","#purgePGChangeLog","#sqlitePurgeScheduled","#sqlitePurgeRunning","#sqlitePurgeContinuation","#purgeSQLiteChangeLog","#getCleanupFloor","#loggedBehindWatermark","#currentTransactionCompletion","#transactionForwardStartedAt","#recordTransactionForwardDuration","#fallbackOrReject","#declineSQLiteCatchup","#openSQLiteCatchup","#changeLogUnavailableSince"],"sources":["../../../../../../zero-cache/src/services/change-streamer/change-streamer-service.ts"],"sourcesContent":["import {getDefaultHighWaterMark} from 'node:stream';\nimport type {LogContext} from '@rocicorp/logger';\nimport {resolver, type Resolver} from '@rocicorp/resolver';\nimport {defu} from 'defu';\nimport postgres, {type Options, type PostgresType} from 'postgres';\nimport {assert, unreachable} from '../../../../shared/src/asserts.ts';\nimport {must} from '../../../../shared/src/must.ts';\nimport {promiseOrAbort} from '../../../../shared/src/promise-race.ts';\nimport {promiseVoid} from '../../../../shared/src/resolved-promises.ts';\nimport {publishCriticalEvent} from '../../observability/events.ts';\nimport {\n getOrCreateCounter,\n getOrCreateLatencyHistogram,\n} from '../../observability/metrics.ts';\nimport {min} from '../../types/lexi-version.ts';\nimport type {PostgresDB} from '../../types/pg.ts';\nimport type {ShardID} from '../../types/shards.ts';\nimport type {PreSerialized, Source} from '../../types/streams.ts';\nimport {Subscription} from '../../types/subscription.ts';\nimport type {\n ChangeSource,\n ChangeStream,\n} from '../change-source/change-source.ts';\nimport {\n type ChangeStreamControl,\n type ChangeStreamData,\n type Rollback,\n} from '../change-source/protocol/current/downstream.ts';\nimport type {LitestreamVersion} from '../litestream/metrics.ts';\nimport {\n publishReplicationError,\n replicationStatusError,\n type ReplicationStatusPublisher,\n} from '../replicator/replication-status.ts';\nimport type {SubscriptionState} from '../replicator/schema/replication-state.ts';\nimport {\n DEFAULT_MAX_RETRY_DELAY_MS,\n RunningState,\n UnrecoverableError,\n} from '../running-state.ts';\nimport {BackfillStateTracker} from './backfill-state-tracker.ts';\nimport type {PreSerializedBatch} from './broadcast.ts';\nimport {serializeChangeStreamDataWithChange} from './change-log-codec.ts';\nimport {\n ChangeLogInitializer,\n replicaInitializationSource,\n} from './change-log-initializer.ts';\nimport {\n type ChangeStreamerService,\n type SubscriberContext,\n type WatermarkedChange,\n} from './change-streamer.ts';\nimport * as ErrorType from './error-type-enum.ts';\nimport {Forwarder} from './forwarder.ts';\nimport {\n AutoResetSignal,\n ensureReplicationConfig,\n markResetRequired,\n} from './schema/tables.ts';\nimport type {SnapshotMessage} from './snapshot-message.ts';\nimport {SnapshotReservations} from './snapshot-reservations.ts';\nimport {\n SQLiteChangeLogCatchup,\n type SQLiteChangeLogCleanupGuard,\n} from './sqlite-change-log-catchup.ts';\nimport {\n SQLiteChangeLogComparator,\n type SQLiteChangeLogCompareOptions,\n} from './sqlite-change-log-comparator.ts';\nimport {\n SQLiteChangeLogPurgeScheduler,\n type PurgeContinuation,\n type SQLiteChangeLogPurgeSchedulerOptions,\n} from './sqlite-change-log-purge-scheduler.ts';\nimport {\n inspectSQLiteChangeLog,\n SQLiteChangeLogReadRouter,\n type ChangeLogReadRoute,\n type SQLiteChangeLogCoverage,\n} from './sqlite-change-log-read-router.ts';\nimport {SQLiteChangeLogReader} from './sqlite-change-log-reader.ts';\nimport {\n SQLiteChangeLogWriter,\n type SQLiteChangeLogWriterOptions,\n} from './sqlite-change-log-writer.ts';\nimport type {PostgresDBProvider} from './storer.ts';\nimport {\n Storer,\n type PurgeLock,\n type TuningOptions as StorerOptions,\n} from './storer.ts';\nimport {Subscriber} from './subscriber.ts';\nimport {UpstreamAcker} from './upstream-acker.ts';\n\nexport type BackupConfig = {\n backupURL: string;\n litestreamVersion: LitestreamVersion;\n /**\n * The replica being backed up. Its size is sent to subscribers as an\n * estimate of the size of their restored replica.\n */\n replicaFile?: string | undefined;\n};\n\nexport type SQLiteCatchupOptions = {\n /** The change-log database, i.e. `changeLogFileName(replicaFile)`. */\n changeLogFile: string;\n readBatchRows: number;\n barrierTimeoutMs: number;\n /**\n * Backstop for the change-log writer's ACK, which is what normally releases\n * the barrier. Defaults to the catchup coordinator's interval.\n */\n barrierPollIntervalMs?: number | undefined;\n /**\n * Optional policy hook used by focused tests. Production canary selection is\n * supplied by `sqliteChangeLogServe` and is stable by shard plus task ID.\n * Backup subscribers are rejected before either selector is invoked.\n */\n shouldUse?: ((ctx: SubscriberContext) => boolean) | undefined;\n /**\n * Overrides the purge scheduler's guard, for tests. When absent, the\n * service supplies the scheduler's real writer-serialized guard, or the\n * catchup's no-op default when purging is not configured.\n */\n cleanupGuard?: SQLiteChangeLogCleanupGuard | undefined;\n /**\n * How long the change log may be unavailable before declining to serve from\n * it is reported as a warning rather than a debug line. Defaults to\n * {@link DEFAULT_CHANGE_LOG_UNAVAILABLE_WARN_THRESHOLD_MS}.\n */\n notReadyWarnThresholdMs?: number | undefined;\n};\n\nexport type SQLiteChangeLogServeOptions = {\n /** Stable percentage of eligible serving tasks routed to SQLite. */\n readPercent: number;\n /**\n * Stable percentage of eligible serving tasks routed to a log that has not\n * yet aged through {@link retentionMs}. Zero keeps a reseeded log on PG for\n * that whole window; above zero it serves that share of tasks, and a\n * reservation it cannot cover is demoted rather than held pending.\n * Defaults to zero.\n */\n coldReadPercent?: number | undefined;\n /** The warm-up window a reseeded log ages through. */\n retentionMs: number;\n /** Injectable for deterministic breaker tests. */\n failureCooldownMs?: number | undefined;\n /** Injectable for deterministic warm-up and breaker tests. */\n now?: (() => number) | undefined;\n /** Injectable readiness inspection for service-level tests. */\n inspect?: (() => SQLiteChangeLogCoverage | undefined) | undefined;\n};\n\nexport type TuningOptions = StorerOptions & {\n /**\n * Keeps the legacy Postgres change log on the initialization, persistence,\n * catchup, purge, and ACK paths. Disable only after SQLite and the replica\n * backup are authoritative for all of those responsibilities.\n */\n pgChangeLogEnabled: boolean;\n /**\n * Whether the PG change log's head was behind the position of the\n * replication slot from which replication resumes, in which case the\n * change log is re-initialized from the (restored) replica if it does not\n * contain all of the replica's changes (see `PgChangeLogPurgeLocker`).\n *\n * Required so that a caller cannot silently drop it: without it, the\n * stream resumes from a head that the slot cannot stream from, skipping\n * transactions.\n */\n pgChangeLogBehindSlot: boolean;\n flowControlConsensusTimeoutProportion: number;\n flowControlSlowSubscriberGracePeriodMs?: number | undefined;\n sqliteCatchup?: SQLiteCatchupOptions | undefined;\n /**\n * Supplied when `sqliteChangeLogMode != off`, i.e. this is the gate on the\n * change-log writer. Absent, nothing writes the log.\n */\n sqliteChangeLogWriter?:\n | Omit<\n SQLiteChangeLogWriterOptions,\n 'onCommit' | 'onDisabled' | 'onRebuilt'\n >\n | undefined;\n /**\n * Also supplied when `sqliteChangeLogMode != off`, and deliberately not\n * gated on the read path: in `write` mode no reader ever opens, and this is\n * the configuration the purge scheduler actually ships in. The scheduler\n * runs on the writer's own connection, so with the writer absent (mode\n * `off`) it is never constructed, and a leftover file on disk is left to\n * the replicator's cleanup.\n */\n sqliteChangeLogPurge?: SQLiteChangeLogPurgeSchedulerOptions | undefined;\n /**\n * Supplied in `compare` mode and later modes.\n * `replicaFile` enables the initialization comparison.\n * The other fields configure sampled catchup comparisons.\n * Both checks are advisory, and Postgres remains authoritative.\n * The comparator also requires the writer and catchup options.\n */\n sqliteChangeLogCompare?:\n | (SQLiteChangeLogCompareOptions & {replicaFile: string})\n | undefined;\n /** Supplied only in `serve` mode. A zero percentage keeps every read on PG. */\n sqliteChangeLogServe?: SQLiteChangeLogServeOptions | undefined;\n};\n\n/**\n * Performs initialization and schema migrations to initialize a ChangeStreamerImpl.\n */\nexport async function initializeStreamer(\n lc: LogContext,\n shard: ShardID,\n taskID: string,\n discoveryAddress: string,\n discoveryProtocol: string,\n changeDB: PostgresDB,\n changeSource: ChangeSource,\n replicationStatusPublisher: ReplicationStatusPublisher,\n subscriptionState: SubscriptionState,\n backupConfig: BackupConfig | null,\n purgeLock: PurgeLock | null,\n autoReset: boolean,\n opts: TuningOptions,\n setTimeoutFn = setTimeout,\n): Promise<ChangeStreamerService> {\n await ensureReplicationConfig(\n lc,\n changeDB,\n subscriptionState,\n shard,\n autoReset,\n purgeLock ?? undefined,\n setTimeoutFn,\n opts.pgChangeLogEnabled,\n );\n\n // Dynamically creates connection pools that the implementation uses to\n // isolate concurrent, long-running transactions. This works around a bug in\n // the postgres.js client where connections get swapped in certain conditions.\n //\n // https://github.com/porsager/postgres/issues/1204\n const changeDBProvider: PostgresDBProvider = (\n applicationName: string,\n max: number,\n ) =>\n postgres(\n defu(\n {max, connection: {['application_name']: applicationName}},\n // ParsedOptions are technically compatible with Options, but happen\n // to not be typed that way. The postgres.js author does an equivalent\n // merge of ParsedOptions and Options here:\n // https://github.com/porsager/postgres/blob/089214e85c23c90cf142d47fb30bd03f42874984/src/subscribe.js#L13\n changeDB.options as unknown as Options<Record<string, PostgresType>>,\n ),\n ) as PostgresDB;\n\n const {replicaVersion} = subscriptionState;\n return new ChangeStreamerImpl(\n lc,\n shard,\n taskID,\n discoveryAddress,\n discoveryProtocol,\n changeDBProvider,\n replicaVersion,\n changeSource,\n replicationStatusPublisher,\n backupConfig,\n purgeLock,\n autoReset,\n opts,\n setTimeoutFn,\n );\n}\n\nconst REPLICATION_STATUS_ERROR_DELAY_THRESHOLD_MS = 5000;\n\n// How long the change log may be unavailable before declining to serve from it\n// stops looking like normal startup ordering. Subscriptions can arrive before\n// the stream loop's first reconcile has created and seeded the log, so early\n// declines are expected. Still declining a minute later means the log is not\n// being written at all -- `sqliteChangeLogMode=off`, a writer that has failed\n// soft, or a path mismatch -- which is worth surfacing.\nconst DEFAULT_CHANGE_LOG_UNAVAILABLE_WARN_THRESHOLD_MS = 60_000;\n\n/**\n * Upstream-agnostic dispatch of messages in a {@link ChangeStreamMessage} to a\n * {@link Forwarder} and {@link Storer} to execute the forward-store-ack\n * procedure described in {@link ChangeStreamer}.\n *\n * ### Subscriber Catchup\n *\n * Connecting clients first need to be \"caught up\" to the current watermark\n * (from stored change log entries) before new entries are forwarded to\n * them. This is non-trivial because the replication stream may be in the\n * middle of a pending streamed Transaction for which some entries have\n * already been forwarded but are not yet committed to the store.\n *\n *\n * ```\n * ------------------------------- - - - - - - - - - - - - - - - - - - -\n * | Historic changes in storage | Pending (streamed) tx | Next tx\n * ------------------------------- - - - - - - - - - - - - - - - - - - -\n * Replication stream\n * > > > > > > > > >\n * ^ ---> required catchup ---> ^\n * Subscriber watermark Subscription begins\n * ```\n *\n * Preemptively buffering the changes of every pending transaction\n * would be wasteful and consume too much memory for large transactions.\n *\n * Instead, the streamer synchronously dispatches changes and subscriptions\n * to the {@link Forwarder} and the {@link Storer} such that the two\n * components are aligned as to where in the stream the subscription started.\n * The two components then coordinate catchup and handoff via the\n * {@link Subscriber} object with the following algorithm:\n *\n * * If the streamer is in the middle of a pending Transaction, the\n * Subscriber is \"queued\" on both the Forwarder and the Storer. In this\n * state, new changes are *not* forwarded to the Subscriber, and catchup\n * is not yet executed.\n * * Once the commit message for the pending Transaction is processed\n * by the Storer, it begins catchup on the Subscriber (with a READONLY\n * snapshot so that it does not block subsequent storage operations).\n * This catchup is thus guaranteed to load the change log entries of\n * that last Transaction.\n * * When the Forwarder processes that same commit message, it moves the\n * Subscriber from the \"queued\" to the \"active\" set of clients such that\n * the Subscriber begins receiving new changes, starting from the next\n * Transaction.\n * * The Subscriber does not forward those changes, however, if its catchup\n * is not complete. Until then, it buffers the changes in memory.\n * * Once catchup is complete, the buffered changes are immediately sent\n * and the Subscriber henceforth forwards changes as they are received.\n *\n * In the (common) case where the streamer is not in the middle of a pending\n * transaction when a subscription begins, the Storer begins catchup\n * immediately and the Forwarder directly adds the Subscriber to its active\n * set. However, the Subscriber still buffers any forwarded messages until\n * its catchup is complete.\n *\n * ### Watermarks and ordering\n *\n * The ChangeStreamerService depends on its {@link ChangeSource} to send\n * changes in contiguous [`begin`, `data` ..., `data`, `commit`] sequences\n * in commit order. This follows Postgres's Logical Replication Protocol\n * Message Flow:\n *\n * https://www.postgresql.org/docs/16/protocol-logical-replication.html#PROTOCOL-LOGICAL-MESSAGES-FLOW\n *\n * > The logical replication protocol sends individual transactions one by one.\n * > This means that all messages between a pair of Begin and Commit messages belong to the same transaction.\n *\n * In order to correctly replay (new) and filter (old) messages to subscribers\n * at different points in the replication stream, these changes must be assigned\n * watermarks such that they preserve the order in which they were received\n * from the ChangeSource.\n *\n * A previous implementation incorrectly derived these watermarks from the Postgres\n * Log Sequence Numbers (LSN) of each message. However, LSNs from concurrent,\n * non-conflicting transactions can overlap, which can result in a `begin` message\n * with an earlier LSN arriving after a `commit` message. For example, the\n * changes for these transactions:\n *\n * ```\n * LSN: 1 2 3 4 5 6 7 8 9 10\n * tx1: begin data data data commit\n * tx2: begin data data data commit\n * ```\n *\n * will arrive as:\n *\n * ```\n * begin1, data2, data4, data6, commit8, begin3, data5, data7, data9, commit10\n * ```\n *\n * Thus, LSN of non-commit messages are not suitable for tracking the sorting\n * order of the replication stream.\n *\n * Instead, the ChangeStreamer uses the following algorithm for deterministic\n * catchup and filtering of changes:\n *\n * * A `commit` message is assigned to a watermark corresponding to its LSN.\n * These are guaranteed to be in commit order by definition.\n *\n * * `begin` and `data` messages are assigned to the watermark of the\n * preceding `commit` (the previous transaction, or the replication\n * slot's starting LSN) plus 1. This guarantees that they will be sorted\n * after the previously commit transaction even if their LSNs came before it.\n * This is referred to as the `preCommitWatermark`.\n *\n * * In the ChangeLog DB, messages have a secondary sort column `pos`, which is\n * the position of the message within its transaction, with the `begin` message\n * starting at `0`. This guarantees that `begin` and `data` messages will be\n * fetched in the original ChangeSource order during catchup.\n *\n * `begin` and `data` messages share the same watermark, but this is sufficient for\n * Subscriber filtering because subscribers only know about the `commit` watermarks\n * exposed in the `Downstream` `Commit` message. The Subscriber object thus compares\n * the internal watermarks of the incoming messages against the commit watermark of\n * the caller, updating the watermark at every `Commit` message that is forwarded.\n *\n * ### Cleanup\n *\n * As mentioned in the {@link ChangeStreamer} documentation: \"the ChangeStreamer\n * uses a combination of [the \"initial\", i.e. backup-derived watermark and] ACK\n * responses from connected subscribers to determine the watermark up\n * to which it is safe to purge old change log entries.\"\n *\n * More concretely:\n *\n * * The `initial`, backup-derived watermark is the earliest to which cleanup\n * should ever happen.\n *\n * * However, it is possible for the replica backup to be *ahead* of a connected\n * subscriber; and if a network error causes that subscriber to retry from its\n * last watermark, the change streamer must support it.\n *\n * Thus, before cleaning up to an `initial` backup-derived watermark, the change\n * streamer first confirms that all connected subscribers have also passed\n * that watermark.\n */\nclass ChangeStreamerImpl implements ChangeStreamerService {\n readonly id: string;\n readonly #lc: LogContext;\n readonly #shard: ShardID;\n readonly #changeDBProvider: PostgresDBProvider;\n readonly #replicaVersion: string;\n readonly #source: ChangeSource;\n readonly #storer: Storer;\n readonly #pgChangeLogEnabled: boolean;\n readonly #forwarder: Forwarder;\n readonly #reservations: SnapshotReservations | undefined;\n readonly #replicationStatusPublisher: ReplicationStatusPublisher;\n readonly #sqliteCatchupOptions: SQLiteCatchupOptions | undefined;\n readonly #changeLogWriter: SQLiteChangeLogWriter | undefined;\n readonly #purgeScheduler: SQLiteChangeLogPurgeScheduler | undefined;\n readonly #comparator: SQLiteChangeLogComparator | undefined;\n readonly #acker: UpstreamAcker;\n readonly #initializer: ChangeLogInitializer;\n readonly #readRouter: SQLiteChangeLogReadRouter | undefined;\n\n readonly #autoReset: boolean;\n readonly #state: RunningState;\n\n /** Determines the BackfillRequests with which to (re)start streams. */\n readonly #backfills: BackfillStateTracker;\n\n // Starting the (Postgres) ChangeStream results in killing the previous\n // Postgres subscriber, potentially creating a gap in which the old\n // change-streamer has shut down and the new change-streamer has not yet\n // been recognized as \"healthy\" (and thus does not get any requests).\n //\n // To minimize this gap, delay starting the ChangeStream until the first\n // request from a `serving` replicator, indicating that higher level\n // load-balancing / routing logic has begun routing requests to this task.\n readonly #serving = resolver();\n\n readonly #txCounter = getOrCreateCounter(\n 'replication',\n 'transactions',\n 'Count of replicated transactions',\n );\n readonly #changeCounter = getOrCreateCounter(\n 'replication',\n 'changes',\n 'Count of replicated changes (DML or DDL statements)',\n );\n // The number the SQLite change-log commit lands in. It is labeled by whether\n // the log is being written so that the cost of putting a commit on the\n // forward path is attributable rather than inferred.\n readonly #transactionForwardDuration = getOrCreateLatencyHistogram(\n 'replication',\n 'transaction_forward_duration',\n \"Time from receiving a transaction's `begin` to forwarding its `commit`, \" +\n 'i.e. the change-streamer half of forward-to-subscriber latency.',\n );\n readonly #catchupRoutes = getOrCreateCounter(\n 'replication',\n 'sqlite_change_log.catchup_routes',\n 'Catchup subscriptions by selected source and low-cardinality reason.',\n );\n readonly #reservationDemotions = getOrCreateCounter(\n 'replication',\n 'sqlite_change_log.reservation_demotions',\n 'Snapshot reservations demoted from SQLite to PG because the change ' +\n 'log could not cover the backup being restored. With PG retired ' +\n 'these followers have no fallback, so this is the rate at which one ' +\n 'would instead have to wait for a later backup.',\n );\n readonly #reservationConfirmDelays = getOrCreateCounter(\n 'replication',\n 'sqlite_change_log.reservation_confirm_delays',\n 'Snapshot reservations whose confirmation was deferred because the ' +\n \"selected source's change-log minimum was later than the backup \" +\n 'watermark. Counted once per reservation, by that source.',\n );\n\n #latestLagReportCommitTimeMs = 0;\n // Resolves to whether lag reporting is disabled, once the lag reporter\n // has been started (or failed to start) in run(). Subscribers use this to\n // decide whether to send a stand-in signal when they are caught up.\n readonly #lagReportingDisabled = resolver<boolean>();\n #backupWatermark: string | undefined;\n #pgPurgedWatermark: string = '';\n /**\n * The floor last logged as held back by a laggard. The level-triggered\n * retry re-evaluates every {@link CLEANUP_DELAY_MS}; logging only when the\n * blocking floor moves keeps a stuck subscriber from emitting the same\n * line indefinitely.\n */\n #loggedBehindWatermark: string | undefined;\n #sqlitePurgeContinuation: PurgeContinuation | undefined;\n #purgeLock: PurgeLock | null;\n // PG and SQLite intentionally own separate level-triggered loops. Neither\n // waits for, advances, or retries the other.\n #pgPurgeScheduled = false;\n #pgPurgeRunning = false;\n #sqlitePurgeScheduled = false;\n #sqlitePurgeRunning = false;\n #stream: ChangeStream | undefined;\n #sqliteCatchup: SQLiteChangeLogCatchup | undefined;\n #changeLogUnavailableSince: number | undefined;\n #lastForwardedCommitWatermark: string | undefined;\n #transactionForwardStartedAt: number | undefined;\n #currentTransactionCompletion:\n | Resolver<ForwardedTransactionCompletion>\n | undefined;\n\n constructor(\n lc: LogContext,\n shard: ShardID,\n taskID: string,\n discoveryAddress: string,\n discoveryProtocol: string,\n changeDBProvider: PostgresDBProvider,\n replicaVersion: string,\n source: ChangeSource,\n replicationStatusPublisher: ReplicationStatusPublisher,\n backupConfig: BackupConfig | null,\n initialPurgeLock: PurgeLock | null,\n autoReset: boolean,\n opts: TuningOptions,\n setTimeoutFn = setTimeout,\n ) {\n this.id = `change-streamer`;\n this.#lc = lc.withContext('component', 'change-streamer');\n this.#backfills = new BackfillStateTracker(this.#lc);\n this.#shard = shard;\n this.#changeDBProvider = changeDBProvider;\n this.#replicaVersion = replicaVersion;\n this.#source = source;\n this.#pgChangeLogEnabled = opts.pgChangeLogEnabled;\n if (!this.#pgChangeLogEnabled) {\n assert(\n opts.sqliteChangeLogWriter &&\n opts.sqliteChangeLogPurge &&\n opts.sqliteCatchup &&\n opts.sqliteChangeLogServe?.readPercent === 100 &&\n opts.sqliteChangeLogServe.coldReadPercent === 100 &&\n backupConfig?.litestreamVersion === 'v5',\n 'disabling the PG change log requires SQLite serving at 100 percent and a v5 backup',\n );\n assert(\n opts.sqliteChangeLogCompare === undefined,\n 'SQLite change-log comparison requires the PG change log',\n );\n }\n // Without a restored replica, a change log that is behind the slot cannot\n // be re-initialized, and resuming from its head would silently skip the\n // transactions between it and the slot's position.\n assert(\n !(\n this.#pgChangeLogEnabled &&\n opts.pgChangeLogBehindSlot &&\n !backupConfig?.replicaFile\n ),\n 'the PG change log is behind the replication slot, but there is no ' +\n 'restored replica to re-initialize it from',\n );\n this.#storer = new Storer(\n lc,\n shard,\n taskID,\n discoveryAddress,\n discoveryProtocol,\n changeDBProvider,\n replicaVersion,\n consumed => this.#acker.trackPgChangeLog(consumed[2].watermark),\n err => this.stop(err),\n opts,\n );\n this.#forwarder = new Forwarder(lc, {\n flowControlConsensusTimeoutProportion:\n opts.flowControlConsensusTimeoutProportion,\n flowControlSlowSubscriberGracePeriodMs:\n opts.flowControlSlowSubscriberGracePeriodMs,\n });\n const serveOptions = opts.sqliteChangeLogServe;\n const writerOptions = opts.sqliteChangeLogWriter;\n const catchupOptions = opts.sqliteCatchup;\n this.#readRouter =\n serveOptions && writerOptions && catchupOptions\n ? new SQLiteChangeLogReadRouter({\n shard,\n readPercent: serveOptions.readPercent,\n coldReadPercent: serveOptions.coldReadPercent,\n retentionMs: serveOptions.retentionMs,\n failureCooldownMs: serveOptions.failureCooldownMs,\n now: serveOptions.now,\n inspect:\n serveOptions.inspect ??\n (() =>\n inspectSQLiteChangeLog(\n lc,\n catchupOptions.changeLogFile,\n writerOptions.identity,\n )),\n })\n : undefined;\n this.#reservations = backupConfig\n ? new SnapshotReservations(lc, backupConfig, taskID => {\n this.#readRouter?.release(taskID);\n this.#purgeScheduler?.resume(taskID);\n })\n : undefined;\n this.#replicationStatusPublisher = replicationStatusPublisher;\n this.#changeLogWriter = opts.sqliteChangeLogWriter\n ? new SQLiteChangeLogWriter(lc, {\n ...opts.sqliteChangeLogWriter,\n onCommit: watermark => {\n this.#sqliteCatchup?.onChangeLogCommit(watermark);\n // The commit closed the log's transaction, i.e. opened a purge\n // window (§3.3).\n this.#purgeScheduler?.onWriterIdle();\n },\n // Fail-soft deletes the file, and the reader is cached here, so it\n // has to go with it: otherwise it serves an unlinked inode while\n // every new open sees nothing.\n onDisabled: () => {\n // The writer stays disabled and the file stays absent for the life\n // of this process, so unlike a transient read/barrier failure this\n // breaker never expires.\n this.#readRouter?.trip(true);\n this.#closeSQLiteCatchup();\n this.#comparator?.stop();\n },\n onRebuilt: () => {\n this.#closeSQLiteCatchup();\n // Invalidate cycles that can still read the replaced file.\n this.#comparator?.invalidate();\n },\n })\n : undefined;\n // The purge scheduler runs on the writer's own connection, which is also\n // its gate: no writer (mode `off`) means no scheduler, and a writer that\n // has not created the file yet -- or failed soft and deleted it -- makes\n // cycles skip rather than fail, so a late-appearing file is picked up\n // without a restart.\n this.#purgeScheduler =\n opts.sqliteChangeLogPurge && this.#changeLogWriter\n ? new SQLiteChangeLogPurgeScheduler(\n lc,\n () => this.#changeLogWriter?.connection,\n () => this.#forwarder.getAcks(),\n opts.sqliteChangeLogPurge,\n )\n : undefined;\n this.#acker = new UpstreamAcker({\n trackPgChangeLog: this.#pgChangeLogEnabled,\n trackBackup: backupConfig?.litestreamVersion === 'v5',\n });\n const replicaFileForInitialization =\n opts.sqliteChangeLogCompare?.replicaFile ??\n (!this.#pgChangeLogEnabled\n ? opts.sqliteChangeLogWriter?.replicaFile\n : undefined);\n // A Postgres change log whose head is behind the replication slot may be\n // missing transactions that the slot will not stream (e.g. after running\n // in high-availability mode), in which case it is re-initialized from the\n // restored replica.\n const replicaFileForReinitialization =\n this.#pgChangeLogEnabled && opts.pgChangeLogBehindSlot\n ? backupConfig?.replicaFile\n : undefined;\n const replicaFile =\n replicaFileForInitialization ?? replicaFileForReinitialization;\n const replicaSource = replicaFile\n ? replicaInitializationSource(lc, replicaFile)\n : undefined;\n this.#initializer = new ChangeLogInitializer(\n lc,\n {\n initFromPgChangeLog: this.#pgChangeLogEnabled,\n initFromReplica: replicaFileForInitialization !== undefined,\n },\n {\n pgChangeLog: () =>\n this.#storer.getStartStreamInitializationParameters(),\n // Only reached when `initFromReplica` is set, i.e. when the option\n // that supplies the file is present.\n replica: () => must(replicaSource)(),\n // Use the Postgres resume point when it is present. Otherwise, let the\n // SQLite change log supply its own resume point.\n reconcileChangeLog: (resumeFrom, seed) =>\n resumeFrom\n ? this.#changeLogWriter?.reconcile(resumeFrom)\n : this.#changeLogWriter?.reconcileFromLog(seed),\n changeLog: () => this.#changeLogWriter?.connection,\n reinitializePgChangeLog: replicaFileForReinitialization\n ? params => this.#storer.reinitializeFromReplica(params)\n : undefined,\n },\n );\n this.#sqliteCatchupOptions = opts.sqliteCatchup\n ? {\n ...opts.sqliteCatchup,\n // The real cleanup guard, in place of the catchup's no-op default:\n // registration and purge batches now serialize on one mutex.\n cleanupGuard:\n opts.sqliteCatchup.cleanupGuard ??\n this.#purgeScheduler?.cleanupGuard,\n }\n : undefined;\n // Compare mode requires the writer and catchup configuration.\n this.#comparator =\n this.#pgChangeLogEnabled &&\n opts.sqliteChangeLogCompare &&\n opts.sqliteChangeLogWriter &&\n opts.sqliteCatchup\n ? new SQLiteChangeLogComparator(\n lc,\n shard,\n opts.sqliteCatchup.changeLogFile,\n opts.sqliteChangeLogWriter.identity,\n this.#storer,\n {setTimeoutFn, ...opts.sqliteChangeLogCompare},\n )\n : undefined;\n this.#purgeLock = initialPurgeLock;\n this.#autoReset = autoReset;\n this.#state = new RunningState(this.id, undefined, setTimeoutFn);\n }\n\n async run() {\n this.#lc.info?.('starting change stream');\n\n this.#forwarder.startProgressMonitor();\n\n const lagReportInit = await this.#source.startLagReporter();\n this.#lagReportingDisabled.resolve(!lagReportInit);\n if (lagReportInit) {\n // Record the commit time of the initiated lag report (i.e. \"head\")\n // for the purpose of skipping over any lag reports that are re-streamed\n // by the change-source in the case of a change-streamer starting from\n // an older watermark.\n this.#latestLagReportCommitTimeMs = lagReportInit.firstCommitTimeMs;\n }\n\n if (this.#pgChangeLogEnabled) {\n this.#storer.assumeOwnershipAtHead({\n purgeLock: this.#purgeLock,\n requestRestart: reason => this.#backfills.requestRestart(reason),\n });\n }\n this.#purgeLock = null;\n\n // The threshold in (estimated number of) bytes to send() on subscriber\n // websockets before `await`-ing the I/O buffers to be ready for more.\n const flushBytesThreshold = getDefaultHighWaterMark(false);\n\n while (this.#state.shouldRun()) {\n let err: unknown;\n let watermark: string | null = null;\n let unflushedBytes = 0;\n try {\n // Initialization reconciles the change log for every stream\n // connection. It completes before `startStream`, so no change can\n // arrive during reconciliation.\n const {lastWatermark, backfillRequests: logBackfillRequests} =\n await this.#initializer.initialize();\n const backfillRequests = this.#backfills.startStream(\n // Backfill requests from the change-log are only necessary for\n // backwards compatibility with protocol v7 and RMv1. On RMv2,\n // all of the beginning state comes from the (backup) replica,\n // which contains the progress marks, so there is no need to\n // downgrade to the change-log version which does not have\n // progress marks.\n this.#pgChangeLogEnabled ? logBackfillRequests : [],\n );\n // SQLite catchup must not be eligible until this has been initialized\n // from the selected durable head. Commits observed only since process\n // startup are insufficient after a change-streamer restart.\n this.#lastForwardedCommitWatermark = lastWatermark;\n const stream = await this.#source.startStream(\n lastWatermark,\n backfillRequests,\n );\n this.#stream = stream;\n if (!this.#state.shouldRun()) {\n // Stopped while the stream was being started (which stop() could not\n // cancel). The stream is cancelled on exiting the iteration.\n break;\n }\n if (this.#pgChangeLogEnabled) {\n this.#storer.run().catch(e => stream.changes.cancel(e));\n }\n\n if (\n this.#state.resetBackoff() >\n REPLICATION_STATUS_ERROR_DELAY_THRESHOLD_MS\n ) {\n // After recovering from a backoff for which a replication status\n // error was published, publish an OK status\n this.#replicationStatusPublisher.publish(\n this.#lc,\n 'Replicating',\n `Replicating from ${lastWatermark}`,\n );\n }\n watermark = null;\n\n this.#acker.reset(stream.acks, lastWatermark);\n\n for await (const change of stream.changes) {\n this.#acker.trackDownstream(change);\n\n const [type, msg] = change;\n if (type === 'status' && watermark === null && this.#mayRestart()) {\n // A restart can happen at a status message outside of a\n // transaction, e.g. when the replication stream is otherwise idle.\n // Exiting the iteration cancels the stream.\n break;\n }\n switch (type) {\n case 'status':\n if (watermark === null) {\n // Allows ownership to be assumed on an idle upstream.\n this.#storer.status(change[2].watermark);\n }\n if (\n msg.lagReport &&\n msg.lagReport.lastTimings.commitTimeMs >=\n this.#latestLagReportCommitTimeMs\n ) {\n // Lag reports are not stored in the change log, but rather\n // only forwarded on \"live\" connections. Subscribers that are\n // catching up queue them behind the preceding changes so that\n // they are processed in stream order, and thus measure the\n // lag of the subscriber (including its catchup).\n this.#latestLagReportCommitTimeMs =\n msg.lagReport.lastTimings.commitTimeMs;\n this.#forwarder.sendStatus({\n tag: 'status',\n lagReport: msg.lagReport,\n });\n }\n continue;\n case 'control':\n await this.#handleControlMessage(msg);\n continue; // control messages are not stored/forwarded\n case 'begin':\n watermark = change[2].commitWatermark;\n break;\n case 'commit':\n if (watermark !== change[2].watermark) {\n throw new UnrecoverableError(\n `commit watermark ${change[2].watermark} does not match 'begin' watermark ${watermark}`,\n );\n }\n this.#txCounter.add(1);\n break;\n default:\n if (type === 'data') {\n this.#changeCounter.add(1);\n }\n if (watermark === null) {\n throw new UnrecoverableError(\n `${type} change (${msg.tag}) received before 'begin' message`,\n );\n }\n break;\n }\n\n this.#backfills.track(change);\n\n const serialized = serializeChangeStreamDataWithChange(change);\n const {json} = serialized;\n if (this.#pgChangeLogEnabled) {\n this.#storer.store(watermark, change, serialized);\n }\n // The SQLite change log commits at transaction boundaries, and its\n // commit for this transaction lands here -- before the forward of the\n // `commit` message, and before #recordForwardedTransactionBoundary\n // advances what #captureRequiredHead reads. No `await` separates it\n // from serialization or the optional PG enqueue above. This is the\n // assertable form of invariant 1: a synchronous SQLite commit in the\n // same loop iteration always precedes anything that can advance the\n // watermark this stream would resume from. Never throws; a write\n // failure disables the writer rather than stopping replication.\n this.#changeLogWriter?.write(change, json, serialized.change);\n const entry: WatermarkedChange = [watermark, change[1].tag, json];\n unflushedBytes += json.length;\n let flowControl: Promise<void> | undefined;\n if (unflushedBytes < flushBytesThreshold) {\n // pipeline changes until flushBytesThreshold\n this.#forwarder.forward(entry);\n } else {\n // Wait for messages to clear socket buffers to ensure that they\n // make their way to subscribers. Without this `await`, the\n // messages end up being buffered in this process, which:\n // (1) results in memory pressure and increased GC activity\n // (2) prevents subscribers from processing the messages as they\n // arrive, instead getting them in a large batch after being\n // idle while they were queued (causing further delays).\n flowControl = this.#forwarder.forwardWithFlowControl(entry);\n }\n // Both forwards send the entry and update the Forwarder's transaction\n // state synchronously. Record the boundary, and end the transaction at\n // its commit or rollback, before awaiting flow control: registrations\n // during the wait then observe the forwarded state, and a stream that\n // is interrupted during the wait does not roll back a transaction\n // that was already forwarded whole.\n this.#recordForwardedTransactionBoundary(type, entry[0]);\n if (type === 'commit' || type === 'rollback') {\n watermark = null;\n }\n if (flowControl) {\n await promiseOrAbort(\n flowControl,\n stream.changes.signal,\n this.#state.signal,\n );\n unflushedBytes = 0;\n }\n\n // Allow the PG storer to exert back pressure when it is enabled.\n const readyForMore = this.#pgChangeLogEnabled\n ? this.#storer.readyForMore()\n : undefined;\n if (readyForMore) {\n await promiseOrAbort(\n readyForMore,\n stream.changes.signal,\n this.#state.signal,\n );\n }\n\n // Restarts happen at transaction boundaries, so that the pending\n // backfills of the aligned subscribers (and the change log) are\n // consistent with the watermark from which the stream restarts.\n if (type === 'commit' && this.#mayRestart()) {\n // Exiting the iteration cancels the stream.\n break;\n }\n }\n } catch (e) {\n err = e;\n } finally {\n this.#stream?.changes.cancel();\n this.#stream = undefined;\n }\n\n // When the change stream is interrupted, abort any pending transaction.\n if (watermark) {\n this.#lc.warn?.(`aborting interrupted transaction ${watermark}`);\n if (this.#pgChangeLogEnabled) {\n this.#storer.abort();\n }\n // Rolling back the log leaves no rows for the interrupted transaction,\n // so the next connection's reconciliation sees a head at or below its\n // resume watermark rather than a partial transaction.\n this.#changeLogWriter?.abort();\n // A rollback ends the log's open transaction without a commit\n // notification; wake any purge batch waiting for that window.\n this.#purgeScheduler?.onWriterIdle();\n this.#forwarder.forward([watermark, 'rollback', ROLLBACK_JSON]);\n this.#recordForwardedTransactionBoundary('rollback', watermark);\n this.#backfills.track(['rollback', {tag: 'rollback'}]);\n }\n\n if (this.#backfills.restartReason !== null && err === undefined) {\n // A planned restart: drain the storer and reconnect immediately.\n this.#backfills.recordRestart();\n await this.#storer.stop();\n } else {\n // Backoff and drain any pending entries in the storer before reconnecting.\n await Promise.all([\n this.#storer.stop(),\n this.#state.backoff(this.#lc, err),\n this.#state.retryDelay > REPLICATION_STATUS_ERROR_DELAY_THRESHOLD_MS\n ? publishCriticalEvent(\n this.#lc,\n replicationStatusError(this.#lc, 'Replicating', err),\n )\n : promiseVoid,\n ]);\n }\n }\n\n this.#forwarder.stopProgressMonitor();\n this.#lc.info?.('ChangeStreamer stopped');\n }\n\n async #handleControlMessage(msg: ChangeStreamControl[1]) {\n this.#lc.info?.('received control message', msg);\n const {tag} = msg;\n\n switch (tag) {\n case 'reset-required':\n await markResetRequired(\n this.#changeDBProvider('change-streamer-reset', 1),\n this.#shard,\n );\n await publishReplicationError(\n this.#lc,\n 'Replicating',\n msg.message ?? 'Resync required',\n msg.errorDetails,\n );\n if (this.#autoReset) {\n this.#lc.warn?.('shutting down for auto-reset');\n await this.stop(new AutoResetSignal());\n }\n break;\n default:\n unreachable(tag);\n }\n }\n\n async subscribe(\n ctx: SubscriberContext,\n downstream: Subscription<\n string | PreSerializedBatch\n > = Subscription.create(),\n ): Promise<Source<string | PreSerialized>> {\n const {protocolVersion, id, mode, replicaVersion, watermark, wsBatched} =\n ctx;\n if (mode === 'serving') {\n this.#serving.resolve();\n }\n let cleanupSubscriber = () => {};\n\n // No subscriber's ACK advances the SQLite change log's head any more: the\n // writer runs in this process, so the barrier is notified from the commit\n // itself (see #changeLogWriter's onCommit).\n const lc = this.#lc.withContext('subscriber', id);\n const subscriber = new Subscriber(\n protocolVersion,\n id,\n mode,\n watermark,\n downstream,\n {\n wsBatched,\n lagReportingDisabled: this.#lagReportingDisabled.promise,\n ...this.#backfills.subscriberOptions(lc, ctx),\n },\n );\n this.#backfills.register(subscriber, downstream);\n const removeFromForwarder = () => {\n lc.info?.(`removing subscriber ${subscriber.id}`);\n this.#forwarder.remove(subscriber);\n };\n cleanupSubscriber = removeFromForwarder;\n if (replicaVersion !== this.#replicaVersion) {\n lc.warn?.(`rejecting subscriber at replica version ${replicaVersion}`);\n subscriber.close(\n ErrorType.WrongReplicaVersion,\n `current replica version is ${\n this.#replicaVersion\n } (requested ${replicaVersion})`,\n );\n } else {\n lc.info?.(`adding subscriber ${subscriber.id}`);\n\n const catchupFromPG = () => {\n assert(\n this.#pgChangeLogEnabled,\n 'cannot catch up from a disabled PG change log',\n );\n // Keep the existing PG registration/catchup lockstep unchanged when\n // SQLite was not selected before Forwarder.add().\n cleanupSubscriber = removeFromForwarder;\n this.#forwarder.add(subscriber);\n this.#storer.catchup(subscriber, mode);\n };\n const sqliteDecision = this.#selectSQLiteCatchup(lc, ctx);\n if (!sqliteDecision) {\n catchupFromPG();\n } else if (sqliteDecision.kind === 'rejected') {\n lc.warn?.(\n `${\n sqliteDecision.terminal ? 'rejecting' : 'ending'\n } subscription for ${ctx.id}: ${sqliteDecision.message}`,\n );\n if (sqliteDecision.terminal) {\n subscriber.close(ErrorType.WatermarkTooOld, sqliteDecision.message);\n } else {\n subscriber.close();\n }\n } else {\n const {catchup, reason, coverage, logWarm} = sqliteDecision;\n cleanupSubscriber = () => catchup.remove(subscriber);\n const registration = await catchup.catchup(\n subscriber,\n () => this.#captureRequiredHead(),\n {logWarm},\n );\n switch (registration.kind) {\n case 'registered':\n lc.debug?.(\n `serving ${ctx.id} from SQLite catchup`,\n ...(coverage ? [{sqliteChangeLogCoverage: coverage}] : []),\n );\n this.#recordCatchupRoute('sqlite', reason);\n break;\n case 'uncovered':\n if (this.#pgChangeLogEnabled) {\n lc.info?.(\n `serving ${ctx.id} from PG catchup: subscriber watermark ` +\n `${ctx.watermark} is below the SQLite change-log minimum ` +\n registration.minWatermark,\n );\n this.#recordCatchupRoute('pg', 'watermark-uncovered');\n catchupFromPG();\n } else {\n const message =\n `earliest supported watermark is ` +\n `${registration.minWatermark} (requested ${ctx.watermark})`;\n lc.warn?.(\n `rejecting subscriber at watermark ${ctx.watermark}: ` +\n `the SQLite change log starts at ` +\n `${registration.minWatermark} and the PG change log is disabled`,\n );\n this.#recordCatchupRoute('none', 'watermark-uncovered');\n subscriber.close(ErrorType.WatermarkTooOld, message);\n }\n break;\n case 'declined':\n // Registration failed before the subscriber was committed to\n // SQLite. Use PG when enabled; otherwise end this subscription so\n // it retries. The coordinator has already tripped the breaker,\n // which keeps the retry off SQLite for the cooldown; it is\n // deliberately not closed here, since closing it would abort the\n // catchups of every other subscriber it is serving.\n if (this.#pgChangeLogEnabled) {\n lc.error?.(\n `serving ${ctx.id} from PG catchup: SQLite catchup ` +\n `registration failed`,\n registration.error,\n );\n this.#recordCatchupRoute('pg', 'registration-failed');\n catchupFromPG();\n } else {\n lc.error?.(\n `ending subscription for ${ctx.id} to retry SQLite catchup: ` +\n `registration failed and the PG change log is disabled`,\n registration.error,\n );\n this.#recordCatchupRoute('none', 'registration-failed');\n subscriber.fail(registration.error);\n }\n break;\n case 'handled':\n // The coordinator closed or failed the subscriber itself, so\n // there is nothing left to route. Still counted, so that the\n // route counter sums to the number of subscriptions.\n this.#recordCatchupRoute('sqlite', 'registration-handled');\n break;\n default:\n unreachable(registration);\n }\n }\n }\n // Register the cleanup now that `cleanupSubscriber` points at the real\n // cleanup and the subscriber is registered. If `downstream` was already\n // cancelled (e.g. a client disconnect during the catchup await above),\n // `addCloseHandler` runs the cleanup immediately, and so it must not be\n // called until cleanupSubscriber is properly set up.\n downstream.addCloseHandler(() => cleanupSubscriber());\n\n // Any snapshot reservation held by this task can be closed now that\n // it is subscribed to the change stream.\n this.#reservations?.close(ctx.taskID);\n return downstream;\n }\n\n async startSnapshotReservation(\n taskID: string,\n ): Promise<Source<SnapshotMessage>> {\n if (!this.#reservations) {\n throw new Error('backups are not configured');\n }\n const downstream = this.#reservations.open(taskID);\n\n try {\n // Wait for an in-flight SQLite purge batch before reading and\n // advertising the reservation's bounds.\n await (this.#purgeScheduler?.pause(taskID) ?? promiseVoid);\n // A concurrent retry for this task may have superseded and cancelled\n // this reservation while its purge pause was settling. Only the current\n // owner may replace the task's source pin.\n if (!this.#reservations.isCurrent(taskID, downstream)) {\n return downstream;\n }\n // Pin after the purge pause has settled, so the SQLite minimum captured\n // by the router cannot move before it is advertised. #confirmReservations\n // skips an unpinned task while this await is in flight.\n this.#readRouter?.pin(taskID);\n // If a backup has been confirmed, immediately confirm the reservation.\n await this.#confirmReservations();\n return downstream;\n } catch (e) {\n // Cancel the reservation this call opened, not whatever currently\n // holds the task's slot: an overlapping retry for the same taskID may\n // have already replaced it, and a by-taskID close would tear down the\n // replacement and release its purge pause while it is advertising\n // snapshot bounds. cancel() routes through the subscription's cleanup,\n // which closes the reservation only if this instance still owns it.\n downstream.cancel();\n throw e;\n }\n }\n\n trackBackupWatermark(watermark: string) {\n this.#backupWatermark = watermark;\n this.#acker.trackBackup(watermark);\n // The durable backup floor is an independent input to each change-log\n // implementation. SQLite receives it even when the PG log has already\n // reached this watermark.\n this.#requestSQLitePurge('deferred');\n this.#maybeSchedulePGPurge();\n this.#maybeScheduleSQLitePurge();\n\n // Confirm any waiting reservations now that a backup has been confirmed.\n // Note that this is asynchronous and best effort; if it fails, the watermark\n // is still \"tracked\" and the confirmation will be retried on the next backup.\n void this.#confirmReservations().catch(e =>\n this.#lc.warn?.(`error confirming snapshot reservation`, e),\n );\n }\n\n async #confirmReservations() {\n const backupWatermark = this.#backupWatermark;\n const reservations = this.#reservations;\n if (\n backupWatermark === undefined ||\n !reservations?.confirmationsRequired()\n ) {\n return;\n }\n\n // Resolve PG bounds, when enabled, before touching any pin. Everything\n // below runs to completion without awaiting, which keeps a reservation's\n // advertised bounds and its pin in agreement: a concurrent /snapshot\n // retry for the same task replaces the reservation and re-pins it, and\n // confirming that replacement with the previous pin's bounds is exactly\n // the mismatch pinning exists to prevent.\n const pgState = this.#pgChangeLogEnabled\n ? await this.#getChangeLogState()\n : undefined;\n for (const taskID of reservations.unconfirmedTaskIDs()) {\n let route = this.#readRouter?.peek(taskID);\n // startSnapshotReservation pins only after an in-flight purge has\n // completed. A task with no pin yet is confirmed by that path instead.\n if (this.#readRouter && route === undefined) {\n continue;\n }\n\n // A transiently unavailable or broken SQLite log normally pins the\n // reservation to PG. With PG disabled, re-evaluate that choice on each\n // backup notification and keep the reservation pending until SQLite can\n // supply bounds.\n if (!this.#pgChangeLogEnabled && route?.source === 'pg') {\n this.#readRouter?.release(taskID);\n route = this.#readRouter?.pin(taskID);\n if (route?.source === 'pg') {\n if (reservations.noteConfirmationDelayed(taskID)) {\n this.#reservationConfirmDelays.add(1);\n }\n this.#lc.warn?.(\n `delaying snapshot reservation for ${taskID}: SQLite change log ` +\n `is unavailable and the PG change log is disabled`,\n );\n continue;\n }\n }\n\n if (route?.source === 'sqlite') {\n const coverage = must(\n route.coverage,\n 'a pinned SQLite route must carry its covered range',\n );\n if (\n this.#pgChangeLogEnabled &&\n coverage.minWatermark > backupWatermark\n ) {\n // A log seeded after this backup, most often. Holding the\n // reservation until a backup reaches the log's minimum would stall\n // a follower that PG can serve now, so move it -- pin included.\n this.#lc.info?.(\n `demoting ${taskID} to PG catchup: SQLite change-log minimum ` +\n `${coverage.minWatermark} is later than backupWatermark ` +\n backupWatermark,\n );\n this.#reservationDemotions.add(1);\n route = must(this.#readRouter).demote(taskID);\n }\n }\n\n const source = route?.source ?? 'pg';\n let minWatermark: string;\n if (route?.source === 'sqlite') {\n // Demotion above already moved every SQLite route the backup is\n // outside of, so this one covers it and confirms below.\n minWatermark = must(\n route.coverage,\n 'a pinned SQLite route must carry its covered range',\n ).minWatermark;\n } else {\n minWatermark = must(\n pgState,\n 'a PG reservation route requires the PG change log',\n ).minWatermark;\n }\n\n if (minWatermark <= backupWatermark) {\n reservations.confirmFor(\n taskID,\n this.#replicaVersion,\n backupWatermark,\n source,\n );\n } else {\n // PG cannot catch a restored replica up from this backup yet. Keep\n // the reservation pending until a later backup moves the durable\n // watermark into its covered range.\n if (reservations.noteConfirmationDelayed(taskID)) {\n this.#reservationConfirmDelays.add(1);\n }\n this.#lc.error?.(\n `${source} change-log minWatermark ${minWatermark} is later than ` +\n `backupWatermark ${backupWatermark}. Delaying confirmation of ` +\n `snapshot reservation until next backup.`,\n );\n }\n }\n }\n\n #maybeSchedulePGPurge(): void {\n const backupWatermark = this.#backupWatermark;\n if (\n !this.#pgChangeLogEnabled ||\n this.#pgPurgeScheduled ||\n this.#pgPurgeRunning ||\n backupWatermark === undefined ||\n this.#pgPurgedWatermark >= backupWatermark\n ) {\n return;\n }\n this.#pgPurgeScheduled = true;\n this.#state.setTimeout(() => {\n this.#pgPurgeScheduled = false;\n this.#pgPurgeRunning = true;\n return this.#purgePGChangeLog().finally(() => {\n this.#pgPurgeRunning = false;\n this.#maybeSchedulePGPurge();\n });\n }, CLEANUP_DELAY_MS);\n }\n\n #maybeScheduleSQLitePurge(): void {\n if (\n this.#sqlitePurgeScheduled ||\n this.#sqlitePurgeRunning ||\n this.#backupWatermark === undefined ||\n this.#sqlitePurgeContinuation === undefined\n ) {\n return;\n }\n const delay =\n this.#sqlitePurgeContinuation === 'immediate' ? 0 : CLEANUP_DELAY_MS;\n this.#sqlitePurgeScheduled = true;\n this.#state.setTimeout(() => {\n this.#sqlitePurgeScheduled = false;\n this.#sqlitePurgeRunning = true;\n return this.#purgeSQLiteChangeLog().finally(() => {\n this.#sqlitePurgeRunning = false;\n this.#maybeScheduleSQLitePurge();\n });\n }, delay);\n }\n\n async #getChangeLogState(): Promise<{\n replicaVersion: string;\n minWatermark: string;\n }> {\n const minWatermark = await this.#storer.getMinWatermarkForCatchup();\n if (!minWatermark) {\n this.#lc.warn?.(\n `Unexpected empty changeLog. Resync if \"Local replica watermark\" errors arise`,\n );\n }\n return {\n replicaVersion: this.#replicaVersion,\n minWatermark: minWatermark ?? this.#replicaVersion,\n };\n }\n\n #getCleanupFloor(): {\n backupWatermark: string;\n purgeWatermark: string;\n current: string[];\n } {\n const backupWatermark = this.#backupWatermark;\n assert(\n backupWatermark !== undefined,\n 'cleanup cannot run without a backup watermark',\n );\n const current = [\n ...this.#forwarder.getAcks(),\n ...(this.#reservations?.getReservedWatermarks() ?? []),\n ];\n // The cleanup delay above is the grace period for disconnected\n // subscribers to reconnect and expose their ACKs. Once it expires, an\n // empty set places no additional constraint on the confirmed backup\n // watermark and must not pin either change log indefinitely.\n return {\n backupWatermark,\n purgeWatermark: min(backupWatermark, ...current),\n current,\n };\n }\n\n async #purgePGChangeLog(): Promise<void> {\n if (this.#storer.ownershipPending) {\n // Purging before ownership would only abort on the ownership check (or\n // on this change-streamer's own purge lock).\n return;\n }\n try {\n const {backupWatermark, purgeWatermark, current} =\n this.#getCleanupFloor();\n if (purgeWatermark < backupWatermark) {\n if (this.#loggedBehindWatermark !== purgeWatermark) {\n this.#loggedBehindWatermark = purgeWatermark;\n this.#lc.info?.(\n `At least one client is behind backup ${backupWatermark}`,\n {watermarks: current},\n );\n }\n } else {\n this.#loggedBehindWatermark = undefined;\n }\n if (purgeWatermark <= this.#pgPurgedWatermark) {\n return;\n }\n this.#lc.info?.(`Purging PG changes before ${purgeWatermark} ...`);\n const start = performance.now();\n const deleted = await this.#storer.purgeRecordsBefore(purgeWatermark);\n const elapsed = (performance.now() - start).toFixed(2);\n this.#lc.info?.(\n `Purged ${deleted} PG changes before ${purgeWatermark} (${elapsed} ms)`,\n );\n this.#pgPurgedWatermark = purgeWatermark;\n } catch (e) {\n this.#lc.warn?.(`error purging the PG change log`, e);\n }\n }\n\n async #purgeSQLiteChangeLog(): Promise<void> {\n const scheduler = this.#purgeScheduler;\n if (!scheduler) {\n return;\n }\n try {\n const {backupWatermark, purgeWatermark} = this.#getCleanupFloor();\n // Consume the request before starting. A backup notification that\n // arrives during this pass records another request, which is merged with\n // the continuation returned by this pass rather than being overwritten.\n this.#sqlitePurgeContinuation = undefined;\n if (purgeWatermark < backupWatermark) {\n // Live constraint changes are not edge-triggered. Keep evaluating the\n // floor until it reaches the durable backup watermark, independently\n // of whether the PG implementation still exists.\n this.#requestSQLitePurge('deferred');\n }\n const result = await scheduler.purge(purgeWatermark);\n this.#requestSQLitePurge(result.continuation);\n } catch (e) {\n // purge() is fail-soft, but retain the coordinator's retry if an\n // unexpected caller-boundary failure escapes it.\n this.#requestSQLitePurge('deferred');\n this.#lc.warn?.(`error purging the SQLite change log`, e);\n }\n }\n\n #requestSQLitePurge(continuation: PurgeContinuation | undefined): void {\n if (!this.#purgeScheduler || continuation === undefined) {\n return;\n }\n if (\n continuation === 'immediate' ||\n this.#sqlitePurgeContinuation === undefined\n ) {\n this.#sqlitePurgeContinuation = continuation;\n }\n }\n\n /**\n * Whether the stream should be restarted (at a transaction boundary) for\n * backfills. Restarts are held until ownership of the PG change log is\n * assumed: a restarted stream would decode the WAL up to the change log's\n * head again, and backfills cannot be stored until ownership is assumed\n * anyway (see Storer.assumeOwnershipAtHead()).\n */\n #mayRestart() {\n return (\n this.#backfills.restartReason !== null && !this.#storer.ownershipPending\n );\n }\n\n async stop(err?: unknown) {\n this.#state.stop(this.#lc, err);\n // No-op if already resolved by run().\n this.#lagReportingDisabled.resolve(false);\n this.#stream?.changes.cancel();\n this.#purgeScheduler?.stop();\n this.#comparator?.stop();\n this.#sqliteCatchup?.close();\n this.#changeLogWriter?.close();\n await Promise.allSettled([this.#storer.stop(), this.#source.stop()]);\n }\n\n #recordForwardedTransactionBoundary(\n type: ChangeStreamData[0],\n watermark: string,\n ) {\n switch (type) {\n case 'begin':\n assert(\n this.#currentTransactionCompletion === undefined,\n 'forwarded begin while a transaction is already in progress',\n );\n this.#currentTransactionCompletion =\n resolver<ForwardedTransactionCompletion>();\n this.#transactionForwardStartedAt = performance.now();\n break;\n case 'commit': {\n const completion = this.#currentTransactionCompletion;\n assert(completion, 'forwarded commit without a pending transaction');\n this.#recordTransactionForwardDuration();\n this.#lastForwardedCommitWatermark = watermark;\n this.#currentTransactionCompletion = undefined;\n completion.resolve({kind: 'committed', watermark});\n break;\n }\n case 'rollback': {\n const completion = this.#currentTransactionCompletion;\n assert(completion, 'forwarded rollback without a pending transaction');\n const committed = this.#lastForwardedCommitWatermark;\n assert(committed, 'last forwarded commit watermark is not initialized');\n this.#transactionForwardStartedAt = undefined;\n this.#currentTransactionCompletion = undefined;\n completion.resolve({kind: 'rolled-back', watermark: committed});\n break;\n }\n }\n }\n\n #recordTransactionForwardDuration() {\n const startedAt = this.#transactionForwardStartedAt;\n this.#transactionForwardStartedAt = undefined;\n if (startedAt !== undefined) {\n this.#transactionForwardDuration.recordMs(performance.now() - startedAt, {\n sqlite_change_log: this.#changeLogWriter?.enabled ? 'on' : 'off',\n });\n }\n }\n\n /**\n * Closes and forgets the cached catchup coordinator, so that a later\n * subscription re-opens the log from scratch rather than reading a handle\n * whose file is gone.\n */\n #closeSQLiteCatchup() {\n this.#sqliteCatchup?.close();\n this.#sqliteCatchup = undefined;\n }\n\n #captureRequiredHead(): string | Promise<string> {\n const committed = this.#lastForwardedCommitWatermark;\n assert(committed, 'last forwarded commit watermark is not initialized');\n return (\n this.#currentTransactionCompletion?.promise.then(\n completion => completion.watermark,\n ) ?? committed\n );\n }\n\n #selectSQLiteCatchup(\n lc: LogContext,\n ctx: SubscriberContext,\n ): SQLiteCatchupDecision | undefined {\n const opts = this.#sqliteCatchupOptions;\n if (this.#lastForwardedCommitWatermark === undefined || !opts) {\n return this.#fallbackOrReject(\n 'not-ready',\n 'SQLite catchup is not ready and the PG change log is disabled',\n );\n }\n // While the PG change log is available, backup subscribers retain its\n // existing recovery policy and cannot be moved by a canary selector.\n // Once PG is disabled, the backup replicator must use SQLite catchup: it\n // advances the canonical replica that Litestream backs up.\n if (ctx.mode === 'backup' && this.#pgChangeLogEnabled) {\n lc.info?.(`not serving backup subscriber ${ctx.id} from SQLite catchup`);\n return this.#fallbackOrReject(\n 'ineligible-mode',\n 'backup subscribers retain the PG change-log recovery policy',\n );\n }\n\n let route: ChangeLogReadRoute | undefined;\n if (this.#readRouter) {\n route = this.#readRouter.consume(ctx.taskID);\n if (route.source === 'pg') {\n if (route.reason === 'cold-log') {\n lc.info?.(\n `serving ${ctx.id} from PG catchup: SQLite change log is still warming`,\n {sqliteChangeLogCoverage: route.coverage},\n );\n } else if (route.reason === 'log-unavailable') {\n this.#declineSQLiteCatchup(\n lc,\n ctx,\n opts,\n `${opts.changeLogFile} is unavailable or incompatible`,\n );\n } else {\n lc.debug?.(\n `serving ${ctx.id} from PG catchup: SQLite route ${route.reason}`,\n ...(route.coverage\n ? [{sqliteChangeLogCoverage: route.coverage}]\n : []),\n );\n }\n return this.#fallbackOrReject(\n route.reason,\n `SQLite catchup route ${route.reason} is unavailable and the PG ` +\n `change log is disabled`,\n );\n }\n const coverage = route.coverage;\n assert(coverage, 'a SQLite route must carry its covered range');\n if (ctx.watermark < coverage.minWatermark) {\n lc.info?.(\n `serving ${ctx.id} from PG catchup: subscriber watermark ` +\n `${ctx.watermark} is below the SQLite change-log minimum ` +\n coverage.minWatermark,\n {sqliteChangeLogCoverage: coverage},\n );\n return this.#fallbackOrReject(\n 'watermark-uncovered',\n `earliest supported watermark is ${coverage.minWatermark} ` +\n `(requested ${ctx.watermark})`,\n true,\n );\n }\n }\n\n // `shouldUse` remains as a test hook and an optional extra policy gate.\n // Production selection comes from #readRouter.\n if (!this.#readRouter && !opts.shouldUse?.(ctx)) {\n return this.#fallbackOrReject(\n 'selector',\n 'SQLite catchup was not selected and the PG change log is disabled',\n );\n }\n if (this.#readRouter && opts.shouldUse && !opts.shouldUse(ctx)) {\n return this.#fallbackOrReject(\n 'selector',\n 'SQLite catchup was not selected and the PG change log is disabled',\n );\n }\n\n const catchup =\n this.#sqliteCatchup ?? this.#openSQLiteCatchup(lc, opts, ctx);\n if (catchup) {\n return {\n kind: 'selected',\n catchup,\n reason: route?.reason ?? 'selector',\n coverage: route?.coverage,\n // Legacy focused-test selection has no warm classification.\n logWarm:\n route === undefined ? undefined : route.reason !== 'selected-cold',\n };\n } else {\n return this.#fallbackOrReject(\n 'log-unavailable',\n 'SQLite change log is unavailable and the PG change log is disabled',\n );\n }\n }\n\n #fallbackOrReject(\n reason: string,\n message: string,\n terminal = false,\n ): SQLiteCatchupRejection | undefined {\n this.#recordCatchupRoute(this.#pgChangeLogEnabled ? 'pg' : 'none', reason);\n return this.#pgChangeLogEnabled\n ? undefined\n : {kind: 'rejected', message, terminal};\n }\n\n #recordCatchupRoute(source: 'pg' | 'sqlite' | 'none', reason: string): void {\n this.#catchupRoutes.add(1, {source, reason});\n }\n\n /**\n * Opens the change log and, if it can serve, wraps it in the coordinator that\n * subsequent subscriptions reuse.\n *\n * The log may not exist yet -- the writer creates it at the stream loop's\n * first reconcile, and deletes it when it fails soft -- or may exist without\n * content, or may be unreadable. None of those can be allowed to fail a\n * subscription:\n * this is the last point at which PG catchup can be selected when it is\n * enabled -- past `Forwarder.add()` the subscriber is committed to SQLite --\n * so each declines here instead. With PG disabled, the caller ends the\n * subscription so that it retries. Neither the failure nor the reader is\n * retained, so a later subscription retries from scratch.\n */\n #openSQLiteCatchup(\n lc: LogContext,\n opts: SQLiteCatchupOptions,\n ctx: SubscriberContext,\n ): SQLiteChangeLogCatchup | undefined {\n let reader: SQLiteChangeLogReader | undefined;\n try {\n reader = new SQLiteChangeLogReader(this.#lc, opts.changeLogFile);\n // `plan()` doubles as the readiness check: it reports `not-ready` when\n // the writer has not created or seeded the stream table. The barrier\n // cannot wait its way out of that, since a log with no head can never\n // reach the required one.\n if (reader.plan(ctx.watermark).kind === 'not-ready') {\n reader.close();\n this.#declineSQLiteCatchup(\n lc,\n ctx,\n opts,\n `${opts.changeLogFile} has no changes to serve yet`,\n );\n return undefined;\n }\n } catch (e) {\n // An absent file is the common case, since a readonly handle cannot\n // create one. A corrupt or truncated file lands here too, and is equally\n // not a reason to fail the subscription.\n reader?.close();\n this.#declineSQLiteCatchup(\n lc,\n ctx,\n opts,\n `cannot read ${opts.changeLogFile}`,\n e,\n );\n return undefined;\n }\n this.#changeLogUnavailableSince = undefined;\n // Readiness is checked once, on the way to the cached coordinator: a log\n // with content keeps it. Purging preserves the latest transaction as a\n // catchup boundary, and `reconcileChangeLog` reseeds inside a single\n // transaction, so a reader never observes an emptied log.\n this.#sqliteCatchup = new SQLiteChangeLogCatchup(\n this.#lc,\n this.#forwarder,\n reader,\n {\n batchSize: opts.readBatchRows,\n barrierTimeoutMs: opts.barrierTimeoutMs,\n barrierPollIntervalMs: opts.barrierPollIntervalMs,\n cleanupGuard: opts.cleanupGuard,\n onFailure: failure => {\n this.#lc.warn?.(\n `temporarily disabling SQLite catchup after ${failure}`,\n );\n this.#readRouter?.trip();\n },\n },\n );\n return this.#sqliteCatchup;\n }\n\n /**\n * Reports falling back to PG catchup, tracking how long the log has been\n * unavailable so that a subscription that merely arrived before the writer's\n * first reconcile does not look like an incident.\n */\n #declineSQLiteCatchup(\n lc: LogContext,\n ctx: SubscriberContext,\n opts: SQLiteCatchupOptions,\n reason: string,\n error?: unknown,\n ): void {\n const now = Date.now();\n this.#changeLogUnavailableSince ??= now;\n const unavailableMs = now - this.#changeLogUnavailableSince;\n const threshold =\n opts.notReadyWarnThresholdMs ??\n DEFAULT_CHANGE_LOG_UNAVAILABLE_WARN_THRESHOLD_MS;\n lc[unavailableMs >= threshold ? 'warn' : 'debug']?.(\n (this.#pgChangeLogEnabled\n ? `serving ${ctx.id} from PG catchup: `\n : `cannot serve ${ctx.id} from SQLite catchup: `) +\n `${reason} ` +\n `(unavailable for ${unavailableMs} ms)`,\n ...(error === undefined ? [] : [error]),\n );\n }\n}\n\ntype ForwardedTransactionCompletion =\n | {kind: 'committed'; watermark: string}\n | {kind: 'rolled-back'; watermark: string};\n\ntype SQLiteCatchupSelection = {\n readonly kind: 'selected';\n readonly catchup: SQLiteChangeLogCatchup;\n readonly reason: string;\n readonly coverage: SQLiteChangeLogCoverage | undefined;\n /**\n * Whether the router classified the log as warm for this subscriber, or\n * undefined when selection did not run through the router (focused tests).\n */\n readonly logWarm: boolean | undefined;\n};\n\ntype SQLiteCatchupRejection = {\n readonly kind: 'rejected';\n readonly message: string;\n /** A terminal rejection forces the view-syncer to restore a fresh replica. */\n readonly terminal: boolean;\n};\n\ntype SQLiteCatchupDecision = SQLiteCatchupSelection | SQLiteCatchupRejection;\n\n// The delay between receiving an initial, backup-based watermark\n// and performing a check of whether to purge records before it.\n// This delay should be long enough to handle situations like the following:\n//\n// 1. `litestream restore` downloads a backup for the `replication-manager`\n// 2. `replication-manager` starts up and runs this `change-streamer`\n// 3. `zero-cache`s that are running on a different replica connect to this\n// `change-streamer` after exponential backoff retries.\n//\n// It is possible for a `zero-cache`[3] to be behind the backup restored [1].\n// This cleanup delay (30 seconds) is thus set to be a value comfortably\n// longer than the max delay for exponential backoff (10 seconds) in\n// `services/running-state.ts`. This allows the `zero-cache` [3] to reconnect\n// so that the `change-streamer` can track its progress and know when it has\n// surpassed the initial watermark of the backup [1].\nconst CLEANUP_DELAY_MS = DEFAULT_MAX_RETRY_DELAY_MS * 3;\n\nconst ROLLBACK_JSON = JSON.stringify([\n 'rollback',\n {tag: 'rollback'},\n] satisfies Rollback);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoNA,eAAsB,mBACpB,IACA,OACA,QACA,kBACA,mBACA,UACA,cACA,4BACA,mBACA,cACA,WACA,WACA,MACA,eAAe,YACiB;CAChC,MAAM,wBACJ,IACA,UACA,mBACA,OACA,WACA,aAAa,KAAA,GACb,cACA,KAAK,kBACP;CAOA,MAAM,oBACJ,iBACA,QAEA,SACE,KACE;EAAC;EAAK,YAAY,GAAE,qBAAqB,gBAAe;CAAC,GAKzD,SAAS,OACX,CACF;CAEF,MAAM,EAAC,mBAAkB;CACzB,OAAO,IAAI,mBACT,IACA,OACA,QACA,kBACA,mBACA,kBACA,gBACA,cACA,4BACA,cACA,WACA,WACA,MACA,YACF;AACF;AAEA,IAAM,8CAA8C;AAQpD,IAAM,mDAAmD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4IzD,IAAM,qBAAN,MAA0D;CACxD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;;CAGA;CAUA,WAAoB,SAAS;CAE7B,aAAsB,mBACpB,eACA,gBACA,kCACF;CACA,iBAA0B,mBACxB,eACA,WACA,qDACF;CAIA,8BAAuC,4BACrC,eACA,gCACA,yIAEF;CACA,iBAA0B,mBACxB,eACA,oCACA,sEACF;CACA,wBAAiC,mBAC/B,eACA,2CACA,qPAIF;CACA,4BAAqC,mBACnC,eACA,gDACA,2LAGF;CAEA,+BAA+B;CAI/B,wBAAiC,SAAkB;CACnD;CACA,qBAA6B;;;;;;;CAO7B;CACA;CACA;CAGA,oBAAoB;CACpB,kBAAkB;CAClB,wBAAwB;CACxB,sBAAsB;CACtB;CACA;CACA;CACA;CACA;CACA;CAIA,YACE,IACA,OACA,QACA,kBACA,mBACA,kBACA,gBACA,QACA,4BACA,cACA,kBACA,WACA,MACA,eAA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"}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import type { Enum } from '../../../../shared/src/enum.ts';
|
|
2
2
|
import * as v from '../../../../shared/src/valita.ts';
|
|
3
|
-
import type { Sized, Source } from '../../types/streams.ts';
|
|
3
|
+
import type { PreSerialized, Sized, Source } from '../../types/streams.ts';
|
|
4
|
+
import type { Subscription } from '../../types/subscription.ts';
|
|
4
5
|
import { type ChangeStreamData } from '../change-source/protocol/current/downstream.ts';
|
|
6
|
+
import type { BackfillRequest } from '../change-source/protocol/current/upstream.ts';
|
|
5
7
|
import type { ReplicatorMode } from '../replicator/replicator.ts';
|
|
6
8
|
import type { Service } from '../service.ts';
|
|
9
|
+
import type { PreSerializedBatch } from './broadcast.ts';
|
|
7
10
|
import * as ErrorType from './error-type-enum.ts';
|
|
8
|
-
import type { SnapshotMessage } from './snapshot.ts';
|
|
11
|
+
import type { SnapshotMessage } from './snapshot-message.ts';
|
|
12
|
+
import type { SubscribeDownstream } from './subscribe.ts';
|
|
9
13
|
type ErrorType = Enum<typeof ErrorType>;
|
|
10
14
|
export type ChangeTag = ChangeStreamData[1]['tag'];
|
|
11
15
|
/**
|
|
@@ -62,9 +66,9 @@ export interface ChangeStreamer {
|
|
|
62
66
|
* date. Each result contains parsed, validated data and its approximate
|
|
63
67
|
* serialized transport size.
|
|
64
68
|
*/
|
|
65
|
-
subscribe(ctx: SubscriberContext): Promise<Source<
|
|
69
|
+
subscribe(ctx: SubscriberContext): Promise<Source<Sized<SubscribeDownstream>>>;
|
|
66
70
|
}
|
|
67
|
-
export declare const PROTOCOL_VERSION =
|
|
71
|
+
export declare const PROTOCOL_VERSION = 8;
|
|
68
72
|
export type SubscriberContext = {
|
|
69
73
|
/**
|
|
70
74
|
* The supported change-streamer protocol version.
|
|
@@ -97,18 +101,14 @@ export type SubscriberContext = {
|
|
|
97
101
|
*/
|
|
98
102
|
watermark: string;
|
|
99
103
|
/**
|
|
100
|
-
* Whether
|
|
101
|
-
* i.e. indicating that the watermark comes from a restored replica
|
|
102
|
-
* backup. The ChangeStreamer uses this to determine which changes
|
|
103
|
-
* are safe to purge from the Storer.
|
|
104
|
+
* Whether the subscriber supports batched WebSocket frames.
|
|
104
105
|
*/
|
|
105
|
-
|
|
106
|
+
wsBatched?: boolean | undefined;
|
|
106
107
|
/**
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
* subscriber's ACK advances its head or releases its catchup barrier.
|
|
108
|
+
* The subscriber's pending backfills as of its `watermark`. Reported by
|
|
109
|
+
* subscribers as of protocol v8. `undefined` for older subscribers.
|
|
110
110
|
*/
|
|
111
|
-
|
|
111
|
+
backfills?: BackfillRequest[] | undefined;
|
|
112
112
|
};
|
|
113
113
|
/**
|
|
114
114
|
* The StatusMessage payload for now is empty, but can be extended to
|
|
@@ -194,7 +194,7 @@ export declare const downstreamSchema: v.UnionType<[v.TupleType<[v.Type<"status"
|
|
|
194
194
|
type?: "default" | "full" | "index" | "nothing" | undefined;
|
|
195
195
|
};
|
|
196
196
|
}>;
|
|
197
|
-
key: v.Type<Record<string, import("../../../../shared/src/bigint-json.ts").JSONValue
|
|
197
|
+
key: v.UnionType<[v.Type<null>, v.Type<Record<string, import("../../../../shared/src/bigint-json.ts").JSONValue>>]>;
|
|
198
198
|
new: v.Type<Record<string, import("../../../../shared/src/bigint-json.ts").JSONValue>>;
|
|
199
199
|
}, undefined>, v.ObjectType<{
|
|
200
200
|
tag: v.Type<"delete">;
|
|
@@ -234,6 +234,18 @@ export declare const downstreamSchema: v.UnionType<[v.TupleType<[v.Type<"status"
|
|
|
234
234
|
columns: v.ArrayType<v.Type<string>>;
|
|
235
235
|
watermark: v.Type<string>;
|
|
236
236
|
rowValues: v.ArrayType<v.ArrayType<v.Type<import("../../../../shared/src/bigint-json.ts").JSONValue>>>;
|
|
237
|
+
progressMarks: v.Optional<{
|
|
238
|
+
previous?: {
|
|
239
|
+
[x: string]: string | number;
|
|
240
|
+
progressMark: string;
|
|
241
|
+
timeline?: string | undefined;
|
|
242
|
+
} | undefined;
|
|
243
|
+
current: {
|
|
244
|
+
[x: string]: string | number;
|
|
245
|
+
progressMark: string;
|
|
246
|
+
timeline?: string | undefined;
|
|
247
|
+
};
|
|
248
|
+
}>;
|
|
237
249
|
status: v.Optional<{
|
|
238
250
|
rows: number;
|
|
239
251
|
totalRows: number;
|
|
@@ -378,6 +390,13 @@ export declare const downstreamSchema: v.UnionType<[v.TupleType<[v.Type<"status"
|
|
|
378
390
|
}, undefined>;
|
|
379
391
|
columns: v.ArrayType<v.Type<string>>;
|
|
380
392
|
watermark: v.Type<string>;
|
|
393
|
+
progressMarks: v.Optional<{
|
|
394
|
+
previous?: {
|
|
395
|
+
[x: string]: string | number;
|
|
396
|
+
progressMark: string;
|
|
397
|
+
timeline?: string | undefined;
|
|
398
|
+
} | undefined;
|
|
399
|
+
}>;
|
|
381
400
|
status: v.Optional<{
|
|
382
401
|
rows: number;
|
|
383
402
|
totalRows: number;
|
|
@@ -413,9 +432,12 @@ export type SizedDownstream = Sized<Downstream>;
|
|
|
413
432
|
export interface ChangeStreamerService extends Omit<ChangeStreamer, 'subscribe'>, Service {
|
|
414
433
|
/**
|
|
415
434
|
* The server-side interface overrides `subscribe()` to return a stream
|
|
416
|
-
* of already-stringified {@link Downstream} payloads.
|
|
435
|
+
* of already-stringified {@link Downstream} payloads or pre-serialized Buffers.
|
|
436
|
+
*
|
|
437
|
+
* If a `downstream` subscription is supplied it is used directly as the
|
|
438
|
+
* returned Source. Otherwise, a new one is created.
|
|
417
439
|
*/
|
|
418
|
-
subscribe(ctx: SubscriberContext): Promise<Source<string>>;
|
|
440
|
+
subscribe(ctx: SubscriberContext, downstream?: Subscription<string | PreSerializedBatch>): Promise<Source<string | PreSerialized>>;
|
|
419
441
|
/**
|
|
420
442
|
* Starts a snapshot reservation to preserve change-log entries while
|
|
421
443
|
* a soon-to-be-subscriber downloads the current replica backup. Once
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"change-streamer.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/change-streamer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,gCAAgC,CAAC;AACzD,OAAO,KAAK,CAAC,MAAM,kCAAkC,CAAC;AACtD,OAAO,KAAK,EAAC,KAAK,EAAE,MAAM,EAAC,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"change-streamer.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/change-streamer.ts"],"names":[],"mappings":"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"}
|