@rocicorp/zero 1.10.0-canary.24 → 1.10.0-canary.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/analyze-query/src/bin-analyze.js +8 -9
- package/out/analyze-query/src/bin-analyze.js.map +1 -1
- package/out/replicache/src/btree/node.d.ts.map +1 -1
- package/out/replicache/src/btree/node.js +6 -3
- package/out/replicache/src/btree/node.js.map +1 -1
- package/out/replicache/src/btree/read.d.ts +1 -12
- package/out/replicache/src/btree/read.d.ts.map +1 -1
- package/out/replicache/src/btree/read.js +12 -24
- package/out/replicache/src/btree/read.js.map +1 -1
- package/out/replicache/src/dag/gc.d.ts.map +1 -1
- package/out/replicache/src/dag/gc.js +11 -8
- package/out/replicache/src/dag/gc.js.map +1 -1
- package/out/replicache/src/dag/store-impl.d.ts +2 -12
- package/out/replicache/src/dag/store-impl.d.ts.map +1 -1
- package/out/replicache/src/dag/store-impl.js +8 -22
- package/out/replicache/src/dag/store-impl.js.map +1 -1
- package/out/replicache/src/db/rebase.d.ts +2 -14
- package/out/replicache/src/db/rebase.d.ts.map +1 -1
- package/out/replicache/src/db/rebase.js +5 -27
- package/out/replicache/src/db/rebase.js.map +1 -1
- package/out/replicache/src/db/write.js.map +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.d.ts.map +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.js +5 -28
- package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
- package/out/replicache/src/kv/idb-store-with-mem-fallback.d.ts +5 -15
- package/out/replicache/src/kv/idb-store-with-mem-fallback.d.ts.map +1 -1
- package/out/replicache/src/kv/idb-store-with-mem-fallback.js +25 -25
- package/out/replicache/src/kv/idb-store-with-mem-fallback.js.map +1 -1
- package/out/replicache/src/kv/idb-store.d.ts +0 -9
- package/out/replicache/src/kv/idb-store.d.ts.map +1 -1
- package/out/replicache/src/kv/idb-store.js +2 -14
- package/out/replicache/src/kv/idb-store.js.map +1 -1
- package/out/replicache/src/kv/mem-store.d.ts +0 -1
- package/out/replicache/src/kv/mem-store.d.ts.map +1 -1
- package/out/replicache/src/kv/mem-store.js +0 -3
- package/out/replicache/src/kv/mem-store.js.map +1 -1
- package/out/replicache/src/kv/op-sqlite/store.d.ts.map +1 -1
- package/out/replicache/src/kv/op-sqlite/store.js +1 -1
- package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
- package/out/replicache/src/kv/sqlite-store.d.ts +1 -13
- package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
- package/out/replicache/src/kv/sqlite-store.js +6 -73
- package/out/replicache/src/kv/sqlite-store.js.map +1 -1
- package/out/replicache/src/kv/store.d.ts +0 -6
- package/out/replicache/src/kv/store.d.ts.map +1 -1
- package/out/replicache/src/persist/collect-idb-databases.d.ts +5 -10
- package/out/replicache/src/persist/collect-idb-databases.d.ts.map +1 -1
- package/out/replicache/src/persist/collect-idb-databases.js +9 -16
- package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
- package/out/replicache/src/persist/persist.d.ts.map +1 -1
- package/out/replicache/src/persist/persist.js +6 -13
- package/out/replicache/src/persist/persist.js.map +1 -1
- package/out/replicache/src/persist/refresh.js +2 -6
- package/out/replicache/src/persist/refresh.js.map +1 -1
- package/out/replicache/src/replicache-impl.d.ts +3 -14
- package/out/replicache/src/replicache-impl.d.ts.map +1 -1
- package/out/replicache/src/replicache-impl.js +11 -118
- package/out/replicache/src/replicache-impl.js.map +1 -1
- package/out/replicache/src/replicache-options.d.ts +0 -9
- package/out/replicache/src/replicache-options.d.ts.map +1 -1
- package/out/replicache/src/transactions.d.ts.map +1 -1
- package/out/replicache/src/transactions.js +1 -1
- package/out/replicache/src/transactions.js.map +1 -1
- package/out/shared/src/arrays.d.ts +0 -5
- package/out/shared/src/arrays.d.ts.map +1 -1
- package/out/shared/src/arrays.js +24 -8
- package/out/shared/src/arrays.js.map +1 -1
- package/out/shared/src/base64.d.ts +19 -0
- package/out/shared/src/base64.d.ts.map +1 -0
- package/out/shared/src/base64.js +40 -0
- package/out/shared/src/base64.js.map +1 -0
- package/out/shared/src/error.d.ts +0 -11
- package/out/shared/src/error.d.ts.map +1 -1
- package/out/shared/src/error.js +1 -35
- package/out/shared/src/error.js.map +1 -1
- package/out/shared/src/hash.d.ts +0 -1
- package/out/shared/src/hash.d.ts.map +1 -1
- package/out/shared/src/hash.js +10 -87
- package/out/shared/src/hash.js.map +1 -1
- package/out/shared/src/iterables.d.ts +0 -2
- package/out/shared/src/iterables.d.ts.map +1 -1
- package/out/shared/src/iterables.js +1 -18
- package/out/shared/src/iterables.js.map +1 -1
- package/out/shared/src/json.d.ts.map +1 -1
- package/out/shared/src/json.js +2 -2
- package/out/shared/src/json.js.map +1 -1
- package/out/shared/src/map.d.ts +2 -24
- package/out/shared/src/map.d.ts.map +1 -1
- package/out/shared/src/map.js +4 -18
- package/out/shared/src/map.js.map +1 -1
- package/out/shared/src/ring-buffer.d.ts +0 -8
- package/out/shared/src/ring-buffer.d.ts.map +1 -1
- package/out/shared/src/ring-buffer.js +1 -22
- package/out/shared/src/ring-buffer.js.map +1 -1
- package/out/z2s/src/sql.d.ts.map +1 -1
- package/out/z2s/src/sql.js +2 -2
- package/out/z2s/src/sql.js.map +1 -1
- package/out/zero/package.js +3 -2
- package/out/zero/package.js.map +1 -1
- package/out/zero-cache/src/auth/read-authorizer.js +1 -1
- package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
- package/out/zero-cache/src/auth/write-authorizer.js +0 -1
- package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +2 -42
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +28 -149
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/custom-queries/transform-query.js +1 -1
- package/out/zero-cache/src/db/create.d.ts.map +1 -1
- package/out/zero-cache/src/db/create.js +1 -4
- package/out/zero-cache/src/db/create.js.map +1 -1
- package/out/zero-cache/src/db/lite-tables.d.ts +2 -10
- package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
- package/out/zero-cache/src/db/lite-tables.js +6 -10
- package/out/zero-cache/src/db/lite-tables.js.map +1 -1
- package/out/zero-cache/src/db/specs.d.ts +0 -41
- package/out/zero-cache/src/db/specs.d.ts.map +1 -1
- package/out/zero-cache/src/db/specs.js +3 -30
- package/out/zero-cache/src/db/specs.js.map +1 -1
- package/out/zero-cache/src/observability/metrics.d.ts.map +1 -1
- package/out/zero-cache/src/observability/metrics.js +7 -2
- package/out/zero-cache/src/observability/metrics.js.map +1 -1
- package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +4 -13
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/inspector-delegate.d.ts +2 -18
- package/out/zero-cache/src/server/inspector-delegate.d.ts.map +1 -1
- package/out/zero-cache/src/server/inspector-delegate.js +17 -25
- package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
- package/out/zero-cache/src/server/main.d.ts.map +1 -1
- package/out/zero-cache/src/server/main.js +1 -8
- package/out/zero-cache/src/server/main.js.map +1 -1
- package/out/zero-cache/src/server/mutator.js +0 -2
- package/out/zero-cache/src/server/mutator.js.map +1 -1
- package/out/zero-cache/src/server/reaper.d.ts.map +1 -1
- package/out/zero-cache/src/server/reaper.js +0 -2
- package/out/zero-cache/src/server/reaper.js.map +1 -1
- package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/server/replicator.js +2 -8
- package/out/zero-cache/src/server/replicator.js.map +1 -1
- package/out/zero-cache/src/server/runner/zero-dispatcher.d.ts.map +1 -1
- package/out/zero-cache/src/server/runner/zero-dispatcher.js +0 -3
- package/out/zero-cache/src/server/runner/zero-dispatcher.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 -5
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
- package/out/zero-cache/src/services/analyze.js +9 -11
- package/out/zero-cache/src/services/analyze.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js +1 -14
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js +3 -15
- package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.d.ts +1 -11
- 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 +2 -21
- 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 +0 -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 +7 -8
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts +229 -1513
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +2 -2
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.js +6 -15
- 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/published.d.ts +20 -137
- package/out/zero-cache/src/services/change-source/pg/schema/published.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/published.js +13 -82
- package/out/zero-cache/src/services/change-source/pg/schema/published.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +21 -128
- 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 +5 -5
- 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 +0 -2
- package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +0 -3
- package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +0 -3
- 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 +1 -3
- 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.d.ts +0 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.js +3 -8
- package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
- package/out/zero-cache/src/services/litestream/config-v5.yml +1 -15
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts +0 -5
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js +1 -7
- package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.d.ts +119 -119
- package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.js +6 -5
- package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.js +16 -16
- package/out/zero-cache/src/services/replicator/change-processor.js.map +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 +0 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker-client.d.ts +2 -27
- package/out/zero-cache/src/services/replicator/write-worker-client.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker-client.js +2 -3
- package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker.js +5 -24
- package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
- package/out/zero-cache/src/services/run-ast.d.ts +0 -12
- package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
- package/out/zero-cache/src/services/run-ast.js +15 -27
- package/out/zero-cache/src/services/run-ast.js.map +1 -1
- package/out/zero-cache/src/services/runner.d.ts.map +1 -1
- package/out/zero-cache/src/services/runner.js +1 -1
- package/out/zero-cache/src/services/runner.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts +0 -45
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js +1 -133
- package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts +1 -14
- package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/drain-coordinator.js +4 -30
- package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js +2 -2
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +1 -2
- 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 +27 -48
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/query-covering.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/query-covering.js +6 -2
- package/out/zero-cache/src/services/view-syncer/query-covering.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +4 -42
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js +28 -117
- 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 +1 -11
- 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 +95 -466
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/processes.d.ts +0 -32
- package/out/zero-cache/src/types/processes.d.ts.map +1 -1
- package/out/zero-cache/src/types/processes.js +4 -43
- package/out/zero-cache/src/types/processes.js.map +1 -1
- package/out/zero-cache/src/types/profiler.d.ts +0 -12
- package/out/zero-cache/src/types/profiler.d.ts.map +1 -1
- package/out/zero-cache/src/types/profiler.js +1 -46
- package/out/zero-cache/src/types/profiler.js.map +1 -1
- package/out/zero-cache/src/types/row-key.d.ts.map +1 -1
- package/out/zero-cache/src/types/row-key.js +5 -5
- package/out/zero-cache/src/types/row-key.js.map +1 -1
- package/out/zero-cache/src/workers/connect-params.d.ts +7 -0
- package/out/zero-cache/src/workers/connect-params.d.ts.map +1 -1
- package/out/zero-cache/src/workers/connect-params.js +20 -1
- package/out/zero-cache/src/workers/connect-params.js.map +1 -1
- package/out/zero-cache/src/workers/connection.d.ts +4 -4
- package/out/zero-cache/src/workers/connection.d.ts.map +1 -1
- package/out/zero-cache/src/workers/connection.js +8 -8
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/replicator.d.ts +0 -1
- package/out/zero-cache/src/workers/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/workers/replicator.js +2 -4
- package/out/zero-cache/src/workers/replicator.js.map +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js +16 -21
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/workers/syncer.js +1 -10
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/connection-manager.d.ts +0 -20
- package/out/zero-client/src/client/connection-manager.d.ts.map +1 -1
- package/out/zero-client/src/client/connection-manager.js +13 -77
- package/out/zero-client/src/client/connection-manager.js.map +1 -1
- package/out/zero-client/src/client/connection-status-enum.d.ts +0 -2
- package/out/zero-client/src/client/connection-status-enum.d.ts.map +1 -1
- package/out/zero-client/src/client/connection-status-enum.js +1 -3
- package/out/zero-client/src/client/connection-status-enum.js.map +1 -1
- package/out/zero-client/src/client/connection.d.ts +0 -6
- package/out/zero-client/src/client/connection.d.ts.map +1 -1
- package/out/zero-client/src/client/connection.js +1 -2
- package/out/zero-client/src/client/connection.js.map +1 -1
- package/out/zero-client/src/client/context.d.ts +2 -27
- package/out/zero-client/src/client/context.d.ts.map +1 -1
- package/out/zero-client/src/client/context.js +1 -112
- package/out/zero-client/src/client/context.js.map +1 -1
- package/out/zero-client/src/client/custom.js +2 -2
- package/out/zero-client/src/client/custom.js.map +1 -1
- package/out/zero-client/src/client/error.js +1 -1
- package/out/zero-client/src/client/inspector/lazy-inspector.js +2 -2
- package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
- package/out/zero-client/src/client/ivm-branch.d.ts.map +1 -1
- package/out/zero-client/src/client/ivm-branch.js +3 -20
- package/out/zero-client/src/client/ivm-branch.js.map +1 -1
- package/out/zero-client/src/client/mutation-tracker.d.ts.map +1 -1
- package/out/zero-client/src/client/mutation-tracker.js +1 -1
- package/out/zero-client/src/client/mutation-tracker.js.map +1 -1
- package/out/zero-client/src/client/mutator-proxy.d.ts.map +1 -1
- package/out/zero-client/src/client/mutator-proxy.js +1 -2
- package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
- package/out/zero-client/src/client/options.d.ts +0 -3
- package/out/zero-client/src/client/options.d.ts.map +1 -1
- package/out/zero-client/src/client/options.js.map +1 -1
- package/out/zero-client/src/client/query-manager.d.ts +0 -2
- package/out/zero-client/src/client/query-manager.d.ts.map +1 -1
- package/out/zero-client/src/client/query-manager.js +14 -50
- package/out/zero-client/src/client/query-manager.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero-rep.d.ts +0 -1
- package/out/zero-client/src/client/zero-rep.d.ts.map +1 -1
- package/out/zero-client/src/client/zero-rep.js +6 -9
- package/out/zero-client/src/client/zero-rep.js.map +1 -1
- package/out/zero-client/src/client/zero.d.ts +3 -18
- package/out/zero-client/src/client/zero.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.js +36 -123
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-client/src/types/query-result.d.ts +0 -2
- package/out/zero-client/src/types/query-result.d.ts.map +1 -1
- package/out/zero-protocol/src/analyze-query-result.d.ts +5 -5
- package/out/zero-protocol/src/analyze-query-result.d.ts.map +1 -1
- package/out/zero-protocol/src/analyze-query-result.js +1 -1
- package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
- package/out/zero-protocol/src/ast.d.ts +1 -64
- package/out/zero-protocol/src/ast.d.ts.map +1 -1
- package/out/zero-protocol/src/ast.js +54 -225
- package/out/zero-protocol/src/ast.js.map +1 -1
- package/out/zero-protocol/src/connect.d.ts +13 -0
- package/out/zero-protocol/src/connect.d.ts.map +1 -1
- package/out/zero-protocol/src/connect.js +30 -1
- package/out/zero-protocol/src/connect.js.map +1 -1
- package/out/zero-protocol/src/down.d.ts +2 -2
- package/out/zero-protocol/src/error.d.ts +1 -1
- package/out/zero-protocol/src/inspect-down.d.ts +6 -6
- package/out/zero-protocol/src/poke.d.ts +11 -4
- package/out/zero-protocol/src/poke.d.ts.map +1 -1
- package/out/zero-protocol/src/poke.js +3 -2
- package/out/zero-protocol/src/poke.js.map +1 -1
- package/out/zero-protocol/src/protocol-flag-enum.d.ts +7 -0
- package/out/zero-protocol/src/protocol-flag-enum.d.ts.map +1 -0
- package/out/zero-protocol/src/protocol-flag-enum.js +7 -0
- package/out/zero-protocol/src/protocol-flag-enum.js.map +1 -0
- package/out/zero-protocol/src/protocol-flag.d.ts +14 -0
- package/out/zero-protocol/src/protocol-flag.d.ts.map +1 -0
- package/out/zero-protocol/src/protocol-flag.js +8 -0
- package/out/zero-protocol/src/protocol-flag.js.map +1 -0
- package/out/zero-protocol/src/protocol-version.d.ts +30 -6
- package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
- package/out/zero-protocol/src/protocol-version.js +1 -1
- package/out/zero-protocol/src/protocol-version.js.map +1 -1
- package/out/zero-protocol/src/query-hash.d.ts +2 -7
- package/out/zero-protocol/src/query-hash.d.ts.map +1 -1
- package/out/zero-protocol/src/query-hash.js +10 -10
- package/out/zero-protocol/src/query-hash.js.map +1 -1
- package/out/zero-react/src/use-query.d.ts +4 -5
- package/out/zero-react/src/use-query.d.ts.map +1 -1
- package/out/zero-react/src/use-query.js +9 -86
- package/out/zero-react/src/use-query.js.map +1 -1
- package/out/zero-solid/src/solid-view.d.ts +0 -17
- package/out/zero-solid/src/solid-view.d.ts.map +1 -1
- package/out/zero-solid/src/solid-view.js +1 -39
- package/out/zero-solid/src/solid-view.js.map +1 -1
- package/out/zero-solid/src/use-query.js +3 -5
- package/out/zero-solid/src/use-query.js.map +1 -1
- package/out/zql/src/builder/builder.d.ts +0 -19
- package/out/zql/src/builder/builder.d.ts.map +1 -1
- package/out/zql/src/builder/builder.js +11 -28
- package/out/zql/src/builder/builder.js.map +1 -1
- package/out/zql/src/builder/debug-delegate.d.ts +1 -6
- package/out/zql/src/builder/debug-delegate.d.ts.map +1 -1
- package/out/zql/src/builder/debug-delegate.js +2 -14
- package/out/zql/src/builder/debug-delegate.js.map +1 -1
- package/out/zql/src/ivm/array-view.d.ts +0 -29
- package/out/zql/src/ivm/array-view.d.ts.map +1 -1
- package/out/zql/src/ivm/array-view.js +12 -63
- package/out/zql/src/ivm/array-view.js.map +1 -1
- package/out/zql/src/ivm/cap.d.ts.map +1 -1
- package/out/zql/src/ivm/cap.js +3 -5
- package/out/zql/src/ivm/cap.js.map +1 -1
- package/out/zql/src/ivm/data.d.ts.map +1 -1
- package/out/zql/src/ivm/data.js +3 -8
- package/out/zql/src/ivm/data.js.map +1 -1
- package/out/zql/src/ivm/exists.d.ts +1 -1
- package/out/zql/src/ivm/exists.d.ts.map +1 -1
- package/out/zql/src/ivm/exists.js.map +1 -1
- package/out/zql/src/ivm/fan-in.d.ts +2 -2
- package/out/zql/src/ivm/fan-in.d.ts.map +1 -1
- package/out/zql/src/ivm/fan-in.js +5 -6
- package/out/zql/src/ivm/fan-in.js.map +1 -1
- package/out/zql/src/ivm/fan-out.d.ts +2 -2
- package/out/zql/src/ivm/fan-out.d.ts.map +1 -1
- package/out/zql/src/ivm/fan-out.js.map +1 -1
- package/out/zql/src/ivm/filter-operators.d.ts +6 -7
- package/out/zql/src/ivm/filter-operators.d.ts.map +1 -1
- package/out/zql/src/ivm/filter-operators.js +12 -29
- package/out/zql/src/ivm/filter-operators.js.map +1 -1
- package/out/zql/src/ivm/filter.d.ts +2 -2
- package/out/zql/src/ivm/filter.d.ts.map +1 -1
- package/out/zql/src/ivm/filter.js +4 -7
- package/out/zql/src/ivm/filter.js.map +1 -1
- package/out/zql/src/ivm/flipped-join.js +2 -2
- package/out/zql/src/ivm/flipped-join.js.map +1 -1
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts +1 -1
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts.map +1 -1
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.js +4 -6
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
- package/out/zql/src/ivm/memory-source.d.ts +3 -15
- package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
- package/out/zql/src/ivm/memory-source.js +23 -57
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/ivm/operator.d.ts +0 -40
- package/out/zql/src/ivm/operator.d.ts.map +1 -1
- package/out/zql/src/ivm/operator.js.map +1 -1
- package/out/zql/src/ivm/take.d.ts.map +1 -1
- package/out/zql/src/ivm/take.js +4 -3
- package/out/zql/src/ivm/take.js.map +1 -1
- package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
- package/out/zql/src/ivm/union-fan-in.js +7 -9
- package/out/zql/src/ivm/union-fan-in.js.map +1 -1
- package/out/zql/src/ivm/view-apply-change.d.ts.map +1 -1
- package/out/zql/src/ivm/view-apply-change.js +4 -9
- package/out/zql/src/ivm/view-apply-change.js.map +1 -1
- package/out/zql/src/ivm/view.d.ts +0 -15
- package/out/zql/src/ivm/view.d.ts.map +1 -1
- package/out/zql/src/mutate/mutator-registry.d.ts.map +1 -1
- package/out/zql/src/mutate/mutator-registry.js +2 -2
- package/out/zql/src/mutate/mutator-registry.js.map +1 -1
- package/out/zql/src/planner/planner-builder.d.ts +3 -12
- package/out/zql/src/planner/planner-builder.d.ts.map +1 -1
- package/out/zql/src/planner/planner-builder.js +20 -40
- package/out/zql/src/planner/planner-builder.js.map +1 -1
- package/out/zql/src/planner/planner-connection.d.ts +2 -28
- package/out/zql/src/planner/planner-connection.d.ts.map +1 -1
- package/out/zql/src/planner/planner-connection.js +8 -108
- package/out/zql/src/planner/planner-connection.js.map +1 -1
- package/out/zql/src/planner/planner-debug.d.ts +1 -1
- package/out/zql/src/planner/planner-debug.d.ts.map +1 -1
- package/out/zql/src/planner/planner-debug.js +8 -3
- package/out/zql/src/planner/planner-debug.js.map +1 -1
- package/out/zql/src/planner/planner-fan-out.d.ts +0 -1
- package/out/zql/src/planner/planner-fan-out.d.ts.map +1 -1
- package/out/zql/src/planner/planner-fan-out.js +0 -3
- package/out/zql/src/planner/planner-fan-out.js.map +1 -1
- package/out/zql/src/planner/planner-graph.d.ts +0 -2
- package/out/zql/src/planner/planner-graph.d.ts.map +1 -1
- package/out/zql/src/planner/planner-graph.js +0 -2
- package/out/zql/src/planner/planner-graph.js.map +1 -1
- package/out/zql/src/planner/planner-join.d.ts +0 -2
- package/out/zql/src/planner/planner-join.d.ts.map +1 -1
- package/out/zql/src/planner/planner-join.js +0 -7
- package/out/zql/src/planner/planner-join.js.map +1 -1
- package/out/zql/src/planner/planner-node.d.ts +1 -2
- package/out/zql/src/planner/planner-node.d.ts.map +1 -1
- package/out/zql/src/planner/planner-node.js.map +1 -1
- package/out/zql/src/planner/planner-source.d.ts +2 -2
- package/out/zql/src/planner/planner-source.d.ts.map +1 -1
- package/out/zql/src/planner/planner-source.js +2 -2
- package/out/zql/src/planner/planner-source.js.map +1 -1
- package/out/zql/src/query/query-delegate-base.d.ts +1 -9
- package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
- package/out/zql/src/query/query-delegate-base.js +36 -132
- package/out/zql/src/query/query-delegate-base.js.map +1 -1
- package/out/zql/src/query/query-delegate.d.ts +1 -50
- package/out/zql/src/query/query-delegate.d.ts.map +1 -1
- package/out/zql/src/query/query-impl.d.ts +14 -25
- package/out/zql/src/query/query-impl.d.ts.map +1 -1
- package/out/zql/src/query/query-impl.js +94 -397
- package/out/zql/src/query/query-impl.js.map +1 -1
- package/out/zql/src/query/query-internals.d.ts +11 -21
- package/out/zql/src/query/query-internals.d.ts.map +1 -1
- package/out/zql/src/query/query-internals.js.map +1 -1
- package/out/zql/src/query/query.d.ts +2 -11
- package/out/zql/src/query/query.d.ts.map +1 -1
- package/out/zql/src/query/runnable-query-impl.d.ts +2 -3
- package/out/zql/src/query/runnable-query-impl.d.ts.map +1 -1
- package/out/zql/src/query/runnable-query-impl.js +2 -15
- package/out/zql/src/query/runnable-query-impl.js.map +1 -1
- package/out/zql/src/query/typed-view.d.ts +1 -1
- package/out/zql/src/query/typed-view.d.ts.map +1 -1
- package/out/zqlite/src/db.d.ts +1 -9
- package/out/zqlite/src/db.d.ts.map +1 -1
- package/out/zqlite/src/db.js +2 -8
- package/out/zqlite/src/db.js.map +1 -1
- package/out/zqlite/src/query-builder.d.ts +1 -1
- package/out/zqlite/src/query-builder.d.ts.map +1 -1
- package/out/zqlite/src/query-builder.js +2 -4
- package/out/zqlite/src/query-builder.js.map +1 -1
- package/out/zqlite/src/query-delegate.d.ts.map +1 -1
- package/out/zqlite/src/query-delegate.js +6 -5
- package/out/zqlite/src/query-delegate.js.map +1 -1
- package/out/zqlite/src/sqlite-cost-model.d.ts.map +1 -1
- package/out/zqlite/src/sqlite-cost-model.js +30 -2
- package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
- package/out/zqlite/src/sqlite-stat-fanout.d.ts.map +1 -1
- package/out/zqlite/src/sqlite-stat-fanout.js +5 -4
- package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
- package/out/zqlite/src/table-source.d.ts +3 -16
- package/out/zqlite/src/table-source.d.ts.map +1 -1
- package/out/zqlite/src/table-source.js +14 -33
- package/out/zqlite/src/table-source.js.map +1 -1
- package/package.json +3 -2
- package/out/replicache/src/dag/invalid-ref-count-error.d.ts +0 -12
- package/out/replicache/src/dag/invalid-ref-count-error.d.ts.map +0 -1
- package/out/replicache/src/dag/invalid-ref-count-error.js +0 -19
- package/out/replicache/src/dag/invalid-ref-count-error.js.map +0 -1
- package/out/shared/src/xxhash32.d.ts +0 -36
- package/out/shared/src/xxhash32.d.ts.map +0 -1
- package/out/shared/src/xxhash32.js +0 -51
- package/out/shared/src/xxhash32.js.map +0 -1
- package/out/zero-cache/src/db/index-predicate.d.ts +0 -4
- package/out/zero-cache/src/db/index-predicate.d.ts.map +0 -1
- package/out/zero-cache/src/db/index-predicate.js +0 -46
- package/out/zero-cache/src/db/index-predicate.js.map +0 -1
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts +0 -19
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js +0 -581
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js.map +0 -1
- package/out/zero-cache/src/services/litestream/litestream-checkpointer.js +0 -131
- package/out/zero-cache/src/services/litestream/litestream-checkpointer.js.map +0 -1
- package/out/zero-cache/src/services/litestream/litestream-controller.d.ts +0 -70
- package/out/zero-cache/src/services/litestream/litestream-controller.d.ts.map +0 -1
- package/out/zero-cache/src/services/litestream/litestream-controller.js +0 -101
- package/out/zero-cache/src/services/litestream/litestream-controller.js.map +0 -1
- package/out/zero-cache/src/services/profz.d.ts +0 -7
- package/out/zero-cache/src/services/profz.d.ts.map +0 -1
- package/out/zero-cache/src/services/profz.js +0 -103
- package/out/zero-cache/src/services/profz.js.map +0 -1
- package/out/zero-cache/src/services/view-syncer/hydration-budget.d.ts +0 -33
- package/out/zero-cache/src/services/view-syncer/hydration-budget.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/hydration-budget.js +0 -62
- package/out/zero-cache/src/services/view-syncer/hydration-budget.js.map +0 -1
- package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts +0 -51
- package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js +0 -78
- package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js.map +0 -1
- package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.d.ts +0 -95
- package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.d.ts.map +0 -1
- package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js +0 -155
- package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js.map +0 -1
- package/out/zero-protocol/src/query-hash-visitor.d.ts +0 -81
- package/out/zero-protocol/src/query-hash-visitor.d.ts.map +0 -1
- package/out/zero-protocol/src/query-hash-visitor.js +0 -324
- package/out/zero-protocol/src/query-hash-visitor.js.map +0 -1
- package/out/zql/src/builder/correlated-predicate-pushdown.d.ts +0 -43
- package/out/zql/src/builder/correlated-predicate-pushdown.d.ts.map +0 -1
- package/out/zql/src/builder/correlated-predicate-pushdown.js +0 -161
- package/out/zql/src/builder/correlated-predicate-pushdown.js.map +0 -1
- package/out/zql/src/ivm/connection-index.d.ts +0 -68
- package/out/zql/src/ivm/connection-index.d.ts.map +0 -1
- package/out/zql/src/ivm/connection-index.js +0 -149
- package/out/zql/src/ivm/connection-index.js.map +0 -1
- package/out/zql/src/ivm/deferred-input.d.ts +0 -44
- package/out/zql/src/ivm/deferred-input.d.ts.map +0 -1
- package/out/zql/src/ivm/deferred-input.js +0 -89
- package/out/zql/src/ivm/deferred-input.js.map +0 -1
- package/out/zql/src/planner/planner-filter.d.ts +0 -59
- package/out/zql/src/planner/planner-filter.d.ts.map +0 -1
- package/out/zql/src/planner/planner-filter.js +0 -116
- package/out/zql/src/planner/planner-filter.js.map +0 -1
- package/out/zql/src/query/query-transitions.d.ts +0 -171
- package/out/zql/src/query/query-transitions.d.ts.map +0 -1
- package/out/zql/src/query/query-transitions.js +0 -204
- package/out/zql/src/query/query-transitions.js.map +0 -1
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import { AbortError } from "../../../../shared/src/abort-error.js";
|
|
2
|
-
import { RunningState } from "../running-state.js";
|
|
3
|
-
//#region ../zero-cache/src/services/litestream/litestream-checkpointer.ts
|
|
4
|
-
var SOFT_CHECKPOINT_WAIT_MS = 1e4;
|
|
5
|
-
var LITESTREAM_SYNC_TIMEOUT_MS = 5 * 6e4;
|
|
6
|
-
var PAUSE_POLL_INTERVAL_MS = 1e3;
|
|
7
|
-
var PAUSE_LOG_INTERVAL_POLLS = 30;
|
|
8
|
-
/**
|
|
9
|
-
* Forces litestream to checkpoint the WAL from the write path, so a sustained
|
|
10
|
-
* high write rate can't grow the WAL until litestream's own blocking TRUNCATE
|
|
11
|
-
* checkpoint locks the db (see ReplicationPauseCheckpointConfig).
|
|
12
|
-
*
|
|
13
|
-
* Two tiers:
|
|
14
|
-
* - Soft (`checkpointThresholdPages`): after each committed transaction,
|
|
15
|
-
* once the un-checkpointed WAL backlog reaches the threshold, force an
|
|
16
|
-
* immediate litestream sync (which seals the WAL to LTX and attempts
|
|
17
|
-
* litestream's own PASSIVE checkpoint) and block the write path until it
|
|
18
|
-
* completes, but only up to {@link SOFT_CHECKPOINT_WAIT_MS}. Waiting is the
|
|
19
|
-
* backpressure: it throttles upstream reads to litestream's WAL-seal rate
|
|
20
|
-
* and, in the common case, gives litestream a window to checkpoint without
|
|
21
|
-
* writer interference. A single sync is kept in flight at a time
|
|
22
|
-
* (`#pendingSync`): if one is still running when the budget elapses, the
|
|
23
|
-
* writer proceeds and the next commit waits for that sync rather than
|
|
24
|
-
* issuing (and orphaning) another. Backs off by a full threshold's worth of
|
|
25
|
-
* WAL growth whenever an attempt fails to drain the WAL (e.g. litestream
|
|
26
|
-
* is mid-snapshot and its checkpoints are being skipped), so repeated
|
|
27
|
-
* failures don't reattempt on every commit.
|
|
28
|
-
* - Hard (`walMaxPages`, optional): if the WAL still exceeds this cap after
|
|
29
|
-
* a soft attempt, pause (block the caller) polling litestream until the
|
|
30
|
-
* WAL drains below it. This is the fail-safe against unbounded WAL growth
|
|
31
|
-
* when litestream structurally cannot checkpoint — e.g. an in-progress
|
|
32
|
-
* snapshot holds litestream's checkpoint lock for its full duration, so
|
|
33
|
-
* neither PASSIVE nor TRUNCATE checkpoints can run until it completes.
|
|
34
|
-
*/
|
|
35
|
-
var LitestreamCheckpointer = class {
|
|
36
|
-
#lc;
|
|
37
|
-
#db;
|
|
38
|
-
#litestream;
|
|
39
|
-
#attemptChunk;
|
|
40
|
-
#maxWalPages;
|
|
41
|
-
#state = new RunningState("litestream-checkpointer");
|
|
42
|
-
#nextWalThreshold;
|
|
43
|
-
#pendingSync;
|
|
44
|
-
constructor(lc, db, litestream, config) {
|
|
45
|
-
this.#lc = lc.withContext("component", "litestream-checkpointer");
|
|
46
|
-
this.#db = db;
|
|
47
|
-
this.#litestream = litestream;
|
|
48
|
-
this.#attemptChunk = config.checkpointThresholdPages;
|
|
49
|
-
this.#nextWalThreshold = config.checkpointThresholdPages;
|
|
50
|
-
this.#maxWalPages = config.maxWalPages;
|
|
51
|
-
}
|
|
52
|
-
#getNumWalPages() {
|
|
53
|
-
const [{ log, checkpointed }] = this.#db.pragma("wal_checkpoint(NOOP)");
|
|
54
|
-
return log - checkpointed;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Call after a committed transaction. No-ops unless the WAL has grown to
|
|
58
|
-
* at least the current threshold, in which case it triggers a litestream
|
|
59
|
-
* sync to perform a checkpoint. Assuming that snapshots are disabled, the
|
|
60
|
-
* checkpoint always succeeds. In the case where the server is configured to
|
|
61
|
-
* perform snapshots and checkpoints fail, the "emergency break"
|
|
62
|
-
* {@link #pauseUntilWalDrained()} option provides the disk use protection
|
|
63
|
-
* that litestream's (disabled) truncate-page-n would have served.
|
|
64
|
-
*/
|
|
65
|
-
async maybeCheckpoint() {
|
|
66
|
-
let walPages = this.#getNumWalPages();
|
|
67
|
-
if (walPages < this.#nextWalThreshold) return;
|
|
68
|
-
this.#pendingSync ??= this.#sync(walPages);
|
|
69
|
-
await this.#waitFor(this.#pendingSync, SOFT_CHECKPOINT_WAIT_MS);
|
|
70
|
-
walPages = this.#getNumWalPages();
|
|
71
|
-
if (this.#maxWalPages !== void 0 && walPages > this.#maxWalPages) walPages = await this.#pauseUntilWalDrained(this.#maxWalPages);
|
|
72
|
-
this.#nextWalThreshold = walPages + this.#attemptChunk;
|
|
73
|
-
}
|
|
74
|
-
async #waitFor(p, timeoutMs) {
|
|
75
|
-
if (await Promise.race([p, this.#state.sleep(timeoutMs).then(() => "timed-out")]) === "timed-out") this.#lc.warn?.(`timed out waiting for /sync result (${timeoutMs}ms)`);
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Runs a single litestream sync, logs its outcome, and clears
|
|
79
|
-
* {@link #pendingSync} when it settles. Never rejects: sync failures and
|
|
80
|
-
* post-sync bookkeeping errors (e.g. the db being closed during shutdown)
|
|
81
|
-
* are caught and logged, so the shared promise can be awaited from multiple
|
|
82
|
-
* places without risking an unhandled rejection.
|
|
83
|
-
*/
|
|
84
|
-
async #sync(walPagesBefore) {
|
|
85
|
-
const start = performance.now();
|
|
86
|
-
let result;
|
|
87
|
-
let err;
|
|
88
|
-
try {
|
|
89
|
-
try {
|
|
90
|
-
result = await this.#litestream.sync({
|
|
91
|
-
wait: false,
|
|
92
|
-
timeoutMs: LITESTREAM_SYNC_TIMEOUT_MS
|
|
93
|
-
}, this.#state.signal);
|
|
94
|
-
} catch (e) {
|
|
95
|
-
err = e;
|
|
96
|
-
}
|
|
97
|
-
const elapsed = performance.now() - start;
|
|
98
|
-
const newWalPages = this.#getNumWalPages();
|
|
99
|
-
if (newWalPages < this.#nextWalThreshold) this.#lc.info?.(`checkpointed ${walPagesBefore} -> ${newWalPages} wal pages (${elapsed.toFixed(2)} ms)`, result);
|
|
100
|
-
else if (result) this.#lc.warn?.(`checkpoint skipped ${walPagesBefore} -> ${newWalPages}. a snapshot may be in progress (${elapsed.toFixed(2)} ms)`, result);
|
|
101
|
-
else this.#lc.warn?.(`checkpoint failed ${walPagesBefore} -> ${newWalPages} (${elapsed.toFixed(2)} ms)`, err);
|
|
102
|
-
} catch (e) {
|
|
103
|
-
this.#lc.warn?.("litestream-checkpointer sync post-processing failed", e);
|
|
104
|
-
} finally {
|
|
105
|
-
this.#pendingSync = void 0;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
async #pauseUntilWalDrained(maxWalPages) {
|
|
109
|
-
for (let i = 0; this.#state.shouldRun(); i++) {
|
|
110
|
-
const walPages = this.#getNumWalPages();
|
|
111
|
-
if (walPages < maxWalPages) {
|
|
112
|
-
this.#lc.info?.(`wal size (${walPages} pages) is under the max limit of ${maxWalPages}`);
|
|
113
|
-
return walPages;
|
|
114
|
-
}
|
|
115
|
-
if (i % PAUSE_LOG_INTERVAL_POLLS === 0) this.#lc.warn?.(`waiting for litestream to successfully checkpoint ${walPages} wal pages`);
|
|
116
|
-
await this.#state.sleep(PAUSE_POLL_INTERVAL_MS);
|
|
117
|
-
this.#pendingSync ??= this.#sync(this.#getNumWalPages());
|
|
118
|
-
await this.#pendingSync;
|
|
119
|
-
}
|
|
120
|
-
throw new AbortError("litestream-controller stopped");
|
|
121
|
-
}
|
|
122
|
-
/** Releases the underlying litestream controller's connection. */
|
|
123
|
-
close() {
|
|
124
|
-
this.#litestream.close();
|
|
125
|
-
this.#state.stop(this.#lc);
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
//#endregion
|
|
129
|
-
export { LitestreamCheckpointer };
|
|
130
|
-
|
|
131
|
-
//# sourceMappingURL=litestream-checkpointer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"litestream-checkpointer.js","names":["#lc","#db","#litestream","#attemptChunk","#maxWalPages","#state","#nextWalThreshold","#getNumWalPages","#pendingSync","#sync","#waitFor","#pauseUntilWalDrained"],"sources":["../../../../../../zero-cache/src/services/litestream/litestream-checkpointer.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {AbortError} from '../../../../shared/src/abort-error.ts';\nimport type {Database} from '../../../../zqlite/src/db.ts';\nimport type {ForceCheckpointConfig} from '../replicator/write-worker-client.ts';\nimport {RunningState} from '../running-state.ts';\nimport type {\n LitestreamController,\n SyncResponse,\n} from './litestream-controller.ts';\n\n// How long the soft path blocks the write path waiting for an in-flight sync\n// before letting the writer proceed. This is a backpressure knob, *not* a\n// request timeout: letting it elapse leaves the sync running (see #pendingSync)\n// rather than abandoning it. In normal operation litestream seals + runs its\n// PASSIVE checkpoint well within this budget; a sync that regularly exceeds it\n// indicates a litestream-side problem to fix (e.g. unintended snapshot), not a\n// value to raise.\nexport const SOFT_CHECKPOINT_WAIT_MS = 10_000;\n// Liveness bound on the /sync request itself, well above the soft wait budget.\n// It exists only to recover from a genuinely wedged litestream (so #pendingSync\n// can't hang forever).\nconst LITESTREAM_SYNC_TIMEOUT_MS = 5 * 60_000;\nconst PAUSE_POLL_INTERVAL_MS = 1_000;\nconst PAUSE_LOG_INTERVAL_POLLS = 30;\n\n/**\n * Forces litestream to checkpoint the WAL from the write path, so a sustained\n * high write rate can't grow the WAL until litestream's own blocking TRUNCATE\n * checkpoint locks the db (see ReplicationPauseCheckpointConfig).\n *\n * Two tiers:\n * - Soft (`checkpointThresholdPages`): after each committed transaction,\n * once the un-checkpointed WAL backlog reaches the threshold, force an\n * immediate litestream sync (which seals the WAL to LTX and attempts\n * litestream's own PASSIVE checkpoint) and block the write path until it\n * completes, but only up to {@link SOFT_CHECKPOINT_WAIT_MS}. Waiting is the\n * backpressure: it throttles upstream reads to litestream's WAL-seal rate\n * and, in the common case, gives litestream a window to checkpoint without\n * writer interference. A single sync is kept in flight at a time\n * (`#pendingSync`): if one is still running when the budget elapses, the\n * writer proceeds and the next commit waits for that sync rather than\n * issuing (and orphaning) another. Backs off by a full threshold's worth of\n * WAL growth whenever an attempt fails to drain the WAL (e.g. litestream\n * is mid-snapshot and its checkpoints are being skipped), so repeated\n * failures don't reattempt on every commit.\n * - Hard (`walMaxPages`, optional): if the WAL still exceeds this cap after\n * a soft attempt, pause (block the caller) polling litestream until the\n * WAL drains below it. This is the fail-safe against unbounded WAL growth\n * when litestream structurally cannot checkpoint — e.g. an in-progress\n * snapshot holds litestream's checkpoint lock for its full duration, so\n * neither PASSIVE nor TRUNCATE checkpoints can run until it completes.\n */\nexport class LitestreamCheckpointer {\n readonly #lc: LogContext;\n readonly #db: Database;\n readonly #litestream: LitestreamController;\n readonly #attemptChunk: number;\n readonly #maxWalPages: number | undefined;\n readonly #state = new RunningState('litestream-checkpointer');\n #nextWalThreshold: number;\n #pendingSync: Promise<void> | undefined;\n\n constructor(\n lc: LogContext,\n db: Database,\n litestream: LitestreamController,\n config: ForceCheckpointConfig,\n ) {\n this.#lc = lc.withContext('component', 'litestream-checkpointer');\n this.#db = db;\n this.#litestream = litestream;\n this.#attemptChunk = config.checkpointThresholdPages;\n this.#nextWalThreshold = config.checkpointThresholdPages;\n this.#maxWalPages = config.maxWalPages;\n }\n\n #getNumWalPages(): number {\n // wal_checkpoint(NOOP) reads WAL status without doing any work: `log` is\n // the current WAL frame count and `checkpointed` is how many of those\n // frames are already applied to the main db, so `log - checkpointed` is\n // the un-checkpointed backlog that a forced sync would actually drain.\n // (Both reset once litestream restarts the WAL.)\n const [{log, checkpointed}] = this.#db.pragma<{\n log: number;\n checkpointed: number;\n }>('wal_checkpoint(NOOP)');\n return log - checkpointed;\n }\n\n /**\n * Call after a committed transaction. No-ops unless the WAL has grown to\n * at least the current threshold, in which case it triggers a litestream\n * sync to perform a checkpoint. Assuming that snapshots are disabled, the\n * checkpoint always succeeds. In the case where the server is configured to\n * perform snapshots and checkpoints fail, the \"emergency break\"\n * {@link #pauseUntilWalDrained()} option provides the disk use protection\n * that litestream's (disabled) truncate-page-n would have served.\n */\n async maybeCheckpoint(): Promise<void> {\n let walPages = this.#getNumWalPages();\n if (walPages < this.#nextWalThreshold) {\n return;\n }\n\n // Single-flight: reuse the outstanding sync if one is already running.\n this.#pendingSync ??= this.#sync(walPages);\n\n // Soft backpressure: block the write path until the sync completes, but no\n // longer than the wait budget. Letting the budget elapse lets the writer\n // proceed *without* cancelling the sync (it keeps running, tracked by\n // #pendingSync); unbounded WAL growth is caught by the hard cap below.\n await this.#waitFor(this.#pendingSync, SOFT_CHECKPOINT_WAIT_MS);\n\n walPages = this.#getNumWalPages();\n if (this.#maxWalPages !== undefined && walPages > this.#maxWalPages) {\n // Emergency brake when best-effort checkpoints are being skipped\n // (i.e. during a snapshot) and the WAL is approaching disk capacity.\n // Pausing replication is favorable to litestream's truncate-page-n\n // because the latter involves re-encoding the database under the lock\n // (https://github.com/benbjohnson/litestream/issues/1332), which would\n // incur the full latency of an additional snapshot.\n walPages = await this.#pauseUntilWalDrained(this.#maxWalPages);\n }\n\n // In the common case where checkpoints are successful, this is always\n // just `attemptChunk`. When a checkpoint doesn't drain the WAL (e.g.\n // litestream is performing a snapshot), wait for another chunk of growth\n // before the next attempt.\n this.#nextWalThreshold = walPages + this.#attemptChunk;\n }\n\n async #waitFor(p: Promise<void>, timeoutMs: number): Promise<void> {\n const result = await Promise.race([\n p,\n this.#state.sleep(timeoutMs).then(() => 'timed-out' as const),\n ]);\n if (result === 'timed-out') {\n this.#lc.warn?.(`timed out waiting for /sync result (${timeoutMs}ms)`);\n }\n }\n\n /**\n * Runs a single litestream sync, logs its outcome, and clears\n * {@link #pendingSync} when it settles. Never rejects: sync failures and\n * post-sync bookkeeping errors (e.g. the db being closed during shutdown)\n * are caught and logged, so the shared promise can be awaited from multiple\n * places without risking an unhandled rejection.\n */\n async #sync(walPagesBefore: number): Promise<void> {\n const start = performance.now();\n let result: SyncResponse | undefined;\n let err: unknown;\n try {\n try {\n result = await this.#litestream.sync(\n {wait: false, timeoutMs: LITESTREAM_SYNC_TIMEOUT_MS},\n this.#state.signal,\n );\n } catch (e) {\n err = e;\n }\n\n const elapsed = performance.now() - start;\n const newWalPages = this.#getNumWalPages();\n if (newWalPages < this.#nextWalThreshold) {\n this.#lc.info?.(\n `checkpointed ${walPagesBefore} -> ${newWalPages} wal pages (${elapsed.toFixed(2)} ms)`,\n result,\n );\n } else if (result) {\n this.#lc.warn?.(\n `checkpoint skipped ${walPagesBefore} -> ${newWalPages}. a snapshot may be in progress (${elapsed.toFixed(2)} ms)`,\n result,\n );\n } else {\n this.#lc.warn?.(\n `checkpoint failed ${walPagesBefore} -> ${newWalPages} (${elapsed.toFixed(2)} ms)`,\n err,\n );\n }\n } catch (e) {\n this.#lc.warn?.('litestream-checkpointer sync post-processing failed', e);\n } finally {\n this.#pendingSync = undefined;\n }\n }\n\n async #pauseUntilWalDrained(maxWalPages: number): Promise<number> {\n for (let i = 0; this.#state.shouldRun(); i++) {\n const walPages = this.#getNumWalPages();\n if (walPages < maxWalPages) {\n this.#lc.info?.(\n `wal size (${walPages} pages) is under the max limit of ${maxWalPages}`,\n );\n return walPages;\n }\n\n // Log every 30 seconds.\n if (i % PAUSE_LOG_INTERVAL_POLLS === 0) {\n this.#lc.warn?.(\n `waiting for litestream to successfully checkpoint ${walPages} wal pages`,\n );\n }\n await this.#state.sleep(PAUSE_POLL_INTERVAL_MS);\n // Single-flight: reuse the outstanding sync if one is already running.\n this.#pendingSync ??= this.#sync(this.#getNumWalPages());\n // await the full liveness timeout configured in #sync()\n await this.#pendingSync;\n }\n throw new AbortError('litestream-controller stopped');\n }\n\n /** Releases the underlying litestream controller's connection. */\n close(): void {\n this.#litestream.close();\n this.#state.stop(this.#lc);\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import type { LogContext } from '@rocicorp/logger';
|
|
2
|
-
/**
|
|
3
|
-
* Path of the litestream control socket, derived from the replica file's
|
|
4
|
-
* directory. The socket must live on a filesystem the (non-root) runtime user
|
|
5
|
-
* can write — the replica directory is guaranteed writable since litestream
|
|
6
|
-
* writes the replica there — so it must NOT default to a root-owned location
|
|
7
|
-
* like /var/run. Kept in sync with config-v5.yml's `socket.path`, which is set
|
|
8
|
-
* from `ZERO_LITESTREAM_SOCKET_PATH` (see getLitestream() in commands.ts).
|
|
9
|
-
*/
|
|
10
|
-
export declare function litestreamSocketPath(replicaFile: string): string;
|
|
11
|
-
/** Response body from litestream's `POST /sync` control endpoint. */
|
|
12
|
-
export type SyncResponse = {
|
|
13
|
-
/**
|
|
14
|
-
* `synced` when `wait` was true and the upload completed, `synced_local`
|
|
15
|
-
* when the WAL was sealed locally without waiting on the remote replica, or
|
|
16
|
-
* `no_change` when there was nothing to sync.
|
|
17
|
-
*/
|
|
18
|
-
status: 'synced' | 'synced_local' | 'no_change';
|
|
19
|
-
path: string;
|
|
20
|
-
txid: number;
|
|
21
|
-
replicated_txid: number;
|
|
22
|
-
};
|
|
23
|
-
export type SyncOptions = {
|
|
24
|
-
/**
|
|
25
|
-
* When true, block until the sealed WAL has been uploaded to the remote
|
|
26
|
-
* replica (litestream's SyncAndWait). When false (the default), litestream
|
|
27
|
-
* only seals the WAL into local LTX files and runs its checkpoint thresholds,
|
|
28
|
-
* returning without waiting on the backup store. Use false on the write path
|
|
29
|
-
* where the goal is to keep the WAL small, not to durably back it up.
|
|
30
|
-
*/
|
|
31
|
-
wait?: boolean;
|
|
32
|
-
/** Overall request timeout in milliseconds. Defaults to 30s. */
|
|
33
|
-
timeoutMs?: number;
|
|
34
|
-
};
|
|
35
|
-
/**
|
|
36
|
-
* IPC client for the litestream control server. litestream serves an HTTP API
|
|
37
|
-
* over a Unix domain socket (enabled via the `socket:` block in config-v5.yml);
|
|
38
|
-
* because zero-cache runs litestream as a subprocess in the same container, the
|
|
39
|
-
* socket is reachable directly on the local filesystem.
|
|
40
|
-
*
|
|
41
|
-
* A single keep-alive connection is held open and reused across calls, so a
|
|
42
|
-
* high-frequency caller (e.g. write-path backpressure) does not pay a
|
|
43
|
-
* connect/handshake per request.
|
|
44
|
-
*/
|
|
45
|
-
export declare class LitestreamController {
|
|
46
|
-
#private;
|
|
47
|
-
/**
|
|
48
|
-
* @param replicaFile Path of the replica database litestream is managing —
|
|
49
|
-
* the same value passed to litestream as `ZERO_REPLICA_FILE`. This is the
|
|
50
|
-
* `path` litestream matches control requests against.
|
|
51
|
-
* @param socketPath Path of the control socket; defaults to the path derived
|
|
52
|
-
* from `replicaFile`, matching config-v5.yml's `socket.path`.
|
|
53
|
-
*/
|
|
54
|
-
constructor(lc: LogContext, replicaFile: string, socketPath?: string);
|
|
55
|
-
/**
|
|
56
|
-
* Forces litestream to sync the WAL into LTX files immediately and run its
|
|
57
|
-
* checkpoint thresholds, instead of waiting for the next monitor-interval
|
|
58
|
-
* tick. The checkpoint itself is still litestream's usual (non-blocking)
|
|
59
|
-
* PASSIVE attempt, so it is skipped if the database is busy — call this once
|
|
60
|
-
* writes are quiesced.
|
|
61
|
-
*
|
|
62
|
-
* Best-effort: callers should tolerate rejection (e.g. ECONNREFUSED/ENOENT
|
|
63
|
-
* while litestream is restarting or before it has created the socket) rather
|
|
64
|
-
* than blocking application progress on it.
|
|
65
|
-
*/
|
|
66
|
-
sync(opts?: SyncOptions, signal?: AbortSignal): Promise<SyncResponse>;
|
|
67
|
-
/** Releases the pooled keep-alive connection. */
|
|
68
|
-
close(): void;
|
|
69
|
-
}
|
|
70
|
-
//# sourceMappingURL=litestream-controller.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"litestream-controller.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/litestream/litestream-controller.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEhE;AAID,qEAAqE;AACrE,MAAM,MAAM,YAAY,GAAG;IACzB;;;;OAIG;IACH,MAAM,EAAE,QAAQ,GAAG,cAAc,GAAG,WAAW,CAAC;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;;;GASG;AACH,qBAAa,oBAAoB;;IAM/B;;;;;;OAMG;gBAED,EAAE,EAAE,UAAU,EACd,WAAW,EAAE,MAAM,EACnB,UAAU,SAAoC;IAUhD;;;;;;;;;;OAUG;IACG,IAAI,CACR,IAAI,GAAE,WAAgB,EACtB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,YAAY,CAAC;IAYxB,iDAAiD;IACjD,KAAK,IAAI,IAAI;CA8Dd"}
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { Agent, request } from "node:http";
|
|
2
|
-
import { json } from "node:stream/consumers";
|
|
3
|
-
//#region ../zero-cache/src/services/litestream/litestream-controller.ts
|
|
4
|
-
/**
|
|
5
|
-
* Path of the litestream control socket, derived from the replica file's
|
|
6
|
-
* directory. The socket must live on a filesystem the (non-root) runtime user
|
|
7
|
-
* can write — the replica directory is guaranteed writable since litestream
|
|
8
|
-
* writes the replica there — so it must NOT default to a root-owned location
|
|
9
|
-
* like /var/run. Kept in sync with config-v5.yml's `socket.path`, which is set
|
|
10
|
-
* from `ZERO_LITESTREAM_SOCKET_PATH` (see getLitestream() in commands.ts).
|
|
11
|
-
*/
|
|
12
|
-
function litestreamSocketPath(replicaFile) {
|
|
13
|
-
return `${replicaFile}.sock`;
|
|
14
|
-
}
|
|
15
|
-
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
16
|
-
/**
|
|
17
|
-
* IPC client for the litestream control server. litestream serves an HTTP API
|
|
18
|
-
* over a Unix domain socket (enabled via the `socket:` block in config-v5.yml);
|
|
19
|
-
* because zero-cache runs litestream as a subprocess in the same container, the
|
|
20
|
-
* socket is reachable directly on the local filesystem.
|
|
21
|
-
*
|
|
22
|
-
* A single keep-alive connection is held open and reused across calls, so a
|
|
23
|
-
* high-frequency caller (e.g. write-path backpressure) does not pay a
|
|
24
|
-
* connect/handshake per request.
|
|
25
|
-
*/
|
|
26
|
-
var LitestreamController = class {
|
|
27
|
-
#lc;
|
|
28
|
-
#replicaFile;
|
|
29
|
-
#socketPath;
|
|
30
|
-
#agent;
|
|
31
|
-
/**
|
|
32
|
-
* @param replicaFile Path of the replica database litestream is managing —
|
|
33
|
-
* the same value passed to litestream as `ZERO_REPLICA_FILE`. This is the
|
|
34
|
-
* `path` litestream matches control requests against.
|
|
35
|
-
* @param socketPath Path of the control socket; defaults to the path derived
|
|
36
|
-
* from `replicaFile`, matching config-v5.yml's `socket.path`.
|
|
37
|
-
*/
|
|
38
|
-
constructor(lc, replicaFile, socketPath = litestreamSocketPath(replicaFile)) {
|
|
39
|
-
this.#lc = lc.withContext("component", "litestream-controller");
|
|
40
|
-
this.#replicaFile = replicaFile;
|
|
41
|
-
this.#socketPath = socketPath;
|
|
42
|
-
this.#agent = new Agent({
|
|
43
|
-
keepAlive: true,
|
|
44
|
-
maxSockets: 1
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Forces litestream to sync the WAL into LTX files immediately and run its
|
|
49
|
-
* checkpoint thresholds, instead of waiting for the next monitor-interval
|
|
50
|
-
* tick. The checkpoint itself is still litestream's usual (non-blocking)
|
|
51
|
-
* PASSIVE attempt, so it is skipped if the database is busy — call this once
|
|
52
|
-
* writes are quiesced.
|
|
53
|
-
*
|
|
54
|
-
* Best-effort: callers should tolerate rejection (e.g. ECONNREFUSED/ENOENT
|
|
55
|
-
* while litestream is restarting or before it has created the socket) rather
|
|
56
|
-
* than blocking application progress on it.
|
|
57
|
-
*/
|
|
58
|
-
async sync(opts = {}, signal) {
|
|
59
|
-
const { wait = false, timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS } = opts;
|
|
60
|
-
const result = await this.#post("/sync", {
|
|
61
|
-
path: this.#replicaFile,
|
|
62
|
-
wait
|
|
63
|
-
}, timeoutMs, signal);
|
|
64
|
-
this.#lc.debug?.("litestream sync", result);
|
|
65
|
-
return result;
|
|
66
|
-
}
|
|
67
|
-
/** Releases the pooled keep-alive connection. */
|
|
68
|
-
close() {
|
|
69
|
-
this.#agent.destroy();
|
|
70
|
-
}
|
|
71
|
-
#post(path, body, timeoutMs, signal) {
|
|
72
|
-
const payload = JSON.stringify(body);
|
|
73
|
-
return new Promise((resolve, reject) => {
|
|
74
|
-
const req = request({
|
|
75
|
-
socketPath: this.#socketPath,
|
|
76
|
-
method: "POST",
|
|
77
|
-
path,
|
|
78
|
-
agent: this.#agent,
|
|
79
|
-
timeout: timeoutMs,
|
|
80
|
-
headers: {
|
|
81
|
-
"content-type": "application/json",
|
|
82
|
-
"content-length": Buffer.byteLength(payload)
|
|
83
|
-
},
|
|
84
|
-
signal
|
|
85
|
-
}, (res) => {
|
|
86
|
-
const status = res.statusCode ?? 0;
|
|
87
|
-
json(res).then((body) => {
|
|
88
|
-
if (status < 200 || status >= 300) reject(/* @__PURE__ */ new Error(`litestream ${path} failed: ${status} ${JSON.stringify(body)}`));
|
|
89
|
-
else resolve(body);
|
|
90
|
-
}, (e) => reject(new Error(`litestream ${path} returned invalid JSON`, { cause: e })));
|
|
91
|
-
});
|
|
92
|
-
req.on("timeout", () => req.destroy(/* @__PURE__ */ new Error(`litestream ${path} timed out after ${timeoutMs}ms`)));
|
|
93
|
-
req.on("error", reject);
|
|
94
|
-
req.end(payload);
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
//#endregion
|
|
99
|
-
export { LitestreamController, litestreamSocketPath };
|
|
100
|
-
|
|
101
|
-
//# sourceMappingURL=litestream-controller.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"litestream-controller.js","names":["#lc","#replicaFile","#socketPath","#agent","#post"],"sources":["../../../../../../zero-cache/src/services/litestream/litestream-controller.ts"],"sourcesContent":["import type {IncomingMessage} from 'node:http';\nimport {Agent, request} from 'node:http';\nimport {json as readJSON} from 'node:stream/consumers';\nimport type {LogContext} from '@rocicorp/logger';\n\n/**\n * Path of the litestream control socket, derived from the replica file's\n * directory. The socket must live on a filesystem the (non-root) runtime user\n * can write — the replica directory is guaranteed writable since litestream\n * writes the replica there — so it must NOT default to a root-owned location\n * like /var/run. Kept in sync with config-v5.yml's `socket.path`, which is set\n * from `ZERO_LITESTREAM_SOCKET_PATH` (see getLitestream() in commands.ts).\n */\nexport function litestreamSocketPath(replicaFile: string): string {\n return `${replicaFile}.sock`;\n}\n\nconst DEFAULT_REQUEST_TIMEOUT_MS = 30_000;\n\n/** Response body from litestream's `POST /sync` control endpoint. */\nexport type SyncResponse = {\n /**\n * `synced` when `wait` was true and the upload completed, `synced_local`\n * when the WAL was sealed locally without waiting on the remote replica, or\n * `no_change` when there was nothing to sync.\n */\n status: 'synced' | 'synced_local' | 'no_change';\n path: string;\n txid: number;\n replicated_txid: number;\n};\n\nexport type SyncOptions = {\n /**\n * When true, block until the sealed WAL has been uploaded to the remote\n * replica (litestream's SyncAndWait). When false (the default), litestream\n * only seals the WAL into local LTX files and runs its checkpoint thresholds,\n * returning without waiting on the backup store. Use false on the write path\n * where the goal is to keep the WAL small, not to durably back it up.\n */\n wait?: boolean;\n\n /** Overall request timeout in milliseconds. Defaults to 30s. */\n timeoutMs?: number;\n};\n\n/**\n * IPC client for the litestream control server. litestream serves an HTTP API\n * over a Unix domain socket (enabled via the `socket:` block in config-v5.yml);\n * because zero-cache runs litestream as a subprocess in the same container, the\n * socket is reachable directly on the local filesystem.\n *\n * A single keep-alive connection is held open and reused across calls, so a\n * high-frequency caller (e.g. write-path backpressure) does not pay a\n * connect/handshake per request.\n */\nexport class LitestreamController {\n readonly #lc: LogContext;\n readonly #replicaFile: string;\n readonly #socketPath: string;\n readonly #agent: Agent;\n\n /**\n * @param replicaFile Path of the replica database litestream is managing —\n * the same value passed to litestream as `ZERO_REPLICA_FILE`. This is the\n * `path` litestream matches control requests against.\n * @param socketPath Path of the control socket; defaults to the path derived\n * from `replicaFile`, matching config-v5.yml's `socket.path`.\n */\n constructor(\n lc: LogContext,\n replicaFile: string,\n socketPath = litestreamSocketPath(replicaFile),\n ) {\n this.#lc = lc.withContext('component', 'litestream-controller');\n this.#replicaFile = replicaFile;\n this.#socketPath = socketPath;\n // keepAlive with a single socket keeps one warm connection to the control\n // socket that is reused across calls.\n this.#agent = new Agent({keepAlive: true, maxSockets: 1});\n }\n\n /**\n * Forces litestream to sync the WAL into LTX files immediately and run its\n * checkpoint thresholds, instead of waiting for the next monitor-interval\n * tick. The checkpoint itself is still litestream's usual (non-blocking)\n * PASSIVE attempt, so it is skipped if the database is busy — call this once\n * writes are quiesced.\n *\n * Best-effort: callers should tolerate rejection (e.g. ECONNREFUSED/ENOENT\n * while litestream is restarting or before it has created the socket) rather\n * than blocking application progress on it.\n */\n async sync(\n opts: SyncOptions = {},\n signal?: AbortSignal,\n ): Promise<SyncResponse> {\n const {wait = false, timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS} = opts;\n const result = await this.#post<SyncResponse>(\n '/sync',\n {path: this.#replicaFile, wait},\n timeoutMs,\n signal,\n );\n this.#lc.debug?.('litestream sync', result);\n return result;\n }\n\n /** Releases the pooled keep-alive connection. */\n close(): void {\n this.#agent.destroy();\n }\n\n // Note: Uses ye'old node:http for its unix socketPath support.\n // fetch() would have less boiler-plate, but it would require importing\n // the `unidici` package to provide a fetch Agent that supports sockets.\n #post<T>(\n path: string,\n body: unknown,\n timeoutMs: number,\n signal?: AbortSignal,\n ): Promise<T> {\n const payload = JSON.stringify(body);\n return new Promise<T>((resolve, reject) => {\n const req = request(\n {\n socketPath: this.#socketPath,\n method: 'POST',\n path,\n agent: this.#agent,\n timeout: timeoutMs,\n headers: {\n 'content-type': 'application/json',\n 'content-length': Buffer.byteLength(payload),\n },\n signal,\n },\n (res: IncomingMessage) => {\n const status = res.statusCode ?? 0;\n // litestream's success and error responses are both always JSON\n // (writeJSON / writeJSONError in server.go).\n readJSON(res).then(\n body => {\n if (status < 200 || status >= 300) {\n reject(\n new Error(\n `litestream ${path} failed: ${status} ${JSON.stringify(body)}`,\n ),\n );\n } else {\n resolve(body as T);\n }\n },\n (e: unknown) =>\n reject(\n new Error(`litestream ${path} returned invalid JSON`, {\n cause: e,\n }),\n ),\n );\n },\n );\n req.on('timeout', () =>\n req.destroy(\n new Error(`litestream ${path} timed out after ${timeoutMs}ms`),\n ),\n );\n req.on('error', reject);\n req.end(payload);\n });\n }\n}\n"],"mappings":";;;;;;;;;;;AAaA,SAAgB,qBAAqB,aAA6B;CAChE,OAAO,GAAG,YAAY;AACxB;AAEA,IAAM,6BAA6B;;;;;;;;;;;AAuCnC,IAAa,uBAAb,MAAkC;CAChC;CACA;CACA;CACA;;;;;;;;CASA,YACE,IACA,aACA,aAAa,qBAAqB,WAAW,GAC7C;EACA,KAAKA,MAAM,GAAG,YAAY,aAAa,uBAAuB;EAC9D,KAAKC,eAAe;EACpB,KAAKC,cAAc;EAGnB,KAAKC,SAAS,IAAI,MAAM;GAAC,WAAW;GAAM,YAAY;EAAC,CAAC;CAC1D;;;;;;;;;;;;CAaA,MAAM,KACJ,OAAoB,CAAC,GACrB,QACuB;EACvB,MAAM,EAAC,OAAO,OAAO,YAAY,+BAA8B;EAC/D,MAAM,SAAS,MAAM,KAAKC,MACxB,SACA;GAAC,MAAM,KAAKH;GAAc;EAAI,GAC9B,WACA,MACF;EACA,KAAKD,IAAI,QAAQ,mBAAmB,MAAM;EAC1C,OAAO;CACT;;CAGA,QAAc;EACZ,KAAKG,OAAO,QAAQ;CACtB;CAKA,MACE,MACA,MACA,WACA,QACY;EACZ,MAAM,UAAU,KAAK,UAAU,IAAI;EACnC,OAAO,IAAI,SAAY,SAAS,WAAW;GACzC,MAAM,MAAM,QACV;IACE,YAAY,KAAKD;IACjB,QAAQ;IACR;IACA,OAAO,KAAKC;IACZ,SAAS;IACT,SAAS;KACP,gBAAgB;KAChB,kBAAkB,OAAO,WAAW,OAAO;IAC7C;IACA;GACF,IACC,QAAyB;IACxB,MAAM,SAAS,IAAI,cAAc;IAGjC,KAAS,GAAG,EAAE,MACZ,SAAQ;KACN,IAAI,SAAS,OAAO,UAAU,KAC5B,uBACE,IAAI,MACF,cAAc,KAAK,WAAW,OAAO,GAAG,KAAK,UAAU,IAAI,GAC7D,CACF;UAEA,QAAQ,IAAS;IAErB,IACC,MACC,OACE,IAAI,MAAM,cAAc,KAAK,yBAAyB,EACpD,OAAO,EACT,CAAC,CACH,CACJ;GACF,CACF;GACA,IAAI,GAAG,iBACL,IAAI,wBACF,IAAI,MAAM,cAAc,KAAK,mBAAmB,UAAU,GAAG,CAC/D,CACF;GACA,IAAI,GAAG,SAAS,MAAM;GACtB,IAAI,IAAI,OAAO;EACjB,CAAC;CACH;AACF"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { LogContext } from '@rocicorp/logger';
|
|
2
|
-
import type { FastifyReply, FastifyRequest } from 'fastify';
|
|
3
|
-
import type { NormalizedZeroConfig } from '../config/normalize.ts';
|
|
4
|
-
import { type Worker } from '../types/processes.ts';
|
|
5
|
-
export declare function handleProfzRequest(lc: LogContext, config: Pick<NormalizedZeroConfig, 'adminPassword'>, req: FastifyRequest, res: FastifyReply, getWorker?: (() => Promise<Worker>) | undefined, forcedWorker?: string | undefined, localProcessName?: string): Promise<void>;
|
|
6
|
-
export declare function handleProfrmzRequest(lc: LogContext, config: NormalizedZeroConfig, req: FastifyRequest, res: FastifyReply, getWorker?: (() => Promise<Worker>) | undefined): Promise<void>;
|
|
7
|
-
//# sourceMappingURL=profz.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"profz.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/services/profz.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,YAAY,EAAE,cAAc,EAAC,MAAM,SAAS,CAAC;AAE1D,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AAEjE,OAAO,EAKL,KAAK,MAAM,EACZ,MAAM,uBAAuB,CAAC;AAI/B,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,eAAe,CAAC,EACnD,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,YAAY,EACjB,SAAS,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,EAC/C,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,EACjC,gBAAgB,GAAE,MAAqB,GACtC,OAAO,CAAC,IAAI,CAAC,CAgIf;AAED,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,oBAAoB,EAC5B,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,YAAY,EACjB,SAAS,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,GAC9C,OAAO,CAAC,IAAI,CAAC,CAkDf"}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { sleep } from "../../../shared/src/sleep.js";
|
|
2
|
-
import { singleProcessMode, subscribeToMessageType } from "../types/processes.js";
|
|
3
|
-
import { isAdminPasswordValid } from "../config/zero-config.js";
|
|
4
|
-
import { CpuProfiler } from "../types/profiler.js";
|
|
5
|
-
import { URLParams } from "../types/url-params.js";
|
|
6
|
-
import { randomUUID } from "node:crypto";
|
|
7
|
-
import auth from "basic-auth";
|
|
8
|
-
//#region ../zero-cache/src/services/profz.ts
|
|
9
|
-
async function handleProfzRequest(lc, config, req, res, getWorker, forcedWorker, localProcessName = "dispatcher") {
|
|
10
|
-
if (!isAdminPasswordValid(lc, config, auth(req)?.pass)) {
|
|
11
|
-
res.code(401).header("WWW-Authenticate", "Basic realm=\"Profz Protected Area\"").send("Unauthorized");
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
const params = new URLParams(new URL(req.url, "http://localhost"));
|
|
15
|
-
const rawDuration = params.getInteger("duration", false) ?? params.getInteger("seconds", false) ?? 5;
|
|
16
|
-
const durationMs = Math.max(1, Math.min(60, rawDuration)) * 1e3;
|
|
17
|
-
const targetWorker = forcedWorker ?? params.get("worker", false) ?? void 0;
|
|
18
|
-
const targetWorkerIndex = params.getInteger("index", false) ?? void 0;
|
|
19
|
-
const id = randomUUID();
|
|
20
|
-
const responses = /* @__PURE__ */ new Map();
|
|
21
|
-
const localProfilePromise = singleProcessMode() || getWorker === void 0 || targetWorker === void 0 || targetWorker === localProcessName ? CpuProfiler.profile(durationMs).catch((err) => {
|
|
22
|
-
lc.warn?.("Failed to capture local CPU profile:", err);
|
|
23
|
-
return null;
|
|
24
|
-
}) : Promise.resolve(null);
|
|
25
|
-
if (!singleProcessMode() && getWorker !== void 0) try {
|
|
26
|
-
const worker = await getWorker();
|
|
27
|
-
const onMessage = (msg) => {
|
|
28
|
-
if (msg.id === id && msg.profile) responses.set(msg.name, msg.profile);
|
|
29
|
-
else if (msg.id === id && msg.error) lc.warn?.(`Worker ${msg.name} profile error: ${msg.error}`);
|
|
30
|
-
};
|
|
31
|
-
const unsubscribe = subscribeToMessageType(worker, "profileResponse", onMessage);
|
|
32
|
-
try {
|
|
33
|
-
worker.send(["profile", {
|
|
34
|
-
id,
|
|
35
|
-
durationMs,
|
|
36
|
-
worker: targetWorker === localProcessName ? "__none__" : targetWorker,
|
|
37
|
-
workerIndex: targetWorkerIndex
|
|
38
|
-
}]);
|
|
39
|
-
await sleep(durationMs + 500);
|
|
40
|
-
} finally {
|
|
41
|
-
unsubscribe();
|
|
42
|
-
}
|
|
43
|
-
} catch (err) {
|
|
44
|
-
lc.warn?.("Failed to dispatch profile request to child workers:", err);
|
|
45
|
-
}
|
|
46
|
-
const localProfile = await localProfilePromise;
|
|
47
|
-
if (localProfile) {
|
|
48
|
-
const localName = targetWorker ?? (singleProcessMode() ? "zero-cache" : localProcessName);
|
|
49
|
-
responses.set(localName, localProfile);
|
|
50
|
-
}
|
|
51
|
-
if (targetWorker !== void 0 && targetWorker !== "all") {
|
|
52
|
-
let matchedProfile = responses.get(targetWorker);
|
|
53
|
-
if (!matchedProfile) {
|
|
54
|
-
for (const [name, prof] of responses.entries()) if (name === targetWorker || name.startsWith(`${targetWorker}-`)) {
|
|
55
|
-
matchedProfile = prof;
|
|
56
|
-
break;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
if (matchedProfile) {
|
|
60
|
-
res.header("Content-Type", "application/json").header("Content-Disposition", `attachment; filename="${targetWorker}.cpuprofile"`).send(matchedProfile);
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
res.code(404).send({ error: `No profile captured for worker "${targetWorker}". Available: ${[...responses.keys()].join(", ")}` });
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
const bundle = {};
|
|
67
|
-
for (const [name, prof] of responses.entries()) bundle[name] = prof;
|
|
68
|
-
res.header("Content-Type", "application/json").header("Content-Disposition", `attachment; filename="zero-profile-bundle-${Date.now()}.json"`).send(bundle);
|
|
69
|
-
}
|
|
70
|
-
async function handleProfrmzRequest(lc, config, req, res, getWorker) {
|
|
71
|
-
if (!isAdminPasswordValid(lc, config, auth(req)?.pass)) {
|
|
72
|
-
res.code(401).header("WWW-Authenticate", "Basic realm=\"Profrmz Protected Area\"").send("Unauthorized");
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
if (config.changeStreamer.uri) {
|
|
76
|
-
const upstreamURL = new URL("/profz", config.changeStreamer.uri);
|
|
77
|
-
const incomingURL = new URL(req.url, "http://localhost");
|
|
78
|
-
for (const [key, value] of incomingURL.searchParams.entries()) upstreamURL.searchParams.set(key, value);
|
|
79
|
-
try {
|
|
80
|
-
const headers = {};
|
|
81
|
-
if (req.headers.authorization) headers.authorization = req.headers.authorization;
|
|
82
|
-
const response = await fetch(upstreamURL.toString(), {
|
|
83
|
-
method: "GET",
|
|
84
|
-
headers
|
|
85
|
-
});
|
|
86
|
-
res.code(response.status);
|
|
87
|
-
const contentType = response.headers.get("content-type");
|
|
88
|
-
if (contentType) res.header("content-type", contentType);
|
|
89
|
-
const contentDisposition = response.headers.get("content-disposition");
|
|
90
|
-
if (contentDisposition) res.header("content-disposition", contentDisposition);
|
|
91
|
-
const body = await response.text();
|
|
92
|
-
return res.send(body);
|
|
93
|
-
} catch (err) {
|
|
94
|
-
lc.error?.(`Failed to proxy /profrmz to RM at ${upstreamURL}:`, err);
|
|
95
|
-
return res.code(502).send({ error: `Failed to proxy profiling request to RM at ${upstreamURL}: ${String(err)}` });
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return handleProfzRequest(lc, config, req, res, getWorker, "change-streamer");
|
|
99
|
-
}
|
|
100
|
-
//#endregion
|
|
101
|
-
export { handleProfrmzRequest, handleProfzRequest };
|
|
102
|
-
|
|
103
|
-
//# sourceMappingURL=profz.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"profz.js","names":[],"sources":["../../../../../zero-cache/src/services/profz.ts"],"sourcesContent":["import {randomUUID} from 'node:crypto';\nimport type {LogContext} from '@rocicorp/logger';\nimport auth from 'basic-auth';\nimport type {FastifyReply, FastifyRequest} from 'fastify';\nimport {sleep} from '../../../shared/src/sleep.ts';\nimport type {NormalizedZeroConfig} from '../config/normalize.ts';\nimport {isAdminPasswordValid} from '../config/zero-config.ts';\nimport {\n singleProcessMode,\n subscribeToMessageType,\n type ProfileResponse,\n type ProfileResponseMessage,\n type Worker,\n} from '../types/processes.ts';\nimport {CpuProfiler} from '../types/profiler.ts';\nimport {URLParams} from '../types/url-params.ts';\n\nexport async function handleProfzRequest(\n lc: LogContext,\n config: Pick<NormalizedZeroConfig, 'adminPassword'>,\n req: FastifyRequest,\n res: FastifyReply,\n getWorker?: (() => Promise<Worker>) | undefined,\n forcedWorker?: string | undefined,\n localProcessName: string = 'dispatcher',\n): Promise<void> {\n const credentials = auth(req);\n if (!isAdminPasswordValid(lc, config, credentials?.pass)) {\n void res\n .code(401)\n .header('WWW-Authenticate', 'Basic realm=\"Profz Protected Area\"')\n .send('Unauthorized');\n return;\n }\n\n const params = new URLParams(new URL(req.url, 'http://localhost'));\n const rawDuration =\n params.getInteger('duration', false) ??\n params.getInteger('seconds', false) ??\n 5;\n const durationSec = Math.max(1, Math.min(60, rawDuration));\n const durationMs = durationSec * 1000;\n const targetWorker = forcedWorker ?? params.get('worker', false) ?? undefined;\n const targetWorkerIndex = params.getInteger('index', false) ?? undefined;\n const id = randomUUID();\n\n const responses = new Map<string, unknown>();\n\n // Profile dispatcher / local process if requested, when profiling all processes,\n // or when no worker dispatcher is available (single process / standalone mode)\n const shouldProfileLocal =\n singleProcessMode() ||\n getWorker === undefined ||\n targetWorker === undefined ||\n targetWorker === localProcessName;\n\n const localProfilePromise = shouldProfileLocal\n ? CpuProfiler.profile(durationMs).catch(err => {\n lc.warn?.('Failed to capture local CPU profile:', err);\n return null;\n })\n : Promise.resolve(null);\n\n if (!singleProcessMode() && getWorker !== undefined) {\n try {\n const worker = await getWorker();\n const onMessage = (msg: ProfileResponse) => {\n if (msg.id === id && msg.profile) {\n responses.set(msg.name, msg.profile);\n } else if (msg.id === id && msg.error) {\n lc.warn?.(`Worker ${msg.name} profile error: ${msg.error}`);\n }\n };\n\n // The worker is long-lived (and shared across requests), so the\n // per-request handler must be removed once this request is done.\n const unsubscribe = subscribeToMessageType<ProfileResponseMessage>(\n worker,\n 'profileResponse',\n onMessage,\n );\n try {\n worker.send([\n 'profile',\n {\n id,\n durationMs,\n worker:\n targetWorker === localProcessName ? '__none__' : targetWorker,\n workerIndex: targetWorkerIndex,\n },\n ]);\n\n // Wait for duration plus grace period for IPC transfer\n await sleep(durationMs + 500);\n } finally {\n unsubscribe();\n }\n } catch (err) {\n lc.warn?.('Failed to dispatch profile request to child workers:', err);\n }\n }\n\n const localProfile = await localProfilePromise;\n if (localProfile) {\n const localName =\n targetWorker ?? (singleProcessMode() ? 'zero-cache' : localProcessName);\n responses.set(localName, localProfile);\n }\n\n // If a single specific worker was requested, return that profile directly\n if (targetWorker !== undefined && targetWorker !== 'all') {\n let matchedProfile = responses.get(targetWorker);\n if (!matchedProfile) {\n // Try matching by prefix or substring (e.g. 'syncer' matches 'syncer-0')\n for (const [name, prof] of responses.entries()) {\n if (name === targetWorker || name.startsWith(`${targetWorker}-`)) {\n matchedProfile = prof;\n break;\n }\n }\n }\n\n if (matchedProfile) {\n void res\n .header('Content-Type', 'application/json')\n .header(\n 'Content-Disposition',\n `attachment; filename=\"${targetWorker}.cpuprofile\"`,\n )\n .send(matchedProfile);\n return;\n }\n\n void res.code(404).send({\n error: `No profile captured for worker \"${targetWorker}\". Available: ${[...responses.keys()].join(', ')}`,\n });\n return;\n }\n\n // Default (Option A): Return multi-process profile bundle as a JSON map\n const bundle: Record<string, unknown> = {};\n for (const [name, prof] of responses.entries()) {\n bundle[name] = prof;\n }\n\n void res\n .header('Content-Type', 'application/json')\n .header(\n 'Content-Disposition',\n `attachment; filename=\"zero-profile-bundle-${Date.now()}.json\"`,\n )\n .send(bundle);\n}\n\nexport async function handleProfrmzRequest(\n lc: LogContext,\n config: NormalizedZeroConfig,\n req: FastifyRequest,\n res: FastifyReply,\n getWorker?: (() => Promise<Worker>) | undefined,\n): Promise<void> {\n const credentials = auth(req);\n if (!isAdminPasswordValid(lc, config, credentials?.pass)) {\n void res\n .code(401)\n .header('WWW-Authenticate', 'Basic realm=\"Profrmz Protected Area\"')\n .send('Unauthorized');\n return;\n }\n\n // In distributed mode, proxy to the upstream Replication Manager\n if (config.changeStreamer.uri) {\n const upstreamURL = new URL('/profz', config.changeStreamer.uri);\n const incomingURL = new URL(req.url, 'http://localhost');\n for (const [key, value] of incomingURL.searchParams.entries()) {\n upstreamURL.searchParams.set(key, value);\n }\n\n try {\n const headers: Record<string, string> = {};\n if (req.headers.authorization) {\n headers.authorization = req.headers.authorization;\n }\n const response = await fetch(upstreamURL.toString(), {\n method: 'GET',\n headers,\n });\n\n res.code(response.status);\n const contentType = response.headers.get('content-type');\n if (contentType) {\n res.header('content-type', contentType);\n }\n const contentDisposition = response.headers.get('content-disposition');\n if (contentDisposition) {\n res.header('content-disposition', contentDisposition);\n }\n\n const body = await response.text();\n return res.send(body);\n } catch (err) {\n lc.error?.(`Failed to proxy /profrmz to RM at ${upstreamURL}:`, err);\n return res.code(502).send({\n error: `Failed to proxy profiling request to RM at ${upstreamURL}: ${String(err)}`,\n });\n }\n }\n\n // In single-node mode, profile local change-streamer / RM process\n return handleProfzRequest(lc, config, req, res, getWorker, 'change-streamer');\n}\n"],"mappings":";;;;;;;;AAiBA,eAAsB,mBACpB,IACA,QACA,KACA,KACA,WACA,cACA,mBAA2B,cACZ;CAEf,IAAI,CAAC,qBAAqB,IAAI,QADV,KAAK,GACa,GAAa,IAAI,GAAG;EACxD,IACG,KAAK,GAAG,EACR,OAAO,oBAAoB,sCAAoC,EAC/D,KAAK,cAAc;EACtB;CACF;CAEA,MAAM,SAAS,IAAI,UAAU,IAAI,IAAI,IAAI,KAAK,kBAAkB,CAAC;CACjE,MAAM,cACJ,OAAO,WAAW,YAAY,KAAK,KACnC,OAAO,WAAW,WAAW,KAAK,KAClC;CAEF,MAAM,aADc,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,WAAW,CACrC,IAAc;CACjC,MAAM,eAAe,gBAAgB,OAAO,IAAI,UAAU,KAAK,KAAK,KAAA;CACpE,MAAM,oBAAoB,OAAO,WAAW,SAAS,KAAK,KAAK,KAAA;CAC/D,MAAM,KAAK,WAAW;CAEtB,MAAM,4BAAY,IAAI,IAAqB;CAU3C,MAAM,sBALJ,kBAAkB,KAClB,cAAc,KAAA,KACd,iBAAiB,KAAA,KACjB,iBAAiB,mBAGf,YAAY,QAAQ,UAAU,EAAE,OAAM,QAAO;EAC3C,GAAG,OAAO,wCAAwC,GAAG;EACrD,OAAO;CACT,CAAC,IACD,QAAQ,QAAQ,IAAI;CAExB,IAAI,CAAC,kBAAkB,KAAK,cAAc,KAAA,GACxC,IAAI;EACF,MAAM,SAAS,MAAM,UAAU;EAC/B,MAAM,aAAa,QAAyB;GAC1C,IAAI,IAAI,OAAO,MAAM,IAAI,SACvB,UAAU,IAAI,IAAI,MAAM,IAAI,OAAO;QAC9B,IAAI,IAAI,OAAO,MAAM,IAAI,OAC9B,GAAG,OAAO,UAAU,IAAI,KAAK,kBAAkB,IAAI,OAAO;EAE9D;EAIA,MAAM,cAAc,uBAClB,QACA,mBACA,SACF;EACA,IAAI;GACF,OAAO,KAAK,CACV,WACA;IACE;IACA;IACA,QACE,iBAAiB,mBAAmB,aAAa;IACnD,aAAa;GACf,CACF,CAAC;GAGD,MAAM,MAAM,aAAa,GAAG;EAC9B,UAAU;GACR,YAAY;EACd;CACF,SAAS,KAAK;EACZ,GAAG,OAAO,wDAAwD,GAAG;CACvE;CAGF,MAAM,eAAe,MAAM;CAC3B,IAAI,cAAc;EAChB,MAAM,YACJ,iBAAiB,kBAAkB,IAAI,eAAe;EACxD,UAAU,IAAI,WAAW,YAAY;CACvC;CAGA,IAAI,iBAAiB,KAAA,KAAa,iBAAiB,OAAO;EACxD,IAAI,iBAAiB,UAAU,IAAI,YAAY;EAC/C,IAAI,CAAC;QAEE,MAAM,CAAC,MAAM,SAAS,UAAU,QAAQ,GAC3C,IAAI,SAAS,gBAAgB,KAAK,WAAW,GAAG,aAAa,EAAE,GAAG;IAChE,iBAAiB;IACjB;GACF;;EAIJ,IAAI,gBAAgB;GAClB,IACG,OAAO,gBAAgB,kBAAkB,EACzC,OACC,uBACA,yBAAyB,aAAa,aACxC,EACC,KAAK,cAAc;GACtB;EACF;EAEA,IAAS,KAAK,GAAG,EAAE,KAAK,EACtB,OAAO,mCAAmC,aAAa,gBAAgB,CAAC,GAAG,UAAU,KAAK,CAAC,EAAE,KAAK,IAAI,IACxG,CAAC;EACD;CACF;CAGA,MAAM,SAAkC,CAAC;CACzC,KAAK,MAAM,CAAC,MAAM,SAAS,UAAU,QAAQ,GAC3C,OAAO,QAAQ;CAGjB,IACG,OAAO,gBAAgB,kBAAkB,EACzC,OACC,uBACA,6CAA6C,KAAK,IAAI,EAAE,OAC1D,EACC,KAAK,MAAM;AAChB;AAEA,eAAsB,qBACpB,IACA,QACA,KACA,KACA,WACe;CAEf,IAAI,CAAC,qBAAqB,IAAI,QADV,KAAK,GACa,GAAa,IAAI,GAAG;EACxD,IACG,KAAK,GAAG,EACR,OAAO,oBAAoB,wCAAsC,EACjE,KAAK,cAAc;EACtB;CACF;CAGA,IAAI,OAAO,eAAe,KAAK;EAC7B,MAAM,cAAc,IAAI,IAAI,UAAU,OAAO,eAAe,GAAG;EAC/D,MAAM,cAAc,IAAI,IAAI,IAAI,KAAK,kBAAkB;EACvD,KAAK,MAAM,CAAC,KAAK,UAAU,YAAY,aAAa,QAAQ,GAC1D,YAAY,aAAa,IAAI,KAAK,KAAK;EAGzC,IAAI;GACF,MAAM,UAAkC,CAAC;GACzC,IAAI,IAAI,QAAQ,eACd,QAAQ,gBAAgB,IAAI,QAAQ;GAEtC,MAAM,WAAW,MAAM,MAAM,YAAY,SAAS,GAAG;IACnD,QAAQ;IACR;GACF,CAAC;GAED,IAAI,KAAK,SAAS,MAAM;GACxB,MAAM,cAAc,SAAS,QAAQ,IAAI,cAAc;GACvD,IAAI,aACF,IAAI,OAAO,gBAAgB,WAAW;GAExC,MAAM,qBAAqB,SAAS,QAAQ,IAAI,qBAAqB;GACrE,IAAI,oBACF,IAAI,OAAO,uBAAuB,kBAAkB;GAGtD,MAAM,OAAO,MAAM,SAAS,KAAK;GACjC,OAAO,IAAI,KAAK,IAAI;EACtB,SAAS,KAAK;GACZ,GAAG,QAAQ,qCAAqC,YAAY,IAAI,GAAG;GACnE,OAAO,IAAI,KAAK,GAAG,EAAE,KAAK,EACxB,OAAO,8CAA8C,YAAY,IAAI,OAAO,GAAG,IACjF,CAAC;EACH;CACF;CAGA,OAAO,mBAAmB,IAAI,QAAQ,KAAK,KAAK,WAAW,iBAAiB;AAC9E"}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
export type MonotonicClock = () => number;
|
|
2
|
-
/**
|
|
3
|
-
* A soft wall-clock budget for one view-syncer hydration pass.
|
|
4
|
-
*
|
|
5
|
-
* Callers check the budget only at query boundaries. Consequently, a query
|
|
6
|
-
* that starts before the limit is reached is always allowed to finish.
|
|
7
|
-
*
|
|
8
|
-
* The budget measures hydration work. Intervals that are not hydration --
|
|
9
|
-
* notably the remote custom-query transform round trips -- are discounted via
|
|
10
|
-
* {@link excluding}, so that endpoint latency cannot spend a budget that only
|
|
11
|
-
* evicting queries could recover.
|
|
12
|
-
*/
|
|
13
|
-
export declare class HydrationBudget {
|
|
14
|
-
#private;
|
|
15
|
-
readonly limitMs: number;
|
|
16
|
-
constructor(limitMs: number, now?: MonotonicClock);
|
|
17
|
-
/**
|
|
18
|
-
* Runs `fn`, discounting the time it takes as non-hydration work. The
|
|
19
|
-
* interval is measured with this budget's own clock, so an injected clock
|
|
20
|
-
* governs both the elapsed time and what is excluded from it.
|
|
21
|
-
*/
|
|
22
|
-
excluding<T>(fn: () => Promise<T>): Promise<T>;
|
|
23
|
-
elapsedMs(): number;
|
|
24
|
-
/**
|
|
25
|
-
* Whether the budget is spent. A disabled budget (`limitMs === 0`) is never
|
|
26
|
-
* exhausted. The elapsed time at the first exhausted check is retained in
|
|
27
|
-
* {@link exhaustedAtMs} so that reporting does not re-read the clock.
|
|
28
|
-
*/
|
|
29
|
-
exhausted(): boolean;
|
|
30
|
-
/** The elapsed time at which {@link exhausted} first returned true. */
|
|
31
|
-
get exhaustedAtMs(): number | undefined;
|
|
32
|
-
}
|
|
33
|
-
//# sourceMappingURL=hydration-budget.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hydration-budget.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/hydration-budget.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC;AAE1C;;;;;;;;;;GAUG;AACH,qBAAa,eAAe;;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAOvB,OAAO,EAAE,MAAM,EACf,GAAG,GAAE,cAAkD;IAWzD;;;;OAIG;IACG,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IASpD,SAAS,IAAI,MAAM;IAInB;;;;OAIG;IACH,SAAS,IAAI,OAAO;IAYpB,uEAAuE;IACvE,IAAI,aAAa,IAAI,MAAM,GAAG,SAAS,CAEtC;CACF"}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { assert } from "../../../../shared/src/asserts.js";
|
|
2
|
-
//#region ../zero-cache/src/services/view-syncer/hydration-budget.ts
|
|
3
|
-
/**
|
|
4
|
-
* A soft wall-clock budget for one view-syncer hydration pass.
|
|
5
|
-
*
|
|
6
|
-
* Callers check the budget only at query boundaries. Consequently, a query
|
|
7
|
-
* that starts before the limit is reached is always allowed to finish.
|
|
8
|
-
*
|
|
9
|
-
* The budget measures hydration work. Intervals that are not hydration --
|
|
10
|
-
* notably the remote custom-query transform round trips -- are discounted via
|
|
11
|
-
* {@link excluding}, so that endpoint latency cannot spend a budget that only
|
|
12
|
-
* evicting queries could recover.
|
|
13
|
-
*/
|
|
14
|
-
var HydrationBudget = class {
|
|
15
|
-
limitMs;
|
|
16
|
-
#now;
|
|
17
|
-
#startedAt;
|
|
18
|
-
#excludedMs = 0;
|
|
19
|
-
#exhaustedAtMs;
|
|
20
|
-
constructor(limitMs, now = performance.now.bind(performance)) {
|
|
21
|
-
assert(Number.isSafeInteger(limitMs) && limitMs >= 0, "Hydration budget must be a nonnegative integer");
|
|
22
|
-
this.limitMs = limitMs;
|
|
23
|
-
this.#now = now;
|
|
24
|
-
this.#startedAt = now();
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Runs `fn`, discounting the time it takes as non-hydration work. The
|
|
28
|
-
* interval is measured with this budget's own clock, so an injected clock
|
|
29
|
-
* governs both the elapsed time and what is excluded from it.
|
|
30
|
-
*/
|
|
31
|
-
async excluding(fn) {
|
|
32
|
-
const start = this.#now();
|
|
33
|
-
try {
|
|
34
|
-
return await fn();
|
|
35
|
-
} finally {
|
|
36
|
-
this.#excludedMs += this.#now() - start;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
elapsedMs() {
|
|
40
|
-
return this.#now() - this.#startedAt - this.#excludedMs;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Whether the budget is spent. A disabled budget (`limitMs === 0`) is never
|
|
44
|
-
* exhausted. The elapsed time at the first exhausted check is retained in
|
|
45
|
-
* {@link exhaustedAtMs} so that reporting does not re-read the clock.
|
|
46
|
-
*/
|
|
47
|
-
exhausted() {
|
|
48
|
-
if (this.limitMs === 0) return false;
|
|
49
|
-
const elapsed = this.elapsedMs();
|
|
50
|
-
if (elapsed < this.limitMs) return false;
|
|
51
|
-
this.#exhaustedAtMs ??= elapsed;
|
|
52
|
-
return true;
|
|
53
|
-
}
|
|
54
|
-
/** The elapsed time at which {@link exhausted} first returned true. */
|
|
55
|
-
get exhaustedAtMs() {
|
|
56
|
-
return this.#exhaustedAtMs;
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
//#endregion
|
|
60
|
-
export { HydrationBudget };
|
|
61
|
-
|
|
62
|
-
//# sourceMappingURL=hydration-budget.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hydration-budget.js","names":["#now","#startedAt","#excludedMs","#exhaustedAtMs"],"sources":["../../../../../../zero-cache/src/services/view-syncer/hydration-budget.ts"],"sourcesContent":["import {assert} from '../../../../shared/src/asserts.ts';\n\nexport type MonotonicClock = () => number;\n\n/**\n * A soft wall-clock budget for one view-syncer hydration pass.\n *\n * Callers check the budget only at query boundaries. Consequently, a query\n * that starts before the limit is reached is always allowed to finish.\n *\n * The budget measures hydration work. Intervals that are not hydration --\n * notably the remote custom-query transform round trips -- are discounted via\n * {@link excluding}, so that endpoint latency cannot spend a budget that only\n * evicting queries could recover.\n */\nexport class HydrationBudget {\n readonly limitMs: number;\n readonly #now: MonotonicClock;\n readonly #startedAt: number;\n #excludedMs = 0;\n #exhaustedAtMs: number | undefined;\n\n constructor(\n limitMs: number,\n now: MonotonicClock = performance.now.bind(performance),\n ) {\n assert(\n Number.isSafeInteger(limitMs) && limitMs >= 0,\n 'Hydration budget must be a nonnegative integer',\n );\n this.limitMs = limitMs;\n this.#now = now;\n this.#startedAt = now();\n }\n\n /**\n * Runs `fn`, discounting the time it takes as non-hydration work. The\n * interval is measured with this budget's own clock, so an injected clock\n * governs both the elapsed time and what is excluded from it.\n */\n async excluding<T>(fn: () => Promise<T>): Promise<T> {\n const start = this.#now();\n try {\n return await fn();\n } finally {\n this.#excludedMs += this.#now() - start;\n }\n }\n\n elapsedMs(): number {\n return this.#now() - this.#startedAt - this.#excludedMs;\n }\n\n /**\n * Whether the budget is spent. A disabled budget (`limitMs === 0`) is never\n * exhausted. The elapsed time at the first exhausted check is retained in\n * {@link exhaustedAtMs} so that reporting does not re-read the clock.\n */\n exhausted(): boolean {\n if (this.limitMs === 0) {\n return false;\n }\n const elapsed = this.elapsedMs();\n if (elapsed < this.limitMs) {\n return false;\n }\n this.#exhaustedAtMs ??= elapsed;\n return true;\n }\n\n /** The elapsed time at which {@link exhausted} first returned true. */\n get exhaustedAtMs(): number | undefined {\n return this.#exhaustedAtMs;\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAeA,IAAa,kBAAb,MAA6B;CAC3B;CACA;CACA;CACA,cAAc;CACd;CAEA,YACE,SACA,MAAsB,YAAY,IAAI,KAAK,WAAW,GACtD;EACA,OACE,OAAO,cAAc,OAAO,KAAK,WAAW,GAC5C,gDACF;EACA,KAAK,UAAU;EACf,KAAKA,OAAO;EACZ,KAAKC,aAAa,IAAI;CACxB;;;;;;CAOA,MAAM,UAAa,IAAkC;EACnD,MAAM,QAAQ,KAAKD,KAAK;EACxB,IAAI;GACF,OAAO,MAAM,GAAG;EAClB,UAAU;GACR,KAAKE,eAAe,KAAKF,KAAK,IAAI;EACpC;CACF;CAEA,YAAoB;EAClB,OAAO,KAAKA,KAAK,IAAI,KAAKC,aAAa,KAAKC;CAC9C;;;;;;CAOA,YAAqB;EACnB,IAAI,KAAK,YAAY,GACnB,OAAO;EAET,MAAM,UAAU,KAAK,UAAU;EAC/B,IAAI,UAAU,KAAK,SACjB,OAAO;EAET,KAAKC,mBAAmB;EACxB,OAAO;CACT;;CAGA,IAAI,gBAAoC;EACtC,OAAO,KAAKA;CACd;AACF"}
|