@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,15 +1,38 @@
|
|
|
1
1
|
import { forkChildWorker } from "../../types/processes.js";
|
|
2
|
-
import {
|
|
2
|
+
import { BACKUP_WATERMARK_POLLER_URL } from "../../server/worker-urls.js";
|
|
3
|
+
import { getVfsEnv } from "../litestream/vfs-watermark-poller.js";
|
|
4
|
+
import { Subscription } from "../../types/subscription.js";
|
|
3
5
|
import { getLastBackupTime } from "../litestream/commands.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
6
|
+
import { BackupMonitor } from "./backup-monitor.js";
|
|
7
|
+
import { Litestream3PrometheusPoller } from "./litestream3-prometheus-poller.js";
|
|
8
|
+
import { ReplicaPoller } from "./replica-poller.js";
|
|
7
9
|
//#region ../zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.ts
|
|
8
|
-
function createBackupCleanupMonitor({ lc, config, replicaFile, changeStreamer,
|
|
9
|
-
const {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
function createBackupCleanupMonitor({ lc, config, processes, replicaFile, changeStreamer, verifyBackupState, env = process.env }) {
|
|
11
|
+
const { litestream, replica } = config;
|
|
12
|
+
const { backupURL } = litestream;
|
|
13
|
+
let stream;
|
|
14
|
+
if (!backupURL) stream = new ReplicaPoller(lc, replicaFile).start();
|
|
15
|
+
else if (config.litestream.backupUsingV5) {
|
|
16
|
+
const { logLevel, endpoint, region, vfsLogFile: logFile, vfsProbeIntervalMs: remotePollIntervalMs } = litestream;
|
|
17
|
+
const sub = Subscription.create();
|
|
18
|
+
const vfsEnv = getVfsEnv({
|
|
19
|
+
backupURL,
|
|
20
|
+
endpoint,
|
|
21
|
+
region,
|
|
22
|
+
logLevel,
|
|
23
|
+
logFile,
|
|
24
|
+
remotePollIntervalMs
|
|
25
|
+
});
|
|
26
|
+
processes.addWorker(forkChildWorker(BACKUP_WATERMARK_POLLER_URL, {
|
|
27
|
+
...env,
|
|
28
|
+
...vfsEnv
|
|
29
|
+
}), "supporting", "backup-watermark-poller").onMessageType("backupWatermarkUpdate", (msg) => sub.push(msg));
|
|
30
|
+
stream = sub;
|
|
31
|
+
} else {
|
|
32
|
+
const { port: metricsPort } = litestream;
|
|
33
|
+
stream = new Litestream3PrometheusPoller(lc, replicaFile, backupURL, `http://localhost:${metricsPort}/metrics`, verifyBackupState ?? (() => getLastBackupTime(lc, litestream, replica.file))).start();
|
|
34
|
+
}
|
|
35
|
+
return new BackupMonitor(lc, stream, changeStreamer, replicaFile);
|
|
13
36
|
}
|
|
14
37
|
//#endregion
|
|
15
38
|
export { createBackupCleanupMonitor };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backup-cleanup-monitor-factory.js","names":[],"sources":["../../../../../../zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {NormalizedZeroConfig} from '../../config/normalize.ts';\nimport {
|
|
1
|
+
{"version":3,"file":"backup-cleanup-monitor-factory.js","names":[],"sources":["../../../../../../zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {NormalizedZeroConfig} from '../../config/normalize.ts';\nimport {BACKUP_WATERMARK_POLLER_URL} from '../../server/worker-urls.ts';\nimport {forkChildWorker} from '../../types/processes.ts';\nimport type {Source} from '../../types/streams.ts';\nimport {Subscription} from '../../types/subscription.ts';\nimport type {ProcessManager} from '../life-cycle.ts';\nimport {getLastBackupTime} from '../litestream/commands.ts';\nimport {\n type BackupWatermarkUpdate,\n getVfsEnv,\n} from '../litestream/vfs-watermark-poller.ts';\nimport {type BackedUpWatermark, BackupMonitor} from './backup-monitor.ts';\nimport type {ChangeStreamerService} from './change-streamer.ts';\nimport {\n type BackupStateVerifier,\n Litestream3PrometheusPoller,\n} from './litestream3-prometheus-poller.ts';\nimport {ReplicaPoller} from './replica-poller.ts';\n\nexport type BackupCleanupMonitorFactoryOptions = {\n lc: LogContext;\n config: NormalizedZeroConfig;\n processes: ProcessManager;\n replicaFile: string;\n changeStreamer: ChangeStreamerService;\n verifyBackupState?: BackupStateVerifier | undefined;\n env?: NodeJS.ProcessEnv | undefined;\n};\n\nexport function createBackupCleanupMonitor({\n lc,\n config,\n processes,\n replicaFile,\n changeStreamer,\n verifyBackupState,\n env = process.env,\n}: BackupCleanupMonitorFactoryOptions): BackupMonitor {\n const {litestream, replica} = config;\n const {backupURL} = litestream;\n\n let stream: Source<BackedUpWatermark>;\n\n if (!backupURL) {\n stream = new ReplicaPoller(lc, replicaFile).start();\n } else if (config.litestream.backupUsingV5) {\n const {\n logLevel,\n endpoint,\n region,\n vfsLogFile: logFile,\n vfsProbeIntervalMs: remotePollIntervalMs,\n } = litestream;\n const sub = Subscription.create<BackedUpWatermark>();\n const vfsEnv = getVfsEnv({\n backupURL,\n endpoint,\n region,\n logLevel,\n logFile,\n remotePollIntervalMs,\n });\n processes\n .addWorker(\n forkChildWorker(BACKUP_WATERMARK_POLLER_URL, {...env, ...vfsEnv}),\n 'supporting',\n 'backup-watermark-poller',\n )\n .onMessageType<BackupWatermarkUpdate>('backupWatermarkUpdate', msg =>\n sub.push(msg),\n );\n stream = sub;\n } else {\n const {port: metricsPort} = litestream;\n stream = new Litestream3PrometheusPoller(\n lc,\n replicaFile,\n backupURL,\n `http://localhost:${metricsPort}/metrics`,\n verifyBackupState ??\n (() => getLastBackupTime(lc, litestream, replica.file)),\n ).start();\n }\n\n return new BackupMonitor(lc, stream, changeStreamer, replicaFile);\n}\n"],"mappings":";;;;;;;;;AA8BA,SAAgB,2BAA2B,EACzC,IACA,QACA,WACA,aACA,gBACA,mBACA,MAAM,QAAQ,OACsC;CACpD,MAAM,EAAC,YAAY,YAAW;CAC9B,MAAM,EAAC,cAAa;CAEpB,IAAI;CAEJ,IAAI,CAAC,WACH,SAAS,IAAI,cAAc,IAAI,WAAW,EAAE,MAAM;MAC7C,IAAI,OAAO,WAAW,eAAe;EAC1C,MAAM,EACJ,UACA,UACA,QACA,YAAY,SACZ,oBAAoB,yBAClB;EACJ,MAAM,MAAM,aAAa,OAA0B;EACnD,MAAM,SAAS,UAAU;GACvB;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,UACG,UACC,gBAAgB,6BAA6B;GAAC,GAAG;GAAK,GAAG;EAAM,CAAC,GAChE,cACA,yBACF,EACC,cAAqC,0BAAyB,QAC7D,IAAI,KAAK,GAAG,CACd;EACF,SAAS;CACX,OAAO;EACL,MAAM,EAAC,MAAM,gBAAe;EAC5B,SAAS,IAAI,4BACX,IACA,aACA,WACA,oBAAoB,YAAY,WAChC,4BACS,kBAAkB,IAAI,YAAY,QAAQ,IAAI,EACzD,EAAE,MAAM;CACV;CAEA,OAAO,IAAI,cAAc,IAAI,QAAQ,gBAAgB,WAAW;AAClE"}
|
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { LogContext } from '@rocicorp/logger';
|
|
2
|
+
import type { Source } from '../../types/streams.ts';
|
|
2
3
|
import type { SingletonService } from '../service.ts';
|
|
3
|
-
import type {
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import type { ChangeStreamerService } from './change-streamer.ts';
|
|
5
|
+
export type BackedUpWatermark = {
|
|
6
|
+
watermark: string;
|
|
7
|
+
writeTimeMs?: number | undefined;
|
|
8
|
+
backupTimeMs: number;
|
|
9
|
+
};
|
|
10
|
+
export declare class BackupMonitor implements SingletonService {
|
|
11
|
+
#private;
|
|
12
|
+
readonly id = "backup-monitor";
|
|
13
|
+
constructor(lc: LogContext, watermarks: Source<BackedUpWatermark>, changeStreamer: ChangeStreamerService, replicaFile: string);
|
|
14
|
+
firstBackupReceived(): Promise<void>;
|
|
15
|
+
run(): Promise<void>;
|
|
16
|
+
stop(): Promise<void>;
|
|
7
17
|
}
|
|
8
18
|
//# sourceMappingURL=backup-monitor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backup-monitor.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/backup-monitor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,
|
|
1
|
+
{"version":3,"file":"backup-monitor.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/backup-monitor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAKjD,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,wBAAwB,CAAC;AACnD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,sBAAsB,CAAC;AAEhE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,qBAAa,aAAc,YAAW,gBAAgB;;IACpD,QAAQ,CAAC,EAAE,oBAAoB;gBAU7B,EAAE,EAAE,UAAU,EACd,UAAU,EAAE,MAAM,CAAC,iBAAiB,CAAC,EACrC,cAAc,EAAE,qBAAqB,EACrC,WAAW,EAAE,MAAM;IAQrB,mBAAmB;IAIb,GAAG;IAeT,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAkCtB"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { promiseVoid } from "../../../../shared/src/resolved-promises.js";
|
|
2
|
+
import { Database } from "../../../../zqlite/src/db.js";
|
|
3
|
+
import { getOrCreateGauge } from "../../observability/metrics.js";
|
|
4
|
+
import { resolver } from "@rocicorp/resolver";
|
|
5
|
+
//#region ../zero-cache/src/services/change-streamer/backup-monitor.ts
|
|
6
|
+
var BackupMonitor = class {
|
|
7
|
+
id = "backup-monitor";
|
|
8
|
+
#lc;
|
|
9
|
+
#watermarks;
|
|
10
|
+
#changeStreamer;
|
|
11
|
+
#replicaFile;
|
|
12
|
+
#firstBackupReceived = resolver();
|
|
13
|
+
#latestBackup;
|
|
14
|
+
constructor(lc, watermarks, changeStreamer, replicaFile) {
|
|
15
|
+
this.#lc = lc;
|
|
16
|
+
this.#watermarks = watermarks;
|
|
17
|
+
this.#changeStreamer = changeStreamer;
|
|
18
|
+
this.#replicaFile = replicaFile;
|
|
19
|
+
}
|
|
20
|
+
firstBackupReceived() {
|
|
21
|
+
return this.#firstBackupReceived.promise;
|
|
22
|
+
}
|
|
23
|
+
async run() {
|
|
24
|
+
this.#lc.info?.("starting backup monitor");
|
|
25
|
+
this.#initBackupLagMetric();
|
|
26
|
+
for await (const backedUp of this.#watermarks) {
|
|
27
|
+
if (!this.#latestBackup) {
|
|
28
|
+
this.#lc.info?.(`received first backup up watermark`, { backedUp });
|
|
29
|
+
this.#firstBackupReceived.resolve();
|
|
30
|
+
}
|
|
31
|
+
this.#latestBackup = backedUp;
|
|
32
|
+
this.#changeStreamer.trackBackupWatermark(backedUp.watermark);
|
|
33
|
+
}
|
|
34
|
+
this.#lc.info?.("watermark stream closed. BackupMonitor stopped.");
|
|
35
|
+
}
|
|
36
|
+
stop() {
|
|
37
|
+
this.#watermarks.cancel();
|
|
38
|
+
return promiseVoid;
|
|
39
|
+
}
|
|
40
|
+
#initBackupLagMetric() {
|
|
41
|
+
getOrCreateGauge("replica", "backup_lag", {
|
|
42
|
+
description: "Latency from when a change is written to the replica to when it is backed up to litestream. It is expected to create a saw pattern from 0 to the configured ZERO_LITESTREAM_INCREMENTAL_BACKUP_INTERVAL_MINUTES.",
|
|
43
|
+
unit: "millisecond"
|
|
44
|
+
}).addCallback((o) => {
|
|
45
|
+
const latestBackup = this.#latestBackup;
|
|
46
|
+
if (!latestBackup) {
|
|
47
|
+
this.#lc.warn?.(`no backed up watermarks. unable to report replica.backup_lag`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const db = new Database(this.#lc, this.#replicaFile, { readonly: true });
|
|
51
|
+
try {
|
|
52
|
+
const { writeTimeMs } = db.prepare(`SELECT writeTimeMs FROM "_zero.replicationState"`).get();
|
|
53
|
+
const backupLag = Math.max(0, writeTimeMs - latestBackup.backupTimeMs);
|
|
54
|
+
o.observe(backupLag);
|
|
55
|
+
} catch (e) {
|
|
56
|
+
this.#lc.warn?.(`error measuring replica.backup_lag metric`, e);
|
|
57
|
+
} finally {
|
|
58
|
+
db.close();
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
//#endregion
|
|
64
|
+
export { BackupMonitor };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=backup-monitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backup-monitor.js","names":["#lc","#watermarks","#changeStreamer","#replicaFile","#firstBackupReceived","#initBackupLagMetric","#latestBackup"],"sources":["../../../../../../zero-cache/src/services/change-streamer/backup-monitor.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {promiseVoid} from '../../../../shared/src/resolved-promises.ts';\nimport {Database} from '../../../../zqlite/src/db.ts';\nimport {getOrCreateGauge} from '../../observability/metrics.ts';\nimport type {Source} from '../../types/streams.ts';\nimport type {SingletonService} from '../service.ts';\nimport type {ChangeStreamerService} from './change-streamer.ts';\n\nexport type BackedUpWatermark = {\n watermark: string;\n writeTimeMs?: number | undefined; // optional for debuggin; available on v5\n backupTimeMs: number;\n};\n\nexport class BackupMonitor implements SingletonService {\n readonly id = 'backup-monitor';\n readonly #lc: LogContext;\n readonly #watermarks: Source<BackedUpWatermark>;\n readonly #changeStreamer: ChangeStreamerService;\n readonly #replicaFile: string;\n readonly #firstBackupReceived = resolver();\n\n #latestBackup: BackedUpWatermark | undefined;\n\n constructor(\n lc: LogContext,\n watermarks: Source<BackedUpWatermark>,\n changeStreamer: ChangeStreamerService,\n replicaFile: string,\n ) {\n this.#lc = lc;\n this.#watermarks = watermarks;\n this.#changeStreamer = changeStreamer;\n this.#replicaFile = replicaFile;\n }\n\n firstBackupReceived() {\n return this.#firstBackupReceived.promise;\n }\n\n async run() {\n this.#lc.info?.('starting backup monitor');\n this.#initBackupLagMetric();\n\n for await (const backedUp of this.#watermarks) {\n if (!this.#latestBackup) {\n this.#lc.info?.(`received first backup up watermark`, {backedUp});\n this.#firstBackupReceived.resolve();\n }\n this.#latestBackup = backedUp;\n this.#changeStreamer.trackBackupWatermark(backedUp.watermark);\n }\n this.#lc.info?.('watermark stream closed. BackupMonitor stopped.');\n }\n\n stop(): Promise<void> {\n this.#watermarks.cancel();\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(o => {\n const latestBackup = this.#latestBackup;\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.backupTimeMs);\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":";;;;;AAeA,IAAa,gBAAb,MAAuD;CACrD,KAAc;CACd;CACA;CACA;CACA;CACA,uBAAgC,SAAS;CAEzC;CAEA,YACE,IACA,YACA,gBACA,aACA;EACA,KAAKA,MAAM;EACX,KAAKC,cAAc;EACnB,KAAKC,kBAAkB;EACvB,KAAKC,eAAe;CACtB;CAEA,sBAAsB;EACpB,OAAO,KAAKC,qBAAqB;CACnC;CAEA,MAAM,MAAM;EACV,KAAKJ,IAAI,OAAO,yBAAyB;EACzC,KAAKK,qBAAqB;EAE1B,WAAW,MAAM,YAAY,KAAKJ,aAAa;GAC7C,IAAI,CAAC,KAAKK,eAAe;IACvB,KAAKN,IAAI,OAAO,sCAAsC,EAAC,SAAQ,CAAC;IAChE,KAAKI,qBAAqB,QAAQ;GACpC;GACA,KAAKE,gBAAgB;GACrB,KAAKJ,gBAAgB,qBAAqB,SAAS,SAAS;EAC9D;EACA,KAAKF,IAAI,OAAO,iDAAiD;CACnE;CAEA,OAAsB;EACpB,KAAKC,YAAY,OAAO;EACxB,OAAO;CACT;CAEA,uBAAuB;EACrB,iBAAiB,WAAW,cAAc;GACxC,aACE;GAGF,MAAM;EACR,CAAC,EAAE,aAAY,MAAK;GAClB,MAAM,eAAe,KAAKK;GAC1B,IAAI,CAAC,cAAc;IACjB,KAAKN,IAAI,OACP,8DACF;IACA;GACF;GACA,MAAM,KAAK,IAAI,SAAS,KAAKA,KAAK,KAAKG,cAAc,EAAC,UAAU,KAAI,CAAC;GACrE,IAAI;IACF,MAAM,EAAC,gBAAe,GACnB,QAAgB,kDAAkD,EAClE,IAA2B;IAC9B,MAAM,YAAY,KAAK,IAAI,GAAG,cAAc,aAAa,YAAY;IACrE,EAAE,QAAQ,SAAS;GACrB,SAAS,GAAG;IACV,KAAKH,IAAI,OAAO,6CAA6C,CAAC;GAChE,UAAU;IACR,GAAG,MAAM;GACX;EACF,CAAC;CACH;AACF"}
|
|
@@ -1,17 +1,76 @@
|
|
|
1
1
|
import type { LogContext } from '@rocicorp/logger';
|
|
2
|
-
import type { WatermarkedChange } from './change-streamer
|
|
2
|
+
import type { WatermarkedChange } from './change-streamer.ts';
|
|
3
3
|
import type { Subscriber } from './subscriber.ts';
|
|
4
4
|
export type BroadcastReleaseMode = 'all-subscribers' | 'consensus-timeout';
|
|
5
|
+
/**
|
|
6
|
+
* Enables event-driven early release of a {@link Broadcast}. When provided and a
|
|
7
|
+
* majority has acked, the broadcast schedules its own consensus-timeout release
|
|
8
|
+
* proportional to how long the majority took to ack, instead of relying on the
|
|
9
|
+
* Forwarder's periodic progress tick.
|
|
10
|
+
*/
|
|
11
|
+
export type EarlyReleaseOptions = {
|
|
12
|
+
/** Time to wait, proportional to majority completion, before releasing. */
|
|
13
|
+
consensusTimeoutProportion: number;
|
|
14
|
+
/** Injectable timer, for deterministic testing. */
|
|
15
|
+
setTimeoutFn?: typeof setTimeout;
|
|
16
|
+
/** Injectable timer cancellation, for deterministic testing. */
|
|
17
|
+
clearTimeoutFn?: typeof clearTimeout;
|
|
18
|
+
};
|
|
5
19
|
/**
|
|
6
20
|
* Initiates and tracks the progress of a change broadcasted to
|
|
7
|
-
* a set of subscribers
|
|
21
|
+
* a set of subscribers, implementing **consensus-based timeouts**
|
|
22
|
+
* to effect short-term graceful degradation when a minority of
|
|
23
|
+
* subscribers are behind.
|
|
24
|
+
*
|
|
25
|
+
* ### Background
|
|
26
|
+
*
|
|
27
|
+
* The purpose of flow control is to pull upstream replication changes
|
|
28
|
+
* no faster than the rate they are processed by downstream subscribers
|
|
29
|
+
* in the steady state. In the change-streamer, this is done by occasionally
|
|
30
|
+
* waiting for ACKs from subscribers before continuing; without doing so,
|
|
31
|
+
* I/O buffers fill up and cause the system to spend most of its time in GC.
|
|
32
|
+
*
|
|
33
|
+
* However, the naive algorithm of always waiting for all subscribers (e.g.
|
|
34
|
+
* `Promise.all()`) can behave poorly in scenarios where subscribers
|
|
35
|
+
* are imbalanced:
|
|
36
|
+
* * New subscribers may have a backlog of changes to catch up with.
|
|
37
|
+
* Having all subscribers wait for the new subscriber to catch up results
|
|
38
|
+
* in delaying the entire application.
|
|
39
|
+
* * Broken TCP connections similarly require all subscribers to wait until
|
|
40
|
+
* connection liveness checks kick in and disconnect the subscriber.
|
|
8
41
|
*
|
|
9
|
-
*
|
|
42
|
+
* A simplistic approach is to add a limit to the amount of time waiting for
|
|
43
|
+
* subscribers, i.e. an ack timeout. However, deciding what this timeout
|
|
44
|
+
* should be is non-trivial because of the heterogeneous nature of changes;
|
|
45
|
+
* while most changes operate on single rows and are relatively predictable
|
|
46
|
+
* in terms of running time, some changes are table-wide operations and can
|
|
47
|
+
* legitimately take an arbitrary amount of time. In such scenarios, a
|
|
48
|
+
* timeout that is too short can stop progress on replication altogether.
|
|
10
49
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
50
|
+
* ### Consensus-based Timeout Algorithm
|
|
51
|
+
*
|
|
52
|
+
* To address these shortcomings, a "consensus-based timeout" algorithm is
|
|
53
|
+
* used:
|
|
54
|
+
* * Wait for more than half of the subscribers to finish. (In
|
|
55
|
+
* case of a single node, or the case of one replication-manager
|
|
56
|
+
* and one view-syncer, this reduces to waiting for all subscribers.)
|
|
57
|
+
* * Once more than half of the subscribers have finished, proceed after
|
|
58
|
+
* the configured timeout, proportional to the time it took for the
|
|
59
|
+
* majority of subscribers to ack, even if not all subscribers have
|
|
60
|
+
* finished.
|
|
61
|
+
*
|
|
62
|
+
* In other words, the subscribers themselves are used to determine the
|
|
63
|
+
* timeout of each batch of changes; the majority determines this when
|
|
64
|
+
* they complete, upon which a timeout is logically started.
|
|
65
|
+
*
|
|
66
|
+
* In the common case, the remaining subscribers finish soon afterward and
|
|
67
|
+
* the timeout never elapses. However, in pathological cases where a minority
|
|
68
|
+
* of subscribers have a disproportionate amount of load, some will still
|
|
69
|
+
* be processing (or otherwise unresponsive). These subscribers are given
|
|
70
|
+
* a bounded amount of time to catch up at each flushed batch, up to the
|
|
71
|
+
* timeout interval. This guarantees eventual catchup because the
|
|
72
|
+
* subscribers with a backlog of changes necessarily have a higher
|
|
73
|
+
* processing rate than the subscribers that finished (and are made to wait).
|
|
15
74
|
*/
|
|
16
75
|
export declare class Broadcast {
|
|
17
76
|
#private;
|
|
@@ -24,79 +83,10 @@ export declare class Broadcast {
|
|
|
24
83
|
* Broadcasts the `change` to the `subscribers` and tracks their
|
|
25
84
|
* completion.
|
|
26
85
|
*/
|
|
27
|
-
constructor(subscribers: Iterable<Subscriber>, change: WatermarkedChange);
|
|
86
|
+
constructor(lc: LogContext, subscribers: Iterable<Subscriber>, change: WatermarkedChange, earlyRelease?: EarlyReleaseOptions);
|
|
28
87
|
get isDone(): boolean;
|
|
29
88
|
get done(): Promise<void>;
|
|
30
89
|
get releaseMode(): BroadcastReleaseMode;
|
|
31
|
-
|
|
32
|
-
* Checks for pathological situations in which flow should be reenabled
|
|
33
|
-
* before all subscribers have acked.
|
|
34
|
-
*
|
|
35
|
-
* ### Background
|
|
36
|
-
*
|
|
37
|
-
* The purpose of flow control is to pull upstream replication changes
|
|
38
|
-
* no faster than the rate as they are processed by downstream subscribers
|
|
39
|
-
* in the steady state. In the change-streamer, this is done by occasionally
|
|
40
|
-
* waiting for ACKs from subscribers before continuing; without doing so,
|
|
41
|
-
* I/O buffers fill up and cause the system to spend most of its time in GC.
|
|
42
|
-
*
|
|
43
|
-
* However, the naive algorithm of always waiting for all subscribers (e.g.
|
|
44
|
-
* `Promise.all()`) can behave poorly in scenarios where subscribers
|
|
45
|
-
* are imbalanced:
|
|
46
|
-
* * New subscribers may have a backlog of changes to catch up with.
|
|
47
|
-
* Having all subscribers wait for the new subscriber to catch up results
|
|
48
|
-
* in delaying the entire application.
|
|
49
|
-
* * Broken TCP connections similarly require all subscribers to wait until
|
|
50
|
-
* connection liveness checks kick in and disconnect the subscriber.
|
|
51
|
-
*
|
|
52
|
-
* A simplistic approach is to add a limit to the amount of time waiting for
|
|
53
|
-
* subscribers, i.e. an ack timeout. However, deciding what this timeout
|
|
54
|
-
* should be is non-trivial because of the heterogeneous nature of changes;
|
|
55
|
-
* while most changes operate on single rows and are relatively predictable
|
|
56
|
-
* in terms of running time, some changes are table-wide operations and can
|
|
57
|
-
* legitimately take an arbitrary amount of time. In such scenarios, a
|
|
58
|
-
* timeout that is too short can stop progress on replication altogether.
|
|
59
|
-
*
|
|
60
|
-
* ### Consensus-based Timeout Algorithm
|
|
61
|
-
*
|
|
62
|
-
* To address these shortcomings, a "consensus-based timeout" algorithm is
|
|
63
|
-
* used:
|
|
64
|
-
* * Wait for more than half of the subscribers to finish. (In
|
|
65
|
-
* case of a single node, or the case of one replication-manager
|
|
66
|
-
* and one view-syncer, this reduces to waiting for all subscribers.)
|
|
67
|
-
* * Once more than half of the subscribers have finished, proceed after
|
|
68
|
-
* a fixed timeout elapses (e.g. 1 second), even if not all subscribers
|
|
69
|
-
* have finished.
|
|
70
|
-
*
|
|
71
|
-
* In other words, the subscribers themselves are used to determine the
|
|
72
|
-
* timeout of each batch of changes; the majority determines this when
|
|
73
|
-
* they complete, upon which a timeout is logically started.
|
|
74
|
-
*
|
|
75
|
-
* In the common case, the remaining subscribers finish soon afterward and
|
|
76
|
-
* the timeout never elapses. However, in pathological cases where a minority
|
|
77
|
-
* of subscribers have a disproportionate amount of load, some will still
|
|
78
|
-
* be processing (or otherwise unresponsive). These subscribers are given
|
|
79
|
-
* a bounded amount of time to catch up at each flushed batch, up to the
|
|
80
|
-
* timeout interval. This guarantees eventual catchup because the
|
|
81
|
-
* subscribers with a backlog of changes necessarily have a higher
|
|
82
|
-
* processing rate than the subscribers that finished (and are made to wait).
|
|
83
|
-
*
|
|
84
|
-
* ### Not implemented: Broken connection detection
|
|
85
|
-
*
|
|
86
|
-
* If a subscriber has not made progress for a certain interval, the
|
|
87
|
-
* algorithm could theoretically drop it preemptively, supplementing the
|
|
88
|
-
* existing websocket-level liveness checks.
|
|
89
|
-
*
|
|
90
|
-
* However, a more reliable approach would be to change the replicator
|
|
91
|
-
* to use non-blocking writes, and subsequently increase the frequency of
|
|
92
|
-
* connection-level liveness checks. The current synchronous replica writes
|
|
93
|
-
* can delay both ping responsiveness and change progress arbitrarily (e.g.
|
|
94
|
-
* a large index creation); an independently liveness check that is not
|
|
95
|
-
* delayed by synchronous writes on the subscriber would be a more failsafe
|
|
96
|
-
* solution.
|
|
97
|
-
*
|
|
98
|
-
* @returns `true` if the broadcast was already done or was marked done.
|
|
99
|
-
*/
|
|
100
|
-
checkProgress(lc: LogContext, flowControlConsensusPaddingMs: number, now: number): boolean;
|
|
90
|
+
logSlowRequests(now: number): void;
|
|
101
91
|
}
|
|
102
92
|
//# sourceMappingURL=broadcast.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"broadcast.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/broadcast.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"broadcast.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-streamer/broadcast.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,iBAAiB,CAAC;AAEhD,MAAM,MAAM,oBAAoB,GAAG,iBAAiB,GAAG,mBAAmB,CAAC;AAE3E;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,2EAA2E;IAC3E,0BAA0B,EAAE,MAAM,CAAC;IACnC,mDAAmD;IACnD,YAAY,CAAC,EAAE,OAAO,UAAU,CAAC;IACjC,gEAAgE;IAChE,cAAc,CAAC,EAAE,OAAO,YAAY,CAAC;CACtC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,qBAAa,SAAS;;IACpB;;;OAGG;IACH,MAAM,CAAC,eAAe,CACpB,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,EACjC,MAAM,EAAE,iBAAiB;IA0B3B;;;OAGG;gBAED,EAAE,EAAE,UAAU,EACd,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,EACjC,MAAM,EAAE,iBAAiB,EACzB,YAAY,CAAC,EAAE,mBAAmB;IA4EpC,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,IAAI,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAExB;IAED,IAAI,WAAW,IAAI,oBAAoB,CAEtC;IAED,eAAe,CAAC,GAAG,EAAE,MAAM;CA8B5B"}
|
|
@@ -2,14 +2,59 @@ import { resolver } from "@rocicorp/resolver";
|
|
|
2
2
|
//#region ../zero-cache/src/services/change-streamer/broadcast.ts
|
|
3
3
|
/**
|
|
4
4
|
* Initiates and tracks the progress of a change broadcasted to
|
|
5
|
-
* a set of subscribers
|
|
5
|
+
* a set of subscribers, implementing **consensus-based timeouts**
|
|
6
|
+
* to effect short-term graceful degradation when a minority of
|
|
7
|
+
* subscribers are behind.
|
|
6
8
|
*
|
|
7
|
-
*
|
|
9
|
+
* ### Background
|
|
8
10
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* The purpose of flow control is to pull upstream replication changes
|
|
12
|
+
* no faster than the rate they are processed by downstream subscribers
|
|
13
|
+
* in the steady state. In the change-streamer, this is done by occasionally
|
|
14
|
+
* waiting for ACKs from subscribers before continuing; without doing so,
|
|
15
|
+
* I/O buffers fill up and cause the system to spend most of its time in GC.
|
|
16
|
+
*
|
|
17
|
+
* However, the naive algorithm of always waiting for all subscribers (e.g.
|
|
18
|
+
* `Promise.all()`) can behave poorly in scenarios where subscribers
|
|
19
|
+
* are imbalanced:
|
|
20
|
+
* * New subscribers may have a backlog of changes to catch up with.
|
|
21
|
+
* Having all subscribers wait for the new subscriber to catch up results
|
|
22
|
+
* in delaying the entire application.
|
|
23
|
+
* * Broken TCP connections similarly require all subscribers to wait until
|
|
24
|
+
* connection liveness checks kick in and disconnect the subscriber.
|
|
25
|
+
*
|
|
26
|
+
* A simplistic approach is to add a limit to the amount of time waiting for
|
|
27
|
+
* subscribers, i.e. an ack timeout. However, deciding what this timeout
|
|
28
|
+
* should be is non-trivial because of the heterogeneous nature of changes;
|
|
29
|
+
* while most changes operate on single rows and are relatively predictable
|
|
30
|
+
* in terms of running time, some changes are table-wide operations and can
|
|
31
|
+
* legitimately take an arbitrary amount of time. In such scenarios, a
|
|
32
|
+
* timeout that is too short can stop progress on replication altogether.
|
|
33
|
+
*
|
|
34
|
+
* ### Consensus-based Timeout Algorithm
|
|
35
|
+
*
|
|
36
|
+
* To address these shortcomings, a "consensus-based timeout" algorithm is
|
|
37
|
+
* used:
|
|
38
|
+
* * Wait for more than half of the subscribers to finish. (In
|
|
39
|
+
* case of a single node, or the case of one replication-manager
|
|
40
|
+
* and one view-syncer, this reduces to waiting for all subscribers.)
|
|
41
|
+
* * Once more than half of the subscribers have finished, proceed after
|
|
42
|
+
* the configured timeout, proportional to the time it took for the
|
|
43
|
+
* majority of subscribers to ack, even if not all subscribers have
|
|
44
|
+
* finished.
|
|
45
|
+
*
|
|
46
|
+
* In other words, the subscribers themselves are used to determine the
|
|
47
|
+
* timeout of each batch of changes; the majority determines this when
|
|
48
|
+
* they complete, upon which a timeout is logically started.
|
|
49
|
+
*
|
|
50
|
+
* In the common case, the remaining subscribers finish soon afterward and
|
|
51
|
+
* the timeout never elapses. However, in pathological cases where a minority
|
|
52
|
+
* of subscribers have a disproportionate amount of load, some will still
|
|
53
|
+
* be processing (or otherwise unresponsive). These subscribers are given
|
|
54
|
+
* a bounded amount of time to catch up at each flushed batch, up to the
|
|
55
|
+
* timeout interval. This guarantees eventual catchup because the
|
|
56
|
+
* subscribers with a backlog of changes necessarily have a higher
|
|
57
|
+
* processing rate than the subscribers that finished (and are made to wait).
|
|
13
58
|
*/
|
|
14
59
|
var Broadcast = class {
|
|
15
60
|
/**
|
|
@@ -19,6 +64,7 @@ var Broadcast = class {
|
|
|
19
64
|
static withoutTracking(subscribers, change) {
|
|
20
65
|
for (const sub of subscribers) sub.send(change);
|
|
21
66
|
}
|
|
67
|
+
#lc;
|
|
22
68
|
#pending;
|
|
23
69
|
#completed;
|
|
24
70
|
#done = resolver();
|
|
@@ -27,16 +73,23 @@ var Broadcast = class {
|
|
|
27
73
|
#watermark;
|
|
28
74
|
#majority;
|
|
29
75
|
#start = performance.now();
|
|
30
|
-
#
|
|
76
|
+
#earlyReleaseTimeoutProportion;
|
|
77
|
+
#setTimeout;
|
|
78
|
+
#clearTimeout;
|
|
79
|
+
#earlyReleaseTimer;
|
|
31
80
|
/**
|
|
32
81
|
* Broadcasts the `change` to the `subscribers` and tracks their
|
|
33
82
|
* completion.
|
|
34
83
|
*/
|
|
35
|
-
constructor(subscribers, change) {
|
|
84
|
+
constructor(lc, subscribers, change, earlyRelease) {
|
|
85
|
+
this.#lc = lc;
|
|
36
86
|
this.#pending = new Set(subscribers);
|
|
37
87
|
this.#completed = [];
|
|
38
88
|
this.#watermark = change[0];
|
|
39
89
|
this.#majority = Math.floor(this.#pending.size / 2) + 1;
|
|
90
|
+
this.#earlyReleaseTimeoutProportion = earlyRelease?.consensusTimeoutProportion;
|
|
91
|
+
this.#setTimeout = earlyRelease?.setTimeoutFn ?? setTimeout;
|
|
92
|
+
this.#clearTimeout = earlyRelease?.clearTimeoutFn ?? clearTimeout;
|
|
40
93
|
for (const sub of this.#pending) {
|
|
41
94
|
const changes = sub.numPending + 1;
|
|
42
95
|
sub.send(change).catch(() => {}).finally(() => this.#markCompleted(sub, changes));
|
|
@@ -44,19 +97,35 @@ var Broadcast = class {
|
|
|
44
97
|
if (this.#pending.size === 0) this.#setDone("all-subscribers");
|
|
45
98
|
}
|
|
46
99
|
#markCompleted(sub, changes) {
|
|
47
|
-
|
|
100
|
+
if (this.#isDone) return;
|
|
101
|
+
const elapsed = performance.now() - this.#start;
|
|
102
|
+
sub.trackResponseResult("on-time");
|
|
48
103
|
this.#completed.push({
|
|
49
104
|
sub,
|
|
50
105
|
changes,
|
|
51
106
|
elapsed
|
|
52
107
|
});
|
|
53
108
|
this.#pending.delete(sub);
|
|
54
|
-
if (this.#pending.size === 0)
|
|
109
|
+
if (this.#pending.size === 0) {
|
|
110
|
+
this.#setDone("all-subscribers");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (this.#earlyReleaseTimeoutProportion !== void 0 && this.#earlyReleaseTimer === void 0 && this.#completed.length === this.#majority) {
|
|
114
|
+
const timeoutMs = Math.ceil(elapsed * this.#earlyReleaseTimeoutProportion);
|
|
115
|
+
this.#earlyReleaseTimer = this.#setTimeout(() => this.#setDone("consensus-timeout"), timeoutMs);
|
|
116
|
+
}
|
|
55
117
|
}
|
|
56
118
|
#setDone(releaseMode) {
|
|
57
119
|
if (this.#isDone) return;
|
|
120
|
+
if (releaseMode === "consensus-timeout") {
|
|
121
|
+
const elapsed = performance.now() - this.#start;
|
|
122
|
+
this.#logWithState(`continuing with ${this.#pending.size} subscriber(s) still pending`, elapsed);
|
|
123
|
+
}
|
|
124
|
+
this.#pending.forEach((sub) => sub.trackResponseResult("timed-out"));
|
|
58
125
|
this.#isDone = true;
|
|
59
126
|
this.#releaseMode = releaseMode;
|
|
127
|
+
this.#clearTimeout(this.#earlyReleaseTimer);
|
|
128
|
+
this.#earlyReleaseTimer = void 0;
|
|
60
129
|
this.#done.resolve();
|
|
61
130
|
}
|
|
62
131
|
get isDone() {
|
|
@@ -68,92 +137,15 @@ var Broadcast = class {
|
|
|
68
137
|
get releaseMode() {
|
|
69
138
|
return this.#releaseMode ?? "all-subscribers";
|
|
70
139
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
* before all subscribers have acked.
|
|
74
|
-
*
|
|
75
|
-
* ### Background
|
|
76
|
-
*
|
|
77
|
-
* The purpose of flow control is to pull upstream replication changes
|
|
78
|
-
* no faster than the rate as they are processed by downstream subscribers
|
|
79
|
-
* in the steady state. In the change-streamer, this is done by occasionally
|
|
80
|
-
* waiting for ACKs from subscribers before continuing; without doing so,
|
|
81
|
-
* I/O buffers fill up and cause the system to spend most of its time in GC.
|
|
82
|
-
*
|
|
83
|
-
* However, the naive algorithm of always waiting for all subscribers (e.g.
|
|
84
|
-
* `Promise.all()`) can behave poorly in scenarios where subscribers
|
|
85
|
-
* are imbalanced:
|
|
86
|
-
* * New subscribers may have a backlog of changes to catch up with.
|
|
87
|
-
* Having all subscribers wait for the new subscriber to catch up results
|
|
88
|
-
* in delaying the entire application.
|
|
89
|
-
* * Broken TCP connections similarly require all subscribers to wait until
|
|
90
|
-
* connection liveness checks kick in and disconnect the subscriber.
|
|
91
|
-
*
|
|
92
|
-
* A simplistic approach is to add a limit to the amount of time waiting for
|
|
93
|
-
* subscribers, i.e. an ack timeout. However, deciding what this timeout
|
|
94
|
-
* should be is non-trivial because of the heterogeneous nature of changes;
|
|
95
|
-
* while most changes operate on single rows and are relatively predictable
|
|
96
|
-
* in terms of running time, some changes are table-wide operations and can
|
|
97
|
-
* legitimately take an arbitrary amount of time. In such scenarios, a
|
|
98
|
-
* timeout that is too short can stop progress on replication altogether.
|
|
99
|
-
*
|
|
100
|
-
* ### Consensus-based Timeout Algorithm
|
|
101
|
-
*
|
|
102
|
-
* To address these shortcomings, a "consensus-based timeout" algorithm is
|
|
103
|
-
* used:
|
|
104
|
-
* * Wait for more than half of the subscribers to finish. (In
|
|
105
|
-
* case of a single node, or the case of one replication-manager
|
|
106
|
-
* and one view-syncer, this reduces to waiting for all subscribers.)
|
|
107
|
-
* * Once more than half of the subscribers have finished, proceed after
|
|
108
|
-
* a fixed timeout elapses (e.g. 1 second), even if not all subscribers
|
|
109
|
-
* have finished.
|
|
110
|
-
*
|
|
111
|
-
* In other words, the subscribers themselves are used to determine the
|
|
112
|
-
* timeout of each batch of changes; the majority determines this when
|
|
113
|
-
* they complete, upon which a timeout is logically started.
|
|
114
|
-
*
|
|
115
|
-
* In the common case, the remaining subscribers finish soon afterward and
|
|
116
|
-
* the timeout never elapses. However, in pathological cases where a minority
|
|
117
|
-
* of subscribers have a disproportionate amount of load, some will still
|
|
118
|
-
* be processing (or otherwise unresponsive). These subscribers are given
|
|
119
|
-
* a bounded amount of time to catch up at each flushed batch, up to the
|
|
120
|
-
* timeout interval. This guarantees eventual catchup because the
|
|
121
|
-
* subscribers with a backlog of changes necessarily have a higher
|
|
122
|
-
* processing rate than the subscribers that finished (and are made to wait).
|
|
123
|
-
*
|
|
124
|
-
* ### Not implemented: Broken connection detection
|
|
125
|
-
*
|
|
126
|
-
* If a subscriber has not made progress for a certain interval, the
|
|
127
|
-
* algorithm could theoretically drop it preemptively, supplementing the
|
|
128
|
-
* existing websocket-level liveness checks.
|
|
129
|
-
*
|
|
130
|
-
* However, a more reliable approach would be to change the replicator
|
|
131
|
-
* to use non-blocking writes, and subsequently increase the frequency of
|
|
132
|
-
* connection-level liveness checks. The current synchronous replica writes
|
|
133
|
-
* can delay both ping responsiveness and change progress arbitrarily (e.g.
|
|
134
|
-
* a large index creation); an independently liveness check that is not
|
|
135
|
-
* delayed by synchronous writes on the subscriber would be a more failsafe
|
|
136
|
-
* solution.
|
|
137
|
-
*
|
|
138
|
-
* @returns `true` if the broadcast was already done or was marked done.
|
|
139
|
-
*/
|
|
140
|
-
checkProgress(lc, flowControlConsensusPaddingMs, now) {
|
|
141
|
-
if (this.#isDone) return true;
|
|
142
|
-
if (this.#pending.size === 0) return true;
|
|
140
|
+
logSlowRequests(now) {
|
|
141
|
+
if (this.#isDone || this.#pending.size === 0) return;
|
|
143
142
|
const elapsed = now - this.#start;
|
|
144
143
|
if (this.#completed.length < this.#majority) {
|
|
145
|
-
if (elapsed >= 1e3) this.#logWithState(
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
if (now - this.#latestCompleted >= flowControlConsensusPaddingMs) {
|
|
149
|
-
this.#logWithState(lc, `continuing with ${this.#pending.size} subscriber(s) still pending`, elapsed);
|
|
150
|
-
this.#setDone("consensus-timeout");
|
|
151
|
-
return true;
|
|
144
|
+
if (elapsed >= 1e3) this.#logWithState(`waiting for at least ${this.#majority} subscribers to finish`, elapsed);
|
|
152
145
|
}
|
|
153
|
-
return false;
|
|
154
146
|
}
|
|
155
|
-
#logWithState(
|
|
156
|
-
lc.withContext("watermark", this.#watermark).info?.(`${msg} (${elapsed.toFixed(3)} ms)`, {
|
|
147
|
+
#logWithState(msg, elapsed) {
|
|
148
|
+
this.#lc.withContext("watermark", this.#watermark).info?.(`${msg} (${elapsed.toFixed(3)} ms)`, {
|
|
157
149
|
completed: this.#completed.map((d) => ({
|
|
158
150
|
id: d.sub.id,
|
|
159
151
|
processed: d.changes,
|