@rocicorp/zero 1.10.0-canary.0 → 1.10.0-canary.2
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/replicache/src/db/commit.js +1 -4
- package/out/replicache/src/db/commit.js.map +1 -1
- package/out/replicache/src/db/read.d.ts +1 -0
- package/out/replicache/src/db/read.d.ts.map +1 -1
- package/out/replicache/src/db/read.js +1 -4
- package/out/replicache/src/db/read.js.map +1 -1
- package/out/replicache/src/db/write.d.ts +2 -1
- package/out/replicache/src/db/write.d.ts.map +1 -1
- package/out/replicache/src/db/write.js +4 -51
- package/out/replicache/src/db/write.js.map +1 -1
- package/out/replicache/src/persist/clients.d.ts +1 -0
- package/out/replicache/src/persist/clients.d.ts.map +1 -1
- package/out/replicache/src/persist/clients.js +4 -31
- package/out/replicache/src/persist/clients.js.map +1 -1
- package/out/replicache/src/process-env.d.ts +28 -0
- package/out/replicache/src/process-env.d.ts.map +1 -0
- package/out/replicache/src/replicache-impl.d.ts.map +1 -1
- package/out/replicache/src/replicache-impl.js.map +1 -1
- package/out/replicache/src/scan-iterator.js +2 -14
- package/out/replicache/src/scan-iterator.js.map +1 -1
- package/out/replicache/src/subscriptions.d.ts +1 -0
- package/out/replicache/src/subscriptions.d.ts.map +1 -1
- package/out/replicache/src/subscriptions.js +2 -10
- package/out/replicache/src/subscriptions.js.map +1 -1
- package/out/replicache/src/transactions.d.ts +1 -0
- package/out/replicache/src/transactions.d.ts.map +1 -1
- package/out/replicache/src/transactions.js +2 -4
- package/out/replicache/src/transactions.js.map +1 -1
- package/out/z2s/src/compiler.d.ts.map +1 -1
- package/out/z2s/src/compiler.js +1 -22
- package/out/z2s/src/compiler.js.map +1 -1
- package/out/zero/package.js +3 -3
- package/out/zero/package.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 +10 -1
- package/out/zero-cache/src/config/normalize.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +52 -1
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +171 -22
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/custom/fetch.js +1 -1
- package/out/zero-cache/src/custom/fetch.js.map +1 -1
- package/out/zero-cache/src/db/delete-lite-db.js +2 -1
- package/out/zero-cache/src/db/delete-lite-db.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 -2
- package/out/zero-cache/src/db/migration-lite.js.map +1 -1
- package/out/zero-cache/src/db/migration.d.ts.map +1 -1
- package/out/zero-cache/src/db/migration.js +4 -2
- package/out/zero-cache/src/db/migration.js.map +1 -1
- package/out/zero-cache/src/db/sqlite-corruption.d.ts +3 -2
- package/out/zero-cache/src/db/sqlite-corruption.d.ts.map +1 -1
- package/out/zero-cache/src/db/sqlite-corruption.js +13 -8
- package/out/zero-cache/src/db/sqlite-corruption.js.map +1 -1
- package/out/zero-cache/src/observability/metrics.d.ts +6 -1
- package/out/zero-cache/src/observability/metrics.d.ts.map +1 -1
- package/out/zero-cache/src/observability/metrics.js +20 -2
- package/out/zero-cache/src/observability/metrics.js.map +1 -1
- package/out/zero-cache/src/server/backup-watermark-poller.js +31 -0
- package/out/zero-cache/src/server/backup-watermark-poller.js.map +1 -0
- package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +91 -31
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/main.d.ts.map +1 -1
- package/out/zero-cache/src/server/main.js +6 -16
- package/out/zero-cache/src/server/main.js.map +1 -1
- package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/server/replicator.js +48 -5
- package/out/zero-cache/src/server/replicator.js.map +1 -1
- package/out/zero-cache/src/server/runner/run-worker.js +2 -1
- package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
- package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/server/syncer.js +1 -1
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/server/worker-urls.d.ts +1 -1
- package/out/zero-cache/src/server/worker-urls.js +2 -2
- package/out/zero-cache/src/server/worker-urls.js.map +1 -1
- package/out/zero-cache/src/services/change-source/change-source.d.ts +4 -2
- package/out/zero-cache/src/services/change-source/change-source.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts +35 -0
- package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-source/common/replica-restore.js +34 -0
- package/out/zero-cache/src/services/change-source/common/replica-restore.js.map +1 -0
- package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts +2 -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 +73 -1
- 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 +2 -6
- 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 +14 -5
- 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.js +4 -7
- 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.d.ts +7 -7
- 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 +82 -25
- 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 +9 -1
- 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 +12 -4
- 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 +45 -2
- 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 +59 -8
- 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-slots.d.ts +4 -2
- 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 +7 -4
- 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/init.d.ts +0 -5
- 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 +29 -41
- 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/shard.d.ts +35 -16
- 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 +77 -7
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts +2 -1
- 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 +5 -13
- 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 +6 -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/schema-change-tags.d.ts +6 -0
- package/out/zero-cache/src/services/change-source/protocol/current/schema-change-tags.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-source/protocol/current/schema-change-tags.js +21 -0
- package/out/zero-cache/src/services/change-source/protocol/current/schema-change-tags.js.map +1 -0
- package/out/zero-cache/src/services/change-source/protocol/current/status.d.ts +1 -0
- package/out/zero-cache/src/services/change-source/protocol/current/status.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts +1 -0
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts +5 -6
- 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 +32 -9
- 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 +15 -5
- 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 +66 -0
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/broadcast.d.ts +68 -78
- package/out/zero-cache/src/services/change-streamer/broadcast.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/broadcast.js +84 -92
- 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 -0
- package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/change-log-codec.js +50 -0
- package/out/zero-cache/src/services/change-streamer/change-log-codec.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/change-log-initializer.d.ts +141 -0
- package/out/zero-cache/src/services/change-streamer/change-log-initializer.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/change-log-initializer.js +270 -0
- package/out/zero-cache/src/services/change-streamer/change-log-initializer.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.d.ts +19 -0
- package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.js +39 -0
- package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +3 -4
- 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 +12 -17
- 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 +69 -24
- 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 +333 -66
- 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 +56 -13
- 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/error-type-enum.d.ts +8 -0
- package/out/zero-cache/src/services/change-streamer/error-type-enum.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/forwarder.d.ts +14 -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 +41 -11
- package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/{litestream3-backup-monitor.d.ts → litestream3-prometheus-poller.d.ts} +19 -45
- package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/{litestream3-backup-monitor.js → litestream3-prometheus-poller.js} +57 -171
- package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/replica-poller.d.ts +15 -0
- package/out/zero-cache/src/services/change-streamer/replica-poller.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/replica-poller.js +55 -0
- package/out/zero-cache/src/services/change-streamer/replica-poller.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts +5 -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 +2 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts +14 -0
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js +86 -0
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/snapshot.d.ts +4 -1
- package/out/zero-cache/src/services/change-streamer/snapshot.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot.js +39 -14
- package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.d.ts +75 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js +261 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.d.ts +121 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.js +391 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts +62 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js +159 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts +109 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +258 -0
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/storer.d.ts +28 -12
- package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js +116 -121
- package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +61 -10
- package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.js +101 -8
- 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 +34 -0
- package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/upstream-acker.js +86 -0
- package/out/zero-cache/src/services/change-streamer/upstream-acker.js.map +1 -0
- package/out/zero-cache/src/services/life-cycle.d.ts +1 -1
- package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
- package/out/zero-cache/src/services/life-cycle.js +4 -3
- package/out/zero-cache/src/services/life-cycle.js.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.d.ts +12 -11
- package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.js +107 -148
- package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
- package/out/zero-cache/src/services/litestream/config-v5.yml +42 -0
- package/out/zero-cache/src/services/litestream/config.yml +3 -2
- package/out/zero-cache/src/services/litestream/metrics.d.ts +4 -4
- package/out/zero-cache/src/services/litestream/metrics.d.ts.map +1 -1
- package/out/zero-cache/src/services/litestream/metrics.js +7 -10
- package/out/zero-cache/src/services/litestream/metrics.js.map +1 -1
- package/out/zero-cache/src/services/litestream/vfs-watermark-poller.d.ts +57 -0
- package/out/zero-cache/src/services/litestream/vfs-watermark-poller.d.ts.map +1 -0
- package/out/zero-cache/src/services/litestream/vfs-watermark-poller.js +137 -0
- package/out/zero-cache/src/services/litestream/vfs-watermark-poller.js.map +1 -0
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js +4 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-log-cookies.d.ts +169 -0
- package/out/zero-cache/src/services/replicator/change-log-cookies.d.ts.map +1 -0
- package/out/zero-cache/src/services/replicator/change-log-cookies.js +434 -0
- package/out/zero-cache/src/services/replicator/change-log-cookies.js.map +1 -0
- package/out/zero-cache/src/services/replicator/change-log-db.d.ts +358 -0
- package/out/zero-cache/src/services/replicator/change-log-db.d.ts.map +1 -0
- package/out/zero-cache/src/services/replicator/change-log-db.js +477 -0
- package/out/zero-cache/src/services/replicator/change-log-db.js.map +1 -0
- package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts +55 -0
- package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts.map +1 -0
- package/out/zero-cache/src/services/replicator/change-log-stream-writer.js +150 -0
- package/out/zero-cache/src/services/replicator/change-log-stream-writer.js.map +1 -0
- package/out/zero-cache/src/services/replicator/change-processor.d.ts +6 -0
- package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.js +70 -25
- package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.d.ts +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.js +19 -6
- package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
- package/out/zero-cache/src/services/replicator/notifier.d.ts +0 -22
- package/out/zero-cache/src/services/replicator/notifier.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/notifier.js +7 -1
- package/out/zero-cache/src/services/replicator/notifier.js.map +1 -1
- package/out/zero-cache/src/services/replicator/replicator.d.ts +17 -2
- package/out/zero-cache/src/services/replicator/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/replicator.js +9 -3
- package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
- package/out/zero-cache/src/services/replicator/reporter/recorder.d.ts +27 -0
- package/out/zero-cache/src/services/replicator/reporter/recorder.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/reporter/recorder.js +32 -0
- package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
- package/out/zero-cache/src/services/replicator/reporter/report-schema.d.ts +1 -1
- package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts +119 -0
- package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts.map +1 -0
- package/out/zero-cache/src/services/replicator/schema/backfilling.js +214 -0
- package/out/zero-cache/src/services/replicator/schema/backfilling.js.map +1 -0
- package/out/zero-cache/src/services/replicator/schema/change-log.d.ts +3 -3
- package/out/zero-cache/src/services/replicator/schema/column-metadata.js +1 -1
- package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/replication-state.d.ts +1 -1
- package/out/zero-cache/src/services/replicator/schema/replication-state.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/replication-state.js +9 -5
- package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts +131 -0
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts.map +1 -0
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +413 -0
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -0
- package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.d.ts +120 -0
- package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.d.ts.map +1 -0
- package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js +283 -0
- package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js.map +1 -0
- package/out/zero-cache/src/services/replicator/write-worker.js +23 -12
- package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
- package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
- package/out/zero-cache/src/services/run-ast.js +2 -1
- package/out/zero-cache/src/services/run-ast.js.map +1 -1
- package/out/zero-cache/src/services/running-state.d.ts +1 -1
- package/out/zero-cache/src/services/running-state.d.ts.map +1 -1
- package/out/zero-cache/src/services/running-state.js +1 -0
- package/out/zero-cache/src/services/running-state.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.d.ts +53 -0
- package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.js +68 -0
- package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.js.map +1 -0
- 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 +18 -3
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
- 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 +64 -6
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/processes.d.ts +1 -2
- package/out/zero-cache/src/types/processes.d.ts.map +1 -1
- package/out/zero-cache/src/types/processes.js +1 -25
- package/out/zero-cache/src/types/processes.js.map +1 -1
- package/out/zero-cache/src/types/streams.d.ts +21 -0
- package/out/zero-cache/src/types/streams.d.ts.map +1 -1
- package/out/zero-cache/src/types/streams.js +25 -4
- package/out/zero-cache/src/types/streams.js.map +1 -1
- package/out/zero-cache/src/types/subscription.d.ts +11 -0
- package/out/zero-cache/src/types/subscription.d.ts.map +1 -1
- package/out/zero-cache/src/types/subscription.js +27 -0
- package/out/zero-cache/src/types/subscription.js.map +1 -1
- package/out/zero-cache/src/workers/connection.d.ts +1 -0
- package/out/zero-cache/src/workers/connection.d.ts.map +1 -1
- package/out/zero-cache/src/workers/connection.js +24 -2
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/replicator.d.ts +25 -0
- package/out/zero-cache/src/workers/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/workers/replicator.js +33 -1
- package/out/zero-cache/src/workers/replicator.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.d.ts +12 -0
- package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/workers/syncer.js +75 -0
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero.d.ts +6 -7
- package/out/zero-client/src/client/zero.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.js +156 -101
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-schema/src/builder/table-builder.d.ts +23 -0
- package/out/zero-schema/src/builder/table-builder.d.ts.map +1 -1
- package/out/zero-schema/src/builder/table-builder.js +18 -0
- package/out/zero-schema/src/builder/table-builder.js.map +1 -1
- package/out/zero-server/src/custom.js +10 -3
- package/out/zero-server/src/custom.js.map +1 -1
- package/out/zero-server/src/schema.d.ts +8 -0
- package/out/zero-server/src/schema.d.ts.map +1 -1
- package/out/zero-server/src/schema.js +63 -24
- package/out/zero-server/src/schema.js.map +1 -1
- package/out/zero-types/src/schema.d.ts +11 -0
- package/out/zero-types/src/schema.d.ts.map +1 -1
- package/out/zql/src/mutate/crud.d.ts +10 -8
- package/out/zql/src/mutate/crud.d.ts.map +1 -1
- package/out/zql/src/mutate/crud.js.map +1 -1
- package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
- package/out/zql/src/query/query-delegate-base.js +5 -1
- package/out/zql/src/query/query-delegate-base.js.map +1 -1
- package/out/zql/src/query/query-impl.d.ts +2 -2
- package/out/zql/src/query/query-impl.d.ts.map +1 -1
- package/out/zql/src/query/query-impl.js.map +1 -1
- package/out/zql/src/query/query.d.ts +83 -14
- package/out/zql/src/query/query.d.ts.map +1 -1
- package/out/zql/src/query/static-query.d.ts +1 -1
- package/out/zql/src/query/static-query.d.ts.map +1 -1
- package/out/zqlite/src/db.js +2 -2
- package/out/zqlite/src/resolve-scalar-subqueries.d.ts +15 -0
- package/out/zqlite/src/resolve-scalar-subqueries.d.ts.map +1 -1
- package/out/zqlite/src/resolve-scalar-subqueries.js +33 -14
- package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
- package/out/zqlite/src/sqlite-cost-model.js +5 -5
- package/out/zqlite/src/table-source.js +3 -3
- package/package.json +3 -3
- package/out/replicache/src/db/index.js +0 -91
- package/out/replicache/src/db/index.js.map +0 -1
- package/out/replicache/src/lazy.d.ts +0 -2
- package/out/replicache/src/lazy.d.ts.map +0 -1
- package/out/replicache/src/lazy.js +0 -12
- package/out/replicache/src/lazy.js.map +0 -1
- package/out/zero-cache/src/server/backup-watermark-reader.js +0 -35
- package/out/zero-cache/src/server/backup-watermark-reader.js.map +0 -1
- package/out/zero-cache/src/services/change-streamer/litestream3-backup-monitor.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/litestream3-backup-monitor.js.map +0 -1
- package/out/zero-cache/src/services/change-streamer/replica-monitor.d.ts +0 -16
- package/out/zero-cache/src/services/change-streamer/replica-monitor.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/replica-monitor.js +0 -54
- package/out/zero-cache/src/services/change-streamer/replica-monitor.js.map +0 -1
- package/out/zero-cache/src/services/change-streamer/vfs-backup-monitor.d.ts +0 -25
- package/out/zero-cache/src/services/change-streamer/vfs-backup-monitor.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-streamer/vfs-backup-monitor.js +0 -159
- package/out/zero-cache/src/services/change-streamer/vfs-backup-monitor.js.map +0 -1
- package/out/zero-cache/src/services/litestream/vfs-watermark-reader.d.ts +0 -44
- package/out/zero-cache/src/services/litestream/vfs-watermark-reader.d.ts.map +0 -1
- package/out/zero-cache/src/services/litestream/vfs-watermark-reader.js +0 -65
- package/out/zero-cache/src/services/litestream/vfs-watermark-reader.js.map +0 -1
- package/out/zero-cache/src/services/litestream/vfs-watermark-worker-source.d.ts +0 -11
- package/out/zero-cache/src/services/litestream/vfs-watermark-worker-source.d.ts.map +0 -1
- package/out/zero-cache/src/services/litestream/vfs-watermark-worker-source.js +0 -74
- package/out/zero-cache/src/services/litestream/vfs-watermark-worker-source.js.map +0 -1
- package/out/zero-cache/src/services/litestream/vfs-watermark-worker.d.ts +0 -42
- package/out/zero-cache/src/services/litestream/vfs-watermark-worker.d.ts.map +0 -1
- package/out/zero-cache/src/services/litestream/vfs-watermark-worker.js +0 -117
- package/out/zero-cache/src/services/litestream/vfs-watermark-worker.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-scalar-subqueries.js","names":[],"sources":["../../../../zqlite/src/resolve-scalar-subqueries.ts"],"sourcesContent":["import type {\n AST,\n Condition,\n CorrelatedSubqueryCondition,\n LiteralValue,\n SimpleCondition,\n} from '../../zero-protocol/src/ast.ts';\nimport type {PrimaryKey} from '../../zero-protocol/src/primary-key.ts';\n\ntype TableSpecWithUniqueKeys = {\n tableSpec: {\n uniqueKeys: PrimaryKey[];\n };\n};\n\nexport type CompanionSubquery = {\n /** The original scalar subquery AST (the subquery table query). */\n ast: AST;\n /** The field in the subquery row whose value was resolved. */\n childField: string;\n /** The resolved value, `null` if a row matched but the field was `NULL`,\n * or `undefined` if no row matched. */\n resolvedValue: LiteralValue | null | undefined;\n};\n\nexport type ResolveResult = {\n ast: AST;\n companions: CompanionSubquery[];\n};\n\n/**\n * Callback that executes a scalar subquery and returns the value of\n * `childField` from the (at most one) matching row, or `undefined`\n * if no rows match.\n */\nexport type ScalarExecutor = (\n subqueryAST: AST,\n childField: string,\n) => LiteralValue | null | undefined;\n\n/**\n * Resolves \"simple\" scalar subqueries by calling the provided executor\n * and replacing them with literal conditions. A scalar subquery is simple\n * when all columns of at least one unique index on the subquery table are\n * equality-constrained by literal values in the subquery's WHERE clause\n * (using only AND conjunctions).\n *\n * Non-simple scalar subqueries are left untouched for the existing\n * EXISTS rewrite in buildPipelineInternal.\n *\n * Returns the resolved AST and a list of companion subquery ASTs whose\n * rows need to be synced to the client for the EXISTS rewrite to work.\n */\nexport function resolveSimpleScalarSubqueries(\n ast: AST,\n tableSpecs: Map<string, TableSpecWithUniqueKeys>,\n execute: ScalarExecutor,\n): ResolveResult {\n const companions: CompanionSubquery[] = [];\n const resolved = resolveASTRecursive(ast, tableSpecs, execute, companions);\n return {ast: resolved, companions};\n}\n\nfunction resolveASTRecursive(\n ast: AST,\n tableSpecs: Map<string, TableSpecWithUniqueKeys>,\n execute: ScalarExecutor,\n companions: CompanionSubquery[],\n): AST {\n const where = ast.where\n ? resolveCondition(ast.where, tableSpecs, execute, companions)\n : undefined;\n\n const related = ast.related?.map(r => ({\n ...r,\n subquery: resolveASTRecursive(r.subquery, tableSpecs, execute, companions),\n }));\n\n if (where !== ast.where || related !== ast.related) {\n return {...ast, where, related};\n }\n return ast;\n}\n\nfunction resolveCondition(\n condition: Condition,\n tableSpecs: Map<string, TableSpecWithUniqueKeys>,\n execute: ScalarExecutor,\n companions: CompanionSubquery[],\n): Condition {\n switch (condition.type) {\n case 'correlatedSubquery':\n if (condition.scalar) {\n return resolveScalarSubquery(\n condition,\n tableSpecs,\n execute,\n companions,\n );\n }\n // Non-scalar correlated subquery: recurse into its subquery\n {\n const resolvedSubquery = resolveASTRecursive(\n condition.related.subquery,\n tableSpecs,\n execute,\n companions,\n );\n if (resolvedSubquery !== condition.related.subquery) {\n return {\n ...condition,\n related: {...condition.related, subquery: resolvedSubquery},\n };\n }\n return condition;\n }\n case 'and':\n case 'or': {\n const resolved = condition.conditions.map(c =>\n resolveCondition(c, tableSpecs, execute, companions),\n );\n if (resolved.every((c, i) => c === condition.conditions[i])) {\n return condition;\n }\n return {type: condition.type, conditions: resolved};\n }\n default:\n return condition;\n }\n}\n\nfunction resolveScalarSubquery(\n condition: CorrelatedSubqueryCondition,\n tableSpecs: Map<string, TableSpecWithUniqueKeys>,\n execute: ScalarExecutor,\n companions: CompanionSubquery[],\n): Condition {\n const parentField = condition.related.correlation.parentField[0];\n const childField = condition.related.correlation.childField[0];\n\n // Recursively resolve any scalar subqueries nested in the\n // subquery's own WHERE (and related) before evaluating this one.\n const subquery = resolveASTRecursive(\n condition.related.subquery,\n tableSpecs,\n execute,\n companions,\n );\n\n if (!isSimpleSubquery(subquery, tableSpecs)) {\n // Return with the (possibly partially-resolved) subquery.\n if (subquery !== condition.related.subquery) {\n return {\n ...condition,\n related: {...condition.related, subquery},\n };\n }\n return condition;\n }\n\n const value = execute(subquery, childField);\n\n // Record the companion subquery AST so its rows are synced to the client.\n // The client rewrites scalar subqueries to EXISTS and needs those rows.\n companions.push({\n ast: subquery,\n childField,\n resolvedValue: value,\n });\n\n if (value === undefined || value === null) {\n // No rows or NULL value — both x = NULL and x != NULL are false in SQL\n return ALWAYS_FALSE;\n }\n\n const op = condition.op === 'EXISTS' ? '=' : 'IS NOT';\n return {\n type: 'simple',\n op,\n left: {type: 'column', name: parentField},\n right: {type: 'literal', value},\n } satisfies SimpleCondition;\n}\n\nconst ALWAYS_FALSE: SimpleCondition = {\n type: 'simple',\n op: '=',\n left: {type: 'literal', value: 1},\n right: {type: 'literal', value: 0},\n};\n\n/**\n * Checks if the subquery is guaranteed to return at most one deterministic row.\n *\n * This is true when all columns of at least one unique index on the subquery\n * table are equality-constrained by literal values in the WHERE clause\n * (using only AND conjunctions).\n */\nexport function isSimpleSubquery(\n subquery: AST,\n tableSpecs: Map<string, TableSpecWithUniqueKeys>,\n): boolean {\n const spec = tableSpecs.get(subquery.table);\n if (!spec) {\n return false;\n }\n\n if (!subquery.where) {\n return false;\n }\n\n const constraints = extractLiteralEqualityConstraints(subquery.where);\n if (constraints.size === 0) {\n return false;\n }\n\n const {uniqueKeys} = spec.tableSpec;\n return uniqueKeys.some(key => key.every(col => constraints.has(col)));\n}\n\n/**\n * Extracts column=literal equality constraints from a condition tree,\n * only following AND conjunctions (not OR).\n */\nexport function extractLiteralEqualityConstraints(\n condition: Condition,\n): Map<string, LiteralValue> {\n const constraints = new Map<string, LiteralValue>();\n collectConstraints(condition, constraints);\n return constraints;\n}\n\nfunction collectConstraints(\n condition: Condition,\n constraints: Map<string, LiteralValue>,\n): void {\n switch (condition.type) {\n case 'simple':\n if (condition.op === '=') {\n if (\n condition.left.type === 'column' &&\n condition.right.type === 'literal'\n ) {\n constraints.set(condition.left.name, condition.right.value);\n }\n }\n break;\n case 'and':\n for (const c of condition.conditions) {\n collectConstraints(c, constraints);\n }\n break;\n // OR, correlatedSubquery (non-scalar) — don't contribute constraints\n default:\n break;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"resolve-scalar-subqueries.js","names":[],"sources":["../../../../zqlite/src/resolve-scalar-subqueries.ts"],"sourcesContent":["import type {\n AST,\n Condition,\n CorrelatedSubqueryCondition,\n LiteralValue,\n SimpleCondition,\n} from '../../zero-protocol/src/ast.ts';\nimport type {PrimaryKey} from '../../zero-protocol/src/primary-key.ts';\n\ntype TableSpecWithUniqueKeys = {\n tableSpec: {\n uniqueKeys: PrimaryKey[];\n };\n};\n\nexport type CompanionSubquery = {\n /** The original scalar subquery AST (the subquery table query). */\n ast: AST;\n /** The field in the subquery row whose value was resolved. */\n childField: string;\n /** The resolved value, `null` if a row matched but the field was `NULL`,\n * or `undefined` if no row matched. */\n resolvedValue: LiteralValue | null | undefined;\n};\n\n/**\n * A `scalar` hint that could not be honored: the subquery was not provably\n * limited to one row, so the gate was left as a plain EXISTS.\n */\nexport type IgnoredScalarHint = {\n /** The subquery's table. */\n table: string;\n /** The unique keys that were available to pin it. */\n uniqueKeys: readonly PrimaryKey[];\n};\n\nexport type ResolveResult = {\n ast: AST;\n companions: CompanionSubquery[];\n /**\n * Hints the caller may want to surface: each one is a `{scalar: true}` the\n * author asked for and did not get. See {@link IgnoredScalarHint}.\n */\n ignoredScalarHints: IgnoredScalarHint[];\n};\n\n/** Accumulators threaded through the recursion. */\ntype Out = {\n companions: CompanionSubquery[];\n ignoredScalarHints: IgnoredScalarHint[];\n};\n\n/**\n * Callback that executes a scalar subquery and returns the value of\n * `childField` from the (at most one) matching row, or `undefined`\n * if no rows match.\n */\nexport type ScalarExecutor = (\n subqueryAST: AST,\n childField: string,\n) => LiteralValue | null | undefined;\n\n/**\n * Resolves \"simple\" scalar subqueries by calling the provided executor\n * and replacing them with literal conditions. A scalar subquery is simple\n * when all columns of at least one unique index on the subquery table are\n * equality-constrained by literal values in the subquery's WHERE clause\n * (using only AND conjunctions).\n *\n * Non-simple scalar subqueries are left untouched for the existing\n * EXISTS rewrite in buildPipelineInternal.\n *\n * Returns the resolved AST and a list of companion subquery ASTs whose\n * rows need to be synced to the client for the EXISTS rewrite to work.\n */\nexport function resolveSimpleScalarSubqueries(\n ast: AST,\n tableSpecs: Map<string, TableSpecWithUniqueKeys>,\n execute: ScalarExecutor,\n): ResolveResult {\n const out: Out = {companions: [], ignoredScalarHints: []};\n const resolved = resolveASTRecursive(ast, tableSpecs, execute, out);\n return {ast: resolved, ...out};\n}\n\nfunction resolveASTRecursive(\n ast: AST,\n tableSpecs: Map<string, TableSpecWithUniqueKeys>,\n execute: ScalarExecutor,\n out: Out,\n): AST {\n const where = ast.where\n ? resolveCondition(ast.where, tableSpecs, execute, out)\n : undefined;\n\n const related = ast.related?.map(r => ({\n ...r,\n subquery: resolveASTRecursive(r.subquery, tableSpecs, execute, out),\n }));\n\n if (where !== ast.where || related !== ast.related) {\n return {...ast, where, related};\n }\n return ast;\n}\n\nfunction resolveCondition(\n condition: Condition,\n tableSpecs: Map<string, TableSpecWithUniqueKeys>,\n execute: ScalarExecutor,\n out: Out,\n): Condition {\n switch (condition.type) {\n case 'correlatedSubquery':\n if (condition.scalar) {\n return resolveScalarSubquery(condition, tableSpecs, execute, out);\n }\n // Non-scalar correlated subquery: recurse into its subquery\n {\n const resolvedSubquery = resolveASTRecursive(\n condition.related.subquery,\n tableSpecs,\n execute,\n out,\n );\n if (resolvedSubquery !== condition.related.subquery) {\n return {\n ...condition,\n related: {...condition.related, subquery: resolvedSubquery},\n };\n }\n return condition;\n }\n case 'and':\n case 'or': {\n const resolved = condition.conditions.map(c =>\n resolveCondition(c, tableSpecs, execute, out),\n );\n if (resolved.every((c, i) => c === condition.conditions[i])) {\n return condition;\n }\n return {type: condition.type, conditions: resolved};\n }\n default:\n return condition;\n }\n}\n\nfunction resolveScalarSubquery(\n condition: CorrelatedSubqueryCondition,\n tableSpecs: Map<string, TableSpecWithUniqueKeys>,\n execute: ScalarExecutor,\n out: Out,\n): Condition {\n const parentField = condition.related.correlation.parentField[0];\n const childField = condition.related.correlation.childField[0];\n\n // Recursively resolve any scalar subqueries nested in the\n // subquery's own WHERE (and related) before evaluating this one.\n const subquery = resolveASTRecursive(\n condition.related.subquery,\n tableSpecs,\n execute,\n out,\n );\n\n if (!isSimpleSubquery(subquery, tableSpecs)) {\n // The author asked for a scalar rewrite and is not getting one. Record it\n // so the caller can say so — silently falling back is the whole reason the\n // hint is easy to get wrong.\n out.ignoredScalarHints.push({\n table: subquery.table,\n uniqueKeys: tableSpecs.get(subquery.table)?.tableSpec.uniqueKeys ?? [],\n });\n // Return with the (possibly partially-resolved) subquery.\n if (subquery !== condition.related.subquery) {\n return {\n ...condition,\n related: {...condition.related, subquery},\n };\n }\n return condition;\n }\n\n const value = execute(subquery, childField);\n\n // Record the companion subquery AST so its rows are synced to the client.\n // The client rewrites scalar subqueries to EXISTS and needs those rows.\n out.companions.push({\n ast: subquery,\n childField,\n resolvedValue: value,\n });\n\n if (value === undefined || value === null) {\n // No row matched, or the matched row's `childField` is NULL (which can\n // never satisfy the correlation, since `parentField = NULL` is never\n // true). Either way the correlated EXISTS is false for *every* parent\n // row, so the gate collapses to a constant: false for EXISTS, and its\n // negation — true — for NOT EXISTS.\n return condition.op === 'EXISTS' ? ALWAYS_FALSE : ALWAYS_TRUE;\n }\n\n const op = condition.op === 'EXISTS' ? '=' : 'IS NOT';\n return {\n type: 'simple',\n op,\n left: {type: 'column', name: parentField},\n right: {type: 'literal', value},\n } satisfies SimpleCondition;\n}\n\nconst ALWAYS_FALSE: SimpleCondition = {\n type: 'simple',\n op: '=',\n left: {type: 'literal', value: 1},\n right: {type: 'literal', value: 0},\n};\n\nconst ALWAYS_TRUE: SimpleCondition = {\n type: 'simple',\n op: '=',\n left: {type: 'literal', value: 1},\n right: {type: 'literal', value: 1},\n};\n\n/**\n * Checks if the subquery is guaranteed to return at most one deterministic row.\n *\n * This is true when all columns of at least one unique index on the subquery\n * table are equality-constrained by literal values in the WHERE clause\n * (using only AND conjunctions).\n */\nexport function isSimpleSubquery(\n subquery: AST,\n tableSpecs: Map<string, TableSpecWithUniqueKeys>,\n): boolean {\n const spec = tableSpecs.get(subquery.table);\n if (!spec) {\n return false;\n }\n\n if (!subquery.where) {\n return false;\n }\n\n const constraints = extractLiteralEqualityConstraints(subquery.where);\n if (constraints.size === 0) {\n return false;\n }\n\n const {uniqueKeys} = spec.tableSpec;\n return uniqueKeys.some(key => key.every(col => constraints.has(col)));\n}\n\n/**\n * Extracts column=literal equality constraints from a condition tree,\n * only following AND conjunctions (not OR).\n */\nexport function extractLiteralEqualityConstraints(\n condition: Condition,\n): Map<string, LiteralValue> {\n const constraints = new Map<string, LiteralValue>();\n collectConstraints(condition, constraints);\n return constraints;\n}\n\nfunction collectConstraints(\n condition: Condition,\n constraints: Map<string, LiteralValue>,\n): void {\n switch (condition.type) {\n case 'simple':\n if (condition.op === '=') {\n if (\n condition.left.type === 'column' &&\n condition.right.type === 'literal'\n ) {\n constraints.set(condition.left.name, condition.right.value);\n }\n }\n break;\n case 'and':\n for (const c of condition.conditions) {\n collectConstraints(c, constraints);\n }\n break;\n // OR, correlatedSubquery (non-scalar) — don't contribute constraints\n default:\n break;\n }\n}\n"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import { must } from "../../shared/src/must.js";
|
|
|
3
3
|
import { buildSelectQuery } from "./query-builder.js";
|
|
4
4
|
import { compileInline } from "./internal/sql-inline.js";
|
|
5
5
|
import { SQLiteStatFanout } from "./sqlite-stat-fanout.js";
|
|
6
|
-
import
|
|
6
|
+
import SQLite3Database from "@rocicorp/zero-sqlite3";
|
|
7
7
|
//#region ../zqlite/src/sqlite-cost-model.ts
|
|
8
8
|
/**
|
|
9
9
|
* Creates a SQLite-based cost model for query planning.
|
|
@@ -66,13 +66,13 @@ function removeCorrelatedSubqueries(condition) {
|
|
|
66
66
|
function getScanstatusLoops(stmt) {
|
|
67
67
|
const loops = [];
|
|
68
68
|
for (let idx = 0;; idx++) {
|
|
69
|
-
const selectId = stmt.scanStatus(idx,
|
|
69
|
+
const selectId = stmt.scanStatus(idx, SQLite3Database.SQLITE_SCANSTAT_SELECTID, 1);
|
|
70
70
|
if (selectId === void 0) break;
|
|
71
71
|
loops.push({
|
|
72
72
|
selectId: must(selectId),
|
|
73
|
-
parentId: must(stmt.scanStatus(idx,
|
|
74
|
-
explain: must(stmt.scanStatus(idx,
|
|
75
|
-
est: must(stmt.scanStatus(idx,
|
|
73
|
+
parentId: must(stmt.scanStatus(idx, SQLite3Database.SQLITE_SCANSTAT_PARENTID, 1)),
|
|
74
|
+
explain: must(stmt.scanStatus(idx, SQLite3Database.SQLITE_SCANSTAT_EXPLAIN, 1)),
|
|
75
|
+
est: must(stmt.scanStatus(idx, SQLite3Database.SQLITE_SCANSTAT_EST, 1))
|
|
76
76
|
});
|
|
77
77
|
}
|
|
78
78
|
return loops.sort((a, b) => a.selectId - b.selectId);
|
|
@@ -8,7 +8,7 @@ import { timeSampled } from "../../otel/src/maybe-time.js";
|
|
|
8
8
|
import { compile, format, sql } from "./internal/sql.js";
|
|
9
9
|
import { StatementCache } from "./internal/statement-cache.js";
|
|
10
10
|
import { buildSelectQuery, toSQLiteType } from "./query-builder.js";
|
|
11
|
-
import
|
|
11
|
+
import SQLite3Database from "@rocicorp/zero-sqlite3";
|
|
12
12
|
//#region ../zqlite/src/table-source.ts
|
|
13
13
|
var eventCount = 0;
|
|
14
14
|
/**
|
|
@@ -154,10 +154,10 @@ var TableSource = class {
|
|
|
154
154
|
let totalNvisit = 0;
|
|
155
155
|
const planLines = [];
|
|
156
156
|
for (let i = 0;; i++) {
|
|
157
|
-
const nvisit = cachedStatement.statement.scanStatus(i,
|
|
157
|
+
const nvisit = cachedStatement.statement.scanStatus(i, SQLite3Database.SQLITE_SCANSTAT_NVISIT, 1);
|
|
158
158
|
if (nvisit === void 0) break;
|
|
159
159
|
totalNvisit += Number(nvisit);
|
|
160
|
-
const explain = cachedStatement.statement.scanStatus(i,
|
|
160
|
+
const explain = cachedStatement.statement.scanStatus(i, SQLite3Database.SQLITE_SCANSTAT_EXPLAIN, 1);
|
|
161
161
|
if (typeof explain === "string" && explain.length > 0) planLines.push(explain);
|
|
162
162
|
}
|
|
163
163
|
if (totalNvisit !== 0) debug.recordNVisit(this.#table, sqlAndBindings.text, totalNvisit);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rocicorp/zero",
|
|
3
|
-
"version": "1.10.0-canary.
|
|
3
|
+
"version": "1.10.0-canary.2",
|
|
4
4
|
"description": "Zero is a web framework for serverless web development.",
|
|
5
5
|
"homepage": "https://zero.rocicorp.dev",
|
|
6
6
|
"bugs": {
|
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
"@rocicorp/lock": "^1.0.4",
|
|
142
142
|
"@rocicorp/logger": "^6.1.0",
|
|
143
143
|
"@rocicorp/resolver": "^1.0.2",
|
|
144
|
-
"@rocicorp/zero-sqlite3": "^1.1.
|
|
144
|
+
"@rocicorp/zero-sqlite3": "^1.1.4",
|
|
145
145
|
"@standard-schema/spec": "^1.0.0",
|
|
146
146
|
"@types/basic-auth": "^1.1.8",
|
|
147
147
|
"@types/ws": "^8.5.12",
|
|
@@ -196,5 +196,5 @@
|
|
|
196
196
|
"engines": {
|
|
197
197
|
"node": ">=22"
|
|
198
198
|
},
|
|
199
|
-
"packageManager": "pnpm@11.
|
|
199
|
+
"packageManager": "pnpm@11.11.0"
|
|
200
200
|
}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
//#region ../replicache/src/db/index.ts
|
|
2
|
-
var IndexRead = class {
|
|
3
|
-
meta;
|
|
4
|
-
map;
|
|
5
|
-
constructor(meta, map) {
|
|
6
|
-
this.meta = meta;
|
|
7
|
-
this.map = map;
|
|
8
|
-
}
|
|
9
|
-
};
|
|
10
|
-
var IndexWrite = class extends IndexRead {
|
|
11
|
-
flush() {
|
|
12
|
-
return this.map.flush();
|
|
13
|
-
}
|
|
14
|
-
clear() {
|
|
15
|
-
return this.map.clear();
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
async function indexValue(lc, index, op, key, val, jsonPointer, allowEmpty) {
|
|
19
|
-
try {
|
|
20
|
-
for (const entry of getIndexKeys(key, val, jsonPointer, allowEmpty)) switch (op) {
|
|
21
|
-
case 0:
|
|
22
|
-
await index.put(entry, val);
|
|
23
|
-
break;
|
|
24
|
-
case 1:
|
|
25
|
-
await index.del(entry);
|
|
26
|
-
break;
|
|
27
|
-
}
|
|
28
|
-
} catch (e) {
|
|
29
|
-
lc.info?.("Not indexing value", val, ":", e);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
function getIndexKeys(primary, value, jsonPointer, allowEmpty) {
|
|
33
|
-
const target = evaluateJSONPointer(value, jsonPointer);
|
|
34
|
-
if (target === void 0) {
|
|
35
|
-
if (allowEmpty) return [];
|
|
36
|
-
throw new Error(`No value at path: ${jsonPointer}`);
|
|
37
|
-
}
|
|
38
|
-
const values = Array.isArray(target) ? target : [target];
|
|
39
|
-
const indexKeys = [];
|
|
40
|
-
for (const value of values) if (typeof value === "string") indexKeys.push(encodeIndexKey([value, primary]));
|
|
41
|
-
else throw new Error("Unsupported target type");
|
|
42
|
-
return indexKeys;
|
|
43
|
-
}
|
|
44
|
-
function encodeIndexKey(indexKey) {
|
|
45
|
-
const secondary = indexKey[0];
|
|
46
|
-
const primary = indexKey[1];
|
|
47
|
-
if (secondary.includes("\0")) throw new Error("Secondary key cannot contain null byte");
|
|
48
|
-
return "\0" + secondary + "\0" + primary;
|
|
49
|
-
}
|
|
50
|
-
function encodeIndexScanKey(secondary, primary) {
|
|
51
|
-
const k = encodeIndexKey([secondary, primary || ""]);
|
|
52
|
-
if (primary === void 0) return k.slice(0, k.length - 1);
|
|
53
|
-
return k;
|
|
54
|
-
}
|
|
55
|
-
function decodeIndexKey(encodedIndexKey) {
|
|
56
|
-
if (encodedIndexKey[0] !== "\0") throw new Error("Invalid version");
|
|
57
|
-
const versionLen = 1;
|
|
58
|
-
const separatorLen = 1;
|
|
59
|
-
const separatorOffset = encodedIndexKey.indexOf("\0", versionLen);
|
|
60
|
-
if (separatorOffset === -1) throw new Error("Invalid formatting");
|
|
61
|
-
return [encodedIndexKey.slice(versionLen, separatorOffset), encodedIndexKey.slice(separatorOffset + separatorLen)];
|
|
62
|
-
}
|
|
63
|
-
function evaluateJSONPointer(value, pointer) {
|
|
64
|
-
function parseIndex(s) {
|
|
65
|
-
if (s.startsWith("+") || s.startsWith("0") && s.length !== 1) return;
|
|
66
|
-
return parseInt(s, 10);
|
|
67
|
-
}
|
|
68
|
-
if (pointer === "") return value;
|
|
69
|
-
if (!pointer.startsWith("/")) throw new Error(`Invalid JSON pointer: ${pointer}`);
|
|
70
|
-
const tokens = pointer.split("/").slice(1).map((x) => x.replace(/~1/g, "/").replace(/~0/g, "~"));
|
|
71
|
-
let target = value;
|
|
72
|
-
for (const token of tokens) {
|
|
73
|
-
let targetOpt;
|
|
74
|
-
if (Array.isArray(target)) {
|
|
75
|
-
const i = parseIndex(token);
|
|
76
|
-
if (i === void 0) return;
|
|
77
|
-
targetOpt = target[i];
|
|
78
|
-
} else if (target === null) return;
|
|
79
|
-
else if (typeof target === "object") {
|
|
80
|
-
target = target;
|
|
81
|
-
targetOpt = target[token];
|
|
82
|
-
}
|
|
83
|
-
if (targetOpt === void 0) return;
|
|
84
|
-
target = targetOpt;
|
|
85
|
-
}
|
|
86
|
-
return target;
|
|
87
|
-
}
|
|
88
|
-
//#endregion
|
|
89
|
-
export { IndexRead, IndexWrite, decodeIndexKey, encodeIndexScanKey, indexValue };
|
|
90
|
-
|
|
91
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../../replicache/src/db/index.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport type {BTreeRead} from '../btree/read.ts';\nimport type {BTreeWrite} from '../btree/write.ts';\nimport type {FrozenJSONObject, FrozenJSONValue} from '../frozen-json.ts';\nimport type {Hash} from '../hash.ts';\nimport type {IndexRecord} from './commit.ts';\nimport * as IndexOperation from './index-operation-enum.ts';\n\ntype IndexOperation = Enum<typeof IndexOperation>;\n\nexport class IndexRead<BTree = BTreeRead> {\n readonly meta: IndexRecord;\n readonly map: BTree;\n\n constructor(meta: IndexRecord, map: BTree) {\n this.meta = meta;\n this.map = map;\n }\n}\n\nexport class IndexWrite extends IndexRead<BTreeWrite> {\n // Note: does not update self.meta.valueHash (doesn't need to at this point as flush\n // is only called during commit.)\n flush(): Promise<Hash> {\n return this.map.flush();\n }\n\n clear(): Promise<void> {\n return this.map.clear();\n }\n}\n\n// Index or de-index a single primary entry.\nexport async function indexValue(\n lc: LogContext,\n index: BTreeWrite,\n op: IndexOperation,\n key: string,\n val: FrozenJSONValue,\n jsonPointer: string,\n allowEmpty: boolean,\n): Promise<void> {\n try {\n for (const entry of getIndexKeys(key, val, jsonPointer, allowEmpty)) {\n switch (op) {\n case IndexOperation.Add:\n await index.put(entry, val);\n break;\n case IndexOperation.Remove:\n await index.del(entry);\n break;\n }\n }\n } catch (e) {\n // Right now all the errors that index_value() returns are customers dev\n // problems: either the value is not json, the pointer is into nowhere, etc.\n // So we ignore them.\n lc.info?.('Not indexing value', val, ':', e);\n }\n}\n\n// Gets the set of index keys for a given primary key and value.\nexport function getIndexKeys(\n primary: string,\n value: FrozenJSONValue,\n jsonPointer: string,\n allowEmpty: boolean,\n): string[] {\n const target = evaluateJSONPointer(value, jsonPointer);\n if (target === undefined) {\n if (allowEmpty) {\n return [];\n }\n throw new Error(`No value at path: ${jsonPointer}`);\n }\n\n const values = Array.isArray(target) ? target : [target];\n\n const indexKeys: string[] = [];\n for (const value of values) {\n if (typeof value === 'string') {\n indexKeys.push(encodeIndexKey([value, primary]));\n } else {\n throw new Error('Unsupported target type');\n }\n }\n\n return indexKeys;\n}\n\nexport const KEY_VERSION_0 = '\\u0000';\nexport const KEY_SEPARATOR = '\\u0000';\n\n/**\n * When using indexes the key is a tuple of the secondary key and the primary\n * key.\n */\nexport type IndexKey = readonly [secondary: string, primary: string];\n\n// An index key is encoded to vec of bytes in the following order:\n// - key version byte(s), followed by\n// - the secondary key bytes (which for now is a UTF8 encoded string), followed by\n// - the key separator, a null byte, followed by\n// - the primary key bytes\n//\n// The null separator byte ensures that if a secondary key A is longer than B then\n// A always sorts after B. Appending the primary key ensures index keys with\n// identical secondary keys sort in primary key order. Secondary keys must not\n// contain a zero (null) byte.\nexport function encodeIndexKey(indexKey: IndexKey): string {\n const secondary = indexKey[0];\n const primary = indexKey[1];\n\n if (secondary.includes('\\u0000')) {\n throw new Error('Secondary key cannot contain null byte');\n }\n return KEY_VERSION_0 + secondary + KEY_SEPARATOR + primary;\n}\n\n// Returns bytes that can be used to scan for the given secondary index value.\n//\n// Consider a scan for start_secondary_key=\"a\" (97). We want to scan with scan\n// key [0, 97]. We could also scan with [0, 97, 0], but then we couldn't use\n// this function for prefix scans, so we lop off the null byte. If we want\n// the scan to be exclusive, we scan with the next greater value, [0, 97, 1]\n// (we disallow zero bytes in secondary keys).\n//\n// Now it gets a little tricky. We also want to be able to scan using the\n// primary key, start_key. When we do this we have to encode the scan key\n// a little differently We essentially have to fix the value of the\n// secondary key so we can vary the start_key. That is, the match on\n// start_secondary_key becomes an exact match.\n//\n// Consider the scan for start_secondary_key=\"a\" and start_key=[2]. We want\n// to scan with [0, 97, 0, 2]. If we want exclusive we want to scan with\n// the next highest value, [0, 97, 0, 2, 0] (zero bytes are allowed in primary\n// keys). So far so good. It is important to notice that we need to\n// be able to distinguish between not wanting use start_key and wanting to\n// use start_key=[]. In the former case we want to scan with the secondary\n// key value, possibly followed by a 1 with no trailing zero byte ([0, 97]\n// or [0, 97, 1]). In the latter case we want to scan by the secondary\n// key value, followed by the zero byte, followed by the primary key value\n// and another zero if it is exclusive ([0, 97, 0] or [0, 97, 0, 0]).\n// This explains why we need the Option around start_key.\nexport function encodeIndexScanKey(\n secondary: string,\n primary: string | undefined,\n): string {\n const k = encodeIndexKey([secondary, primary || '']);\n if (primary === undefined) {\n return k.slice(0, k.length - 1);\n }\n return k;\n}\n\n// Decodes an IndexKey encoded by encode_index_key.\nexport function decodeIndexKey(encodedIndexKey: string): IndexKey {\n if (encodedIndexKey[0] !== KEY_VERSION_0) {\n throw new Error('Invalid version');\n }\n\n const versionLen = KEY_VERSION_0.length;\n const separatorLen = KEY_SEPARATOR.length;\n const separatorOffset = encodedIndexKey.indexOf(KEY_SEPARATOR, versionLen);\n if (separatorOffset === -1) {\n throw new Error('Invalid formatting');\n }\n\n const secondary = encodedIndexKey.slice(versionLen, separatorOffset);\n const primary = encodedIndexKey.slice(separatorOffset + separatorLen);\n return [secondary, primary];\n}\n\nexport function evaluateJSONPointer(\n value: FrozenJSONValue,\n pointer: string,\n): FrozenJSONValue | undefined {\n function parseIndex(s: string): number | undefined {\n if (s.startsWith('+') || (s.startsWith('0') && s.length !== 1)) {\n return undefined;\n }\n return parseInt(s, 10);\n }\n\n if (pointer === '') {\n return value;\n }\n if (!pointer.startsWith('/')) {\n throw new Error(`Invalid JSON pointer: ${pointer}`);\n }\n\n const tokens = pointer\n .split('/')\n .slice(1)\n .map(x => x.replace(/~1/g, '/').replace(/~0/g, '~'));\n\n let target = value;\n for (const token of tokens) {\n let targetOpt;\n if (Array.isArray(target)) {\n const i = parseIndex(token);\n if (i === undefined) {\n return undefined;\n }\n targetOpt = target[i];\n } else if (target === null) {\n return undefined;\n } else if (typeof target === 'object') {\n target = target as FrozenJSONObject;\n targetOpt = target[token];\n }\n if (targetOpt === undefined) {\n return undefined;\n }\n target = targetOpt;\n }\n return target;\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lazy.d.ts","sourceRoot":"","sources":["../../../../replicache/src/lazy.ts"],"names":[],"mappings":"AAAA,wBAAgB,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,CAQjD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lazy.js","names":[],"sources":["../../../../replicache/src/lazy.ts"],"sourcesContent":["export function lazy<T>(factory: () => T): () => T {\n let value: T | undefined;\n return () => {\n if (value === undefined) {\n value = factory();\n }\n return value;\n };\n}\n"],"mappings":";AAAA,SAAgB,KAAQ,SAA2B;CACjD,IAAI;CACJ,aAAa;EACX,IAAI,UAAU,KAAA,GACZ,QAAQ,QAAQ;EAElB,OAAO;CACT;AACF"}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { must } from "../../../shared/src/must.js";
|
|
2
|
-
import { parentWorker, shouldStartWorker, singleProcessMode } from "../types/processes.js";
|
|
3
|
-
import { getZeroConfig } from "../config/zero-config.js";
|
|
4
|
-
import { exitAfter, runUntilKilled } from "../services/life-cycle.js";
|
|
5
|
-
import { createLogContext } from "./logging.js";
|
|
6
|
-
import { VfsBackupWatermarkReader, buildLitestreamVfsReplicaURL } from "../services/litestream/vfs-watermark-reader.js";
|
|
7
|
-
import { VfsBackupWatermarkWorkerService } from "../services/litestream/vfs-watermark-worker.js";
|
|
8
|
-
import { LogContext, consoleLogSink } from "@rocicorp/logger";
|
|
9
|
-
//#region ../zero-cache/src/server/backup-watermark-reader.ts
|
|
10
|
-
var lc = new LogContext("info", {}, consoleLogSink);
|
|
11
|
-
async function runWorker(parent, env, ...argv) {
|
|
12
|
-
const config = getZeroConfig({
|
|
13
|
-
env,
|
|
14
|
-
argv
|
|
15
|
-
});
|
|
16
|
-
lc = createLogContext(config, "backup-watermark-reader");
|
|
17
|
-
const { litestream } = config;
|
|
18
|
-
const replicaURL = buildLitestreamVfsReplicaURL({
|
|
19
|
-
backupURL: must(litestream.backupURL, "Missing --litestream-backup-url for backup watermark reader"),
|
|
20
|
-
endpoint: litestream.endpoint,
|
|
21
|
-
region: litestream.region
|
|
22
|
-
});
|
|
23
|
-
const createReader = () => new VfsBackupWatermarkReader(lc, {
|
|
24
|
-
replicaURL,
|
|
25
|
-
extensionPath: litestream.vfsExtensionPath,
|
|
26
|
-
logLevel: litestream.logLevel,
|
|
27
|
-
logFile: litestream.vfsLogFile
|
|
28
|
-
});
|
|
29
|
-
await runUntilKilled(lc, parent ?? process, new VfsBackupWatermarkWorkerService(lc, parent, createReader, litestream.vfsProbeIntervalMs));
|
|
30
|
-
}
|
|
31
|
-
if (shouldStartWorker(parentWorker, singleProcessMode())) exitAfter(() => lc, () => runWorker(parentWorker, process.env, ...process.argv.slice(2)));
|
|
32
|
-
//#endregion
|
|
33
|
-
export { runWorker as default };
|
|
34
|
-
|
|
35
|
-
//# sourceMappingURL=backup-watermark-reader.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"backup-watermark-reader.js","names":[],"sources":["../../../../../zero-cache/src/server/backup-watermark-reader.ts"],"sourcesContent":["import {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {must} from '../../../shared/src/must.ts';\nimport {getZeroConfig} from '../config/zero-config.ts';\nimport {exitAfter, runUntilKilled} from '../services/life-cycle.ts';\nimport {\n buildLitestreamVfsReplicaURL,\n VfsBackupWatermarkReader,\n} from '../services/litestream/vfs-watermark-reader.ts';\nimport {VfsBackupWatermarkWorkerService} from '../services/litestream/vfs-watermark-worker.ts';\nimport {\n parentWorker,\n shouldStartWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {createLogContext} from './logging.ts';\n\nlet lc = new LogContext('info', {}, consoleLogSink);\n\nexport default async function runWorker(\n parent: Worker | null,\n env: NodeJS.ProcessEnv,\n ...argv: string[]\n): Promise<void> {\n const config = getZeroConfig({env, argv});\n lc = createLogContext(config, 'backup-watermark-reader');\n const {litestream} = config;\n\n const replicaURL = buildLitestreamVfsReplicaURL({\n backupURL: must(\n litestream.backupURL,\n 'Missing --litestream-backup-url for backup watermark reader',\n ),\n endpoint: litestream.endpoint,\n region: litestream.region,\n });\n\n const createReader = () =>\n new VfsBackupWatermarkReader(lc, {\n replicaURL,\n extensionPath: litestream.vfsExtensionPath,\n logLevel: litestream.logLevel,\n logFile: litestream.vfsLogFile,\n });\n\n await runUntilKilled(\n lc,\n parent ?? process,\n new VfsBackupWatermarkWorkerService(\n lc,\n parent,\n createReader,\n litestream.vfsProbeIntervalMs,\n ),\n );\n}\n\n// Unlike the other workers, the backup watermark reader is *always* run as its\n// own OS process — forked on demand by the `VfsBackupMonitor` (via\n// `forkChildWorker`), or launched directly as a standalone debug tool — so that\n// the native Litestream VFS extension and its process-global `LITESTREAM_*` env\n// stay isolated from the rest of zero-cache. See `shouldStartWorker`.\nif (shouldStartWorker(parentWorker, singleProcessMode())) {\n void exitAfter(\n () => lc,\n () => runWorker(parentWorker, process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":";;;;;;;;;AAiBA,IAAI,KAAK,IAAI,WAAW,QAAQ,CAAC,GAAG,cAAc;AAElD,eAA8B,UAC5B,QACA,KACA,GAAG,MACY;CACf,MAAM,SAAS,cAAc;EAAC;EAAK;CAAI,CAAC;CACxC,KAAK,iBAAiB,QAAQ,yBAAyB;CACvD,MAAM,EAAC,eAAc;CAErB,MAAM,aAAa,6BAA6B;EAC9C,WAAW,KACT,WAAW,WACX,6DACF;EACA,UAAU,WAAW;EACrB,QAAQ,WAAW;CACrB,CAAC;CAED,MAAM,qBACJ,IAAI,yBAAyB,IAAI;EAC/B;EACA,eAAe,WAAW;EAC1B,UAAU,WAAW;EACrB,SAAS,WAAW;CACtB,CAAC;CAEH,MAAM,eACJ,IACA,UAAU,SACV,IAAI,gCACF,IACA,QACA,cACA,WAAW,kBACb,CACF;AACF;AAOA,IAAI,kBAAkB,cAAc,kBAAkB,CAAC,GACrD,gBACQ,UACA,UAAU,cAAc,QAAQ,KAAK,GAAG,QAAQ,KAAK,MAAM,CAAC,CAAC,CACrE"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"litestream3-backup-monitor.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/litestream3-backup-monitor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAWjD,OAAO,EAAC,YAAY,EAAC,MAAM,6BAA6B,CAAC;AAOzD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,sBAAsB,CAAC;AAChE,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,eAAe,CAAC;AAEnD,eAAO,MAAM,iBAAiB,QAAS,CAAC;AAGxC;;;GAGG;AACH,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,wBAAwB,QAAc,CAAC;AAIpD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,gCAAgC,QAAc,CAAC;AAG5D;;;;GAIG;AACH,eAAO,MAAM,kCAAkC,QAAS,CAAC;AAEzD;;;;;;;GAOG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AAOtD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,qBAAa,wBAAyB,YAAW,aAAa;;IAC5D,QAAQ,CAAC,EAAE,oBAAoB;gBA8C7B,EAAE,EAAE,UAAU,EACd,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,qBAAqB,EACrC,qBAAqB,EAAE,MAAM,EAC7B,iBAAiB,EAAE,mBAAmB;IAkBxC,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAepB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAAC,eAAe,CAAC;IAgFvE,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,kBAAkB,UAAO;IAwBxD,QAAQ,CAAC,iCAAiC,sBAgBxC;IA6RF,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAkDtB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"litestream3-backup-monitor.js","names":["#lc","#replicaFile","#backupURL","#metricsEndpoint","#changeStreamer","#state","#reservations","#watermarks","#verifyBackupState","#purgesBlocked","#fatal","#cleanupDelayMs","#runStartTime","#checkMetricsTimer","#initBackupLagMetric","#pushStatusWhenRestorable","#lastVerifiedUploadTime","#confirmRestorableBackup","#verifyBackupStateTimed","#checkWatermarks","#scheduleCleanup","#checkInitialBackupDeadline","#fetchWatermarks","#lastWatermark","#latestBackupTime","#maxWatermarkUpTo","#confirmedDurable","#backupStaleSince","#shutDownOnWedgedBackup","#initialBackupGraceMs","#shutDownOnMissingInitialBackup"],"sources":["../../../../../../zero-cache/src/services/change-streamer/litestream3-backup-monitor.ts"],"sourcesContent":["import {statSync} from 'node:fs';\nimport type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport parsePrometheusTextFormat from 'parse-prometheus-text-format';\nimport {must} from '../../../../shared/src/must.ts';\nimport {promiseVoid} from '../../../../shared/src/resolved-promises.ts';\nimport {sleep} from '../../../../shared/src/sleep.ts';\nimport {Database} from '../../../../zqlite/src/db.ts';\nimport {\n getOrCreateCounter,\n getOrCreateGauge,\n} from '../../observability/metrics.ts';\nimport {Subscription} from '../../types/subscription.ts';\nimport {\n litestreamBackupVerificationDuration,\n litestreamMonitorMetricAttrs,\n litestreamSnapshotReservationDuration,\n} from '../litestream/metrics.ts';\nimport {RunningState, UnrecoverableError} from '../running-state.ts';\nimport type {BackupMonitor} from './backup-monitor.ts';\nimport type {ChangeStreamerService} from './change-streamer.ts';\nimport type {SnapshotMessage} from './snapshot.ts';\n\nexport const CHECK_INTERVAL_MS = 60_000;\nconst MIN_CLEANUP_DELAY_MS = 30_000;\n\n/**\n * Allowance for clock skew between the machine reporting litestream metrics\n * and the timestamps reported by the backup destination (e.g. S3).\n */\nexport const BACKUP_VERIFICATION_SLACK_MS = 60_000;\n\n/**\n * How long the actual backup state may remain *continuously* behind the\n * backup progress claimed by litestream metrics before the backup is\n * considered genuinely wedged and the process is shut down.\n *\n * A wedged backup is dangerous: litestream believes it is making durable\n * progress when it is not, which can silently corrupt the backup (the WAL\n * format only makes *some* such gaps detectable). Once that is confirmed to\n * be the persistent state, continuing to run is worse than crashing, so the\n * process exits loudly (non-zero, with a logged error) and the wedged backup\n * destination becomes the priority to investigate.\n *\n * The change-log is conservatively *not* purged for the entire time the\n * backup is stale, so the only cost of a generous grace period is unbounded\n * change-log growth. We therefore err well on the side of slack: the backup\n * must stay stuck across many {@link CHECK_INTERVAL_MS} checks before we tear\n * the server down, so that a transient hiccup (a slow or briefly unreachable\n * destination, or litestream restarting) does not trigger a shutdown. The\n * staleness clock is reset the moment a purge is confirmed against a real\n * upload.\n */\nexport const WEDGED_SHUTDOWN_GRACE_MS = 15 * 60_000; // 15 minutes\n\nconst GiB = 1024 ** 3;\n\n/**\n * How long the BackupMonitor waits for the *first* restorable backup to appear\n * before concluding that the backup pipeline is broken and shutting the process\n * down (see {@link BackupMonitor.#shutDownOnMissingInitialBackup}).\n *\n * On a fresh stack the first backup is not restorable until the initial\n * Postgres->replica sync completes and litestream finishes uploading the\n * initial snapshot — a multipart upload that is not listable in the destination\n * until it is committed. The producer cannot observe that sub-snapshot progress,\n * so this is necessarily a generous timeout. It is scaled by replica size to\n * mirror the platform's storage-scaled startup-probe allowance (~1 hour per\n * 50GB) so that it never fires before the platform would restart a view-syncer\n * that is waiting on the backup.\n */\nexport const INITIAL_BACKUP_GRACE_MS_PER_UNIT = 60 * 60_000; // 1 hour ...\nconst INITIAL_BACKUP_GRACE_UNIT_BYTES = 50 * GiB; // ... per 50 GiB\n\n/**\n * How often the BackupMonitor re-checks whether the first restorable backup has\n * appeared while a view-syncer holds a snapshot reservation open during a cold\n * start.\n */\nexport const RESTORABLE_BACKUP_POLL_INTERVAL_MS = 10_000;\n\n/**\n * Returns the time of the most recent object actually uploaded to the\n * backup replica destination (e.g. as determined by listing the snapshots\n * and WAL segments in S3). Rejects if the backup state cannot be determined.\n *\n * See `getLastBackupTime()` in `../litestream/commands.ts` for the\n * production implementation.\n */\nexport type BackupStateVerifier = () => Promise<Date>;\n\ntype Reservation = {\n start: Date;\n sub: Subscription<SnapshotMessage>;\n};\n\n/**\n * The Litestream3BackupMonitor polls the litestream \"/metrics\" endpoint to\n * track the watermark (label) value of the `litestream_replica_progress` gauge\n * and schedules cleanup of change log entries that can be purged as a result.\n *\n * See: https://github.com/rocicorp/litestream/pull/3\n *\n * Note that change log entries cannot simply be purged as soon as they\n * have been applied and backed up by litestream. Consider the case in which\n * litestream backs up new wal segments every minute, but it takes 5 minutes\n * to restore a replica: if a zero-cache starts restoring a replica at\n * minute 0, and new watermarks are replicated at minutes 1, 2, 3, 4, and 5,\n * purging changelog records as soon as those watermarks are replicated would\n * result in the zero-cache not being able to catch up from minute 0 once it\n * has finished restoring the replica.\n *\n * The `/snapshot` reservation protocol is used to prevent premature change\n * log cleanup:\n * - Clients restoring a snapshot initiate a `/snapshot` request and hold that\n * request open while it restores its snapshot, prepares it, and\n * starts its subscription to the change stream. During this time, no\n * cleanups are scheduled.\n * - When the subscription is started, the interval since the beginning of\n * of the reservation is tracked to increase the background cleanup delay\n * interval if needed. The reservation is ended (and request closed), and\n * cleanup scheduling is resumed with the current delay interval.\n *\n * Note that the reservation request is the primary mechanism by which\n * premature change log cleanup is prevented. The cleanup delay interval is\n * a secondary safeguard.\n *\n * Additionally, because the watermarks reported by litestream metrics\n * reflect what litestream *believes* has been backed up (which has been\n * observed to diverge from reality when uploads silently fail), the cleanup\n * watermark is only advanced after verifying it against the actual backup\n * state in the replica destination via a {@link BackupStateVerifier}. If the\n * actual backup state stays behind the claimed progress for longer than\n * {@link WEDGED_SHUTDOWN_GRACE_MS}, the backup is treated as wedged and the\n * process is shut down rather than risk corrupting the backup.\n */\nexport class Litestream3BackupMonitor implements BackupMonitor {\n readonly id = 'backup-monitor';\n readonly #lc: LogContext;\n readonly #replicaFile: string;\n readonly #backupURL: string;\n readonly #metricsEndpoint: string;\n readonly #changeStreamer: ChangeStreamerService;\n readonly #state = new RunningState(this.id);\n\n readonly #reservations = new Map<string, Reservation>();\n readonly #watermarks = new Map<string, Date>();\n\n readonly #verifyBackupState: BackupStateVerifier;\n readonly #purgesBlocked = getOrCreateCounter('replica', 'purge_blocked', {\n description:\n 'Number of change-log purges blocked because the actual backup state ' +\n '(as listed from the replica destination) could not be verified, or ' +\n 'is older than the backup progress claimed by litestream metrics. ' +\n 'A steadily increasing value indicates a wedged or failing backup. ' +\n 'The \"backup-wedged\" reason is emitted once just before the process ' +\n 'shuts itself down due to a persistently stale backup.',\n });\n\n // Rejected when the backup is determined to be genuinely wedged, which\n // surfaces as a rejection of the `run()` promise and shuts the process down.\n readonly #fatal = resolver<void>();\n\n #lastWatermark: string = '';\n #latestBackupTime: Date | null = null;\n // The time of the most recent object actually verified in the backup\n // destination, or `null` if none has ever been verified. Only assigned from a\n // successful verifyBackupState() and never cleared, so a non-null value also\n // means a restorable backup has been confirmed to exist (which is what gates\n // the snapshot `status` signal and satisfies the initial-backup deadline).\n #lastVerifiedUploadTime: Date | null = null;\n // Epoch ms at which the actual backup state was first observed to be\n // continuously behind the watermark litestream claims to have backed up,\n // or `null` while the backup is keeping up. Reset whenever a purge is\n // confirmed against a real upload.\n #backupStaleSince: number | null = null;\n // Epoch ms at which run() started, used to bound how long to wait for the\n // first restorable backup to appear. `null` until run() is called.\n #runStartTime: number | null = null;\n #cleanupDelayMs: number;\n #checkMetricsTimer: NodeJS.Timeout | undefined;\n\n constructor(\n lc: LogContext,\n replicaFile: string,\n backupURL: string,\n metricsEndpoint: string,\n changeStreamer: ChangeStreamerService,\n initialCleanupDelayMs: number,\n verifyBackupState: BackupStateVerifier,\n ) {\n this.#lc = lc.withContext('component', this.id);\n this.#replicaFile = replicaFile;\n this.#backupURL = backupURL;\n this.#metricsEndpoint = metricsEndpoint;\n this.#changeStreamer = changeStreamer;\n this.#verifyBackupState = verifyBackupState;\n this.#cleanupDelayMs = Math.max(\n initialCleanupDelayMs,\n MIN_CLEANUP_DELAY_MS, // purely for peace of mind\n );\n\n this.#lc.info?.(\n `backup monitor started ${initialCleanupDelayMs} ms after snapshot restore`,\n );\n }\n\n run(): Promise<void> {\n this.#runStartTime = Date.now();\n this.#lc.info?.(\n `monitoring backups at ${this.#metricsEndpoint} with ` +\n `${this.#cleanupDelayMs} ms cleanup delay`,\n );\n this.#checkMetricsTimer = setInterval(\n this.checkWatermarksAndScheduleCleanup,\n CHECK_INTERVAL_MS,\n );\n this.#initBackupLagMetric();\n // Resolves on a normal stop; rejects if the backup is found to be wedged.\n return Promise.race([this.#state.stopped(), this.#fatal.promise]);\n }\n\n startSnapshotReservation(taskID: string): Subscription<SnapshotMessage> {\n this.#lc.info?.(`pausing change-log cleanup while ${taskID} snapshots`);\n // In the case of retries, only track the last reservation.\n this.#reservations.get(taskID)?.sub.cancel();\n\n const sub = Subscription.create<SnapshotMessage>({\n // If the reservation still exists when the connection closes\n // (e.g. subscriber crashed), clean it up without updating the\n // cleanup delay.\n cleanup: () => this.endReservation(taskID, false),\n });\n this.#reservations.set(taskID, {start: new Date(), sub});\n // Note: the Subscription must be returned immediately so that the\n // websocket can begin sending liveness pings. The `status` signal\n // (which tells the view-syncer to restore) is withheld until a\n // restorable backup actually exists; see #pushStatusWhenRestorable.\n void this.#pushStatusWhenRestorable(taskID, sub);\n return sub;\n }\n\n /**\n * Pushes the `status` snapshot signal once a restorable backup is confirmed\n * to exist in the backup destination. On a warm start this is immediate; on a\n * cold start (a fresh stack whose first backup is still being produced) the\n * reservation is held open — kept alive by websocket liveness pings — and the\n * view-syncer stays unready until the first backup lands, rather than being\n * told to restore a backup that does not yet exist.\n */\n async #pushStatusWhenRestorable(\n taskID: string,\n sub: Subscription<SnapshotMessage>,\n ): Promise<void> {\n try {\n while (this.#lastVerifiedUploadTime === null) {\n if (!sub.active) {\n return; // subscriber disconnected, or a newer reservation took over\n }\n if (await this.#confirmRestorableBackup()) {\n break;\n }\n this.#lc.info?.(\n `no restorable backup at ${this.#backupURL} yet; ` +\n `holding ${taskID}'s reservation open until it lands`,\n );\n await sleep(RESTORABLE_BACKUP_POLL_INTERVAL_MS, this.#state.signal);\n }\n const changeLogState = await this.#changeStreamer.getChangeLogState();\n if (sub.active) {\n sub.push([\n 'status',\n {tag: 'status', backupURL: this.#backupURL, ...changeLogState},\n ]);\n }\n } catch (e) {\n if (this.#state.signal.aborted) {\n return; // shutting down; not an error\n }\n this.#lc.warn?.(`failing snapshot reservation`, e);\n sub.fail(e instanceof Error ? e : new Error(String(e)));\n }\n }\n\n /**\n * Verifies whether a restorable backup actually exists in the destination\n * (as opposed to what litestream metrics merely claim). On success the\n * verified upload time is cached — seeding the cleanup-verification fast path\n * ({@link #confirmedDurable}) — and the initial-backup deadline is satisfied.\n * Returns false if no backup is listable yet (e.g. the initial snapshot is\n * still uploading).\n */\n async #confirmRestorableBackup(): Promise<boolean> {\n try {\n this.#lastVerifiedUploadTime = await this.#verifyBackupStateTimed();\n return true;\n } catch (e) {\n this.#lc.info?.(`backup not yet restorable at ${this.#backupURL}`, e);\n return false;\n }\n }\n\n endReservation(taskID: string, updateCleanupDelay = true) {\n const res = this.#reservations.get(taskID);\n if (res === undefined) {\n return;\n }\n this.#reservations.delete(taskID);\n const {start, sub} = res;\n sub.cancel(); // closes the connection if still open\n const duration = Date.now() - start.getTime();\n litestreamSnapshotReservationDuration().recordMs(duration, {\n ...litestreamMonitorMetricAttrs(this.#backupURL, 'legacy', 'view_syncer'),\n result: updateCleanupDelay ? 'success' : 'cancelled',\n });\n\n if (updateCleanupDelay) {\n this.#lc.info?.(`snapshot initialized by ${taskID} in ${duration} ms`);\n if (duration > this.#cleanupDelayMs) {\n this.#cleanupDelayMs = duration;\n this.#lc.info?.(`increased cleanup delay to ${duration} ms`);\n }\n }\n }\n\n // Exported for testing\n readonly checkWatermarksAndScheduleCleanup = async () => {\n try {\n await this.#checkWatermarks();\n } catch (e) {\n this.#lc.warn?.(`unable to fetch metrics at ${this.#metricsEndpoint}`, e);\n }\n try {\n await this.#scheduleCleanup();\n } catch (e) {\n this.#lc.warn?.(`error scheduling cleanup`, e);\n }\n try {\n await this.#checkInitialBackupDeadline();\n } catch (e) {\n this.#lc.warn?.(`error checking initial backup deadline`, e);\n }\n };\n\n async *#fetchWatermarks(): AsyncGenerator<{\n watermark: string;\n time: Date;\n name?: string | undefined;\n }> {\n const metricsEndpoint = this.#metricsEndpoint;\n const signal = this.#state.signal;\n let resp;\n try {\n resp = await fetch(metricsEndpoint, {signal});\n } catch (e) {\n if (signal.aborted) {\n // not an error.\n return;\n }\n // Treat exceptions from fetch (e.g. network errors) as non-fatal, and simply\n // log them and skip the watermark check until the next interval.\n this.#lc.warn?.(`unable to fetch metrics at ${this.#metricsEndpoint}`, e);\n return;\n }\n if (!resp.ok) {\n this.#lc.warn?.(\n `unable to fetch metrics at ${this.#metricsEndpoint}: ${await resp.text()}`,\n );\n return;\n }\n\n const families = parsePrometheusTextFormat(await resp.text());\n for (const family of families) {\n if (\n family.type === 'GAUGE' &&\n family.name === 'litestream_replica_progress'\n ) {\n for (const metric of family.metrics) {\n const watermark = metric.labels?.watermark;\n const name = metric.labels?.name;\n const time = new Date(parseFloat(metric.value) * 1000);\n\n if (watermark) {\n yield {watermark, time, name};\n }\n }\n }\n }\n }\n\n async #checkWatermarks() {\n for await (const {watermark, name, time} of this.#fetchWatermarks()) {\n if (watermark > this.#lastWatermark && !this.#watermarks.has(watermark)) {\n this.#lc.info?.(\n `replicated watermark=${watermark} to ${name}` +\n ` at ${time.toISOString()}.`,\n );\n this.#watermarks.set(watermark, time);\n this.#latestBackupTime = time;\n }\n }\n return this.#latestBackupTime;\n }\n\n async #scheduleCleanup() {\n if (this.#reservations.size > 0) {\n this.#lc.info?.(\n `watermark cleanup paused for snapshot(s): ${[...this.#reservations.keys()]}`,\n );\n return;\n }\n const latestCleanupTime = Date.now() - this.#cleanupDelayMs;\n const maxWatermark = this.#maxWatermarkUpTo(latestCleanupTime);\n if (maxWatermark.length === 0) {\n return;\n }\n // Purge guard: the watermarks (and their backup times) come from\n // litestream metrics, which are exported when litestream *believes*\n // an upload succeeded, and have been observed to advance even when\n // nothing was actually written to the backup destination. Purging the\n // change-log based on a falsely advancing watermark permanently breaks\n // the ability to restore + catch up. Before advancing the cleanup\n // watermark, verify it against the actual backup state: a claimed\n // backup time is only trusted if an object was actually uploaded to\n // the replica destination at (or after) that time, modulo clock skew.\n const claimedTime = must(this.#watermarks.get(maxWatermark));\n if (!this.#confirmedDurable(claimedTime)) {\n try {\n this.#lastVerifiedUploadTime = await this.#verifyBackupStateTimed();\n } catch (e) {\n this.#purgesBlocked.add(1, {reason: 'verification-failed'});\n // Skipping the purge is safe: the change-log just grows.\n this.#lc.warn?.(\n `unable to verify backup state. skipping change-log cleanup ` +\n `up to watermark ${maxWatermark} ` +\n `(claimed backup time ${claimedTime.toISOString()})`,\n e,\n );\n return;\n }\n }\n // Watermarks whose backup time isn't yet confirmed durable remain in the\n // map and are re-evaluated at the next check.\n const lastUpload = must(this.#lastVerifiedUploadTime);\n const verifiedWatermark = this.#maxWatermarkUpTo(\n Math.min(\n latestCleanupTime,\n lastUpload.getTime() + BACKUP_VERIFICATION_SLACK_MS,\n ),\n );\n if (verifiedWatermark.length === 0) {\n this.#purgesBlocked.add(1, {reason: 'backup-stale'});\n const now = Date.now();\n if (this.#backupStaleSince === null) {\n this.#backupStaleSince = now;\n }\n const staleForMs = now - this.#backupStaleSince;\n this.#lc.warn?.(\n `blocked change-log cleanup up to watermark ${maxWatermark}: ` +\n `litestream claims it was backed up at ` +\n `${claimedTime.toISOString()}, but the last object actually ` +\n `uploaded to ${this.#backupURL} was at ` +\n `${lastUpload.toISOString()}. ` +\n `The backup has been stale for ${staleForMs} ms.`,\n );\n if (staleForMs >= WEDGED_SHUTDOWN_GRACE_MS) {\n this.#shutDownOnWedgedBackup(staleForMs, claimedTime, lastUpload);\n }\n return;\n }\n // The cleanup watermark advanced past a real upload, so the backup is\n // keeping up: clear the staleness clock.\n this.#backupStaleSince = null;\n this.#changeStreamer.scheduleCleanup(verifiedWatermark);\n for (const watermark of this.#watermarks.keys()) {\n if (watermark <= verifiedWatermark) {\n this.#watermarks.delete(watermark);\n }\n }\n this.#lastWatermark = verifiedWatermark;\n }\n\n async #verifyBackupStateTimed(): Promise<Date> {\n const start = performance.now();\n let result: 'success' | 'error' = 'error';\n try {\n const ret = await this.#verifyBackupState();\n result = 'success';\n return ret;\n } finally {\n litestreamBackupVerificationDuration().recordMs(\n performance.now() - start,\n {\n ...litestreamMonitorMetricAttrs(\n this.#backupURL,\n 'legacy',\n 'replication_manager',\n ),\n result,\n },\n );\n }\n }\n\n /**\n * Called when the backup has been continuously stale for longer than\n * {@link WEDGED_SHUTDOWN_GRACE_MS}. Continuing to run risks corrupting the\n * backup, so the process is shut down by rejecting the {@link run()}\n * promise. The exit is non-zero and logged at ERROR for alerting; on\n * restart the monitor re-verifies, so a still-wedged backup keeps the\n * process down until the destination is fixed.\n */\n #shutDownOnWedgedBackup(\n staleForMs: number,\n claimedTime: Date,\n lastUpload: Date,\n ) {\n this.#purgesBlocked.add(1, {reason: 'backup-wedged'});\n const err = new UnrecoverableError(\n `backup at ${this.#backupURL} is wedged: litestream claims a backup ` +\n `at ${claimedTime.toISOString()}, but the last object actually ` +\n `uploaded was at ${lastUpload.toISOString()}, and the backup has ` +\n `not advanced for ${staleForMs} ms (grace period ` +\n `${WEDGED_SHUTDOWN_GRACE_MS} ms). Shutting down to avoid corrupting ` +\n `the backup; investigate the backup destination.`,\n );\n this.#lc.error?.(err.message);\n clearInterval(this.#checkMetricsTimer);\n this.#fatal.reject(err);\n }\n\n /**\n * On a fresh stack, fails loudly if no restorable backup has appeared within\n * a generous, replica-size-scaled grace period (see\n * {@link INITIAL_BACKUP_GRACE_MS_PER_UNIT}). This moves the \"give up\" decision\n * onto the replication-manager — the producer responsible for creating the\n * backup — instead of leaving view-syncers to wait (and the platform to keep\n * restarting them) while the real fault is here. Does nothing once a\n * restorable backup has been confirmed.\n */\n async #checkInitialBackupDeadline() {\n if (this.#lastVerifiedUploadTime !== null || this.#runStartTime === null) {\n return;\n }\n const elapsed = Date.now() - this.#runStartTime;\n if (elapsed < this.#initialBackupGraceMs()) {\n return;\n }\n // The deadline has elapsed without a confirmed backup. Do one definitive\n // check against the destination before giving up, in case a backup landed\n // but was never exercised by a reservation or a cleanup verification.\n if (await this.#confirmRestorableBackup()) {\n return;\n }\n this.#shutDownOnMissingInitialBackup(elapsed);\n }\n\n /**\n * The grace period for the first restorable backup, scaled by replica size to\n * mirror the platform's storage-scaled startup-probe allowance. Falls back to\n * the minimum (one unit) if the replica file size cannot be determined.\n */\n #initialBackupGraceMs(): number {\n let bytes = 0;\n try {\n bytes = statSync(this.#replicaFile).size;\n } catch {\n // Replica file not present/readable yet; fall back to the minimum grace.\n }\n const units = Math.max(\n 1,\n Math.ceil(bytes / INITIAL_BACKUP_GRACE_UNIT_BYTES),\n );\n return units * INITIAL_BACKUP_GRACE_MS_PER_UNIT;\n }\n\n /**\n * Called when no restorable backup has appeared within\n * {@link #initialBackupGraceMs}. Continuing to run is pointless — no\n * view-syncer can restore — and the fault is the backup pipeline, so the\n * process exits non-zero with a logged error for alerting, mirroring\n * {@link #shutDownOnWedgedBackup}.\n */\n #shutDownOnMissingInitialBackup(elapsedMs: number) {\n const err = new UnrecoverableError(\n `no restorable backup has appeared at ${this.#backupURL} within ` +\n `${elapsedMs} ms of startup (grace period ` +\n `${this.#initialBackupGraceMs()} ms). The initial backup pipeline ` +\n `appears to be broken; shutting down so that the replication-manager ` +\n `is flagged as the cause rather than the view-syncers waiting on it. ` +\n `Investigate litestream replication to ${this.#backupURL}.`,\n );\n this.#lc.error?.(err.message);\n clearInterval(this.#checkMetricsTimer);\n this.#fatal.reject(err);\n }\n\n /**\n * Returns the newest watermark whose backup time is at or before `cutoff`\n * (epoch ms), or `''` if there is none.\n */\n #maxWatermarkUpTo(cutoff: number): string {\n let max = '';\n for (const [watermark, backupTime] of this.#watermarks) {\n if (backupTime.getTime() <= cutoff && watermark > max) {\n max = watermark;\n }\n }\n return max;\n }\n\n /**\n * Returns `true` if the actual backup state, as last verified against the\n * backup destination, confirms that data claimed to be backed up at\n * `claimedTime` is durable (i.e. an object was actually uploaded at or\n * after `claimedTime`, allowing {@link BACKUP_VERIFICATION_SLACK_MS} of\n * clock skew).\n */\n #confirmedDurable(claimedTime: Date): boolean {\n const lastUpload = this.#lastVerifiedUploadTime;\n return (\n lastUpload !== null &&\n claimedTime.getTime() <=\n lastUpload.getTime() + BACKUP_VERIFICATION_SLACK_MS\n );\n }\n\n stop(): Promise<void> {\n clearInterval(this.#checkMetricsTimer);\n for (const {sub} of this.#reservations.values()) {\n // Close any pending reservations. This commonly happens when a new\n // replication-manager makes a `/snapshot` reservation on the existing\n // replication-manager, and then shuts it down when it takes over the\n // replication slot.\n sub.cancel();\n }\n this.#state.stop(this.#lc);\n return promiseVoid;\n }\n\n #initBackupLagMetric() {\n getOrCreateGauge('replica', 'backup_lag', {\n description:\n 'Latency from when a change is written to the replica ' +\n 'to when it is backed up to litestream. It is expected to create a saw ' +\n 'pattern from 0 to the configured ZERO_LITESTREAM_INCREMENTAL_BACKUP_INTERVAL_MINUTES.',\n unit: 'millisecond',\n }).addCallback(async o => {\n // For legacy litestream, we use the watermark metric (and its associated\n // backup time) exported by litestream metrics to determine the time of\n // of the backed up watermark. This is technically imprecise--it would be\n // more correct to use the committed writeTimeMs--but it is good enough\n // in that it serves the purpose of detecting a non-functioning backup.\n // With litestream v5, this can be made more precise by querying the\n // _zero.replicationState row from the backup directly using an LTX-based\n // database reader.\n const latestBackup = await this.#checkWatermarks();\n if (!latestBackup) {\n this.#lc.warn?.(\n `no backed up watermarks. unable to report replica.backup_lag`,\n );\n return;\n }\n const db = new Database(this.#lc, this.#replicaFile, {readonly: true});\n try {\n const {writeTimeMs} = db\n .prepare(/*sql*/ `SELECT writeTimeMs FROM \"_zero.replicationState\"`)\n .get<{writeTimeMs: number}>();\n const backupLag = Math.max(0, writeTimeMs - latestBackup.getTime());\n o.observe(backupLag);\n } catch (e) {\n this.#lc.warn?.(`error measuring replica.backup_lag metric`, e);\n } finally {\n db.close();\n }\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { LogContext } from '@rocicorp/logger';
|
|
2
|
-
import type { Service } from '../service.ts';
|
|
3
|
-
import type { ChangeStreamerService } from './change-streamer.ts';
|
|
4
|
-
/**
|
|
5
|
-
* The single-node equivalent of a backup monitor polls the replica file every
|
|
6
|
-
* 30 seconds and schedules cleanup when the watermark
|
|
7
|
-
* (i.e. stateVersion) moves forward.
|
|
8
|
-
*/
|
|
9
|
-
export declare class ReplicaMonitor implements Service {
|
|
10
|
-
#private;
|
|
11
|
-
readonly id = "replica-monitor";
|
|
12
|
-
constructor(lc: LogContext, replicaFile: string, changeStreamer: ChangeStreamerService);
|
|
13
|
-
run(): Promise<void>;
|
|
14
|
-
stop(): Promise<void>;
|
|
15
|
-
}
|
|
16
|
-
//# sourceMappingURL=replica-monitor.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"replica-monitor.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/replica-monitor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAMjD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,sBAAsB,CAAC;AAIhE;;;;GAIG;AACH,qBAAa,cAAe,YAAW,OAAO;;IAC5C,QAAQ,CAAC,EAAE,qBAAqB;gBAS9B,EAAE,EAAE,UAAU,EACd,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,qBAAqB;IAOjC,GAAG;IAwBT,IAAI;CAIL"}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { promiseVoid } from "../../../../shared/src/resolved-promises.js";
|
|
2
|
-
import { Database } from "../../../../zqlite/src/db.js";
|
|
3
|
-
import { getReplicationState } from "../replicator/schema/replication-state.js";
|
|
4
|
-
import { StatementRunner } from "../../db/statements.js";
|
|
5
|
-
import { RunningState } from "../running-state.js";
|
|
6
|
-
//#region ../zero-cache/src/services/change-streamer/replica-monitor.ts
|
|
7
|
-
var CHECK_INTERVAL_MS = 30 * 1e3;
|
|
8
|
-
/**
|
|
9
|
-
* The single-node equivalent of a backup monitor polls the replica file every
|
|
10
|
-
* 30 seconds and schedules cleanup when the watermark
|
|
11
|
-
* (i.e. stateVersion) moves forward.
|
|
12
|
-
*/
|
|
13
|
-
var ReplicaMonitor = class {
|
|
14
|
-
id = "replica-monitor";
|
|
15
|
-
#lc;
|
|
16
|
-
#replicaFile;
|
|
17
|
-
#changeStreamer;
|
|
18
|
-
#state = new RunningState(this.id);
|
|
19
|
-
#lastWatermark = "";
|
|
20
|
-
constructor(lc, replicaFile, changeStreamer) {
|
|
21
|
-
this.#lc = lc.withContext("component", this.id);
|
|
22
|
-
this.#replicaFile = replicaFile;
|
|
23
|
-
this.#changeStreamer = changeStreamer;
|
|
24
|
-
}
|
|
25
|
-
async run() {
|
|
26
|
-
this.#lc.info?.(`starting replica monitor`);
|
|
27
|
-
await this.#state.sleep(CHECK_INTERVAL_MS);
|
|
28
|
-
while (this.#state.shouldRun()) {
|
|
29
|
-
const db = new Database(this.#lc, this.#replicaFile);
|
|
30
|
-
try {
|
|
31
|
-
const { stateVersion } = getReplicationState(new StatementRunner(db));
|
|
32
|
-
if (stateVersion !== this.#lastWatermark) {
|
|
33
|
-
this.#lastWatermark = stateVersion;
|
|
34
|
-
this.#lc.debug?.(`replicated up to watermark ${stateVersion}`);
|
|
35
|
-
this.#changeStreamer.scheduleCleanup(stateVersion);
|
|
36
|
-
}
|
|
37
|
-
} catch (e) {
|
|
38
|
-
this.#lc.error?.(`Unable to read watermark from replica`, e);
|
|
39
|
-
} finally {
|
|
40
|
-
db.close();
|
|
41
|
-
}
|
|
42
|
-
await this.#state.sleep(CHECK_INTERVAL_MS);
|
|
43
|
-
}
|
|
44
|
-
this.#lc.info?.(`replica monitor stopped`);
|
|
45
|
-
}
|
|
46
|
-
stop() {
|
|
47
|
-
this.#state.stop(this.#lc);
|
|
48
|
-
return promiseVoid;
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
//#endregion
|
|
52
|
-
export { ReplicaMonitor };
|
|
53
|
-
|
|
54
|
-
//# sourceMappingURL=replica-monitor.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"replica-monitor.js","names":["#lc","#replicaFile","#changeStreamer","#state","#lastWatermark"],"sources":["../../../../../../zero-cache/src/services/change-streamer/replica-monitor.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {promiseVoid} from '../../../../shared/src/resolved-promises.ts';\nimport {Database} from '../../../../zqlite/src/db.ts';\nimport {StatementRunner} from '../../db/statements.ts';\nimport {getReplicationState} from '../replicator/schema/replication-state.ts';\nimport {RunningState} from '../running-state.ts';\nimport type {Service} from '../service.ts';\nimport type {ChangeStreamerService} from './change-streamer.ts';\n\nconst CHECK_INTERVAL_MS = 30 * 1000;\n\n/**\n * The single-node equivalent of a backup monitor polls the replica file every\n * 30 seconds and schedules cleanup when the watermark\n * (i.e. stateVersion) moves forward.\n */\nexport class ReplicaMonitor implements Service {\n readonly id = 'replica-monitor';\n readonly #lc: LogContext;\n readonly #replicaFile: string;\n readonly #changeStreamer: ChangeStreamerService;\n readonly #state = new RunningState(this.id);\n\n #lastWatermark: string = '';\n\n constructor(\n lc: LogContext,\n replicaFile: string,\n changeStreamer: ChangeStreamerService,\n ) {\n this.#lc = lc.withContext('component', this.id);\n this.#replicaFile = replicaFile;\n this.#changeStreamer = changeStreamer;\n }\n\n async run() {\n this.#lc.info?.(`starting replica monitor`);\n await this.#state.sleep(CHECK_INTERVAL_MS);\n\n while (this.#state.shouldRun()) {\n const db = new Database(this.#lc, this.#replicaFile);\n try {\n const {stateVersion} = getReplicationState(new StatementRunner(db));\n if (stateVersion !== this.#lastWatermark) {\n this.#lastWatermark = stateVersion;\n this.#lc.debug?.(`replicated up to watermark ${stateVersion}`);\n this.#changeStreamer.scheduleCleanup(stateVersion);\n }\n } catch (e) {\n this.#lc.error?.(`Unable to read watermark from replica`, e);\n } finally {\n db.close();\n }\n\n await this.#state.sleep(CHECK_INTERVAL_MS);\n }\n this.#lc.info?.(`replica monitor stopped`);\n }\n\n stop() {\n this.#state.stop(this.#lc);\n return promiseVoid;\n }\n}\n"],"mappings":";;;;;;AASA,IAAM,oBAAoB,KAAK;;;;;;AAO/B,IAAa,iBAAb,MAA+C;CAC7C,KAAc;CACd;CACA;CACA;CACA,SAAkB,IAAI,aAAa,KAAK,EAAE;CAE1C,iBAAyB;CAEzB,YACE,IACA,aACA,gBACA;EACA,KAAKA,MAAM,GAAG,YAAY,aAAa,KAAK,EAAE;EAC9C,KAAKC,eAAe;EACpB,KAAKC,kBAAkB;CACzB;CAEA,MAAM,MAAM;EACV,KAAKF,IAAI,OAAO,0BAA0B;EAC1C,MAAM,KAAKG,OAAO,MAAM,iBAAiB;EAEzC,OAAO,KAAKA,OAAO,UAAU,GAAG;GAC9B,MAAM,KAAK,IAAI,SAAS,KAAKH,KAAK,KAAKC,YAAY;GACnD,IAAI;IACF,MAAM,EAAC,iBAAgB,oBAAoB,IAAI,gBAAgB,EAAE,CAAC;IAClE,IAAI,iBAAiB,KAAKG,gBAAgB;KACxC,KAAKA,iBAAiB;KACtB,KAAKJ,IAAI,QAAQ,8BAA8B,cAAc;KAC7D,KAAKE,gBAAgB,gBAAgB,YAAY;IACnD;GACF,SAAS,GAAG;IACV,KAAKF,IAAI,QAAQ,yCAAyC,CAAC;GAC7D,UAAU;IACR,GAAG,MAAM;GACX;GAEA,MAAM,KAAKG,OAAO,MAAM,iBAAiB;EAC3C;EACA,KAAKH,IAAI,OAAO,yBAAyB;CAC3C;CAEA,OAAO;EACL,KAAKG,OAAO,KAAK,KAAKH,GAAG;EACzB,OAAO;CACT;AACF"}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { LogContext } from '@rocicorp/logger';
|
|
2
|
-
import { Subscription } from '../../types/subscription.ts';
|
|
3
|
-
import type { VfsBackupWatermark } from '../litestream/vfs-watermark-reader.ts';
|
|
4
|
-
import type { BackupMonitor } from './backup-monitor.ts';
|
|
5
|
-
import type { ChangeStreamerService } from './change-streamer.ts';
|
|
6
|
-
import type { SnapshotMessage } from './snapshot.ts';
|
|
7
|
-
export interface VfsBackupWatermarkSource {
|
|
8
|
-
readWatermark(): Promise<VfsBackupWatermark>;
|
|
9
|
-
close?(): void;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Monitors a Litestream v0.5.x backup by reading Zero's replication state from
|
|
13
|
-
* the backup itself through the Litestream SQLite VFS.
|
|
14
|
-
*/
|
|
15
|
-
export declare class VfsBackupMonitor implements BackupMonitor {
|
|
16
|
-
#private;
|
|
17
|
-
readonly id = "vfs-backup-monitor";
|
|
18
|
-
constructor(lc: LogContext, backupURL: string, changeStreamer: ChangeStreamerService, initialCleanupDelayMs: number, probeIntervalMs: number, source: VfsBackupWatermarkSource);
|
|
19
|
-
run(): Promise<void>;
|
|
20
|
-
startSnapshotReservation(taskID: string): Subscription<SnapshotMessage>;
|
|
21
|
-
endReservation(taskID: string, updateCleanupDelay?: boolean): void;
|
|
22
|
-
readonly checkWatermarkAndScheduleCleanup: () => Promise<void>;
|
|
23
|
-
stop(): Promise<void>;
|
|
24
|
-
}
|
|
25
|
-
//# sourceMappingURL=vfs-backup-monitor.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"vfs-backup-monitor.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/vfs-backup-monitor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAMjD,OAAO,EAAC,YAAY,EAAC,MAAM,6BAA6B,CAAC;AAMzD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,uCAAuC,CAAC;AAE9E,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,sBAAsB,CAAC;AAChE,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,eAAe,CAAC;AASnD,MAAM,WAAW,wBAAwB;IACvC,aAAa,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC7C,KAAK,CAAC,IAAI,IAAI,CAAC;CAChB;AAED;;;GAGG;AACH,qBAAa,gBAAiB,YAAW,aAAa;;IACpD,QAAQ,CAAC,EAAE,wBAAwB;gBAuBjC,EAAE,EAAE,UAAU,EACd,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,qBAAqB,EACrC,qBAAqB,EAAE,MAAM,EAC7B,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,wBAAwB;IAalC,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAapB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAAC,eAAe,CAAC;IAwBvE,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,kBAAkB,UAAO,GAAG,IAAI;IAwB/D,QAAQ,CAAC,gCAAgC,sBAcvC;IAwFF,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAoCtB"}
|