@rocicorp/zero 1.11.0 → 1.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/_virtual/{_@oxc-project_runtime@0.130.0/helpers → _@oxc-project_runtime@0.154.0/helpers/esm}/usingCtx.js +2 -2
- package/out/analyze-query/src/analyze-cli.js +2 -1
- package/out/analyze-query/src/analyze-cli.js.map +1 -1
- package/out/analyze-query/src/bin-analyze.js +5 -3
- package/out/analyze-query/src/bin-analyze.js.map +1 -1
- package/out/analyze-query/src/bin-transform.js +3 -2
- package/out/analyze-query/src/bin-transform.js.map +1 -1
- package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
- package/out/ast-to-zql/src/bin.js +3 -2
- package/out/ast-to-zql/src/bin.js.map +1 -1
- package/out/ast-to-zql/src/format.js.map +1 -1
- package/out/datadog/src/datadog-log-sink.js +5 -4
- package/out/datadog/src/datadog-log-sink.js.map +1 -1
- package/out/otel/src/log-options.js.map +1 -1
- package/out/replicache/src/btree/node.js +13 -11
- package/out/replicache/src/btree/node.js.map +1 -1
- package/out/replicache/src/btree/read.js +4 -2
- package/out/replicache/src/btree/read.js.map +1 -1
- package/out/replicache/src/btree/splice.js.map +1 -1
- package/out/replicache/src/btree/write.js +9 -5
- package/out/replicache/src/btree/write.js.map +1 -1
- package/out/replicache/src/config.js +1 -2
- package/out/replicache/src/connection-loop.js.map +1 -1
- package/out/replicache/src/dag/chunk.js +1 -1
- package/out/replicache/src/dag/gc.js.map +1 -1
- package/out/replicache/src/dag/lazy-store.js +8 -7
- package/out/replicache/src/dag/lazy-store.js.map +1 -1
- package/out/replicache/src/dag/store-impl.js +1 -1
- package/out/replicache/src/dag/store-impl.js.map +1 -1
- package/out/replicache/src/dag/visitor.js.map +1 -1
- package/out/replicache/src/db/commit.js +5 -3
- package/out/replicache/src/db/commit.js.map +1 -1
- package/out/replicache/src/db/read.js +1 -1
- package/out/replicache/src/db/read.js.map +1 -1
- package/out/replicache/src/db/rebase.js.map +1 -1
- package/out/replicache/src/db/write.js +5 -2
- package/out/replicache/src/db/write.js.map +1 -1
- package/out/replicache/src/deleted-clients.js +3 -2
- package/out/replicache/src/deleted-clients.js.map +1 -1
- package/out/replicache/src/expo-sqlite.js +1 -2
- package/out/replicache/src/frozen-json.js +1 -1
- package/out/replicache/src/frozen-json.js.map +1 -1
- package/out/replicache/src/get-default-puller.js +1 -1
- package/out/replicache/src/hash.js +1 -1
- package/out/replicache/src/hash.js.map +1 -1
- package/out/replicache/src/impl.js +1 -2
- package/out/replicache/src/index-defs.js +5 -3
- package/out/replicache/src/index-defs.js.map +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.js +1 -1
- package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
- package/out/replicache/src/kv/idb-store.js +1 -1
- package/out/replicache/src/kv/idb-store.js.map +1 -1
- package/out/replicache/src/kv/mem-fallback-store.js +1 -1
- package/out/replicache/src/kv/mem-fallback-store.js.map +1 -1
- package/out/replicache/src/kv/mem-store.js.map +1 -1
- package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
- package/out/replicache/src/kv/op-sqlite/types.js.map +1 -1
- package/out/replicache/src/kv/read-impl.js.map +1 -1
- package/out/replicache/src/kv/sqlite-store.js +1 -1
- package/out/replicache/src/kv/sqlite-store.js.map +1 -1
- package/out/replicache/src/kv/write-impl-base.js +4 -1
- package/out/replicache/src/kv/write-impl-base.js.map +1 -1
- package/out/replicache/src/kv/write-impl.js.map +1 -1
- package/out/replicache/src/log-options.js +2 -1
- package/out/replicache/src/log-options.js.map +1 -1
- package/out/replicache/src/make-idb-name.js +1 -1
- package/out/replicache/src/new-client-channel.js +9 -7
- package/out/replicache/src/new-client-channel.js.map +1 -1
- package/out/replicache/src/on-persist-channel.js +1 -1
- package/out/replicache/src/on-persist-channel.js.map +1 -1
- package/out/replicache/src/op-sqlite.js +1 -2
- package/out/replicache/src/pending-mutations.js +2 -1
- package/out/replicache/src/pending-mutations.js.map +1 -1
- package/out/replicache/src/persist/client-gc.js +2 -2
- package/out/replicache/src/persist/client-gc.js.map +1 -1
- package/out/replicache/src/persist/client-group-gc.js +2 -2
- package/out/replicache/src/persist/client-group-gc.js.map +1 -1
- package/out/replicache/src/persist/client-groups.js +1 -1
- package/out/replicache/src/persist/client-groups.js.map +1 -1
- package/out/replicache/src/persist/clients.js +1 -1
- package/out/replicache/src/persist/clients.js.map +1 -1
- package/out/replicache/src/persist/collect-idb-databases.js +5 -4
- package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
- package/out/replicache/src/persist/gather-mem-only-visitor.js.map +1 -1
- package/out/replicache/src/persist/gather-not-cached-visitor.js.map +1 -1
- package/out/replicache/src/persist/heartbeat.js +2 -2
- package/out/replicache/src/persist/heartbeat.js.map +1 -1
- package/out/replicache/src/persist/idb-databases-store.js +1 -1
- package/out/replicache/src/persist/idb-databases-store.js.map +1 -1
- package/out/replicache/src/persist/make-client-id.js +1 -1
- package/out/replicache/src/persist/make-client-id.js.map +1 -1
- package/out/replicache/src/persist/persist.js +3 -2
- package/out/replicache/src/persist/persist.js.map +1 -1
- package/out/replicache/src/persist/refresh.js +8 -5
- package/out/replicache/src/persist/refresh.js.map +1 -1
- package/out/replicache/src/process-scheduler.js.map +1 -1
- package/out/replicache/src/replicache-impl.js +3 -3
- package/out/replicache/src/replicache-impl.js.map +1 -1
- package/out/replicache/src/scan-iterator.js.map +1 -1
- package/out/replicache/src/scan-options.js +7 -6
- package/out/replicache/src/scan-options.js.map +1 -1
- package/out/replicache/src/sqlite.js +1 -2
- package/out/replicache/src/subscriptions.js +1 -1
- package/out/replicache/src/subscriptions.js.map +1 -1
- package/out/replicache/src/sync/diff.js +3 -1
- package/out/replicache/src/sync/diff.js.map +1 -1
- package/out/replicache/src/sync/ids.js.map +1 -1
- package/out/replicache/src/sync/patch.js +2 -4
- package/out/replicache/src/sync/patch.js.map +1 -1
- package/out/replicache/src/sync/pull.js +3 -1
- package/out/replicache/src/sync/pull.js.map +1 -1
- package/out/replicache/src/sync/push.js.map +1 -1
- package/out/replicache/src/sync/request-id.js +1 -1
- package/out/replicache/src/sync/request-id.js.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.js +4 -1
- package/out/shared/src/arrays.js.map +1 -1
- package/out/shared/src/asserts.js +1 -1
- package/out/shared/src/bigint-json.js.map +1 -1
- package/out/shared/src/broadcast-channel.js +1 -1
- package/out/shared/src/btree-set.js +11 -8
- package/out/shared/src/btree-set.js.map +1 -1
- package/out/shared/src/cache.js.map +1 -1
- package/out/shared/src/config.js +1 -1
- package/out/shared/src/custom-key-map.js.map +1 -1
- package/out/shared/src/custom-key-set.js.map +1 -1
- package/out/shared/src/deep-clone.js +1 -1
- package/out/shared/src/document-visible.js.map +1 -1
- package/out/shared/src/dotenv.js.map +1 -1
- package/out/shared/src/error.js +1 -1
- package/out/shared/src/error.js.map +1 -1
- package/out/shared/src/hash.js +2 -2
- package/out/shared/src/hash.js.map +1 -1
- package/out/shared/src/iterables.js.map +1 -1
- package/out/shared/src/json-schema.js.map +1 -1
- package/out/shared/src/json.js.map +1 -1
- package/out/shared/src/logging-test-utils.js +1 -1
- package/out/shared/src/logging.js +1 -1
- package/out/shared/src/logging.js.map +1 -1
- package/out/shared/src/navigator.js +1 -1
- package/out/shared/src/options.js +9 -12
- package/out/shared/src/options.js.map +1 -1
- package/out/shared/src/promise-race.js.map +1 -1
- package/out/shared/src/queue.js.map +1 -1
- package/out/shared/src/record-proxy.js.map +1 -1
- package/out/shared/src/ring-buffer.js.map +1 -1
- package/out/shared/src/size-of-value.js +7 -10
- package/out/shared/src/size-of-value.js.map +1 -1
- package/out/shared/src/sorted-entries.js.map +1 -1
- package/out/shared/src/tdigest-schema.js.map +1 -1
- package/out/shared/src/tdigest.js.map +1 -1
- package/out/shared/src/valita.js +1 -1
- package/out/shared/src/valita.js.map +1 -1
- package/out/z2s/src/compiler.js +2 -4
- package/out/z2s/src/compiler.js.map +1 -1
- package/out/z2s/src/sql.js +1 -1
- package/out/z2s/src/sql.js.map +1 -1
- package/out/zero/package.js +212 -189
- package/out/zero/package.js.map +1 -1
- package/out/zero/src/adapters/drizzle.js +1 -2
- package/out/zero/src/adapters/kysely.js +1 -2
- package/out/zero/src/adapters/pg.js +1 -2
- package/out/zero/src/adapters/postgresjs.js +1 -2
- package/out/zero/src/adapters/prisma.js +1 -2
- package/out/zero/src/analyze.js +1 -2
- package/out/zero/src/change-protocol/v0.js +1 -2
- package/out/zero/src/expo-sqlite.js +1 -2
- package/out/zero/src/op-sqlite.js +1 -2
- package/out/zero/src/react-native.js.map +1 -1
- package/out/zero/src/sqlite.js +1 -2
- package/out/zero/src/zero-cache-dev.js +10 -9
- package/out/zero/src/zero-cache-dev.js.map +1 -1
- package/out/zero/src/zero-out.js +2 -1
- package/out/zero/src/zero-out.js.map +1 -1
- package/out/zero-cache/src/auth/auth.js +1 -1
- package/out/zero-cache/src/auth/auth.js.map +1 -1
- package/out/zero-cache/src/auth/jwt.js +4 -1
- package/out/zero-cache/src/auth/jwt.js.map +1 -1
- package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
- package/out/zero-cache/src/auth/read-authorizer.js +3 -2
- package/out/zero-cache/src/auth/read-authorizer.js.map +1 -1
- package/out/zero-cache/src/auth/write-authorizer.js +5 -16
- package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
- package/out/zero-cache/src/config/network.js +1 -1
- package/out/zero-cache/src/config/network.js.map +1 -1
- package/out/zero-cache/src/config/server-context.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +2 -2
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/custom/fetch.js +1 -1
- package/out/zero-cache/src/custom/fetch.js.map +1 -1
- package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
- package/out/zero-cache/src/db/create.js.map +1 -1
- package/out/zero-cache/src/db/index-predicate.js.map +1 -1
- package/out/zero-cache/src/db/lite-tables.js.map +1 -1
- package/out/zero-cache/src/db/migration-lite.js +1 -1
- package/out/zero-cache/src/db/migration-lite.js.map +1 -1
- package/out/zero-cache/src/db/migration.js +5 -3
- package/out/zero-cache/src/db/migration.js.map +1 -1
- package/out/zero-cache/src/db/pg-copy-binary.js +4 -3
- package/out/zero-cache/src/db/pg-copy-binary.js.map +1 -1
- package/out/zero-cache/src/db/pg-copy.js +0 -1
- package/out/zero-cache/src/db/pg-copy.js.map +1 -1
- package/out/zero-cache/src/db/pg-to-lite.js +1 -1
- package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
- package/out/zero-cache/src/db/run-transaction.js.map +1 -1
- package/out/zero-cache/src/db/specs.js +7 -5
- package/out/zero-cache/src/db/specs.js.map +1 -1
- package/out/zero-cache/src/db/sqlite-corruption.js.map +1 -1
- package/out/zero-cache/src/db/transaction-pool.js +5 -3
- package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
- package/out/zero-cache/src/db/warmup.js.map +1 -1
- package/out/zero-cache/src/observability/events.js.map +1 -1
- package/out/zero-cache/src/observability/log-throttle.js.map +1 -1
- package/out/zero-cache/src/observability/metrics.js.map +1 -1
- package/out/zero-cache/src/scripts/deploy-permissions.js +4 -2
- package/out/zero-cache/src/scripts/deploy-permissions.js.map +1 -1
- package/out/zero-cache/src/scripts/permissions.js.map +1 -1
- package/out/zero-cache/src/server/anonymous-otel-start.js +2 -1
- package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
- package/out/zero-cache/src/server/logging.js +1 -1
- package/out/zero-cache/src/server/logging.js.map +1 -1
- package/out/zero-cache/src/server/main.js.map +1 -1
- package/out/zero-cache/src/server/otel-log-sink.js.map +1 -1
- package/out/zero-cache/src/server/otel-start.js.map +1 -1
- package/out/zero-cache/src/server/reaper.js +2 -2
- package/out/zero-cache/src/server/reaper.js.map +1 -1
- package/out/zero-cache/src/server/replicator.js.map +1 -1
- package/out/zero-cache/src/server/runner/run-worker.js +2 -1
- package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
- package/out/zero-cache/src/server/runner/runtime.js +2 -1
- package/out/zero-cache/src/server/runner/runtime.js.map +1 -1
- package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
- package/out/zero-cache/src/server/shadow-syncer.js +1 -1
- package/out/zero-cache/src/server/shadow-syncer.js.map +1 -1
- package/out/zero-cache/src/server/slot-keeper.js.map +1 -1
- package/out/zero-cache/src/server/syncer-assigner.js.map +1 -1
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/server/worker-dispatcher.js +1 -1
- package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
- package/out/zero-cache/src/server/worker-urls.js.map +1 -1
- package/out/zero-cache/src/services/analyze.js +1 -1
- package/out/zero-cache/src/services/analyze.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js +1 -1
- 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.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/init-cleanup.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-restore.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.js +3 -3
- package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-metadata.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +3 -3
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source-init.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source-init.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.js +4 -6
- package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/decommission.js +2 -1
- package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js +5 -3
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput-parser.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +3 -2
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/lsn.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js +2 -2
- package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slot-health.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slot-health.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slots.js +3 -3
- package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +1 -1
- 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/index-predicate.js +13 -12
- package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/published.js +1 -1
- 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/replica-stage-enum.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js +9 -6
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/validation.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/validation.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js +1 -1
- package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/control.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/downstream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/status.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current.js +1 -1
- package/out/zero-cache/src/services/change-source/protocol/mod.js +1 -2
- package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backfill-state.js +1 -1
- package/out/zero-cache/src/services/change-streamer/backfill-state.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/broadcast.js +1 -1
- package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-log-initializer.js +2 -2
- package/out/zero-cache/src/services/change-streamer/change-log-initializer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +4 -2
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +21 -21
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.js +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/drain-tracker.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/forwarder.js +11 -10
- package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js +1 -1
- package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js +3 -3
- package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/replica-poller.js +1 -1
- package/out/zero-cache/src/services/change-streamer/replica-poller.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/restore-admission.js +12 -1
- package/out/zero-cache/src/services/change-streamer/restore-admission.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/init.js +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/init.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.js +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-message.js +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-message.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +1 -3
- package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscribe.js +1 -1
- package/out/zero-cache/src/services/change-streamer/subscribe.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.js +2 -2
- package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/upstream-acker.js +0 -1
- package/out/zero-cache/src/services/change-streamer/upstream-acker.js.map +1 -1
- package/out/zero-cache/src/services/heapz.js +2 -1
- package/out/zero-cache/src/services/heapz.js.map +1 -1
- package/out/zero-cache/src/services/http-service.js.map +1 -1
- package/out/zero-cache/src/services/life-cycle.js +1 -1
- package/out/zero-cache/src/services/life-cycle.js.map +1 -1
- package/out/zero-cache/src/services/limiter/sliding-window-limiter.js +4 -2
- package/out/zero-cache/src/services/limiter/sliding-window-limiter.js.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.js +1 -1
- package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
- package/out/zero-cache/src/services/litestream/litestream-checkpointer.js +2 -2
- package/out/zero-cache/src/services/litestream/litestream-checkpointer.js.map +1 -1
- package/out/zero-cache/src/services/litestream/litestream-controller.js.map +1 -1
- package/out/zero-cache/src/services/litestream/metrics.js.map +1 -1
- package/out/zero-cache/src/services/litestream/restore-progress.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.js +3 -2
- package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
- package/out/zero-cache/src/services/plannerz.js +4 -3
- package/out/zero-cache/src/services/plannerz.js.map +1 -1
- package/out/zero-cache/src/services/profz.js +5 -3
- package/out/zero-cache/src/services/profz.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-log-cookies.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-log-db.js +3 -2
- package/out/zero-cache/src/services/replicator/change-log-db.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-log-stream-writer.js +1 -1
- package/out/zero-cache/src/services/replicator/change-log-stream-writer.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
- package/out/zero-cache/src/services/replicator/drain-handler.js.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.js +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
- package/out/zero-cache/src/services/replicator/notifier.js.map +1 -1
- package/out/zero-cache/src/services/replicator/replication-status.js +4 -3
- package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
- package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
- package/out/zero-cache/src/services/replicator/reporter/recorder.js +1 -1
- package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
- package/out/zero-cache/src/services/replicator/reporter/report-schema.js +1 -1
- package/out/zero-cache/src/services/replicator/reporter/report-schema.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/backfilling.js +1 -1
- package/out/zero-cache/src/services/replicator/schema/backfilling.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/change-log.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/column-metadata.js +1 -1
- package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/replication-state.js +10 -7
- package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/table-metadata.js.map +1 -1
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +1 -1
- package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -1
- package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js +1 -1
- package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker-client.js +1 -1
- package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
- package/out/zero-cache/src/services/run-ast.js.map +1 -1
- package/out/zero-cache/src/services/runner.js.map +1 -1
- package/out/zero-cache/src/services/running-state.js.map +1 -1
- package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js.map +1 -1
- package/out/zero-cache/src/services/statz.js +2 -1
- package/out/zero-cache/src/services/statz.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/active-users-gauge.js +2 -2
- package/out/zero-cache/src/services/view-syncer/active-users-gauge.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/advancement-metrics.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/batch-pacer.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.js +4 -2
- package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-schema.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-purger.js +2 -2
- package/out/zero-cache/src/services/view-syncer/cvr-purger.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.js +9 -5
- package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js +3 -2
- package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js +1 -1
- package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/hydration-budget.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js +2 -2
- package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js +5 -3
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +6 -4
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/plan-warnings.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/push-cost.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/push-cutoff.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/query-covering.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/query-shape.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/query-stats.js +1 -1
- package/out/zero-cache/src/services/view-syncer/query-stats.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/cvr.js +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/types.js +1 -1
- package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js +7 -1
- package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js +3 -2
- package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/ttl-clock.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +4 -6
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/error-with-level.js.map +1 -1
- package/out/zero-cache/src/types/lexi-version.js.map +1 -1
- package/out/zero-cache/src/types/lite.js +1 -1
- package/out/zero-cache/src/types/lite.js.map +1 -1
- package/out/zero-cache/src/types/pg-data-type.js +1 -1
- package/out/zero-cache/src/types/pg-data-type.js.map +1 -1
- package/out/zero-cache/src/types/pg.js +6 -4
- package/out/zero-cache/src/types/pg.js.map +1 -1
- package/out/zero-cache/src/types/poke-chunk.js +2 -2
- package/out/zero-cache/src/types/poke-chunk.js.map +1 -1
- package/out/zero-cache/src/types/processes.js +1 -1
- package/out/zero-cache/src/types/processes.js.map +1 -1
- package/out/zero-cache/src/types/profiler.js.map +1 -1
- package/out/zero-cache/src/types/row-key.js.map +1 -1
- package/out/zero-cache/src/types/sql.js.map +1 -1
- package/out/zero-cache/src/types/streams.js +10 -9
- package/out/zero-cache/src/types/streams.js.map +1 -1
- package/out/zero-cache/src/types/strings.js.map +1 -1
- package/out/zero-cache/src/types/subscription.js.map +1 -1
- package/out/zero-cache/src/types/timeout.js +1 -1
- package/out/zero-cache/src/types/websocket-handoff.js +1 -1
- package/out/zero-cache/src/types/ws.js +1 -1
- package/out/zero-cache/src/workers/connection.js +2 -3
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/mutator.js.map +1 -1
- package/out/zero-cache/src/workers/replicator.js +5 -4
- package/out/zero-cache/src/workers/replicator.js.map +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.js +2 -2
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/active-clients-manager.js +1 -1
- package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
- package/out/zero-client/src/client/bindings.js +11 -11
- package/out/zero-client/src/client/connection-manager.js.map +1 -1
- package/out/zero-client/src/client/connection.js +1 -1
- package/out/zero-client/src/client/connection.js.map +1 -1
- package/out/zero-client/src/client/context.js.map +1 -1
- package/out/zero-client/src/client/crud-impl.js +1 -1
- package/out/zero-client/src/client/crud-impl.js.map +1 -1
- package/out/zero-client/src/client/crud.js +1 -1
- package/out/zero-client/src/client/crud.js.map +1 -1
- package/out/zero-client/src/client/custom.js +1 -1
- package/out/zero-client/src/client/custom.js.map +1 -1
- package/out/zero-client/src/client/delete-clients-manager.js.map +1 -1
- package/out/zero-client/src/client/enable-analytics.js +1 -1
- package/out/zero-client/src/client/enable-analytics.js.map +1 -1
- package/out/zero-client/src/client/error.js.map +1 -1
- package/out/zero-client/src/client/inspector/client-group.js.map +1 -1
- package/out/zero-client/src/client/inspector/client.js.map +1 -1
- package/out/zero-client/src/client/inspector/html-dialog-prompt.js +1 -1
- package/out/zero-client/src/client/inspector/html-dialog-prompt.js.map +1 -1
- package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
- package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
- package/out/zero-client/src/client/inspector/query.js.map +1 -1
- package/out/zero-client/src/client/ivm-branch.js +1 -3
- package/out/zero-client/src/client/ivm-branch.js.map +1 -1
- package/out/zero-client/src/client/keys.js +2 -1
- package/out/zero-client/src/client/keys.js.map +1 -1
- package/out/zero-client/src/client/log-options.js.map +1 -1
- package/out/zero-client/src/client/make-replicache-mutators.js +5 -3
- package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
- package/out/zero-client/src/client/metrics.js +3 -5
- package/out/zero-client/src/client/metrics.js.map +1 -1
- package/out/zero-client/src/client/mutation-tracker.js.map +1 -1
- package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
- package/out/zero-client/src/client/options.js.map +1 -1
- package/out/zero-client/src/client/query-manager.js +2 -2
- package/out/zero-client/src/client/query-manager.js.map +1 -1
- package/out/zero-client/src/client/reload-error-handler.js +4 -3
- package/out/zero-client/src/client/reload-error-handler.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero-poke-handler.js +1 -1
- package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
- package/out/zero-client/src/client/zero-rep.js.map +1 -1
- package/out/zero-client/src/client/zero.js +19 -11
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-client/src/mod.js +23 -23
- package/out/zero-client/src/util/nanoid.js.map +1 -1
- package/out/zero-permissions/src/permissions.js +3 -2
- package/out/zero-permissions/src/permissions.js.map +1 -1
- package/out/zero-pg/src/mod.js +9 -9
- 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/application-error.js.map +1 -1
- package/out/zero-protocol/src/ast.js +19 -9
- package/out/zero-protocol/src/ast.js.map +1 -1
- package/out/zero-protocol/src/change-desired-queries.js.map +1 -1
- package/out/zero-protocol/src/client-schema.js +1 -1
- package/out/zero-protocol/src/client-schema.js.map +1 -1
- package/out/zero-protocol/src/close-connection.js +1 -1
- package/out/zero-protocol/src/close-connection.js.map +1 -1
- package/out/zero-protocol/src/connect.js +1 -1
- package/out/zero-protocol/src/connect.js.map +1 -1
- package/out/zero-protocol/src/custom-queries.js +1 -1
- package/out/zero-protocol/src/custom-queries.js.map +1 -1
- package/out/zero-protocol/src/data.js +4 -2
- package/out/zero-protocol/src/data.js.map +1 -1
- package/out/zero-protocol/src/delete-clients.js.map +1 -1
- package/out/zero-protocol/src/down.js.map +1 -1
- package/out/zero-protocol/src/error.js.map +1 -1
- package/out/zero-protocol/src/inspect-down.d.ts +81 -81
- package/out/zero-protocol/src/inspect-down.js +1 -1
- package/out/zero-protocol/src/inspect-down.js.map +1 -1
- package/out/zero-protocol/src/inspect-up.js +1 -1
- package/out/zero-protocol/src/inspect-up.js.map +1 -1
- package/out/zero-protocol/src/mutate-server.js +1 -1
- package/out/zero-protocol/src/mutate-server.js.map +1 -1
- package/out/zero-protocol/src/mutation-id.js.map +1 -1
- package/out/zero-protocol/src/mutation.js +1 -1
- package/out/zero-protocol/src/mutation.js.map +1 -1
- package/out/zero-protocol/src/mutations-patch.js +1 -1
- package/out/zero-protocol/src/mutations-patch.js.map +1 -1
- package/out/zero-protocol/src/ping.js +1 -1
- package/out/zero-protocol/src/ping.js.map +1 -1
- package/out/zero-protocol/src/poke.js +1 -1
- package/out/zero-protocol/src/poke.js.map +1 -1
- package/out/zero-protocol/src/pong.js +1 -1
- package/out/zero-protocol/src/pong.js.map +1 -1
- package/out/zero-protocol/src/primary-key.js.map +1 -1
- package/out/zero-protocol/src/protocol-version.js +1 -0
- package/out/zero-protocol/src/protocol-version.js.map +1 -1
- package/out/zero-protocol/src/pull.js +1 -1
- package/out/zero-protocol/src/pull.js.map +1 -1
- package/out/zero-protocol/src/push.js +1 -1
- package/out/zero-protocol/src/push.js.map +1 -1
- package/out/zero-protocol/src/queries-patch.js +1 -1
- package/out/zero-protocol/src/queries-patch.js.map +1 -1
- package/out/zero-protocol/src/query-hash-visitor.js.map +1 -1
- package/out/zero-protocol/src/query-hash.js +2 -2
- package/out/zero-protocol/src/query-server.js +1 -1
- package/out/zero-protocol/src/query-server.js.map +1 -1
- package/out/zero-protocol/src/row-patch.js.map +1 -1
- package/out/zero-protocol/src/up.js.map +1 -1
- package/out/zero-protocol/src/update-auth.js.map +1 -1
- package/out/zero-protocol/src/version.js.map +1 -1
- package/out/zero-react/src/bindings.js +11 -11
- package/out/zero-react/src/mod.js +5 -5
- package/out/zero-react/src/use-query.js +1 -1
- package/out/zero-react/src/use-query.js.map +1 -1
- package/out/zero-react/src/zero-provider.d.ts +1 -1
- package/out/zero-react/src/zero-provider.d.ts.map +1 -1
- package/out/zero-react/src/zero-provider.js +2 -1
- package/out/zero-react/src/zero-provider.js.map +1 -1
- package/out/zero-react/src/zero.js +23 -23
- package/out/zero-schema/src/builder/schema-builder.js +3 -2
- package/out/zero-schema/src/builder/schema-builder.js.map +1 -1
- package/out/zero-schema/src/builder/table-builder.js +1 -1
- package/out/zero-schema/src/builder/table-builder.js.map +1 -1
- package/out/zero-schema/src/compiled-permissions.js.map +1 -1
- package/out/zero-schema/src/name-mapper.js +7 -5
- package/out/zero-schema/src/name-mapper.js.map +1 -1
- package/out/zero-schema/src/schema-config.js +1 -1
- package/out/zero-schema/src/schema-config.js.map +1 -1
- package/out/zero-server/src/adapters/drizzle.js.map +1 -1
- package/out/zero-server/src/adapters/kysely.js.map +1 -1
- package/out/zero-server/src/adapters/pg.js.map +1 -1
- package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
- package/out/zero-server/src/adapters/prisma.js.map +1 -1
- package/out/zero-server/src/closable-db-transaction.js.map +1 -1
- package/out/zero-server/src/custom.js +1 -1
- package/out/zero-server/src/custom.js.map +1 -1
- package/out/zero-server/src/mod.js +8 -8
- package/out/zero-server/src/process-mutations.js.map +1 -1
- package/out/zero-server/src/push-processor.js.map +1 -1
- package/out/zero-server/src/queries/process-queries.js +2 -1
- package/out/zero-server/src/queries/process-queries.js.map +1 -1
- package/out/zero-server/src/schema.js +7 -7
- package/out/zero-server/src/schema.js.map +1 -1
- package/out/zero-server/src/zql-database.js.map +1 -1
- package/out/zero-solid/src/bindings.js +11 -11
- package/out/zero-solid/src/mod.js +5 -5
- package/out/zero-solid/src/solid-view.js +1 -1
- package/out/zero-solid/src/solid-view.js.map +1 -1
- package/out/zero-solid/src/use-connection-state.js +2 -1
- package/out/zero-solid/src/use-connection-state.js.map +1 -1
- package/out/zero-solid/src/use-query.js.map +1 -1
- package/out/zero-solid/src/use-zero-online.js +2 -1
- package/out/zero-solid/src/use-zero-online.js.map +1 -1
- package/out/zero-solid/src/use-zero.js +3 -2
- package/out/zero-solid/src/use-zero.js.map +1 -1
- package/out/zero-solid/src/zero.js +23 -23
- package/out/zero-types/src/name-mapper.js.map +1 -1
- package/out/zql/src/builder/builder.js +15 -13
- package/out/zql/src/builder/builder.js.map +1 -1
- package/out/zql/src/builder/correlated-predicate-pushdown.js.map +1 -1
- package/out/zql/src/builder/debug-delegate.js.map +1 -1
- package/out/zql/src/builder/like.js +0 -1
- package/out/zql/src/builder/like.js.map +1 -1
- package/out/zql/src/ivm/array-view.js.map +1 -1
- package/out/zql/src/ivm/cap.js.map +1 -1
- package/out/zql/src/ivm/constraint.js +1 -1
- package/out/zql/src/ivm/constraint.js.map +1 -1
- package/out/zql/src/ivm/data.js.map +1 -1
- package/out/zql/src/ivm/deferred-input.js.map +1 -1
- package/out/zql/src/ivm/exists.js +20 -18
- package/out/zql/src/ivm/exists.js.map +1 -1
- package/out/zql/src/ivm/fan-in.js.map +1 -1
- package/out/zql/src/ivm/fan-out.js.map +1 -1
- package/out/zql/src/ivm/filter-operators.js +1 -1
- package/out/zql/src/ivm/filter-operators.js.map +1 -1
- package/out/zql/src/ivm/filter-push.js.map +1 -1
- package/out/zql/src/ivm/filter.js.map +1 -1
- package/out/zql/src/ivm/flipped-join.js +1 -3
- package/out/zql/src/ivm/flipped-join.js.map +1 -1
- package/out/zql/src/ivm/join-index-tap.js.map +1 -1
- package/out/zql/src/ivm/join-index.js.map +1 -1
- package/out/zql/src/ivm/join-utils.js +11 -13
- package/out/zql/src/ivm/join-utils.js.map +1 -1
- package/out/zql/src/ivm/join.js.map +1 -1
- package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
- package/out/zql/src/ivm/memory-source.js +26 -22
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/ivm/memory-storage.js.map +1 -1
- package/out/zql/src/ivm/operator.js +2 -2
- package/out/zql/src/ivm/push-accumulated.js +0 -1
- package/out/zql/src/ivm/push-accumulated.js.map +1 -1
- package/out/zql/src/ivm/skip-yields.js.map +1 -1
- package/out/zql/src/ivm/skip.js.map +1 -1
- package/out/zql/src/ivm/take-gate.js.map +1 -1
- package/out/zql/src/ivm/take.js +1 -1
- package/out/zql/src/ivm/take.js.map +1 -1
- package/out/zql/src/ivm/union-fan-in.js +1 -1
- package/out/zql/src/ivm/union-fan-in.js.map +1 -1
- package/out/zql/src/ivm/union-fan-out.js.map +1 -1
- package/out/zql/src/ivm/view-apply-change.js +1 -1
- package/out/zql/src/ivm/view-apply-change.js.map +1 -1
- package/out/zql/src/mutate/crud.js +3 -2
- package/out/zql/src/mutate/crud.js.map +1 -1
- package/out/zql/src/planner/planner-builder.js +1 -1
- package/out/zql/src/planner/planner-builder.js.map +1 -1
- package/out/zql/src/planner/planner-connection.js.map +1 -1
- package/out/zql/src/planner/planner-debug.js.map +1 -1
- package/out/zql/src/planner/planner-fan-in.js.map +1 -1
- package/out/zql/src/planner/planner-fan-out.js.map +1 -1
- package/out/zql/src/planner/planner-filter.js.map +1 -1
- package/out/zql/src/planner/planner-graph.js +1 -5
- package/out/zql/src/planner/planner-graph.js.map +1 -1
- package/out/zql/src/planner/planner-join.js +1 -1
- package/out/zql/src/planner/planner-join.js.map +1 -1
- package/out/zql/src/planner/planner-source.js.map +1 -1
- package/out/zql/src/planner/planner-terminus.js.map +1 -1
- package/out/zql/src/query/create-builder.js +2 -1
- package/out/zql/src/query/create-builder.js.map +1 -1
- package/out/zql/src/query/expression.js +1 -1
- package/out/zql/src/query/expression.js.map +1 -1
- package/out/zql/src/query/measure-push-operator.js.map +1 -1
- package/out/zql/src/query/named.js +2 -1
- package/out/zql/src/query/named.js.map +1 -1
- package/out/zql/src/query/query-delegate-base.js +1 -1
- package/out/zql/src/query/query-delegate-base.js.map +1 -1
- package/out/zql/src/query/query-impl.js +3 -2
- package/out/zql/src/query/query-impl.js.map +1 -1
- package/out/zql/src/query/query-registry.js +1 -1
- package/out/zql/src/query/query-registry.js.map +1 -1
- package/out/zql/src/query/query-transitions.js +1 -1
- package/out/zql/src/query/query-transitions.js.map +1 -1
- package/out/zql/src/query/runnable-query-impl.js +3 -2
- package/out/zql/src/query/runnable-query-impl.js.map +1 -1
- package/out/zql/src/query/static-query.js.map +1 -1
- package/out/zql/src/query/ttl.js +8 -8
- package/out/zql/src/query/ttl.js.map +1 -1
- package/out/zql/src/query/validate-input.js.map +1 -1
- package/out/zqlite/src/database-storage.js +2 -2
- package/out/zqlite/src/database-storage.js.map +1 -1
- package/out/zqlite/src/db.js +2 -2
- package/out/zqlite/src/db.js.map +1 -1
- package/out/zqlite/src/explain-queries.js.map +1 -1
- package/out/zqlite/src/index-usage.js.map +1 -1
- package/out/zqlite/src/internal/sql-inline.js.map +1 -1
- package/out/zqlite/src/internal/sql.js.map +1 -1
- package/out/zqlite/src/internal/statement-cache.js +1 -1
- package/out/zqlite/src/internal/statement-cache.js.map +1 -1
- package/out/zqlite/src/mod.js +5 -5
- package/out/zqlite/src/pending-delta.js.map +1 -1
- package/out/zqlite/src/query-builder.js +1 -1
- package/out/zqlite/src/query-builder.js.map +1 -1
- package/out/zqlite/src/query-delegate.js.map +1 -1
- package/out/zqlite/src/resolve-scalar-subqueries.js +2 -5
- package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
- package/out/zqlite/src/sqlite-cost-model.js +3 -2
- package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
- package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
- package/out/zqlite/src/table-source.js +1 -1
- package/out/zqlite/src/table-source.js.map +1 -1
- package/package.json +16 -14
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","names":[],"sources":["../../../../../zero-cache/src/server/main.ts"],"sourcesContent":["import path from 'node:path';\nimport {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {must} from '../../../shared/src/must.ts';\nimport {runsChangeStreamer} from '../config/normalize.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {registerSQLiteCorruptionDiagnosticTarget} from '../db/sqlite-corruption.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {\n exitAfter,\n ProcessManager,\n recordStartupDurationMs,\n runUntilKilled,\n type WorkerType,\n} from '../services/life-cycle.ts';\nimport {\n childWorker,\n parentWorker,\n singleProcessMode,\n type DrainRequestedMessage,\n type ProfileMessage,\n type ProfileResponseMessage,\n type Worker,\n} from '../types/processes.ts';\nimport {\n createNotifierFrom,\n handleSubscriptionsFrom,\n subscribeTo,\n type ReplicaFileMode,\n} from '../workers/replicator.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\nimport {WorkerDispatcher} from './worker-dispatcher.ts';\nimport {\n CHANGE_STREAMER_URL,\n MUTATOR_URL,\n REAPER_URL,\n REPLICATOR_URL,\n SHADOW_SYNCER_URL,\n SYNCER_URL,\n} from './worker-urls.ts';\n\nconst clientConnectionBifurcated = false;\n\n// Default LogContext, overridden in runWorker\nlet lc = new LogContext('info', {}, consoleLogSink);\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n): Promise<void> {\n const startMs = Date.now();\n const config = getNormalizedZeroConfig({env});\n\n startOtelAuto(\n createLogContext(config, 'dispatcher', 0, false),\n 'dispatcher',\n 0,\n );\n lc = createLogContext(config, 'dispatcher');\n registerSQLiteCorruptionDiagnosticTarget(\n {\n debugName: 'dispatcher replica',\n dbPath: config.replica.file,\n },\n config.sqliteCorruptionChecks,\n );\n initEventSink(lc, config);\n\n const processes = new ProcessManager(lc, parent);\n\n const {numSyncWorkers: numSyncers} = config;\n if (config.enableCrudMutations && config.upstream.maxConns < numSyncers) {\n throw new Error(\n `Insufficient upstream connections (${config.upstream.maxConns}) for ${numSyncers} syncers.` +\n `Increase ZERO_UPSTREAM_MAX_CONNS or decrease ZERO_NUM_SYNC_WORKERS (which defaults to available cores).`,\n );\n }\n if (config.cvr.maxConns < numSyncers) {\n throw new Error(\n `Insufficient cvr connections (${config.cvr.maxConns}) for ${numSyncers} syncers.` +\n `Increase ZERO_CVR_MAX_CONNS or decrease ZERO_NUM_SYNC_WORKERS (which defaults to available cores).`,\n );\n }\n\n const internalFlags: string[] =\n numSyncers === 0\n ? []\n : [\n '--upstream-max-conns-per-worker',\n String(Math.floor(config.upstream.maxConns / numSyncers)),\n '--cvr-max-conns-per-worker',\n String(Math.floor(config.cvr.maxConns / numSyncers)),\n ];\n\n const allSubWorkers: Worker[] = [];\n function loadWorker(\n moduleUrl: URL,\n type: WorkerType,\n id?: string | number,\n ...args: string[]\n ): Worker {\n const worker = childWorker(moduleUrl, env, ...args, ...internalFlags);\n const name = path.basename(moduleUrl.pathname) + (id ? ` (${id})` : '');\n const w = processes.addWorker(worker, type, name);\n allSubWorkers.push(w);\n w.onMessageType<ProfileResponseMessage>('profileResponse', res =>\n parent.send(['profileResponse', res]),\n );\n return w;\n }\n\n const {taskID, litestream} = config;\n const runChangeStreamer = runsChangeStreamer(config);\n const sqliteChangeLogEnabled =\n config.changeStreamer.sqliteChangeLogMode !== 'off';\n if (sqliteChangeLogEnabled && !runChangeStreamer) {\n // A multi-node deployment configures every task from one environment, so\n // the change log's options reach the view-syncers too. The log lives in\n // the change-streamer, and a task that connects to one simply never writes\n // a log. Warning rather than refusing to start is what makes the\n // fleet-wide environment -- often the only knob an operator has -- able to\n // express the rollout at all.\n lc.warn?.(\n `ignoring --change-streamer-sqlite-change-log-mode=` +\n `${config.changeStreamer.sqliteChangeLogMode}: this task connects to ` +\n `a change-streamer rather than running one, and the SQLite change log ` +\n `lives in the change-streamer`,\n );\n }\n\n let changeStreamer: Worker | undefined;\n\n if (runChangeStreamer) {\n const {promise: changeStreamerReady, resolve: changeStreamerStarted} =\n resolver();\n // In a dedicated replication-manager (i.e. no syncers), the\n // change-streamer is the user-facing worker: it receives the SIGTERM\n // first so that it can drain subscribers before other supporting\n // workers are stopped. When syncers run in the same task, the\n // change-streamer supports them and thus must outlive them.\n changeStreamer = loadWorker(\n CHANGE_STREAMER_URL,\n numSyncers > 0 ? 'supporting' : 'user-facing',\n ).once('message', changeStreamerStarted);\n // Wait for the change-streamer to be ready to guarantee that a replica\n // file is present.\n await changeStreamerReady;\n }\n\n if (numSyncers > 0) {\n const {promise: reaperReady, resolve: reaperStarted} = resolver();\n loadWorker(REAPER_URL, 'supporting').once('message', reaperStarted);\n // Before starting the view-syncers, ensure that the reaper has started\n // up, indicating that any CVR db migrations have been performed.\n await reaperReady;\n }\n\n // Only run the shadow-sync canary on the replication-manager (or in\n // single-node mode, where it also owns upstream). Running on every\n // view-syncer would hammer the upstream with N redundant canaries.\n if (config.shadowSync.enabled && runChangeStreamer) {\n const {promise: shadowReady, resolve: shadowStarted} = resolver();\n loadWorker(SHADOW_SYNCER_URL, 'supporting').once('message', shadowStarted);\n await shadowReady;\n }\n\n const syncers: Worker[] = [];\n if (numSyncers) {\n const mode: ReplicaFileMode =\n runChangeStreamer && litestream.backupURL ? 'serving-copy' : 'serving';\n const {promise: replicaReady, resolve} = resolver();\n const replicator = loadWorker(\n REPLICATOR_URL,\n 'supporting',\n mode,\n mode,\n ).once('message', () => {\n subscribeTo(lc, replicator);\n resolve();\n });\n // Relay drain requests to the runner, which coordinates the SelfDrain.\n replicator.onMessageType<DrainRequestedMessage>('drainRequested', msg => {\n lc.info?.(`drain requested by the replicator`);\n parent.send<DrainRequestedMessage>(['drainRequested', msg]);\n });\n await replicaReady;\n\n const notifier = createNotifierFrom(lc, replicator);\n for (let i = 0; i < numSyncers; i++) {\n syncers.push(loadWorker(SYNCER_URL, 'user-facing', i, mode, String(i)));\n }\n syncers.forEach(syncer => handleSubscriptionsFrom(lc, syncer, notifier));\n }\n let mutator: Worker | undefined;\n if (clientConnectionBifurcated) {\n mutator = loadWorker(MUTATOR_URL, 'supporting', 'mutator');\n }\n\n lc.info?.('waiting for workers to be ready ...');\n const logWaiting = setInterval(\n () => lc.info?.(`still waiting for ${processes.initializing().join(', ')}`),\n 10_000,\n );\n await processes.allWorkersReady();\n clearInterval(logWaiting);\n const startupDurationMs = Date.now() - startMs;\n lc.info?.(`all workers ready (${startupDurationMs} ms)`);\n recordStartupDurationMs(startupDurationMs);\n\n parent.send(['ready', {ready: true}]);\n parent.onMessageType<ProfileMessage>('profile', req => {\n for (const w of allSubWorkers) {\n w.send(['profile', req]);\n }\n });\n\n try {\n await runUntilKilled(\n lc,\n parent,\n new WorkerDispatcher(\n lc,\n taskID,\n parent,\n syncers,\n mutator,\n changeStreamer,\n ),\n );\n } catch (err) {\n processes.logErrorAndExit(err, 'dispatcher');\n } finally {\n await processes.shutdown();\n }\n}\n\nif (!singleProcessMode()) {\n void exitAfter(\n () => lc,\n () => runWorker(must(parentWorker), process.env),\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"main.js","names":[],"sources":["../../../../../zero-cache/src/server/main.ts"],"sourcesContent":["import path from 'node:path';\nimport {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {must} from '../../../shared/src/must.ts';\nimport {runsChangeStreamer} from '../config/normalize.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {registerSQLiteCorruptionDiagnosticTarget} from '../db/sqlite-corruption.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {\n exitAfter,\n ProcessManager,\n recordStartupDurationMs,\n runUntilKilled,\n type WorkerType,\n} from '../services/life-cycle.ts';\nimport {\n childWorker,\n parentWorker,\n singleProcessMode,\n type DrainRequestedMessage,\n type ProfileMessage,\n type ProfileResponseMessage,\n type Worker,\n} from '../types/processes.ts';\nimport {\n createNotifierFrom,\n handleSubscriptionsFrom,\n subscribeTo,\n type ReplicaFileMode,\n} from '../workers/replicator.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\nimport {WorkerDispatcher} from './worker-dispatcher.ts';\nimport {\n CHANGE_STREAMER_URL,\n MUTATOR_URL,\n REAPER_URL,\n REPLICATOR_URL,\n SHADOW_SYNCER_URL,\n SYNCER_URL,\n} from './worker-urls.ts';\n\nconst clientConnectionBifurcated = false;\n\n// Default LogContext, overridden in runWorker\nlet lc = new LogContext('info', {}, consoleLogSink);\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n): Promise<void> {\n const startMs = Date.now();\n const config = getNormalizedZeroConfig({env});\n\n startOtelAuto(\n createLogContext(config, 'dispatcher', 0, false),\n 'dispatcher',\n 0,\n );\n lc = createLogContext(config, 'dispatcher');\n registerSQLiteCorruptionDiagnosticTarget(\n {\n debugName: 'dispatcher replica',\n dbPath: config.replica.file,\n },\n config.sqliteCorruptionChecks,\n );\n initEventSink(lc, config);\n\n const processes = new ProcessManager(lc, parent);\n\n const {numSyncWorkers: numSyncers} = config;\n if (config.enableCrudMutations && config.upstream.maxConns < numSyncers) {\n throw new Error(\n `Insufficient upstream connections (${config.upstream.maxConns}) for ${numSyncers} syncers.` +\n `Increase ZERO_UPSTREAM_MAX_CONNS or decrease ZERO_NUM_SYNC_WORKERS (which defaults to available cores).`,\n );\n }\n if (config.cvr.maxConns < numSyncers) {\n throw new Error(\n `Insufficient cvr connections (${config.cvr.maxConns}) for ${numSyncers} syncers.` +\n `Increase ZERO_CVR_MAX_CONNS or decrease ZERO_NUM_SYNC_WORKERS (which defaults to available cores).`,\n );\n }\n\n const internalFlags: string[] =\n numSyncers === 0\n ? []\n : [\n '--upstream-max-conns-per-worker',\n String(Math.floor(config.upstream.maxConns / numSyncers)),\n '--cvr-max-conns-per-worker',\n String(Math.floor(config.cvr.maxConns / numSyncers)),\n ];\n\n const allSubWorkers: Worker[] = [];\n function loadWorker(\n moduleUrl: URL,\n type: WorkerType,\n id?: string | number,\n ...args: string[]\n ): Worker {\n const worker = childWorker(moduleUrl, env, ...args, ...internalFlags);\n const name = path.basename(moduleUrl.pathname) + (id ? ` (${id})` : '');\n const w = processes.addWorker(worker, type, name);\n allSubWorkers.push(w);\n w.onMessageType<ProfileResponseMessage>('profileResponse', res =>\n parent.send(['profileResponse', res]),\n );\n return w;\n }\n\n const {taskID, litestream} = config;\n const runChangeStreamer = runsChangeStreamer(config);\n const sqliteChangeLogEnabled =\n config.changeStreamer.sqliteChangeLogMode !== 'off';\n if (sqliteChangeLogEnabled && !runChangeStreamer) {\n // A multi-node deployment configures every task from one environment, so\n // the change log's options reach the view-syncers too. The log lives in\n // the change-streamer, and a task that connects to one simply never writes\n // a log. Warning rather than refusing to start is what makes the\n // fleet-wide environment -- often the only knob an operator has -- able to\n // express the rollout at all.\n lc.warn?.(\n `ignoring --change-streamer-sqlite-change-log-mode=` +\n `${config.changeStreamer.sqliteChangeLogMode}: this task connects to ` +\n `a change-streamer rather than running one, and the SQLite change log ` +\n `lives in the change-streamer`,\n );\n }\n\n let changeStreamer: Worker | undefined;\n\n if (runChangeStreamer) {\n const {promise: changeStreamerReady, resolve: changeStreamerStarted} =\n resolver();\n // In a dedicated replication-manager (i.e. no syncers), the\n // change-streamer is the user-facing worker: it receives the SIGTERM\n // first so that it can drain subscribers before other supporting\n // workers are stopped. When syncers run in the same task, the\n // change-streamer supports them and thus must outlive them.\n changeStreamer = loadWorker(\n CHANGE_STREAMER_URL,\n numSyncers > 0 ? 'supporting' : 'user-facing',\n ).once('message', changeStreamerStarted);\n // Wait for the change-streamer to be ready to guarantee that a replica\n // file is present.\n await changeStreamerReady;\n }\n\n if (numSyncers > 0) {\n const {promise: reaperReady, resolve: reaperStarted} = resolver();\n loadWorker(REAPER_URL, 'supporting').once('message', reaperStarted);\n // Before starting the view-syncers, ensure that the reaper has started\n // up, indicating that any CVR db migrations have been performed.\n await reaperReady;\n }\n\n // Only run the shadow-sync canary on the replication-manager (or in\n // single-node mode, where it also owns upstream). Running on every\n // view-syncer would hammer the upstream with N redundant canaries.\n if (config.shadowSync.enabled && runChangeStreamer) {\n const {promise: shadowReady, resolve: shadowStarted} = resolver();\n loadWorker(SHADOW_SYNCER_URL, 'supporting').once('message', shadowStarted);\n await shadowReady;\n }\n\n const syncers: Worker[] = [];\n if (numSyncers) {\n const mode: ReplicaFileMode =\n runChangeStreamer && litestream.backupURL ? 'serving-copy' : 'serving';\n const {promise: replicaReady, resolve} = resolver();\n const replicator = loadWorker(\n REPLICATOR_URL,\n 'supporting',\n mode,\n mode,\n ).once('message', () => {\n subscribeTo(lc, replicator);\n resolve();\n });\n // Relay drain requests to the runner, which coordinates the SelfDrain.\n replicator.onMessageType<DrainRequestedMessage>('drainRequested', msg => {\n lc.info?.(`drain requested by the replicator`);\n parent.send<DrainRequestedMessage>(['drainRequested', msg]);\n });\n await replicaReady;\n\n const notifier = createNotifierFrom(lc, replicator);\n for (let i = 0; i < numSyncers; i++) {\n syncers.push(loadWorker(SYNCER_URL, 'user-facing', i, mode, String(i)));\n }\n syncers.forEach(syncer => handleSubscriptionsFrom(lc, syncer, notifier));\n }\n let mutator: Worker | undefined;\n if (clientConnectionBifurcated) {\n mutator = loadWorker(MUTATOR_URL, 'supporting', 'mutator');\n }\n\n lc.info?.('waiting for workers to be ready ...');\n const logWaiting = setInterval(\n () => lc.info?.(`still waiting for ${processes.initializing().join(', ')}`),\n 10_000,\n );\n await processes.allWorkersReady();\n clearInterval(logWaiting);\n const startupDurationMs = Date.now() - startMs;\n lc.info?.(`all workers ready (${startupDurationMs} ms)`);\n recordStartupDurationMs(startupDurationMs);\n\n parent.send(['ready', {ready: true}]);\n parent.onMessageType<ProfileMessage>('profile', req => {\n for (const w of allSubWorkers) {\n w.send(['profile', req]);\n }\n });\n\n try {\n await runUntilKilled(\n lc,\n parent,\n new WorkerDispatcher(\n lc,\n taskID,\n parent,\n syncers,\n mutator,\n changeStreamer,\n ),\n );\n } catch (err) {\n processes.logErrorAndExit(err, 'dispatcher');\n } finally {\n await processes.shutdown();\n }\n}\n\nif (!singleProcessMode()) {\n void exitAfter(\n () => lc,\n () => runWorker(must(parentWorker), process.env),\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA6CA,IAAI,KAAK,IAAI,WAAW,QAAQ,CAAC,GAAG,cAAc;AAElD,eAA8B,UAC5B,QACA,KACe;CACf,MAAM,UAAU,KAAK,IAAI;CACzB,MAAM,SAAS,wBAAwB,EAAC,IAAG,CAAC;CAE5C,cACE,iBAAiB,QAAQ,cAAc,GAAG,KAAK,GAC/C,cACA,CACF;CACA,KAAK,iBAAiB,QAAQ,YAAY;CAC1C,yCACE;EACE,WAAW;EACX,QAAQ,OAAO,QAAQ;CACzB,GACA,OAAO,sBACT;CACA,cAAc,IAAI,MAAM;CAExB,MAAM,YAAY,IAAI,eAAe,IAAI,MAAM;CAE/C,MAAM,EAAC,gBAAgB,eAAc;CACrC,IAAI,OAAO,uBAAuB,OAAO,SAAS,WAAW,YAC3D,MAAM,IAAI,MACR,sCAAsC,OAAO,SAAS,SAAS,QAAQ,WAAW,iHAEpF;CAEF,IAAI,OAAO,IAAI,WAAW,YACxB,MAAM,IAAI,MACR,iCAAiC,OAAO,IAAI,SAAS,QAAQ,WAAW,4GAE1E;CAGF,MAAM,gBACJ,eAAe,IACX,CAAC,IACD;EACE;EACA,OAAO,KAAK,MAAM,OAAO,SAAS,WAAW,UAAU,CAAC;EACxD;EACA,OAAO,KAAK,MAAM,OAAO,IAAI,WAAW,UAAU,CAAC;CACrD;CAEN,MAAM,gBAA0B,CAAC;CACjC,SAAS,WACP,WACA,MACA,IACA,GAAG,MACK;EACR,MAAM,SAAS,YAAY,WAAW,KAAK,GAAG,MAAM,GAAG,aAAa;EACpE,MAAM,OAAO,KAAK,SAAS,UAAU,QAAQ,KAAK,KAAK,KAAK,GAAG,KAAK;EACpE,MAAM,IAAI,UAAU,UAAU,QAAQ,MAAM,IAAI;EAChD,cAAc,KAAK,CAAC;EACpB,EAAE,cAAsC,oBAAmB,QACzD,OAAO,KAAK,CAAC,mBAAmB,GAAG,CAAC,CACtC;EACA,OAAO;CACT;CAEA,MAAM,EAAC,QAAQ,eAAc;CAC7B,MAAM,oBAAoB,mBAAmB,MAAM;CAGnD,IADE,OAAO,eAAe,wBAAwB,SAClB,CAAC,mBAO7B,GAAG,OACD,qDACK,OAAO,eAAe,oBAAoB,0HAGjD;CAGF,IAAI;CAEJ,IAAI,mBAAmB;EACrB,MAAM,EAAC,SAAS,qBAAqB,SAAS,0BAC5C,SAAS;EAMX,iBAAiB,WACf,qBACA,aAAa,IAAI,eAAe,aAClC,CAAC,CAAC,KAAK,WAAW,qBAAqB;EAGvC,MAAM;CACR;CAEA,IAAI,aAAa,GAAG;EAClB,MAAM,EAAC,SAAS,aAAa,SAAS,kBAAiB,SAAS;EAChE,WAAW,YAAY,YAAY,CAAC,CAAC,KAAK,WAAW,aAAa;EAGlE,MAAM;CACR;CAKA,IAAI,OAAO,WAAW,WAAW,mBAAmB;EAClD,MAAM,EAAC,SAAS,aAAa,SAAS,kBAAiB,SAAS;EAChE,WAAW,mBAAmB,YAAY,CAAC,CAAC,KAAK,WAAW,aAAa;EACzE,MAAM;CACR;CAEA,MAAM,UAAoB,CAAC;CAC3B,IAAI,YAAY;EACd,MAAM,OACJ,qBAAqB,WAAW,YAAY,iBAAiB;EAC/D,MAAM,EAAC,SAAS,cAAc,YAAW,SAAS;EAClD,MAAM,aAAa,WACjB,gBACA,cACA,MACA,IACF,CAAC,CAAC,KAAK,iBAAiB;GACtB,YAAY,IAAI,UAAU;GAC1B,QAAQ;EACV,CAAC;EAED,WAAW,cAAqC,mBAAkB,QAAO;GACvE,GAAG,OAAO,mCAAmC;GAC7C,OAAO,KAA4B,CAAC,kBAAkB,GAAG,CAAC;EAC5D,CAAC;EACD,MAAM;EAEN,MAAM,WAAW,mBAAmB,IAAI,UAAU;EAClD,KAAK,IAAI,IAAI,GAAG,IAAI,YAAY,KAC9B,QAAQ,KAAK,WAAW,YAAY,eAAe,GAAG,MAAM,OAAO,CAAC,CAAC,CAAC;EAExE,QAAQ,SAAQ,WAAU,wBAAwB,IAAI,QAAQ,QAAQ,CAAC;CACzE;CACA,IAAI;CAKJ,GAAG,OAAO,qCAAqC;CAC/C,MAAM,aAAa,kBACX,GAAG,OAAO,qBAAqB,UAAU,aAAa,CAAC,CAAC,KAAK,IAAI,GAAG,GAC1E,GACF;CACA,MAAM,UAAU,gBAAgB;CAChC,cAAc,UAAU;CACxB,MAAM,oBAAoB,KAAK,IAAI,IAAI;CACvC,GAAG,OAAO,sBAAsB,kBAAkB,KAAK;CACvD,wBAAwB,iBAAiB;CAEzC,OAAO,KAAK,CAAC,SAAS,EAAC,OAAO,KAAI,CAAC,CAAC;CACpC,OAAO,cAA8B,YAAW,QAAO;EACrD,KAAK,MAAM,KAAK,eACd,EAAE,KAAK,CAAC,WAAW,GAAG,CAAC;CAE3B,CAAC;CAED,IAAI;EACF,MAAM,eACJ,IACA,QACA,IAAI,iBACF,IACA,QACA,QACA,SACA,SACA,cACF,CACF;CACF,SAAS,KAAK;EACZ,UAAU,gBAAgB,KAAK,YAAY;CAC7C,UAAU;EACR,MAAM,UAAU,SAAS;CAC3B;AACF;AAEA,IAAI,CAAC,kBAAkB,GACrB,gBACQ,UACA,UAAU,KAAK,YAAY,GAAG,QAAQ,GAAG,CACjD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"otel-log-sink.js","names":[
|
|
1
|
+
{"version":3,"file":"otel-log-sink.js","names":[],"sources":["../../../../../zero-cache/src/server/otel-log-sink.ts"],"sourcesContent":["import {\n logs,\n SeverityNumber,\n type AnyValueMap,\n type Logger,\n type LoggerProvider,\n type LogRecord,\n} from '@opentelemetry/api-logs';\nimport type {Context, LogLevel, LogSink} from '@rocicorp/logger';\nimport {stringify} from '../../../shared/src/bigint-json.ts';\nimport {errorOrObject} from '../../../shared/src/logging.ts';\n\ntype FlushableLoggerProvider = LoggerProvider & {\n forceFlush?: (() => Promise<void>) | undefined;\n};\n\nexport class OtelLogSink implements LogSink {\n readonly #logger: Logger;\n readonly #loggerProvider: FlushableLoggerProvider;\n\n constructor(\n loggerProvider = logs.getLoggerProvider() as FlushableLoggerProvider,\n ) {\n this.#loggerProvider = loggerProvider;\n this.#logger = loggerProvider.getLogger('zero-cache');\n }\n\n log(level: LogLevel, context: Context | undefined, ...args: unknown[]): void {\n const lastObj = errorOrObject(args.at(-1));\n if (lastObj) {\n args.pop();\n }\n\n let message = args.length\n ? args.map(s => (typeof s === 'string' ? s : stringify(s))).join(' ')\n : '';\n\n if (lastObj) {\n message += ` ${stringify(lastObj)}`;\n }\n\n const payload: LogRecord = {\n severityText: level,\n severityNumber: toErrorNum(level),\n body: message,\n };\n if (context) {\n payload.attributes = context as AnyValueMap;\n }\n this.#logger.emit(payload);\n }\n\n async flush(): Promise<void> {\n await this.#loggerProvider.forceFlush?.();\n }\n}\n\nfunction toErrorNum(level: LogLevel): SeverityNumber {\n switch (level) {\n case 'error':\n return SeverityNumber.ERROR;\n case 'warn':\n return SeverityNumber.WARN;\n case 'info':\n return SeverityNumber.INFO;\n case 'debug':\n return SeverityNumber.DEBUG;\n default:\n throw new Error(`Unknown log level: ${level}`);\n }\n}\n"],"mappings":";;;;AAgBA,IAAa,cAAb,MAA4C;CAC1C;CACA;CAEA,YACE,iBAAiB,KAAK,kBAAkB,GACxC;EACA,KAAK,kBAAkB;EACvB,KAAK,UAAU,eAAe,UAAU,YAAY;CACtD;CAEA,IAAI,OAAiB,SAA8B,GAAG,MAAuB;EAC3E,MAAM,UAAU,cAAc,KAAK,GAAG,EAAE,CAAC;EACzC,IAAI,SACF,KAAK,IAAI;EAGX,IAAI,UAAU,KAAK,SACf,KAAK,KAAI,MAAM,OAAO,MAAM,WAAW,IAAI,UAAU,CAAC,CAAE,CAAC,CAAC,KAAK,GAAG,IAClE;EAEJ,IAAI,SACF,WAAW,IAAI,UAAU,OAAO;EAGlC,MAAM,UAAqB;GACzB,cAAc;GACd,gBAAgB,WAAW,KAAK;GAChC,MAAM;EACR;EACA,IAAI,SACF,QAAQ,aAAa;EAEvB,KAAK,QAAQ,KAAK,OAAO;CAC3B;CAEA,MAAM,QAAuB;EAC3B,MAAM,KAAK,gBAAgB,aAAa;CAC1C;AACF;AAEA,SAAS,WAAW,OAAiC;CACnD,QAAQ,OAAR;EACE,KAAK,SACH,OAAO,eAAe;EACxB,KAAK,QACH,OAAO,eAAe;EACxB,KAAK,QACH,OAAO,eAAe;EACxB,KAAK,SACH,OAAO,eAAe;EACxB,SACE,MAAM,IAAI,MAAM,sBAAsB,OAAO;CACjD;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"otel-start.js","names":[
|
|
1
|
+
{"version":3,"file":"otel-start.js","names":[],"sources":["../../../../../zero-cache/src/server/otel-start.ts"],"sourcesContent":["import {logs} from '@opentelemetry/api-logs';\nimport {getNodeAutoInstrumentations} from '@opentelemetry/auto-instrumentations-node';\nimport {resourceFromAttributes} from '@opentelemetry/resources';\nimport {AggregationType, InstrumentType} from '@opentelemetry/sdk-metrics';\nimport {NodeSDK} from '@opentelemetry/sdk-node';\nimport {ATTR_SERVICE_VERSION} from '@opentelemetry/semantic-conventions';\nimport type {LogContext} from '@rocicorp/logger';\nimport {\n otelEnabled,\n otelLogsEnabled,\n otelMetricsEnabled,\n otelTracesEnabled,\n} from '../../../otel/src/enabled.ts';\nimport {NATIVE_HISTOGRAM_INSTRUMENT_NAMES} from '../observability/metrics.ts';\nimport {setupOtelDiagnosticLogger} from './otel-diag-logger.ts';\n\nclass OtelManager {\n static #instance: OtelManager;\n #started = false;\n\n private constructor() {}\n\n static getInstance(): OtelManager {\n if (!OtelManager.#instance) {\n OtelManager.#instance = new OtelManager();\n }\n return OtelManager.#instance;\n }\n\n startOtelAuto(\n lc: LogContext | undefined,\n workerName: string,\n workerIndex: number,\n ) {\n if (this.#started || !otelEnabled()) {\n return;\n }\n this.#started = true;\n\n // Store and temporarily remove OTEL_LOG_LEVEL to prevent NodeSDK from setting its own logger\n const otelLogLevel = process.env.OTEL_LOG_LEVEL;\n delete process.env.OTEL_LOG_LEVEL;\n\n // Use exponential histograms by default to reduce cardinality from auto-instrumentation\n // This affects HTTP server/client and other auto-instrumented histogram metrics\n // Exponential histograms automatically adjust bucket boundaries and use fewer buckets\n process.env.OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION ??=\n 'base2_exponential_bucket_histogram';\n\n const resource = resourceFromAttributes({\n [ATTR_SERVICE_VERSION]: process.env.ZERO_SERVER_VERSION ?? 'unknown',\n // Tag every metric/trace/log with the worker name and index so each\n // worker process in a multi-worker pod is distinguishable. Without\n // this, N syncer workers sharing the same pod labels clobber each\n // other in the OTel collector on every scrape interval.\n // These mirror the 'worker' and 'workerIndex' keys in every log\n // context so logs and metrics can be correlated on the same fields.\n // Using a stable index instead of PID avoids label churn in Prometheus.\n 'process.worker': workerName,\n 'process.worker_index': workerIndex,\n });\n\n // Set defaults to be backwards compatible with the previously\n // hard-coded exporters\n process.env.OTEL_EXPORTER_OTLP_PROTOCOL ??= 'http/json';\n process.env.OTEL_METRICS_EXPORTER ??= otelMetricsEnabled()\n ? 'otlp'\n : 'none';\n process.env.OTEL_TRACES_EXPORTER ??= otelTracesEnabled() ? 'otlp' : 'none';\n process.env.OTEL_LOGS_EXPORTER ??= otelLogsEnabled() ? 'otlp' : 'none';\n\n const sdk = new NodeSDK({\n resource,\n autoDetectResources: true,\n views: NATIVE_HISTOGRAM_INSTRUMENT_NAMES.map(instrumentName => ({\n instrumentName,\n instrumentType: InstrumentType.HISTOGRAM,\n aggregation: {type: AggregationType.EXPONENTIAL_HISTOGRAM},\n })),\n instrumentations:\n process.env.OTEL_NODE_ENABLED_INSTRUMENTATIONS ||\n process.env.OTEL_NODE_DISABLED_INSTRUMENTATIONS\n ? [getNodeAutoInstrumentations()]\n : [],\n });\n\n try {\n sdk.start();\n } finally {\n if (otelLogLevel) {\n process.env.OTEL_LOG_LEVEL = otelLogLevel;\n }\n }\n setupOtelDiagnosticLogger(lc, true);\n\n logs.getLogger('zero-cache').emit({\n severityText: 'INFO',\n body: 'OpenTelemetry SDK started successfully',\n });\n }\n}\n\nexport const startOtelAuto = (\n lc: LogContext | undefined,\n workerName: string,\n workerIndex: number,\n) => OtelManager.getInstance().startOtelAuto(lc, workerName, workerIndex);\n"],"mappings":";;;;;;;;;;AAgBA,IAAM,cAAN,MAAM,YAAY;CAChB,OAAO;CACP,WAAW;CAEX,cAAsB,CAAC;CAEvB,OAAO,cAA2B;EAChC,IAAI,CAAC,YAAY,WACf,YAAY,YAAY,IAAI,YAAY;EAE1C,OAAO,YAAY;CACrB;CAEA,cACE,IACA,YACA,aACA;EACA,IAAI,KAAK,YAAY,CAAC,YAAY,GAChC;EAEF,KAAK,WAAW;EAGhB,MAAM,eAAe,QAAQ,IAAI;EACjC,OAAO,QAAQ,IAAI;EAKnB,QAAQ,IAAI,6DACV;EAEF,MAAM,WAAW,uBAAuB;IACrC,uBAAuB,QAAQ,IAAI,uBAAuB;GAQ3D,kBAAkB;GAClB,wBAAwB;EAC1B,CAAC;EAID,QAAQ,IAAI,gCAAgC;EAC5C,QAAQ,IAAI,0BAA0B,mBAAmB,IACrD,SACA;EACJ,QAAQ,IAAI,yBAAyB,kBAAkB,IAAI,SAAS;EACpE,QAAQ,IAAI,uBAAuB,gBAAgB,IAAI,SAAS;EAEhE,MAAM,MAAM,IAAI,QAAQ;GACtB;GACA,qBAAqB;GACrB,OAAO,kCAAkC,KAAI,oBAAmB;IAC9D;IACA,gBAAgB,eAAe;IAC/B,aAAa,EAAC,MAAM,gBAAgB,sBAAqB;GAC3D,EAAE;GACF,kBACE,QAAQ,IAAI,sCACZ,QAAQ,IAAI,sCACR,CAAC,4BAA4B,CAAC,IAC9B,CAAC;EACT,CAAC;EAED,IAAI;GACF,IAAI,MAAM;EACZ,UAAU;GACR,IAAI,cACF,QAAQ,IAAI,iBAAiB;EAEjC;EACA,0BAA0B,IAAI,IAAI;EAElC,KAAK,UAAU,YAAY,CAAC,CAAC,KAAK;GAChC,cAAc;GACd,MAAM;EACR,CAAC;CACH;AACF;AAEA,IAAa,iBACX,IACA,YACA,gBACG,YAAY,YAAY,CAAC,CAAC,cAAc,IAAI,YAAY,WAAW"}
|
|
@@ -14,7 +14,7 @@ import { CVRPurger } from "../services/view-syncer/cvr-purger.js";
|
|
|
14
14
|
import { initViewSyncerSchema } from "../services/view-syncer/schema/init.js";
|
|
15
15
|
import { LogContext, consoleLogSink } from "@rocicorp/logger";
|
|
16
16
|
//#region ../zero-cache/src/server/reaper.ts
|
|
17
|
-
var MS_PER_HOUR =
|
|
17
|
+
var MS_PER_HOUR = 36e5;
|
|
18
18
|
var lc = new LogContext("info", {}, consoleLogSink);
|
|
19
19
|
async function runWorker(parent, env, ...argv) {
|
|
20
20
|
installProfileHandler(parent, "reaper");
|
|
@@ -35,7 +35,7 @@ async function runWorker(parent, env, ...argv) {
|
|
|
35
35
|
inactivityThresholdMs: cvr.garbageCollectionInactivityThresholdHours * MS_PER_HOUR,
|
|
36
36
|
initialBatchSize: cvr.garbageCollectionInitialBatchSize,
|
|
37
37
|
initialIntervalMs: cvr.garbageCollectionInitialIntervalSeconds * 1e3
|
|
38
|
-
}), new ActiveUsersGauge(lc, cvrDB, shard, { updateIntervalMs:
|
|
38
|
+
}), new ActiveUsersGauge(lc, cvrDB, shard, { updateIntervalMs: 6e5 }));
|
|
39
39
|
}
|
|
40
40
|
if (!singleProcessMode()) exitAfter(() => lc, () => runWorker(must(parentWorker), process.env, ...process.argv.slice(2)));
|
|
41
41
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reaper.js","names":[],"sources":["../../../../../zero-cache/src/server/reaper.ts"],"sourcesContent":["import {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {must} from '../../../shared/src/must.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {exitAfter, runUntilKilled} from '../services/life-cycle.ts';\nimport {ActiveUsersGauge} from '../services/view-syncer/active-users-gauge.ts';\nimport {CVRPurger} from '../services/view-syncer/cvr-purger.ts';\nimport {initViewSyncerSchema} from '../services/view-syncer/schema/init.ts';\nimport {connectPgClient} from '../types/pg.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {installProfileHandler} from '../types/profiler.ts';\nimport {getShardID} from '../types/shards.ts';\nimport {startAnonymousTelemetry} from './anonymous-otel-start.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\n\nconst MS_PER_HOUR = 1000 * 60 * 60;\n\n// Default LogContext, overridden in runWorker\nlet lc = new LogContext('info', {}, consoleLogSink);\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...argv: string[]\n): Promise<void> {\n installProfileHandler(parent, 'reaper');\n const config = getNormalizedZeroConfig({env, argv});\n\n startOtelAuto(createLogContext(config, 'reaper', 0, false), 'reaper', 0);\n lc = createLogContext(config, 'reaper');\n initEventSink(lc, config);\n startAnonymousTelemetry(lc, config);\n\n const {cvr} = config;\n const shard = getShardID(config);\n const cvrDB = await connectPgClient(lc, cvr.db, `sync-cvr-purger`, {\n max: 1,\n });\n await initViewSyncerSchema(lc, cvrDB, shard);\n parent.send(['ready', {ready: true}]);\n\n return runUntilKilled(\n lc,\n parent,\n new CVRPurger(lc, cvrDB, shard, {\n inactivityThresholdMs:\n cvr.garbageCollectionInactivityThresholdHours * MS_PER_HOUR,\n initialBatchSize: cvr.garbageCollectionInitialBatchSize,\n initialIntervalMs: cvr.garbageCollectionInitialIntervalSeconds * 1000,\n }),\n // Periodically computes and exports active users gauge to anonymous telemetry\n new ActiveUsersGauge(lc, cvrDB, shard, {\n // Default 10minutes refresh; can be made configurable later if needed\n updateIntervalMs: 10 * 60 * 1000,\n }),\n );\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(\n () => lc,\n () => runWorker(must(parentWorker), process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAoBA,IAAM,cAAc
|
|
1
|
+
{"version":3,"file":"reaper.js","names":[],"sources":["../../../../../zero-cache/src/server/reaper.ts"],"sourcesContent":["import {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {must} from '../../../shared/src/must.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {exitAfter, runUntilKilled} from '../services/life-cycle.ts';\nimport {ActiveUsersGauge} from '../services/view-syncer/active-users-gauge.ts';\nimport {CVRPurger} from '../services/view-syncer/cvr-purger.ts';\nimport {initViewSyncerSchema} from '../services/view-syncer/schema/init.ts';\nimport {connectPgClient} from '../types/pg.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {installProfileHandler} from '../types/profiler.ts';\nimport {getShardID} from '../types/shards.ts';\nimport {startAnonymousTelemetry} from './anonymous-otel-start.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\n\nconst MS_PER_HOUR = 1000 * 60 * 60;\n\n// Default LogContext, overridden in runWorker\nlet lc = new LogContext('info', {}, consoleLogSink);\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...argv: string[]\n): Promise<void> {\n installProfileHandler(parent, 'reaper');\n const config = getNormalizedZeroConfig({env, argv});\n\n startOtelAuto(createLogContext(config, 'reaper', 0, false), 'reaper', 0);\n lc = createLogContext(config, 'reaper');\n initEventSink(lc, config);\n startAnonymousTelemetry(lc, config);\n\n const {cvr} = config;\n const shard = getShardID(config);\n const cvrDB = await connectPgClient(lc, cvr.db, `sync-cvr-purger`, {\n max: 1,\n });\n await initViewSyncerSchema(lc, cvrDB, shard);\n parent.send(['ready', {ready: true}]);\n\n return runUntilKilled(\n lc,\n parent,\n new CVRPurger(lc, cvrDB, shard, {\n inactivityThresholdMs:\n cvr.garbageCollectionInactivityThresholdHours * MS_PER_HOUR,\n initialBatchSize: cvr.garbageCollectionInitialBatchSize,\n initialIntervalMs: cvr.garbageCollectionInitialIntervalSeconds * 1000,\n }),\n // Periodically computes and exports active users gauge to anonymous telemetry\n new ActiveUsersGauge(lc, cvrDB, shard, {\n // Default 10minutes refresh; can be made configurable later if needed\n updateIntervalMs: 10 * 60 * 1000,\n }),\n );\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(\n () => lc,\n () => runWorker(must(parentWorker), process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAoBA,IAAM,cAAc;AAGpB,IAAI,KAAK,IAAI,WAAW,QAAQ,CAAC,GAAG,cAAc;AAElD,eAA8B,UAC5B,QACA,KACA,GAAG,MACY;CACf,sBAAsB,QAAQ,QAAQ;CACtC,MAAM,SAAS,wBAAwB;EAAC;EAAK;CAAI,CAAC;CAElD,cAAc,iBAAiB,QAAQ,UAAU,GAAG,KAAK,GAAG,UAAU,CAAC;CACvE,KAAK,iBAAiB,QAAQ,QAAQ;CACtC,cAAc,IAAI,MAAM;CACxB,wBAAwB,IAAI,MAAM;CAElC,MAAM,EAAC,QAAO;CACd,MAAM,QAAQ,WAAW,MAAM;CAC/B,MAAM,QAAQ,MAAM,gBAAgB,IAAI,IAAI,IAAI,mBAAmB,EACjE,KAAK,EACP,CAAC;CACD,MAAM,qBAAqB,IAAI,OAAO,KAAK;CAC3C,OAAO,KAAK,CAAC,SAAS,EAAC,OAAO,KAAI,CAAC,CAAC;CAEpC,OAAO,eACL,IACA,QACA,IAAI,UAAU,IAAI,OAAO,OAAO;EAC9B,uBACE,IAAI,4CAA4C;EAClD,kBAAkB,IAAI;EACtB,mBAAmB,IAAI,0CAA0C;CACnE,CAAC,GAED,IAAI,iBAAiB,IAAI,OAAO,OAAO,EAErC,kBAAkB,IACpB,CAAC,CACH;AACF;AAGA,IAAI,CAAC,kBAAkB,GACrB,gBACQ,UACA,UAAU,KAAK,YAAY,GAAG,QAAQ,KAAK,GAAG,QAAQ,KAAK,MAAM,CAAC,CAAC,CAC3E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replicator.js","names":[],"sources":["../../../../../zero-cache/src/server/replicator.ts"],"sourcesContent":["import {stat} from 'node:fs/promises';\nimport {pid} from 'node:process';\nimport type {ObservableCallback} from '@opentelemetry/api';\nimport {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {sleep} from '../../../shared/src/sleep.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {Database} from '../../../zqlite/src/db.ts';\nimport type {\n LitestreamConfig,\n NormalizedZeroConfig,\n} from '../config/normalize.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {registerSQLiteCorruptionDiagnosticTarget} from '../db/sqlite-corruption.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {getOrCreateGauge} from '../observability/metrics.ts';\nimport {\n ChangeStreamerHttpClient,\n type ReservationFollowup,\n type SnapshotReserver,\n} from '../services/change-streamer/change-streamer-http.ts';\nimport {reserveAndGetSnapshotStatus} from '../services/change-streamer/snapshot.ts';\nimport {\n exitAfter,\n runUntilKilled,\n SupportingProcesses,\n} from '../services/life-cycle.ts';\nimport {\n readLocalReplica,\n startReplicaBackupProcess,\n tryRestore,\n type RestoreAttempt,\n type RestoreResult,\n} from '../services/litestream/commands.ts';\nimport {\n litestreamRestoreDuration,\n litestreamRestoreMetricAttrs,\n litestreamRestoreRuns,\n} from '../services/litestream/metrics.ts';\nimport {RestoreProgressReporter} from '../services/litestream/restore-progress.ts';\nimport {ReplicationStatusPublisher} from '../services/replicator/replication-status.ts';\nimport {\n ReplicatorService,\n type ReplicatorMode,\n} from '../services/replicator/replicator.ts';\nimport {sqliteFileBytes} from '../services/replicator/sqlite-change-log-observability.ts';\nimport {\n ThreadWriteWorkerClient,\n type ForceCheckpointConfig,\n} from '../services/replicator/write-worker-client.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type DrainRequestedMessage,\n type Worker,\n} from '../types/processes.ts';\nimport {installProfileHandler} from '../types/profiler.ts';\nimport {getShardConfig} from '../types/shards.ts';\nimport {\n deleteStaleChangeLog,\n getPragmaConfig,\n replicaFileModeSchema,\n setUpMessageHandlers,\n setupReplica,\n type WalMode,\n} from '../workers/replicator.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\n\n// Default LogContext, overridden in runWorker\nlet lc = new LogContext('info', {}, consoleLogSink);\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...args: string[]\n): Promise<void> {\n assert(args.length > 0, `replicator mode not specified`);\n const fileMode = v.parse(args[0], replicaFileModeSchema);\n\n const config = getNormalizedZeroConfig({env, argv: args.slice(1)});\n const mode: ReplicatorMode = fileMode === 'backup' ? 'backup' : 'serving';\n const runningLocalChangeStreamer =\n config.changeStreamer.mode === 'dedicated' && !config.changeStreamer.uri;\n const workerName = `${mode}-replicator`;\n installProfileHandler(parent, workerName);\n startOtelAuto(createLogContext(config, workerName, 0, false), workerName, 0);\n lc = createLogContext(config, workerName);\n const unregisterInitialCorruptionDiagnosticTarget =\n registerSQLiteCorruptionDiagnosticTarget(\n {\n debugName: `${workerName} replica`,\n dbPath: config.replica.file,\n },\n config.sqliteCorruptionChecks,\n );\n initEventSink(lc, config);\n\n const shard = getShardConfig(config);\n const {\n taskID,\n change,\n changeStreamer: {\n port,\n uri: changeStreamerURI = runningLocalChangeStreamer\n ? `http://localhost:${port}/`\n : undefined,\n },\n replicationLag: {initialReadinessThresholdMs, maxReadinessDelayMs},\n } = config;\n const changeStreamer = new ChangeStreamerHttpClient(\n lc,\n shard,\n change.db,\n changeStreamerURI,\n );\n\n // Remote view-syncers restore from the replication-manager, which supplies\n // the backup URL. A local change-streamer owns the canonical replica and\n // must not infer backup configuration from bundled executable paths.\n //\n // The reservation's connection (`initialConnection`) is threaded through to\n // IncrementalSyncer's first subscribe(), so the replication-manager that\n // reserved the change log for this restore is the one that serves the\n // subscription (single-connection handoff).\n let initialConnection: ReservationFollowup | undefined;\n if (\n fileMode === 'serving' &&\n !runningLocalChangeStreamer &&\n (config.litestream.executable || config.litestream.executableV5)\n ) {\n initialConnection = await restoreReplica(lc, config, changeStreamer);\n }\n\n const {\n file: dbPath,\n walMode,\n pageSize,\n } = await setupReplica(lc, fileMode, config.replica);\n unregisterInitialCorruptionDiagnosticTarget();\n registerSQLiteCorruptionDiagnosticTarget(\n {\n debugName: `${workerName} replica`,\n dbPath,\n },\n config.sqliteCorruptionChecks,\n );\n\n // A file left behind by a run with the writer enabled would otherwise hold\n // local disk indefinitely. See `replicatorDeletesStaleChangeLog` for why\n // this is keyed on the config flag and nothing else.\n deleteStaleChangeLog(config.changeStreamer.sqliteChangeLogMode, dbPath);\n\n setupMetrics(lc, dbPath, walMode, pageSize);\n\n // Create the write worker for async SQLite writes. Enable write-path\n // checkpoint backpressure only for the backup replicator on litestream v5,\n // which owns the replica litestream backs up.\n const pragmas = getPragmaConfig(fileMode);\n const checkpoint: ForceCheckpointConfig | null =\n mode === 'backup' &&\n config.litestream.backupURL &&\n config.litestream.backupUsingV5 &&\n config.litestream.forceCheckpointThresholdMB\n ? {\n checkpointThresholdPages:\n (config.litestream.forceCheckpointThresholdMB * 1024 ** 2) /\n pageSize,\n maxWalPages: !config.litestream.maxWalSizeMB\n ? undefined\n : (config.litestream.maxWalSizeMB * 1024 ** 2) / pageSize,\n }\n : null;\n\n const workerClient = new ThreadWriteWorkerClient();\n await workerClient.init(dbPath, mode, pragmas, config.log, checkpoint);\n\n // The backup-replicator manages the litestream backup process. It is\n // started after the write worker has prepared the db file in order to\n // avoid contending on the sqlite lock.\n const processes = new SupportingProcesses(lc, parent);\n if (mode === 'backup') {\n processes.add(\n startReplicaBackupProcess(lc, config.litestream, dbPath),\n 'litestream',\n );\n }\n\n const replicator = new ReplicatorService(\n lc,\n taskID,\n `${workerName}-${pid}`,\n mode,\n changeStreamer,\n workerClient,\n runningLocalChangeStreamer\n ? // publish ReplicationStatusEvents from backup-replicator only\n ReplicationStatusPublisher.forReplicaFile(dbPath)\n : null,\n {\n readinessThresholdMs: initialReadinessThresholdMs,\n maxReadinessDelayMs,\n },\n initialConnection,\n // View-syncers (i.e. with a remote change-streamer) switch over to\n // another replication-manager when theirs drains.\n runningLocalChangeStreamer\n ? undefined\n : {\n onRestoreGranted: () =>\n parent.send<DrainRequestedMessage>(['drainRequested', {}]),\n replicaFile: dbPath,\n },\n );\n\n setUpMessageHandlers(lc, replicator, parent);\n\n void replicator.ready().then(() => {\n parent.send(['ready', {ready: true}]);\n });\n\n return runUntilKilled(lc, parent, replicator, processes);\n}\n\nexport function setupMetrics(\n lc: LogContext,\n file: string,\n walMode: WalMode,\n pageSize: number,\n) {\n let db: Database | undefined;\n const getDb = () => {\n if (!db) {\n try {\n db = new Database(lc, file, {readonly: true});\n } catch (e) {\n lc.warn?.(`unable to open ${file} for wal metrics`, e);\n }\n }\n return db;\n };\n\n getOrCreateGauge('replica', 'db_size', {\n description:\n `The size of the replica's main db file, ` +\n `which does not include the wal file(s)`,\n unit: 'bytes',\n }).addCallback(observeFileSize(lc, file));\n\n getOrCreateGauge('replica', 'wal_size', {\n description: `The size of the replica's wal file`,\n unit: 'bytes',\n }).addCallback(observeFileSize(lc, `${file}-wal`));\n\n if (walMode === 'wal2') {\n getOrCreateGauge('replica', 'wal2_size', {\n description: `The size of the replica's wal2 file`,\n unit: 'bytes',\n }).addCallback(observeFileSize(lc, `${file}-wal2`));\n }\n\n getOrCreateGauge('replica', 'wal_active_bytes', {\n description:\n `The actual size of active WAL frames currently in use ` +\n `(excluding pre-allocated or reclaimed space).`,\n unit: 'bytes',\n }).addCallback(observeWalActiveBytes(lc, getDb, pageSize));\n\n getOrCreateGauge('replica', 'wal_uncheckpointed_bytes', {\n description:\n `The size of un-checkpointed WAL frames that have not yet ` +\n `been backfilled into the main database.`,\n unit: 'bytes',\n }).addCallback(observeWalUncheckpointedBytes(lc, getDb, pageSize));\n\n // A whole-database total rather than a per-file one. Its counterpart,\n // `sqlite_change_log.file_bytes`, is emitted by the change-streamer, which is\n // the process that writes the log.\n getOrCreateGauge('replica', 'file_bytes', {\n description:\n `The replica's total on-disk footprint: its main db file plus its ` +\n `wal sidecars.`,\n unit: 'By',\n }).addCallback(observeSQLiteFileBytes(lc, file));\n}\n\nfunction observeFileSize(lc: LogContext, file: string): ObservableCallback {\n return async o => {\n try {\n const stats = await stat(file);\n o.observe(stats.size);\n } catch (e) {\n lc.warn?.(`unable to stat ${file} for size metrics`, e);\n }\n };\n}\n\nfunction observeWalActiveBytes(\n lc: LogContext,\n getDb: () => Database | undefined,\n pageSize: number,\n): ObservableCallback {\n return o => {\n try {\n const db = getDb();\n if (!db) {\n return;\n }\n const [{log}] = db.pragma<{log: number}>('wal_checkpoint(NOOP)');\n if (typeof log === 'number') {\n o.observe(Math.max(0, log) * pageSize);\n }\n } catch (e) {\n lc.warn?.('unable to inspect wal_checkpoint for active bytes metric', e);\n }\n };\n}\n\nfunction observeWalUncheckpointedBytes(\n lc: LogContext,\n getDb: () => Database | undefined,\n pageSize: number,\n): ObservableCallback {\n return o => {\n try {\n const db = getDb();\n if (!db) {\n return;\n }\n const [{log, checkpointed}] = db.pragma<{\n log: number;\n checkpointed: number;\n }>('wal_checkpoint(NOOP)');\n if (typeof log === 'number' && typeof checkpointed === 'number') {\n o.observe(Math.max(0, log - checkpointed) * pageSize);\n }\n } catch (e) {\n lc.warn?.(\n 'unable to inspect wal_checkpoint for uncheckpointed bytes metric',\n e,\n );\n }\n };\n}\n\nfunction observeSQLiteFileBytes(\n lc: LogContext,\n file: string,\n): ObservableCallback {\n return async o => o.observe(await sqliteFileBytes(lc, file));\n}\n\nconst RETRY_INTERVAL_MS = 3000;\n\n// The number of times a view-syncer retries a snapshot reservation from a\n// replication-manager with an older replica version than its local replica\n// (e.g. a not-yet-terminated replication-manager during a resync rollout),\n// before accepting the older version (e.g. if the upstream was legitimately\n// replaced by one with a lower replica version).\nconst MAX_OLDER_REPLICA_VERSION_RETRIES = 20;\n\n// View-syncers wait indefinitely for the replication-manager to publish a\n// restorable backup. On a fresh stack the first backup is not durable until\n// the initial sync completes and litestream uploads the initial snapshot,\n// which can take many minutes for a large replica. The platform's startup\n// probe budget (which scales with replica size) is the backstop, so\n// restoreReplica must not impose its own shorter cap and self-terminate\n// while the backup is still being produced.\n//\n// Returns the `followup` of the reservation whose backup was actually\n// restored, still open, so the caller can hand it to IncrementalSyncer's\n// first subscribe() via `followup.subscribe()`: the same replication-manager\n// that reserved the change log is the one that serves the subscription.\n/** Exported for testing. */\nexport async function restoreReplica(\n lc: LogContext,\n config: NormalizedZeroConfig,\n changeStreamer: SnapshotReserver,\n): Promise<ReservationFollowup> {\n const start = performance.now();\n let backupURL: string | undefined;\n let result: RestoreResult | undefined;\n const progress = new RestoreProgressReporter(lc, config.replica.file);\n let olderReplicaVersionRetries = 0;\n // Describing the local replica (if any) allows the replication-manager to\n // account for replicas that are being replaced (see RestoreAdmission). It\n // is read once, before the first attempt, as a failed attempt may have\n // already deleted it; it continues to describe the replica being replaced\n // (and the version not to downgrade from) on every retry.\n const localReplica = readLocalReplica(lc, config.replica.file);\n const localReplicaVersion = localReplica?.replicaVersion;\n try {\n for (;;) {\n const {reserved, followup} = await reserveAndGetSnapshotStatus(\n lc,\n config.taskID,\n changeStreamer,\n localReplica,\n );\n if (\n localReplicaVersion !== undefined &&\n reserved.replicaVersion < localReplicaVersion &&\n olderReplicaVersionRetries++ < MAX_OLDER_REPLICA_VERSION_RETRIES\n ) {\n // Don't replace a newer local replica with an older one, e.g. from\n // a replication-manager that has yet to be replaced by a resync.\n followup.cancel();\n lc.info?.(\n `replication-manager is at an older replica version ` +\n `(${reserved.replicaVersion}) than the local replica ` +\n `(${localReplicaVersion}). retrying in ${RETRY_INTERVAL_MS / 1000} seconds`,\n );\n await sleep(RETRY_INTERVAL_MS);\n continue;\n }\n // The backupURL comes from the replication-manager's snapshot response.\n ({backupURL} = reserved);\n const litestream: LitestreamConfig = {...config.litestream, backupURL};\n progress.start(reserved.replicaSize);\n let attempt: RestoreAttempt;\n try {\n attempt = await tryRestore(\n lc,\n litestream,\n config.replica.file,\n reserved,\n 'view_syncer',\n followup.signal, // abort the restore if the reservation disconnects\n );\n } catch (e) {\n if (followup.signal.aborted) {\n lc.info?.(\n `reservation disconnected during restore. retrying`,\n String(e),\n );\n continue;\n }\n followup.cancel();\n throw e;\n } finally {\n progress.stop();\n }\n if (attempt.restored) {\n result = attempt.result;\n progress.done();\n return followup; // connection to be converted to a subscription\n }\n // This attempt's reservation is stale; a fresh one is made on retry.\n followup.cancel();\n lc.info?.(\n `replica not found. retrying in ${RETRY_INTERVAL_MS / 1000} seconds`,\n );\n await sleep(RETRY_INTERVAL_MS);\n }\n } finally {\n progress.stop();\n const attrs = litestreamRestoreMetricAttrs(\n config.litestream,\n 'view_syncer',\n backupURL,\n );\n const labels = {...attrs, result: result ?? 'error'};\n litestreamRestoreRuns().add(1, labels);\n litestreamRestoreDuration().recordMs(performance.now() - start, labels);\n }\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(\n () => lc,\n () => runWorker(must(parentWorker), process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"replicator.js","names":["v.parse"],"sources":["../../../../../zero-cache/src/server/replicator.ts"],"sourcesContent":["import {stat} from 'node:fs/promises';\nimport {pid} from 'node:process';\nimport type {ObservableCallback} from '@opentelemetry/api';\nimport {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {sleep} from '../../../shared/src/sleep.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {Database} from '../../../zqlite/src/db.ts';\nimport type {\n LitestreamConfig,\n NormalizedZeroConfig,\n} from '../config/normalize.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {registerSQLiteCorruptionDiagnosticTarget} from '../db/sqlite-corruption.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {getOrCreateGauge} from '../observability/metrics.ts';\nimport {\n ChangeStreamerHttpClient,\n type ReservationFollowup,\n type SnapshotReserver,\n} from '../services/change-streamer/change-streamer-http.ts';\nimport {reserveAndGetSnapshotStatus} from '../services/change-streamer/snapshot.ts';\nimport {\n exitAfter,\n runUntilKilled,\n SupportingProcesses,\n} from '../services/life-cycle.ts';\nimport {\n readLocalReplica,\n startReplicaBackupProcess,\n tryRestore,\n type RestoreAttempt,\n type RestoreResult,\n} from '../services/litestream/commands.ts';\nimport {\n litestreamRestoreDuration,\n litestreamRestoreMetricAttrs,\n litestreamRestoreRuns,\n} from '../services/litestream/metrics.ts';\nimport {RestoreProgressReporter} from '../services/litestream/restore-progress.ts';\nimport {ReplicationStatusPublisher} from '../services/replicator/replication-status.ts';\nimport {\n ReplicatorService,\n type ReplicatorMode,\n} from '../services/replicator/replicator.ts';\nimport {sqliteFileBytes} from '../services/replicator/sqlite-change-log-observability.ts';\nimport {\n ThreadWriteWorkerClient,\n type ForceCheckpointConfig,\n} from '../services/replicator/write-worker-client.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type DrainRequestedMessage,\n type Worker,\n} from '../types/processes.ts';\nimport {installProfileHandler} from '../types/profiler.ts';\nimport {getShardConfig} from '../types/shards.ts';\nimport {\n deleteStaleChangeLog,\n getPragmaConfig,\n replicaFileModeSchema,\n setUpMessageHandlers,\n setupReplica,\n type WalMode,\n} from '../workers/replicator.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\n\n// Default LogContext, overridden in runWorker\nlet lc = new LogContext('info', {}, consoleLogSink);\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...args: string[]\n): Promise<void> {\n assert(args.length > 0, `replicator mode not specified`);\n const fileMode = v.parse(args[0], replicaFileModeSchema);\n\n const config = getNormalizedZeroConfig({env, argv: args.slice(1)});\n const mode: ReplicatorMode = fileMode === 'backup' ? 'backup' : 'serving';\n const runningLocalChangeStreamer =\n config.changeStreamer.mode === 'dedicated' && !config.changeStreamer.uri;\n const workerName = `${mode}-replicator`;\n installProfileHandler(parent, workerName);\n startOtelAuto(createLogContext(config, workerName, 0, false), workerName, 0);\n lc = createLogContext(config, workerName);\n const unregisterInitialCorruptionDiagnosticTarget =\n registerSQLiteCorruptionDiagnosticTarget(\n {\n debugName: `${workerName} replica`,\n dbPath: config.replica.file,\n },\n config.sqliteCorruptionChecks,\n );\n initEventSink(lc, config);\n\n const shard = getShardConfig(config);\n const {\n taskID,\n change,\n changeStreamer: {\n port,\n uri: changeStreamerURI = runningLocalChangeStreamer\n ? `http://localhost:${port}/`\n : undefined,\n },\n replicationLag: {initialReadinessThresholdMs, maxReadinessDelayMs},\n } = config;\n const changeStreamer = new ChangeStreamerHttpClient(\n lc,\n shard,\n change.db,\n changeStreamerURI,\n );\n\n // Remote view-syncers restore from the replication-manager, which supplies\n // the backup URL. A local change-streamer owns the canonical replica and\n // must not infer backup configuration from bundled executable paths.\n //\n // The reservation's connection (`initialConnection`) is threaded through to\n // IncrementalSyncer's first subscribe(), so the replication-manager that\n // reserved the change log for this restore is the one that serves the\n // subscription (single-connection handoff).\n let initialConnection: ReservationFollowup | undefined;\n if (\n fileMode === 'serving' &&\n !runningLocalChangeStreamer &&\n (config.litestream.executable || config.litestream.executableV5)\n ) {\n initialConnection = await restoreReplica(lc, config, changeStreamer);\n }\n\n const {\n file: dbPath,\n walMode,\n pageSize,\n } = await setupReplica(lc, fileMode, config.replica);\n unregisterInitialCorruptionDiagnosticTarget();\n registerSQLiteCorruptionDiagnosticTarget(\n {\n debugName: `${workerName} replica`,\n dbPath,\n },\n config.sqliteCorruptionChecks,\n );\n\n // A file left behind by a run with the writer enabled would otherwise hold\n // local disk indefinitely. See `replicatorDeletesStaleChangeLog` for why\n // this is keyed on the config flag and nothing else.\n deleteStaleChangeLog(config.changeStreamer.sqliteChangeLogMode, dbPath);\n\n setupMetrics(lc, dbPath, walMode, pageSize);\n\n // Create the write worker for async SQLite writes. Enable write-path\n // checkpoint backpressure only for the backup replicator on litestream v5,\n // which owns the replica litestream backs up.\n const pragmas = getPragmaConfig(fileMode);\n const checkpoint: ForceCheckpointConfig | null =\n mode === 'backup' &&\n config.litestream.backupURL &&\n config.litestream.backupUsingV5 &&\n config.litestream.forceCheckpointThresholdMB\n ? {\n checkpointThresholdPages:\n (config.litestream.forceCheckpointThresholdMB * 1024 ** 2) /\n pageSize,\n maxWalPages: !config.litestream.maxWalSizeMB\n ? undefined\n : (config.litestream.maxWalSizeMB * 1024 ** 2) / pageSize,\n }\n : null;\n\n const workerClient = new ThreadWriteWorkerClient();\n await workerClient.init(dbPath, mode, pragmas, config.log, checkpoint);\n\n // The backup-replicator manages the litestream backup process. It is\n // started after the write worker has prepared the db file in order to\n // avoid contending on the sqlite lock.\n const processes = new SupportingProcesses(lc, parent);\n if (mode === 'backup') {\n processes.add(\n startReplicaBackupProcess(lc, config.litestream, dbPath),\n 'litestream',\n );\n }\n\n const replicator = new ReplicatorService(\n lc,\n taskID,\n `${workerName}-${pid}`,\n mode,\n changeStreamer,\n workerClient,\n runningLocalChangeStreamer\n ? // publish ReplicationStatusEvents from backup-replicator only\n ReplicationStatusPublisher.forReplicaFile(dbPath)\n : null,\n {\n readinessThresholdMs: initialReadinessThresholdMs,\n maxReadinessDelayMs,\n },\n initialConnection,\n // View-syncers (i.e. with a remote change-streamer) switch over to\n // another replication-manager when theirs drains.\n runningLocalChangeStreamer\n ? undefined\n : {\n onRestoreGranted: () =>\n parent.send<DrainRequestedMessage>(['drainRequested', {}]),\n replicaFile: dbPath,\n },\n );\n\n setUpMessageHandlers(lc, replicator, parent);\n\n void replicator.ready().then(() => {\n parent.send(['ready', {ready: true}]);\n });\n\n return runUntilKilled(lc, parent, replicator, processes);\n}\n\nexport function setupMetrics(\n lc: LogContext,\n file: string,\n walMode: WalMode,\n pageSize: number,\n) {\n let db: Database | undefined;\n const getDb = () => {\n if (!db) {\n try {\n db = new Database(lc, file, {readonly: true});\n } catch (e) {\n lc.warn?.(`unable to open ${file} for wal metrics`, e);\n }\n }\n return db;\n };\n\n getOrCreateGauge('replica', 'db_size', {\n description:\n `The size of the replica's main db file, ` +\n `which does not include the wal file(s)`,\n unit: 'bytes',\n }).addCallback(observeFileSize(lc, file));\n\n getOrCreateGauge('replica', 'wal_size', {\n description: `The size of the replica's wal file`,\n unit: 'bytes',\n }).addCallback(observeFileSize(lc, `${file}-wal`));\n\n if (walMode === 'wal2') {\n getOrCreateGauge('replica', 'wal2_size', {\n description: `The size of the replica's wal2 file`,\n unit: 'bytes',\n }).addCallback(observeFileSize(lc, `${file}-wal2`));\n }\n\n getOrCreateGauge('replica', 'wal_active_bytes', {\n description:\n `The actual size of active WAL frames currently in use ` +\n `(excluding pre-allocated or reclaimed space).`,\n unit: 'bytes',\n }).addCallback(observeWalActiveBytes(lc, getDb, pageSize));\n\n getOrCreateGauge('replica', 'wal_uncheckpointed_bytes', {\n description:\n `The size of un-checkpointed WAL frames that have not yet ` +\n `been backfilled into the main database.`,\n unit: 'bytes',\n }).addCallback(observeWalUncheckpointedBytes(lc, getDb, pageSize));\n\n // A whole-database total rather than a per-file one. Its counterpart,\n // `sqlite_change_log.file_bytes`, is emitted by the change-streamer, which is\n // the process that writes the log.\n getOrCreateGauge('replica', 'file_bytes', {\n description:\n `The replica's total on-disk footprint: its main db file plus its ` +\n `wal sidecars.`,\n unit: 'By',\n }).addCallback(observeSQLiteFileBytes(lc, file));\n}\n\nfunction observeFileSize(lc: LogContext, file: string): ObservableCallback {\n return async o => {\n try {\n const stats = await stat(file);\n o.observe(stats.size);\n } catch (e) {\n lc.warn?.(`unable to stat ${file} for size metrics`, e);\n }\n };\n}\n\nfunction observeWalActiveBytes(\n lc: LogContext,\n getDb: () => Database | undefined,\n pageSize: number,\n): ObservableCallback {\n return o => {\n try {\n const db = getDb();\n if (!db) {\n return;\n }\n const [{log}] = db.pragma<{log: number}>('wal_checkpoint(NOOP)');\n if (typeof log === 'number') {\n o.observe(Math.max(0, log) * pageSize);\n }\n } catch (e) {\n lc.warn?.('unable to inspect wal_checkpoint for active bytes metric', e);\n }\n };\n}\n\nfunction observeWalUncheckpointedBytes(\n lc: LogContext,\n getDb: () => Database | undefined,\n pageSize: number,\n): ObservableCallback {\n return o => {\n try {\n const db = getDb();\n if (!db) {\n return;\n }\n const [{log, checkpointed}] = db.pragma<{\n log: number;\n checkpointed: number;\n }>('wal_checkpoint(NOOP)');\n if (typeof log === 'number' && typeof checkpointed === 'number') {\n o.observe(Math.max(0, log - checkpointed) * pageSize);\n }\n } catch (e) {\n lc.warn?.(\n 'unable to inspect wal_checkpoint for uncheckpointed bytes metric',\n e,\n );\n }\n };\n}\n\nfunction observeSQLiteFileBytes(\n lc: LogContext,\n file: string,\n): ObservableCallback {\n return async o => o.observe(await sqliteFileBytes(lc, file));\n}\n\nconst RETRY_INTERVAL_MS = 3000;\n\n// The number of times a view-syncer retries a snapshot reservation from a\n// replication-manager with an older replica version than its local replica\n// (e.g. a not-yet-terminated replication-manager during a resync rollout),\n// before accepting the older version (e.g. if the upstream was legitimately\n// replaced by one with a lower replica version).\nconst MAX_OLDER_REPLICA_VERSION_RETRIES = 20;\n\n// View-syncers wait indefinitely for the replication-manager to publish a\n// restorable backup. On a fresh stack the first backup is not durable until\n// the initial sync completes and litestream uploads the initial snapshot,\n// which can take many minutes for a large replica. The platform's startup\n// probe budget (which scales with replica size) is the backstop, so\n// restoreReplica must not impose its own shorter cap and self-terminate\n// while the backup is still being produced.\n//\n// Returns the `followup` of the reservation whose backup was actually\n// restored, still open, so the caller can hand it to IncrementalSyncer's\n// first subscribe() via `followup.subscribe()`: the same replication-manager\n// that reserved the change log is the one that serves the subscription.\n/** Exported for testing. */\nexport async function restoreReplica(\n lc: LogContext,\n config: NormalizedZeroConfig,\n changeStreamer: SnapshotReserver,\n): Promise<ReservationFollowup> {\n const start = performance.now();\n let backupURL: string | undefined;\n let result: RestoreResult | undefined;\n const progress = new RestoreProgressReporter(lc, config.replica.file);\n let olderReplicaVersionRetries = 0;\n // Describing the local replica (if any) allows the replication-manager to\n // account for replicas that are being replaced (see RestoreAdmission). It\n // is read once, before the first attempt, as a failed attempt may have\n // already deleted it; it continues to describe the replica being replaced\n // (and the version not to downgrade from) on every retry.\n const localReplica = readLocalReplica(lc, config.replica.file);\n const localReplicaVersion = localReplica?.replicaVersion;\n try {\n for (;;) {\n const {reserved, followup} = await reserveAndGetSnapshotStatus(\n lc,\n config.taskID,\n changeStreamer,\n localReplica,\n );\n if (\n localReplicaVersion !== undefined &&\n reserved.replicaVersion < localReplicaVersion &&\n olderReplicaVersionRetries++ < MAX_OLDER_REPLICA_VERSION_RETRIES\n ) {\n // Don't replace a newer local replica with an older one, e.g. from\n // a replication-manager that has yet to be replaced by a resync.\n followup.cancel();\n lc.info?.(\n `replication-manager is at an older replica version ` +\n `(${reserved.replicaVersion}) than the local replica ` +\n `(${localReplicaVersion}). retrying in ${RETRY_INTERVAL_MS / 1000} seconds`,\n );\n await sleep(RETRY_INTERVAL_MS);\n continue;\n }\n // The backupURL comes from the replication-manager's snapshot response.\n ({backupURL} = reserved);\n const litestream: LitestreamConfig = {...config.litestream, backupURL};\n progress.start(reserved.replicaSize);\n let attempt: RestoreAttempt;\n try {\n attempt = await tryRestore(\n lc,\n litestream,\n config.replica.file,\n reserved,\n 'view_syncer',\n followup.signal, // abort the restore if the reservation disconnects\n );\n } catch (e) {\n if (followup.signal.aborted) {\n lc.info?.(\n `reservation disconnected during restore. retrying`,\n String(e),\n );\n continue;\n }\n followup.cancel();\n throw e;\n } finally {\n progress.stop();\n }\n if (attempt.restored) {\n result = attempt.result;\n progress.done();\n return followup; // connection to be converted to a subscription\n }\n // This attempt's reservation is stale; a fresh one is made on retry.\n followup.cancel();\n lc.info?.(\n `replica not found. retrying in ${RETRY_INTERVAL_MS / 1000} seconds`,\n );\n await sleep(RETRY_INTERVAL_MS);\n }\n } finally {\n progress.stop();\n const attrs = litestreamRestoreMetricAttrs(\n config.litestream,\n 'view_syncer',\n backupURL,\n );\n const labels = {...attrs, result: result ?? 'error'};\n litestreamRestoreRuns().add(1, labels);\n litestreamRestoreDuration().recordMs(performance.now() - start, labels);\n }\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(\n () => lc,\n () => runWorker(must(parentWorker), process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":"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"}
|
|
@@ -31,7 +31,8 @@ async function runWorker(parent, env) {
|
|
|
31
31
|
emitDeprecationWarnings: true
|
|
32
32
|
});
|
|
33
33
|
const lc = createLogContext(cfg, "runner");
|
|
34
|
-
const
|
|
34
|
+
const defaultTaskID = await getTaskID(lc);
|
|
35
|
+
const config = normalizeZeroConfig(lc, cfg, env, defaultTaskID);
|
|
35
36
|
const processes = new ProcessManager(lc, parent ?? process);
|
|
36
37
|
const { port, keepaliveTimeoutMs, lazyStartup, selfDrainDelayMs } = config;
|
|
37
38
|
const serverVersion = getServerVersion(config);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-worker.js","names":[],"sources":["../../../../../../zero-cache/src/server/runner/run-worker.ts"],"sourcesContent":["import '../../../../shared/src/dotenv.ts';\n\nimport {styleText} from 'node:util';\nimport {resolver, type Resolver} from '@rocicorp/resolver';\nimport {colorConsole} from '../../../../shared/src/logging.ts';\nimport {PROTOCOL_VERSION} from '../../../../zero-protocol/src/protocol-version.ts';\nimport {normalizeZeroConfig} from '../../config/normalize.ts';\nimport {getServerVersion, getZeroConfig} from '../../config/zero-config.ts';\nimport {\n ProcessManager,\n runUntilKilled,\n SelfDrain,\n} from '../../services/life-cycle.ts';\nimport {\n childWorker,\n type DrainRequestedMessage,\n type Worker,\n} from '../../types/processes.ts';\nimport {createLogContext} from '../logging.ts';\nimport {MAIN_URL} from '../worker-urls.ts';\nimport {getTaskID} from './runtime.ts';\nimport {ZeroDispatcher} from './zero-dispatcher.ts';\n\nconst startupMessageEnv = 'ZERO_ENABLE_STARTUP_MESSAGE';\n\nfunction printStartupMessage(env: NodeJS.ProcessEnv) {\n if (env[startupMessageEnv] !== '1') {\n return;\n }\n\n colorConsole.log(\n `\\nBTW, ${styleText(['bold', 'cyan'], 'Cloud Zero')} ` +\n 'is now available - professional Zero hosting from the team that built it.\\n' +\n `Get started now: ${styleText(['blue', 'underline'], 'https://zero.rocicorp.dev/cloud')}\\n\\n` +\n styleText('dim', `Disable this message with ${startupMessageEnv}=0`) +\n '\\n',\n );\n}\n\n/**\n * Top-level `runner` entry point to the zero-cache. This layer is responsible for:\n * * runtime-based config normalization\n * * lazy startup\n * * serving /statsz\n * * auto-reset restarts (TODO)\n */\nexport async function runWorker(\n parent: Worker | null,\n env: NodeJS.ProcessEnv,\n): Promise<void> {\n // Note: Deprecation warnings are only emitted at this top-level parse;\n // they are suppressed when parsed in subprocesses.\n const cfg = getZeroConfig({env, emitDeprecationWarnings: true});\n const lc = createLogContext(cfg, 'runner');\n\n const defaultTaskID = await getTaskID(lc);\n const config = normalizeZeroConfig(lc, cfg, env, defaultTaskID);\n const processes = new ProcessManager(lc, parent ?? process);\n\n const {port, keepaliveTimeoutMs, lazyStartup, selfDrainDelayMs} = config;\n const serverVersion = getServerVersion(config);\n lc.info?.(`starting server${!serverVersion ? '' : `@${serverVersion}`} `, {\n protocolVersion: PROTOCOL_VERSION,\n taskID: config.taskID,\n app: config.app,\n shard: config.shard,\n port: config.port,\n });\n\n const dispatcher = new ZeroDispatcher(\n config,\n lc,\n {port, keepaliveTimeoutMs},\n startZeroCache,\n () => printStartupMessage(env),\n );\n const selfDrain = new SelfDrain(lc, parent ?? process, {\n delayMs: selfDrainDelayMs,\n onRequested: () => dispatcher.failHealthChecks(),\n });\n\n let zeroCache: Resolver<Worker> | undefined;\n function startZeroCache(): Promise<Worker> {\n if (zeroCache === undefined) {\n const startMs = performance.now();\n lc.info?.('starting zero-cache');\n\n const r = (zeroCache = resolver<Worker>());\n const w = childWorker(MAIN_URL, env)\n .once('message', () => {\n r.resolve(w);\n lc.info?.(`zero-cache ready (${performance.now() - startMs} ms)`);\n })\n .once('error', r.reject);\n w.onMessageType<DrainRequestedMessage>('drainRequested', () =>\n selfDrain.request(),\n );\n\n processes.addWorker(w, 'user-facing', 'zero-cache');\n }\n return zeroCache.promise;\n }\n\n // Eagerly start the zero-cache if it was not configured with --lazy-startup.\n if (!lazyStartup) {\n void startZeroCache();\n }\n\n await processes.allWorkersReady();\n parent?.send(['ready', {ready: true}]);\n\n try {\n await runUntilKilled(lc, parent ?? process, dispatcher);\n } catch (err) {\n processes.logErrorAndExit(err, 'main');\n } finally {\n await processes.shutdown();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAuBA,IAAM,oBAAoB;AAE1B,SAAS,oBAAoB,KAAwB;CACnD,IAAI,IAAI,uBAAuB,KAC7B;CAGF,aAAa,IACX,UAAU,UAAU,CAAC,QAAQ,MAAM,GAAG,YAAY,EAAE;mBAE9B,UAAU,CAAC,QAAQ,WAAW,GAAG,iCAAiC,EAAE,QACxF,UAAU,OAAO,6BAA6B,kBAAkB,GAAG,IACnE,IACJ;AACF;;;;;;;;AASA,eAAsB,UACpB,QACA,KACe;CAGf,MAAM,MAAM,cAAc;EAAC;EAAK,yBAAyB;CAAI,CAAC;CAC9D,MAAM,KAAK,iBAAiB,KAAK,QAAQ;
|
|
1
|
+
{"version":3,"file":"run-worker.js","names":[],"sources":["../../../../../../zero-cache/src/server/runner/run-worker.ts"],"sourcesContent":["import '../../../../shared/src/dotenv.ts';\n\nimport {styleText} from 'node:util';\nimport {resolver, type Resolver} from '@rocicorp/resolver';\nimport {colorConsole} from '../../../../shared/src/logging.ts';\nimport {PROTOCOL_VERSION} from '../../../../zero-protocol/src/protocol-version.ts';\nimport {normalizeZeroConfig} from '../../config/normalize.ts';\nimport {getServerVersion, getZeroConfig} from '../../config/zero-config.ts';\nimport {\n ProcessManager,\n runUntilKilled,\n SelfDrain,\n} from '../../services/life-cycle.ts';\nimport {\n childWorker,\n type DrainRequestedMessage,\n type Worker,\n} from '../../types/processes.ts';\nimport {createLogContext} from '../logging.ts';\nimport {MAIN_URL} from '../worker-urls.ts';\nimport {getTaskID} from './runtime.ts';\nimport {ZeroDispatcher} from './zero-dispatcher.ts';\n\nconst startupMessageEnv = 'ZERO_ENABLE_STARTUP_MESSAGE';\n\nfunction printStartupMessage(env: NodeJS.ProcessEnv) {\n if (env[startupMessageEnv] !== '1') {\n return;\n }\n\n colorConsole.log(\n `\\nBTW, ${styleText(['bold', 'cyan'], 'Cloud Zero')} ` +\n 'is now available - professional Zero hosting from the team that built it.\\n' +\n `Get started now: ${styleText(['blue', 'underline'], 'https://zero.rocicorp.dev/cloud')}\\n\\n` +\n styleText('dim', `Disable this message with ${startupMessageEnv}=0`) +\n '\\n',\n );\n}\n\n/**\n * Top-level `runner` entry point to the zero-cache. This layer is responsible for:\n * * runtime-based config normalization\n * * lazy startup\n * * serving /statsz\n * * auto-reset restarts (TODO)\n */\nexport async function runWorker(\n parent: Worker | null,\n env: NodeJS.ProcessEnv,\n): Promise<void> {\n // Note: Deprecation warnings are only emitted at this top-level parse;\n // they are suppressed when parsed in subprocesses.\n const cfg = getZeroConfig({env, emitDeprecationWarnings: true});\n const lc = createLogContext(cfg, 'runner');\n\n const defaultTaskID = await getTaskID(lc);\n const config = normalizeZeroConfig(lc, cfg, env, defaultTaskID);\n const processes = new ProcessManager(lc, parent ?? process);\n\n const {port, keepaliveTimeoutMs, lazyStartup, selfDrainDelayMs} = config;\n const serverVersion = getServerVersion(config);\n lc.info?.(`starting server${!serverVersion ? '' : `@${serverVersion}`} `, {\n protocolVersion: PROTOCOL_VERSION,\n taskID: config.taskID,\n app: config.app,\n shard: config.shard,\n port: config.port,\n });\n\n const dispatcher = new ZeroDispatcher(\n config,\n lc,\n {port, keepaliveTimeoutMs},\n startZeroCache,\n () => printStartupMessage(env),\n );\n const selfDrain = new SelfDrain(lc, parent ?? process, {\n delayMs: selfDrainDelayMs,\n onRequested: () => dispatcher.failHealthChecks(),\n });\n\n let zeroCache: Resolver<Worker> | undefined;\n function startZeroCache(): Promise<Worker> {\n if (zeroCache === undefined) {\n const startMs = performance.now();\n lc.info?.('starting zero-cache');\n\n const r = (zeroCache = resolver<Worker>());\n const w = childWorker(MAIN_URL, env)\n .once('message', () => {\n r.resolve(w);\n lc.info?.(`zero-cache ready (${performance.now() - startMs} ms)`);\n })\n .once('error', r.reject);\n w.onMessageType<DrainRequestedMessage>('drainRequested', () =>\n selfDrain.request(),\n );\n\n processes.addWorker(w, 'user-facing', 'zero-cache');\n }\n return zeroCache.promise;\n }\n\n // Eagerly start the zero-cache if it was not configured with --lazy-startup.\n if (!lazyStartup) {\n void startZeroCache();\n }\n\n await processes.allWorkersReady();\n parent?.send(['ready', {ready: true}]);\n\n try {\n await runUntilKilled(lc, parent ?? process, dispatcher);\n } catch (err) {\n processes.logErrorAndExit(err, 'main');\n } finally {\n await processes.shutdown();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAuBA,IAAM,oBAAoB;AAE1B,SAAS,oBAAoB,KAAwB;CACnD,IAAI,IAAI,uBAAuB,KAC7B;CAGF,aAAa,IACX,UAAU,UAAU,CAAC,QAAQ,MAAM,GAAG,YAAY,EAAE;mBAE9B,UAAU,CAAC,QAAQ,WAAW,GAAG,iCAAiC,EAAE,QACxF,UAAU,OAAO,6BAA6B,kBAAkB,GAAG,IACnE,IACJ;AACF;;;;;;;;AASA,eAAsB,UACpB,QACA,KACe;CAGf,MAAM,MAAM,cAAc;EAAC;EAAK,yBAAyB;CAAI,CAAC;CAC9D,MAAM,KAAK,iBAAiB,KAAK,QAAQ;CAEzC,MAAM,gBAAgB,MAAM,UAAU,EAAE;CACxC,MAAM,SAAS,oBAAoB,IAAI,KAAK,KAAK,aAAa;CAC9D,MAAM,YAAY,IAAI,eAAe,IAAI,UAAU,OAAO;CAE1D,MAAM,EAAC,MAAM,oBAAoB,aAAa,qBAAoB;CAClE,MAAM,gBAAgB,iBAAiB,MAAM;CAC7C,GAAG,OAAO,kBAAkB,CAAC,gBAAgB,KAAK,IAAI,gBAAgB,IAAI;EACxE,iBAAA;EACA,QAAQ,OAAO;EACf,KAAK,OAAO;EACZ,OAAO,OAAO;EACd,MAAM,OAAO;CACf,CAAC;CAED,MAAM,aAAa,IAAI,eACrB,QACA,IACA;EAAC;EAAM;CAAkB,GACzB,sBACM,oBAAoB,GAAG,CAC/B;CACA,MAAM,YAAY,IAAI,UAAU,IAAI,UAAU,SAAS;EACrD,SAAS;EACT,mBAAmB,WAAW,iBAAiB;CACjD,CAAC;CAED,IAAI;CACJ,SAAS,iBAAkC;EACzC,IAAI,cAAc,KAAA,GAAW;GAC3B,MAAM,UAAU,YAAY,IAAI;GAChC,GAAG,OAAO,qBAAqB;GAE/B,MAAM,IAAK,YAAY,SAAiB;GACxC,MAAM,IAAI,YAAY,UAAU,GAAG,CAAC,CACjC,KAAK,iBAAiB;IACrB,EAAE,QAAQ,CAAC;IACX,GAAG,OAAO,qBAAqB,YAAY,IAAI,IAAI,QAAQ,KAAK;GAClE,CAAC,CAAC,CACD,KAAK,SAAS,EAAE,MAAM;GACzB,EAAE,cAAqC,wBACrC,UAAU,QAAQ,CACpB;GAEA,UAAU,UAAU,GAAG,eAAe,YAAY;EACpD;EACA,OAAO,UAAU;CACnB;CAGA,IAAI,CAAC,aACH,eAAoB;CAGtB,MAAM,UAAU,gBAAgB;CAChC,QAAQ,KAAK,CAAC,SAAS,EAAC,OAAO,KAAI,CAAC,CAAC;CAErC,IAAI;EACF,MAAM,eAAe,IAAI,UAAU,SAAS,UAAU;CACxD,SAAS,KAAK;EACZ,UAAU,gBAAgB,KAAK,MAAM;CACvC,UAAU;EACR,MAAM,UAAU,SAAS;CAC3B;AACF"}
|
|
@@ -12,7 +12,8 @@ async function getTaskID(lc) {
|
|
|
12
12
|
lc.warn?.("unable to lookup container metadata", e);
|
|
13
13
|
}
|
|
14
14
|
try {
|
|
15
|
-
const
|
|
15
|
+
const resp = await fetch(`${containerURI}/task`);
|
|
16
|
+
const metadata = parse(await resp.json(), containerMetadataSchema, "passthrough");
|
|
16
17
|
lc.info?.(`Task metadata`, { metadata });
|
|
17
18
|
const { TaskARN: taskID } = metadata;
|
|
18
19
|
const lastSlash = taskID.lastIndexOf("/");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.js","names":[],"sources":["../../../../../../zero-cache/src/server/runner/runtime.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {nanoid} from 'nanoid';\nimport * as v from '../../../../shared/src/valita.ts';\n\n// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint-v4-response.html\nconst containerMetadataSchema = v.object({['TaskARN']: v.string()});\n\nexport async function getTaskID(lc: LogContext) {\n const containerURI = process.env['ECS_CONTAINER_METADATA_URI_V4'];\n if (containerURI) {\n try {\n const resp = await fetch(`${containerURI}`);\n const metadata = await resp.json();\n // Logged purely for debugging.\n lc.info?.(`Container metadata`, {metadata});\n } catch (e) {\n lc.warn?.('unable to lookup container metadata', e);\n }\n\n try {\n const resp = await fetch(`${containerURI}/task`);\n const metadata = v.parse(\n await resp.json(),\n containerMetadataSchema,\n 'passthrough',\n );\n lc.info?.(`Task metadata`, {metadata});\n const {TaskARN: taskID} = metadata;\n // Task ARN's are long, e.g.\n // \"arn:aws:ecs:us-east-1:712907626835:task/zbugs-prod-Cluster-vvNFcPUVpGHr/0042ea25bf534dc19975e26f61441737\"\n // We only care about the unique ID, i.e. the last path component.\n const lastSlash = taskID.lastIndexOf('/');\n return taskID.substring(lastSlash + 1); // works for lastSlash === -1 too\n } catch (e) {\n lc.warn?.('unable to determine task ID. falling back to random ID', e);\n }\n }\n return nanoid();\n}\n"],"mappings":";;;AAKA,IAAM,
|
|
1
|
+
{"version":3,"file":"runtime.js","names":["v","v.parse"],"sources":["../../../../../../zero-cache/src/server/runner/runtime.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {nanoid} from 'nanoid';\nimport * as v from '../../../../shared/src/valita.ts';\n\n// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint-v4-response.html\nconst containerMetadataSchema = v.object({['TaskARN']: v.string()});\n\nexport async function getTaskID(lc: LogContext) {\n const containerURI = process.env['ECS_CONTAINER_METADATA_URI_V4'];\n if (containerURI) {\n try {\n const resp = await fetch(`${containerURI}`);\n const metadata = await resp.json();\n // Logged purely for debugging.\n lc.info?.(`Container metadata`, {metadata});\n } catch (e) {\n lc.warn?.('unable to lookup container metadata', e);\n }\n\n try {\n const resp = await fetch(`${containerURI}/task`);\n const metadata = v.parse(\n await resp.json(),\n containerMetadataSchema,\n 'passthrough',\n );\n lc.info?.(`Task metadata`, {metadata});\n const {TaskARN: taskID} = metadata;\n // Task ARN's are long, e.g.\n // \"arn:aws:ecs:us-east-1:712907626835:task/zbugs-prod-Cluster-vvNFcPUVpGHr/0042ea25bf534dc19975e26f61441737\"\n // We only care about the unique ID, i.e. the last path component.\n const lastSlash = taskID.lastIndexOf('/');\n return taskID.substring(lastSlash + 1); // works for lastSlash === -1 too\n } catch (e) {\n lc.warn?.('unable to determine task ID. falling back to random ID', e);\n }\n }\n return nanoid();\n}\n"],"mappings":";;;AAKA,IAAM,0BAA0BA,eAAE,OAAO,GAAE,YAAYA,eAAE,OAAO,EAAC,CAAC;AAElE,eAAsB,UAAU,IAAgB;CAC9C,MAAM,eAAe,QAAQ,IAAI;CACjC,IAAI,cAAc;EAChB,IAAI;GAEF,MAAM,WAAW,OAAM,MADJ,MAAM,GAAG,cAAc,EAAA,CACd,KAAK;GAEjC,GAAG,OAAO,sBAAsB,EAAC,SAAQ,CAAC;EAC5C,SAAS,GAAG;GACV,GAAG,OAAO,uCAAuC,CAAC;EACpD;EAEA,IAAI;GACF,MAAM,OAAO,MAAM,MAAM,GAAG,aAAa,MAAM;GAC/C,MAAM,WAAWC,MACf,MAAM,KAAK,KAAK,GAChB,yBACA,aACF;GACA,GAAG,OAAO,iBAAiB,EAAC,SAAQ,CAAC;GACrC,MAAM,EAAC,SAAS,WAAU;GAI1B,MAAM,YAAY,OAAO,YAAY,GAAG;GACxC,OAAO,OAAO,UAAU,YAAY,CAAC;EACvC,SAAS,GAAG;GACV,GAAG,OAAO,0DAA0D,CAAC;EACvE;CACF;CACA,OAAO,OAAO;AAChB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zero-dispatcher.js","names":[
|
|
1
|
+
{"version":3,"file":"zero-dispatcher.js","names":[],"sources":["../../../../../../zero-cache/src/server/runner/zero-dispatcher.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {NormalizedZeroConfig} from '../../config/normalize.ts';\nimport {handleHeapzRequest} from '../../services/heapz.ts';\nimport {HttpService, type Options} from '../../services/http-service.ts';\nimport {handlePlannerzRequest} from '../../services/plannerz.ts';\nimport {\n handleProfrmzRequest,\n handleProfzRequest,\n} from '../../services/profz.ts';\nimport {handleStatzRequest} from '../../services/statz.ts';\nimport type {IncomingMessageSubset} from '../../types/http.ts';\nimport type {Worker} from '../../types/processes.ts';\nimport {\n installWebSocketHandoff,\n type HandoffSpec,\n} from '../../types/websocket-handoff.ts';\n\nexport class ZeroDispatcher extends HttpService {\n readonly id = 'zero-dispatcher';\n readonly #getWorker: () => Promise<Worker>;\n readonly #onStart: (() => void) | undefined;\n\n constructor(\n config: NormalizedZeroConfig,\n lc: LogContext,\n opts: Options,\n getWorker: () => Promise<Worker>,\n onStart?: () => void,\n ) {\n super(`zero-dispatcher`, lc, opts, fastify => {\n fastify.get('/statz', (req, res) =>\n handleStatzRequest(lc, config, req, res),\n );\n fastify.get('/plannerz', (req, res) =>\n handlePlannerzRequest(lc, config, req, res),\n );\n fastify.get('/heapz', (req, res) =>\n handleHeapzRequest(lc, config, req, res),\n );\n fastify.get('/profz', (req, res) =>\n handleProfzRequest(lc, config, req, res, this.#getWorker),\n );\n fastify.get('/profrmz', (req, res) =>\n handleProfrmzRequest(lc, config, req, res, this.#getWorker),\n );\n installWebSocketHandoff(lc, this.#handoff, fastify.server);\n });\n this.#getWorker = getWorker;\n this.#onStart = onStart;\n }\n\n protected override _onStart() {\n this.#onStart?.();\n }\n\n readonly #handoff = (\n _req: IncomingMessageSubset,\n dispatch: (h: HandoffSpec<string>) => void,\n onError: (error: unknown) => void,\n ) => {\n void this.#getWorker().then(\n sender => dispatch({payload: 'unused', sender}),\n onError,\n );\n };\n}\n"],"mappings":";;;;;;;AAiBA,IAAa,iBAAb,cAAoC,YAAY;CAC9C,KAAc;CACd;CACA;CAEA,YACE,QACA,IACA,MACA,WACA,SACA;EACA,MAAM,mBAAmB,IAAI,OAAM,YAAW;GAC5C,QAAQ,IAAI,WAAW,KAAK,QAC1B,mBAAmB,IAAI,QAAQ,KAAK,GAAG,CACzC;GACA,QAAQ,IAAI,cAAc,KAAK,QAC7B,sBAAsB,IAAI,QAAQ,KAAK,GAAG,CAC5C;GACA,QAAQ,IAAI,WAAW,KAAK,QAC1B,mBAAmB,IAAI,QAAQ,KAAK,GAAG,CACzC;GACA,QAAQ,IAAI,WAAW,KAAK,QAC1B,mBAAmB,IAAI,QAAQ,KAAK,KAAK,KAAK,UAAU,CAC1D;GACA,QAAQ,IAAI,aAAa,KAAK,QAC5B,qBAAqB,IAAI,QAAQ,KAAK,KAAK,KAAK,UAAU,CAC5D;GACA,wBAAwB,IAAI,KAAK,UAAU,QAAQ,MAAM;EAC3D,CAAC;EACD,KAAK,aAAa;EAClB,KAAK,WAAW;CAClB;CAEA,WAA8B;EAC5B,KAAK,WAAW;CAClB;CAEA,YACE,MACA,UACA,YACG;EACH,KAAU,WAAW,CAAC,CAAC,MACrB,WAAU,SAAS;GAAC,SAAS;GAAU;EAAM,CAAC,GAC9C,OACF;CACF;AACF"}
|
|
@@ -10,7 +10,7 @@ import { startOtelAuto } from "./otel-start.js";
|
|
|
10
10
|
import { ShadowSyncService } from "../services/shadow-sync/shadow-sync-service.js";
|
|
11
11
|
import { LogContext, consoleLogSink } from "@rocicorp/logger";
|
|
12
12
|
//#region ../zero-cache/src/server/shadow-syncer.ts
|
|
13
|
-
var MS_PER_HOUR =
|
|
13
|
+
var MS_PER_HOUR = 36e5;
|
|
14
14
|
var lc = new LogContext("info", {}, consoleLogSink);
|
|
15
15
|
function runWorker(parent, env, ...argv) {
|
|
16
16
|
const config = getNormalizedZeroConfig({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shadow-syncer.js","names":[],"sources":["../../../../../zero-cache/src/server/shadow-syncer.ts"],"sourcesContent":["import {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {must} from '../../../shared/src/must.ts';\nimport {getServerContext} from '../config/server-context.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {exitAfter, runUntilKilled} from '../services/life-cycle.ts';\nimport {ShadowSyncService} from '../services/shadow-sync/shadow-sync-service.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {getShardConfig} from '../types/shards.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\n\nconst MS_PER_HOUR = 1000 * 60 * 60;\n\n// Default LogContext, overridden in runWorker\nlet lc = new LogContext('info', {}, consoleLogSink);\n\nexport default function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...argv: string[]\n): Promise<void> {\n const config = getNormalizedZeroConfig({env, argv});\n\n startOtelAuto(\n createLogContext(config, 'shadow-syncer', 0, false),\n 'shadow-syncer',\n 0,\n );\n lc = createLogContext(config, 'shadow-syncer');\n initEventSink(lc, config);\n\n const {shadowSync, upstream, initialSync} = config;\n const shard = getShardConfig(config);\n const service = new ShadowSyncService(\n lc,\n shard,\n upstream.db,\n getServerContext(config),\n {\n intervalMs: shadowSync.intervalHours * MS_PER_HOUR,\n sampleRate: shadowSync.sampleRate,\n maxRowsPerTable: shadowSync.maxRowsPerTable,\n textCopy: initialSync.textCopy,\n },\n );\n\n parent.send(['ready', {ready: true}]);\n\n return runUntilKilled(lc, parent, service);\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(\n () => lc,\n () => runWorker(must(parentWorker), process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":";;;;;;;;;;;;AAgBA,IAAM,cAAc
|
|
1
|
+
{"version":3,"file":"shadow-syncer.js","names":[],"sources":["../../../../../zero-cache/src/server/shadow-syncer.ts"],"sourcesContent":["import {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {must} from '../../../shared/src/must.ts';\nimport {getServerContext} from '../config/server-context.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {exitAfter, runUntilKilled} from '../services/life-cycle.ts';\nimport {ShadowSyncService} from '../services/shadow-sync/shadow-sync-service.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {getShardConfig} from '../types/shards.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\n\nconst MS_PER_HOUR = 1000 * 60 * 60;\n\n// Default LogContext, overridden in runWorker\nlet lc = new LogContext('info', {}, consoleLogSink);\n\nexport default function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...argv: string[]\n): Promise<void> {\n const config = getNormalizedZeroConfig({env, argv});\n\n startOtelAuto(\n createLogContext(config, 'shadow-syncer', 0, false),\n 'shadow-syncer',\n 0,\n );\n lc = createLogContext(config, 'shadow-syncer');\n initEventSink(lc, config);\n\n const {shadowSync, upstream, initialSync} = config;\n const shard = getShardConfig(config);\n const service = new ShadowSyncService(\n lc,\n shard,\n upstream.db,\n getServerContext(config),\n {\n intervalMs: shadowSync.intervalHours * MS_PER_HOUR,\n sampleRate: shadowSync.sampleRate,\n maxRowsPerTable: shadowSync.maxRowsPerTable,\n textCopy: initialSync.textCopy,\n },\n );\n\n parent.send(['ready', {ready: true}]);\n\n return runUntilKilled(lc, parent, service);\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(\n () => lc,\n () => runWorker(must(parentWorker), process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":";;;;;;;;;;;;AAgBA,IAAM,cAAc;AAGpB,IAAI,KAAK,IAAI,WAAW,QAAQ,CAAC,GAAG,cAAc;AAElD,SAAwB,UACtB,QACA,KACA,GAAG,MACY;CACf,MAAM,SAAS,wBAAwB;EAAC;EAAK;CAAI,CAAC;CAElD,cACE,iBAAiB,QAAQ,iBAAiB,GAAG,KAAK,GAClD,iBACA,CACF;CACA,KAAK,iBAAiB,QAAQ,eAAe;CAC7C,cAAc,IAAI,MAAM;CAExB,MAAM,EAAC,YAAY,UAAU,gBAAe;CAC5C,MAAM,QAAQ,eAAe,MAAM;CACnC,MAAM,UAAU,IAAI,kBAClB,IACA,OACA,SAAS,IACT,iBAAiB,MAAM,GACvB;EACE,YAAY,WAAW,gBAAgB;EACvC,YAAY,WAAW;EACvB,iBAAiB,WAAW;EAC5B,UAAU,YAAY;CACxB,CACF;CAEA,OAAO,KAAK,CAAC,SAAS,EAAC,OAAO,KAAI,CAAC,CAAC;CAEpC,OAAO,eAAe,IAAI,QAAQ,OAAO;AAC3C;AAGA,IAAI,CAAC,kBAAkB,GACrB,gBACQ,UACA,UAAU,KAAK,YAAY,GAAG,QAAQ,KAAK,GAAG,QAAQ,KAAK,MAAM,CAAC,CAAC,CAC3E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slot-keeper.js","names":[],"sources":["../../../../../zero-cache/src/server/slot-keeper.ts"],"sourcesContent":["import {resolve} from 'node:path';\nimport type {Readable} from 'node:stream';\nimport {fileURLToPath} from 'node:url';\nimport {PG_ADMIN_SHUTDOWN} from '@drdgvhbh/postgres-error-codes';\nimport {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {defu} from 'defu';\nimport postgres, {type Options, type PostgresType} from 'postgres';\nimport {sleep} from '../../../shared/src/sleep.ts';\nimport {getLogConfig} from '../config/zero-config.ts';\nimport {\n DEFAULT_RETRIES_IF_REPLICATION_SLOT_ACTIVE,\n makeAck,\n startReplicationStream,\n type PgConnectionConfig,\n} from '../services/change-source/pg/logical-replication/stream.ts';\nimport {toBigInt} from '../services/change-source/pg/lsn.ts';\nimport {exitAfter} from '../services/life-cycle.ts';\nimport {inactivityTimeoutSocket, isPostgresError} from '../types/pg.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {createLogContext} from './logging.ts';\n\nexport type SlotKeeperConfig = {\n db: PgConnectionConfig;\n slot: string;\n dummyPublication: string;\n lsn: string;\n ackIntervalMs?: number | undefined;\n};\n\nexport function parseLsn(lsn: string): bigint {\n return lsn.includes('/') ? toBigInt(lsn) : BigInt(lsn);\n}\n\nexport function waitForStartConfig(parent: Worker): Promise<SlotKeeperConfig> {\n return new Promise<SlotKeeperConfig>((resolve, reject) => {\n const cleanup = () => {\n parent.off('disconnect', onDisconnect);\n parent.off('close', onClose);\n if (process.send) {\n process.off('disconnect', onDisconnect);\n }\n };\n const onDisconnect = () => {\n cleanup();\n reject(new Error('Parent disconnected before sending start config'));\n };\n const onClose = () => {\n cleanup();\n reject(new Error('Parent closed before sending start config'));\n };\n parent.once('disconnect', onDisconnect);\n parent.once('close', onClose);\n if (process.send) {\n process.once('disconnect', onDisconnect);\n }\n parent.onceMessageType<['start', SlotKeeperConfig]>('start', cfg => {\n cleanup();\n resolve(cfg);\n });\n parent.send(['init', {}]);\n });\n}\n\nexport type SlotKeeperDependencies = {\n createSession?: (\n config: SlotKeeperConfig,\n lc: LogContext,\n ) => postgres.Sql | {end: (opts?: {timeout?: number}) => Promise<void>};\n startStream?: (\n lc: LogContext,\n session: postgres.Sql,\n slot: string,\n publications: string[],\n startLsn: bigint,\n maxAttempts?: number,\n ) => Promise<[Readable, NodeJS.WritableStream]>;\n sleepFn?: (ms: number) => Promise<void>;\n logContext?: LogContext;\n};\n\nlet lc = new LogContext('info', {}, consoleLogSink);\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n depsOrArg?: string | SlotKeeperDependencies,\n ...argv: string[]\n): Promise<void> {\n // Only logging config is needed; avoid requiring the full (normalized) config.\n const logConfig = getLogConfig({env, argv});\n const deps: SlotKeeperDependencies =\n typeof depsOrArg === 'object' && depsOrArg !== null ? depsOrArg : {};\n const createSession =\n deps.createSession ??\n ((cfg: SlotKeeperConfig, log: LogContext) =>\n postgres(\n defu(\n {\n max: 1,\n fetch_types: false,\n idle_timeout: null,\n max_lifetime: null as unknown as number,\n connection: {\n application_name: `slot-keeper-${cfg.slot}`,\n replication: 'database',\n },\n socket: inactivityTimeoutSocket(log, 0, 60_000),\n },\n cfg.db as Options<Record<string, PostgresType>>,\n ),\n ));\n const startStream = deps.startStream ?? startReplicationStream;\n const sleepFn = deps.sleepFn ?? sleep;\n\n const config = await waitForStartConfig(parent);\n\n lc = (\n deps.logContext ?? createLogContext(logConfig, 'slot-keeper')\n ).withContext('slot', config.slot);\n lc.info?.(`starting slot-keeper for ${config.slot}`);\n\n let stopped = false;\n let stopResolve: (() => void) | null = null;\n let currentReadable: Readable | null = null;\n let currentSession:\n | postgres.Sql\n | {end: (opts?: {timeout?: number}) => Promise<void>}\n | null = null;\n const lsn = parseLsn(config.lsn);\n const ackIntervalMs = config.ackIntervalMs ?? 5_000;\n\n const closeCurrent = async () => {\n if (currentReadable) {\n try {\n currentReadable.destroy();\n } catch {}\n currentReadable = null;\n }\n if (currentSession) {\n try {\n await currentSession.end({timeout: 0});\n lc.info?.(`ended slot-keeper replication session for ${config.slot}`);\n } catch (e) {\n lc.warn?.(`error ending slot-keeper session`, e);\n }\n currentSession = null;\n }\n };\n\n const stop = async () => {\n if (stopped) {\n return;\n }\n stopped = true;\n lc.info?.(`stopping slot-keeper for ${config.slot}`);\n stopResolve?.();\n await closeCurrent();\n };\n\n parent.onceMessageType('stop', () => void stop());\n parent.on('SIGTERM', () => void stop());\n parent.on('SIGINT', () => void stop());\n parent.once('disconnect', () => void stop());\n parent.once('close', () => void stop());\n process.once('SIGTERM', () => void stop());\n process.once('SIGINT', () => void stop());\n if (process.send) {\n process.once('disconnect', () => void stop());\n }\n\n let firstAttempt = true;\n let delayMs = 1000;\n\n while (!stopped) {\n try {\n lc.info?.(`connecting replication session for slot ${config.slot}`);\n const session = createSession(config, lc);\n currentSession = session;\n\n const [readable, writeable] = await startStream(\n lc,\n session as postgres.Sql,\n config.slot,\n [config.dummyPublication],\n lsn,\n DEFAULT_RETRIES_IF_REPLICATION_SLOT_ACTIVE + 1,\n );\n currentReadable = readable;\n // Discard the streamed messages. Otherwise the paused socket backs up the\n // wal_sender (e.g. with keepalives and logical messages) for the duration\n // of the initial sync / restore, and a wal_sender blocked on writing to the\n // client does not exit (and release the slot) when it is terminated by the\n // takeover; it instead blocks on sending the termination error to the\n // client, until signaled again.\n readable.resume();\n\n if (stopped) {\n await closeCurrent();\n break;\n }\n\n lc.info?.(`slot-keeper active for ${config.slot} at lsn ${config.lsn}`);\n if (firstAttempt) {\n firstAttempt = false;\n parent.send(['ready', {ready: true}]);\n }\n\n // Reset backoff delay on successful connection\n delayMs = 1000;\n\n const ackInterval = setInterval(() => {\n if (stopped) {\n clearInterval(ackInterval);\n return;\n }\n try {\n writeable.write(makeAck(lsn));\n } catch (e) {\n lc.warn?.(`error writing ACK to replication stream`, e);\n }\n }, ackIntervalMs);\n\n // Wait until stream closes or errors, or worker is stopped\n await new Promise<void>((resolve, reject) => {\n const onStop = () => {\n cleanup();\n resolve();\n };\n const onClose = () => {\n cleanup();\n resolve();\n };\n const onError = (err: unknown) => {\n cleanup();\n reject(err);\n };\n function cleanup() {\n clearInterval(ackInterval);\n readable.off('close', onClose);\n readable.off('error', onError);\n }\n stopResolve = onStop;\n readable.once('close', onClose);\n readable.once('error', onError);\n });\n\n await closeCurrent();\n\n if (stopped) {\n break;\n }\n } catch (e) {\n await closeCurrent();\n\n if (stopped) {\n break;\n }\n\n if (\n isPostgresError(e, PG_ADMIN_SHUTDOWN) ||\n (e instanceof Error &&\n (e.message.includes('57P01') ||\n e.message.includes(\n 'terminating connection due to administrator command',\n )))\n ) {\n lc.info?.(\n `slot ${config.slot} was taken over (admin shutdown / pg_terminate_backend)`,\n );\n break;\n }\n\n lc.warn?.(\n `slot-keeper stream error for ${config.slot}, will reconnect in ${delayMs}ms`,\n e,\n );\n\n if (firstAttempt) {\n throw e;\n }\n\n await Promise.race([\n sleepFn(delayMs),\n new Promise<void>(resolve => {\n stopResolve = resolve;\n }),\n ]);\n delayMs = Math.min(delayMs * 2, 10_000);\n }\n }\n\n lc.info?.(`slot-keeper finished for ${config.slot}`);\n}\n\n// fork()\nconst isMain =\n typeof process.argv[1] === 'string' &&\n fileURLToPath(import.meta.url) === resolve(process.argv[1]);\n\nconst parent = parentWorker;\nif (isMain && parent !== null && !singleProcessMode()) {\n void exitAfter(\n () => lc,\n () => runWorker(parent, process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"slot-keeper.js","names":[],"sources":["../../../../../zero-cache/src/server/slot-keeper.ts"],"sourcesContent":["import {resolve} from 'node:path';\nimport type {Readable} from 'node:stream';\nimport {fileURLToPath} from 'node:url';\nimport {PG_ADMIN_SHUTDOWN} from '@drdgvhbh/postgres-error-codes';\nimport {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {defu} from 'defu';\nimport postgres, {type Options, type PostgresType} from 'postgres';\nimport {sleep} from '../../../shared/src/sleep.ts';\nimport {getLogConfig} from '../config/zero-config.ts';\nimport {\n DEFAULT_RETRIES_IF_REPLICATION_SLOT_ACTIVE,\n makeAck,\n startReplicationStream,\n type PgConnectionConfig,\n} from '../services/change-source/pg/logical-replication/stream.ts';\nimport {toBigInt} from '../services/change-source/pg/lsn.ts';\nimport {exitAfter} from '../services/life-cycle.ts';\nimport {inactivityTimeoutSocket, isPostgresError} from '../types/pg.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {createLogContext} from './logging.ts';\n\nexport type SlotKeeperConfig = {\n db: PgConnectionConfig;\n slot: string;\n dummyPublication: string;\n lsn: string;\n ackIntervalMs?: number | undefined;\n};\n\nexport function parseLsn(lsn: string): bigint {\n return lsn.includes('/') ? toBigInt(lsn) : BigInt(lsn);\n}\n\nexport function waitForStartConfig(parent: Worker): Promise<SlotKeeperConfig> {\n return new Promise<SlotKeeperConfig>((resolve, reject) => {\n const cleanup = () => {\n parent.off('disconnect', onDisconnect);\n parent.off('close', onClose);\n if (process.send) {\n process.off('disconnect', onDisconnect);\n }\n };\n const onDisconnect = () => {\n cleanup();\n reject(new Error('Parent disconnected before sending start config'));\n };\n const onClose = () => {\n cleanup();\n reject(new Error('Parent closed before sending start config'));\n };\n parent.once('disconnect', onDisconnect);\n parent.once('close', onClose);\n if (process.send) {\n process.once('disconnect', onDisconnect);\n }\n parent.onceMessageType<['start', SlotKeeperConfig]>('start', cfg => {\n cleanup();\n resolve(cfg);\n });\n parent.send(['init', {}]);\n });\n}\n\nexport type SlotKeeperDependencies = {\n createSession?: (\n config: SlotKeeperConfig,\n lc: LogContext,\n ) => postgres.Sql | {end: (opts?: {timeout?: number}) => Promise<void>};\n startStream?: (\n lc: LogContext,\n session: postgres.Sql,\n slot: string,\n publications: string[],\n startLsn: bigint,\n maxAttempts?: number,\n ) => Promise<[Readable, NodeJS.WritableStream]>;\n sleepFn?: (ms: number) => Promise<void>;\n logContext?: LogContext;\n};\n\nlet lc = new LogContext('info', {}, consoleLogSink);\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n depsOrArg?: string | SlotKeeperDependencies,\n ...argv: string[]\n): Promise<void> {\n // Only logging config is needed; avoid requiring the full (normalized) config.\n const logConfig = getLogConfig({env, argv});\n const deps: SlotKeeperDependencies =\n typeof depsOrArg === 'object' && depsOrArg !== null ? depsOrArg : {};\n const createSession =\n deps.createSession ??\n ((cfg: SlotKeeperConfig, log: LogContext) =>\n postgres(\n defu(\n {\n max: 1,\n fetch_types: false,\n idle_timeout: null,\n max_lifetime: null as unknown as number,\n connection: {\n application_name: `slot-keeper-${cfg.slot}`,\n replication: 'database',\n },\n socket: inactivityTimeoutSocket(log, 0, 60_000),\n },\n cfg.db as Options<Record<string, PostgresType>>,\n ),\n ));\n const startStream = deps.startStream ?? startReplicationStream;\n const sleepFn = deps.sleepFn ?? sleep;\n\n const config = await waitForStartConfig(parent);\n\n lc = (\n deps.logContext ?? createLogContext(logConfig, 'slot-keeper')\n ).withContext('slot', config.slot);\n lc.info?.(`starting slot-keeper for ${config.slot}`);\n\n let stopped = false;\n let stopResolve: (() => void) | null = null;\n let currentReadable: Readable | null = null;\n let currentSession:\n | postgres.Sql\n | {end: (opts?: {timeout?: number}) => Promise<void>}\n | null = null;\n const lsn = parseLsn(config.lsn);\n const ackIntervalMs = config.ackIntervalMs ?? 5_000;\n\n const closeCurrent = async () => {\n if (currentReadable) {\n try {\n currentReadable.destroy();\n } catch {}\n currentReadable = null;\n }\n if (currentSession) {\n try {\n await currentSession.end({timeout: 0});\n lc.info?.(`ended slot-keeper replication session for ${config.slot}`);\n } catch (e) {\n lc.warn?.(`error ending slot-keeper session`, e);\n }\n currentSession = null;\n }\n };\n\n const stop = async () => {\n if (stopped) {\n return;\n }\n stopped = true;\n lc.info?.(`stopping slot-keeper for ${config.slot}`);\n stopResolve?.();\n await closeCurrent();\n };\n\n parent.onceMessageType('stop', () => void stop());\n parent.on('SIGTERM', () => void stop());\n parent.on('SIGINT', () => void stop());\n parent.once('disconnect', () => void stop());\n parent.once('close', () => void stop());\n process.once('SIGTERM', () => void stop());\n process.once('SIGINT', () => void stop());\n if (process.send) {\n process.once('disconnect', () => void stop());\n }\n\n let firstAttempt = true;\n let delayMs = 1000;\n\n while (!stopped) {\n try {\n lc.info?.(`connecting replication session for slot ${config.slot}`);\n const session = createSession(config, lc);\n currentSession = session;\n\n const [readable, writeable] = await startStream(\n lc,\n session as postgres.Sql,\n config.slot,\n [config.dummyPublication],\n lsn,\n DEFAULT_RETRIES_IF_REPLICATION_SLOT_ACTIVE + 1,\n );\n currentReadable = readable;\n // Discard the streamed messages. Otherwise the paused socket backs up the\n // wal_sender (e.g. with keepalives and logical messages) for the duration\n // of the initial sync / restore, and a wal_sender blocked on writing to the\n // client does not exit (and release the slot) when it is terminated by the\n // takeover; it instead blocks on sending the termination error to the\n // client, until signaled again.\n readable.resume();\n\n if (stopped) {\n await closeCurrent();\n break;\n }\n\n lc.info?.(`slot-keeper active for ${config.slot} at lsn ${config.lsn}`);\n if (firstAttempt) {\n firstAttempt = false;\n parent.send(['ready', {ready: true}]);\n }\n\n // Reset backoff delay on successful connection\n delayMs = 1000;\n\n const ackInterval = setInterval(() => {\n if (stopped) {\n clearInterval(ackInterval);\n return;\n }\n try {\n writeable.write(makeAck(lsn));\n } catch (e) {\n lc.warn?.(`error writing ACK to replication stream`, e);\n }\n }, ackIntervalMs);\n\n // Wait until stream closes or errors, or worker is stopped\n await new Promise<void>((resolve, reject) => {\n const onStop = () => {\n cleanup();\n resolve();\n };\n const onClose = () => {\n cleanup();\n resolve();\n };\n const onError = (err: unknown) => {\n cleanup();\n reject(err);\n };\n function cleanup() {\n clearInterval(ackInterval);\n readable.off('close', onClose);\n readable.off('error', onError);\n }\n stopResolve = onStop;\n readable.once('close', onClose);\n readable.once('error', onError);\n });\n\n await closeCurrent();\n\n if (stopped) {\n break;\n }\n } catch (e) {\n await closeCurrent();\n\n if (stopped) {\n break;\n }\n\n if (\n isPostgresError(e, PG_ADMIN_SHUTDOWN) ||\n (e instanceof Error &&\n (e.message.includes('57P01') ||\n e.message.includes(\n 'terminating connection due to administrator command',\n )))\n ) {\n lc.info?.(\n `slot ${config.slot} was taken over (admin shutdown / pg_terminate_backend)`,\n );\n break;\n }\n\n lc.warn?.(\n `slot-keeper stream error for ${config.slot}, will reconnect in ${delayMs}ms`,\n e,\n );\n\n if (firstAttempt) {\n throw e;\n }\n\n await Promise.race([\n sleepFn(delayMs),\n new Promise<void>(resolve => {\n stopResolve = resolve;\n }),\n ]);\n delayMs = Math.min(delayMs * 2, 10_000);\n }\n }\n\n lc.info?.(`slot-keeper finished for ${config.slot}`);\n}\n\n// fork()\nconst isMain =\n typeof process.argv[1] === 'string' &&\n fileURLToPath(import.meta.url) === resolve(process.argv[1]);\n\nconst parent = parentWorker;\nif (isMain && parent !== null && !singleProcessMode()) {\n void exitAfter(\n () => lc,\n () => runWorker(parent, process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncer-assigner.js","names":[
|
|
1
|
+
{"version":3,"file":"syncer-assigner.js","names":[],"sources":["../../../../../zero-cache/src/server/syncer-assigner.ts"],"sourcesContent":["import {h32} from '../../../shared/src/hash.ts';\n\nexport type Assignment = {\n readonly worker: number;\n readonly generation: number;\n};\n\nexport class SyncerAssigner {\n readonly #taskID: string;\n readonly #numWorkers: number;\n readonly #assignments = new Map<string, Assignment>();\n readonly #workerLoads: number[];\n #nextGeneration = 0;\n\n constructor(taskID: string, numWorkers: number) {\n this.#taskID = taskID;\n this.#numWorkers = numWorkers;\n this.#workerLoads = new Array<number>(numWorkers).fill(0);\n }\n\n assign(clientGroupID: string): Assignment {\n if (this.#numWorkers <= 0) {\n return {worker: 0, generation: 0};\n }\n\n const generation = ++this.#nextGeneration;\n const existing = this.#assignments.get(clientGroupID);\n if (existing !== undefined) {\n const updated: Assignment = {worker: existing.worker, generation};\n this.#assignments.set(clientGroupID, updated);\n return updated;\n }\n\n // Find worker(s) with minimum active load\n let minLoad = Infinity;\n const tied: number[] = [];\n for (let i = 0; i < this.#numWorkers; i++) {\n const load = this.#workerLoads[i];\n if (load < minLoad) {\n minLoad = load;\n tied.length = 0;\n tied.push(i);\n } else if (load === minLoad) {\n tied.push(i);\n }\n }\n\n const chosen =\n tied.length === 1\n ? tied[0]\n : tied[h32(this.#taskID + '/' + clientGroupID) % tied.length];\n\n this.#workerLoads[chosen]++;\n const assignment: Assignment = {worker: chosen, generation};\n this.#assignments.set(clientGroupID, assignment);\n return assignment;\n }\n\n release(\n clientGroupID: string,\n workerIndex: number,\n generation?: number,\n ): void {\n const existing = this.#assignments.get(clientGroupID);\n if (existing === undefined || existing.worker !== workerIndex) {\n return;\n }\n if (generation !== undefined && existing.generation !== generation) {\n return;\n }\n this.#decrementLoad(workerIndex);\n this.#assignments.delete(clientGroupID);\n }\n\n getWorkerLoad(workerIndex: number): number {\n return this.#workerLoads[workerIndex] ?? 0;\n }\n\n getAssignment(clientGroupID: string): number | undefined {\n return this.#assignments.get(clientGroupID)?.worker;\n }\n\n #decrementLoad(worker: number): void {\n if (this.#workerLoads[worker] > 0) {\n this.#workerLoads[worker]--;\n }\n }\n\n destroy(): void {\n this.#assignments.clear();\n this.#workerLoads.fill(0);\n }\n}\n"],"mappings":";;AAOA,IAAa,iBAAb,MAA4B;CAC1B;CACA;CACA,+BAAwB,IAAI,IAAwB;CACpD;CACA,kBAAkB;CAElB,YAAY,QAAgB,YAAoB;EAC9C,KAAK,UAAU;EACf,KAAK,cAAc;EACnB,KAAK,eAAe,IAAI,MAAc,UAAU,CAAC,CAAC,KAAK,CAAC;CAC1D;CAEA,OAAO,eAAmC;EACxC,IAAI,KAAK,eAAe,GACtB,OAAO;GAAC,QAAQ;GAAG,YAAY;EAAC;EAGlC,MAAM,aAAa,EAAE,KAAK;EAC1B,MAAM,WAAW,KAAK,aAAa,IAAI,aAAa;EACpD,IAAI,aAAa,KAAA,GAAW;GAC1B,MAAM,UAAsB;IAAC,QAAQ,SAAS;IAAQ;GAAU;GAChE,KAAK,aAAa,IAAI,eAAe,OAAO;GAC5C,OAAO;EACT;EAGA,IAAI,UAAU;EACd,MAAM,OAAiB,CAAC;EACxB,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,aAAa,KAAK;GACzC,MAAM,OAAO,KAAK,aAAa;GAC/B,IAAI,OAAO,SAAS;IAClB,UAAU;IACV,KAAK,SAAS;IACd,KAAK,KAAK,CAAC;GACb,OAAO,IAAI,SAAS,SAClB,KAAK,KAAK,CAAC;EAEf;EAEA,MAAM,SACJ,KAAK,WAAW,IACZ,KAAK,KACL,KAAK,IAAI,KAAK,UAAU,MAAM,aAAa,IAAI,KAAK;EAE1D,KAAK,aAAa,OAAO;EACzB,MAAM,aAAyB;GAAC,QAAQ;GAAQ;EAAU;EAC1D,KAAK,aAAa,IAAI,eAAe,UAAU;EAC/C,OAAO;CACT;CAEA,QACE,eACA,aACA,YACM;EACN,MAAM,WAAW,KAAK,aAAa,IAAI,aAAa;EACpD,IAAI,aAAa,KAAA,KAAa,SAAS,WAAW,aAChD;EAEF,IAAI,eAAe,KAAA,KAAa,SAAS,eAAe,YACtD;EAEF,KAAK,eAAe,WAAW;EAC/B,KAAK,aAAa,OAAO,aAAa;CACxC;CAEA,cAAc,aAA6B;EACzC,OAAO,KAAK,aAAa,gBAAgB;CAC3C;CAEA,cAAc,eAA2C;EACvD,OAAO,KAAK,aAAa,IAAI,aAAa,CAAC,EAAE;CAC/C;CAEA,eAAe,QAAsB;EACnC,IAAI,KAAK,aAAa,UAAU,GAC9B,KAAK,aAAa,OAAO;CAE7B;CAEA,UAAgB;EACd,KAAK,aAAa,MAAM;EACxB,KAAK,aAAa,KAAK,CAAC;CAC1B;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncer.js","names":[],"sources":["../../../../../zero-cache/src/server/syncer.ts"],"sourcesContent":["import {randomUUID} from 'node:crypto';\nimport {tmpdir} from 'node:os';\nimport path from 'node:path';\nimport {pid} from 'node:process';\nimport {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {randInt} from '../../../shared/src/rand.ts';\nimport {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {DatabaseStorage} from '../../../zqlite/src/database-storage.ts';\nimport type {ValidateLegacyJWT} from '../auth/auth.ts';\nimport {tokenConfigOptions, verifyToken} from '../auth/jwt.ts';\nimport type {NormalizedZeroConfig} from '../config/normalize.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {CustomQueryTransformer} from '../custom-queries/transform-query.ts';\nimport {registerSQLiteCorruptionDiagnosticTarget} from '../db/sqlite-corruption.ts';\nimport {warmupConnections} from '../db/warmup.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {getOrCreateGauge} from '../observability/metrics.ts';\nimport {exitAfter, runUntilKilled} from '../services/life-cycle.ts';\nimport {MutagenService} from '../services/mutagen/mutagen.ts';\nimport {PusherService} from '../services/mutagen/pusher.ts';\nimport type {ReplicaState} from '../services/replicator/replicator.ts';\nimport {BatchPacer} from '../services/view-syncer/batch-pacer.ts';\nimport {\n type ConnectionContextManager,\n ConnectionContextManagerImpl,\n} from '../services/view-syncer/connection-context-manager.ts';\nimport {DeferredWritesBudget} from '../services/view-syncer/deferred-writes-budget.ts';\nimport type {DrainCoordinator} from '../services/view-syncer/drain-coordinator.ts';\nimport {PipelineDriver} from '../services/view-syncer/pipeline-driver.ts';\nimport {QueryStats} from '../services/view-syncer/query-stats.ts';\nimport {SnapshotRowCache} from '../services/view-syncer/snapshot-row-cache.ts';\nimport {Snapshotter} from '../services/view-syncer/snapshotter.ts';\nimport {ViewSyncerService} from '../services/view-syncer/view-syncer.ts';\nimport {ProtocolErrorWithLevel} from '../types/error-with-level.ts';\nimport {connectPgClient} from '../types/pg.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {installProfileHandler} from '../types/profiler.ts';\nimport {getShardID} from '../types/shards.ts';\nimport type {Subscription} from '../types/subscription.ts';\nimport {replicaFileModeSchema, replicaFileName} from '../workers/replicator.ts';\nimport {Syncer} from '../workers/syncer.ts';\nimport {startAnonymousTelemetry} from './anonymous-otel-start.ts';\nimport {InspectorDelegate} from './inspector-delegate.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\nimport {isPriorityOpRunning, runPriorityOp} from './priority-op.ts';\n\nfunction randomID() {\n return randInt(1, Number.MAX_SAFE_INTEGER).toString(36);\n}\n\nfunction getCustomQueryConfig(\n config: Pick<NormalizedZeroConfig, 'query' | 'getQueries'>,\n) {\n const queryConfig = config.query?.url ? config.query : config.getQueries;\n\n if (!queryConfig?.url) {\n return undefined;\n }\n\n return {\n url: queryConfig.url,\n apiKey: queryConfig.apiKey,\n allowedClientHeaders: queryConfig.allowedClientHeaders,\n allowedRequestHeaders: queryConfig.allowedRequestHeaders,\n forwardCookies: queryConfig.forwardCookies ?? false,\n };\n}\n\n// Default LogContext, overridden in runWorker\nlet lc = new LogContext('info', {}, consoleLogSink);\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...args: string[]\n): Promise<void> {\n assert(args.length >= 2, `expected [fileMode, workerIndex, ...flags]`);\n const fileMode = v.parse(args[0], replicaFileModeSchema);\n const workerIndex = Number(args[1]);\n installProfileHandler(parent, `syncer-${workerIndex}`, workerIndex);\n const config = getNormalizedZeroConfig({env, argv: args.slice(2)});\n\n startOtelAuto(\n createLogContext(config, 'syncer', workerIndex, false),\n 'syncer',\n workerIndex,\n );\n lc = createLogContext(config, 'syncer', workerIndex);\n initEventSink(lc, config);\n\n const {cvr, upstream, enableCrudMutations} = config;\n\n const replicaFile = replicaFileName(config.replica.file, fileMode);\n registerSQLiteCorruptionDiagnosticTarget(\n {\n debugName: 'syncer replica',\n dbPath: replicaFile,\n },\n config.sqliteCorruptionChecks,\n );\n lc.debug?.(`running view-syncer on ${replicaFile}`);\n\n const cvrDB = await connectPgClient(lc, cvr.db, `sync-worker-${pid}-cvr`, {\n max: must(cvr.maxConnsPerWorker, 'cvr.maxConnsPerWorker must be set'),\n });\n\n const upstreamDB =\n enableCrudMutations && upstream.type === 'pg'\n ? await connectPgClient(lc, upstream.db, `sync-worker-${pid}-upstream`, {\n max: must(\n upstream.maxConnsPerWorker,\n 'upstream.maxConnsPerWorker must be set',\n ),\n })\n : undefined;\n\n const dbWarmup = Promise.allSettled([\n warmupConnections(lc, cvrDB, 'cvr'),\n upstreamDB ? warmupConnections(lc, upstreamDB, 'upstream') : promiseVoid,\n ]);\n\n const tmpDir = config.storageDBTmpDir ?? tmpdir();\n const operatorStorage = DatabaseStorage.create(\n lc,\n path.join(tmpDir, `sync-worker-${randomUUID()}`),\n );\n const writeAuthzStorage = DatabaseStorage.create(\n lc,\n path.join(tmpDir, `mutagen-${randomUUID()}`),\n );\n\n const shard = getShardID(config);\n const customQueryConfig = getCustomQueryConfig(config);\n const pushConfig =\n config.push.url === undefined && config.mutate.url === undefined\n ? undefined\n : {\n ...config.push,\n ...config.mutate,\n url: must(\n config.push.url ?? config.mutate.url,\n 'No push or mutate URL configured',\n ),\n };\n\n /** @deprecated used in JWT validation */\n let validateLegacyJWT: ValidateLegacyJWT | undefined = undefined;\n\n const tokenOptions = tokenConfigOptions(config.auth ?? {});\n if (tokenOptions.length === 1) {\n validateLegacyJWT = async (token, {userID}) => {\n if (!userID) {\n throw new ProtocolErrorWithLevel(\n {\n kind: 'Unauthorized',\n message: 'UserID is required for JWT validation.',\n origin: 'zeroCache',\n },\n 'warn',\n );\n }\n\n const decoded = await verifyToken(config.auth, token, {\n subject: userID,\n ...(config.auth?.issuer && {issuer: config.auth.issuer}),\n ...(config.auth?.audience && {\n audience: config.auth.audience,\n }),\n });\n return {\n type: 'jwt',\n raw: token,\n decoded,\n };\n };\n }\n\n // Shared by all of the view-syncers on this worker so that the row reads\n // performed when advancing their pipelines are done once per worker rather\n // than once per client group.\n const snapshotRowCache =\n config.snapshotRowCacheSize > 0\n ? new SnapshotRowCache(config.snapshotRowCacheSize)\n : undefined;\n\n // Shared by all of the view-syncers on this worker, which each hold their\n // own copy of the changes they are advancing through.\n const deferredWritesBudget = config.deferIvmWrites\n ? DeferredWritesBudget.forHeapProportion(\n config.deferIvmWritesHeapProportion,\n )\n : undefined;\n if (deferredWritesBudget) {\n lc.info?.(\n `Deferred IVM writes may hold up to ${deferredWritesBudget.maxRows} ` +\n `rows (~${(deferredWritesBudget.maxBytes / 1024 ** 2).toFixed(2)} MB) ` +\n `across client groups`,\n );\n getOrCreateGauge(\n 'sync',\n 'ivm.deferred-writes-reserved-rows',\n 'Rows reserved by the client groups of a sync worker to hold IVM ' +\n 'changes in memory (deferIvmWrites)',\n ).addCallback(o => o.observe(deferredWritesBudget.reservedRows));\n getOrCreateGauge('sync', 'ivm.deferred-writes-held-bytes', {\n description:\n 'Estimated bytes of the IVM changes that the client groups of a sync ' +\n 'worker hold in memory (deferIvmWrites)',\n unit: 'By',\n }).addCallback(o => o.observe(deferredWritesBudget.heldBytes));\n }\n\n // Shared by all of the view-syncers on this worker, whose batches of\n // changes together are held to a share of the worker's time.\n const batchPacer = config.costBasedSyncEnabled\n ? new BatchPacer({\n shareTarget: config.syncUpdateShareTarget,\n maxIntervalMs: config.syncMaxBatchIntervalMs,\n lc,\n })\n : undefined;\n if (batchPacer) {\n getOrCreateGauge('sync', 'worker-busy-share', {\n description:\n 'Share of its time that a sync worker was busy over the last few ' +\n 'seconds, which the pacing of its client groups holds to a target',\n unit: '1',\n }).addCallback(o => o.observe(batchPacer.share));\n getOrCreateGauge('sync', 'batch-wait-multiplier', {\n description:\n 'How many times the processing time of a batch of changes the ' +\n 'client groups of a sync worker wait before their next batch',\n unit: '1',\n }).addCallback(o => o.observe(batchPacer.multiplier));\n }\n\n // Shared by all of the view-syncers on this worker, so that the work done\n // for each query shape is logged once per worker per interval.\n const queryStats =\n config.log.queryStatsIntervalSeconds > 0 ? new QueryStats() : undefined;\n const stopQueryStats = queryStats?.start(\n lc\n .withContext('taskID', config.taskID)\n .withContext('component', 'view-syncer'),\n config.log.queryStatsIntervalSeconds * 1000,\n );\n\n const viewSyncerFactory = (\n id: string,\n sub: Subscription<ReplicaState>,\n drainCoordinator: DrainCoordinator,\n ) => {\n const logger = lc\n .withContext('taskID', config.taskID)\n .withContext('component', 'view-syncer')\n .withContext('appID', shard.appID)\n .withContext('shardNum', shard.shardNum)\n .withContext('clientGroupID', id)\n .withContext('instance', randomID());\n\n const customQueryTransformer =\n customQueryConfig && new CustomQueryTransformer(logger, shard);\n const connContextManager = new ConnectionContextManagerImpl(\n logger,\n config.auth.revalidateIntervalSeconds,\n config.auth.retransformIntervalSeconds,\n customQueryConfig,\n pushConfig,\n validateLegacyJWT,\n );\n\n lc.debug?.(\n `creating view syncer. Query Planner Enabled: ${config.enableQueryPlanner}`,\n );\n\n const inspectorDelegate = new InspectorDelegate(customQueryTransformer);\n\n const priorityOpRunningYieldThresholdMs = Math.max(\n config.yieldThresholdMs / 4,\n 2,\n );\n const normalYieldThresholdMs = Math.max(config.yieldThresholdMs, 2);\n\n return new ViewSyncerService(\n config,\n logger,\n shard,\n config.taskID,\n id,\n cvrDB,\n new PipelineDriver(\n logger,\n config.log,\n new Snapshotter(\n logger,\n replicaFile,\n shard,\n undefined,\n snapshotRowCache,\n ),\n shard,\n operatorStorage.createClientGroupStorage(id),\n id,\n inspectorDelegate,\n () =>\n isPriorityOpRunning()\n ? priorityOpRunningYieldThresholdMs\n : normalYieldThresholdMs,\n config.enableQueryPlanner,\n config,\n deferredWritesBudget,\n queryStats,\n ),\n sub,\n drainCoordinator,\n config.log.slowHydrateThreshold,\n inspectorDelegate,\n connContextManager,\n customQueryTransformer,\n runPriorityOp,\n undefined,\n undefined,\n undefined,\n undefined,\n batchPacer,\n );\n };\n\n const mutagenFactory = upstreamDB\n ? (id: string) =>\n new MutagenService(\n lc\n .withContext('component', 'mutagen')\n .withContext('clientGroupID', id),\n shard,\n id,\n upstreamDB,\n config,\n writeAuthzStorage,\n )\n : undefined;\n\n const pusherFactory =\n pushConfig === undefined\n ? undefined\n : (id: string, connContextManager: ConnectionContextManager) =>\n new PusherService(\n config,\n lc.withContext('clientGroupID', id),\n id,\n connContextManager,\n );\n\n const syncer = new Syncer(\n lc,\n config,\n viewSyncerFactory,\n mutagenFactory,\n pusherFactory,\n parent,\n validateLegacyJWT,\n );\n\n startAnonymousTelemetry(lc, config);\n\n void dbWarmup.then(() => parent.send(['ready', {ready: true}]));\n\n try {\n return await runUntilKilled(lc, parent, syncer);\n } finally {\n // Logs the work done since the last interval.\n stopQueryStats?.();\n }\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(\n () => lc,\n () => runWorker(must(parentWorker), process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"syncer.js","names":["v.parse"],"sources":["../../../../../zero-cache/src/server/syncer.ts"],"sourcesContent":["import {randomUUID} from 'node:crypto';\nimport {tmpdir} from 'node:os';\nimport path from 'node:path';\nimport {pid} from 'node:process';\nimport {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {randInt} from '../../../shared/src/rand.ts';\nimport {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {DatabaseStorage} from '../../../zqlite/src/database-storage.ts';\nimport type {ValidateLegacyJWT} from '../auth/auth.ts';\nimport {tokenConfigOptions, verifyToken} from '../auth/jwt.ts';\nimport type {NormalizedZeroConfig} from '../config/normalize.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {CustomQueryTransformer} from '../custom-queries/transform-query.ts';\nimport {registerSQLiteCorruptionDiagnosticTarget} from '../db/sqlite-corruption.ts';\nimport {warmupConnections} from '../db/warmup.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {getOrCreateGauge} from '../observability/metrics.ts';\nimport {exitAfter, runUntilKilled} from '../services/life-cycle.ts';\nimport {MutagenService} from '../services/mutagen/mutagen.ts';\nimport {PusherService} from '../services/mutagen/pusher.ts';\nimport type {ReplicaState} from '../services/replicator/replicator.ts';\nimport {BatchPacer} from '../services/view-syncer/batch-pacer.ts';\nimport {\n type ConnectionContextManager,\n ConnectionContextManagerImpl,\n} from '../services/view-syncer/connection-context-manager.ts';\nimport {DeferredWritesBudget} from '../services/view-syncer/deferred-writes-budget.ts';\nimport type {DrainCoordinator} from '../services/view-syncer/drain-coordinator.ts';\nimport {PipelineDriver} from '../services/view-syncer/pipeline-driver.ts';\nimport {QueryStats} from '../services/view-syncer/query-stats.ts';\nimport {SnapshotRowCache} from '../services/view-syncer/snapshot-row-cache.ts';\nimport {Snapshotter} from '../services/view-syncer/snapshotter.ts';\nimport {ViewSyncerService} from '../services/view-syncer/view-syncer.ts';\nimport {ProtocolErrorWithLevel} from '../types/error-with-level.ts';\nimport {connectPgClient} from '../types/pg.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {installProfileHandler} from '../types/profiler.ts';\nimport {getShardID} from '../types/shards.ts';\nimport type {Subscription} from '../types/subscription.ts';\nimport {replicaFileModeSchema, replicaFileName} from '../workers/replicator.ts';\nimport {Syncer} from '../workers/syncer.ts';\nimport {startAnonymousTelemetry} from './anonymous-otel-start.ts';\nimport {InspectorDelegate} from './inspector-delegate.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\nimport {isPriorityOpRunning, runPriorityOp} from './priority-op.ts';\n\nfunction randomID() {\n return randInt(1, Number.MAX_SAFE_INTEGER).toString(36);\n}\n\nfunction getCustomQueryConfig(\n config: Pick<NormalizedZeroConfig, 'query' | 'getQueries'>,\n) {\n const queryConfig = config.query?.url ? config.query : config.getQueries;\n\n if (!queryConfig?.url) {\n return undefined;\n }\n\n return {\n url: queryConfig.url,\n apiKey: queryConfig.apiKey,\n allowedClientHeaders: queryConfig.allowedClientHeaders,\n allowedRequestHeaders: queryConfig.allowedRequestHeaders,\n forwardCookies: queryConfig.forwardCookies ?? false,\n };\n}\n\n// Default LogContext, overridden in runWorker\nlet lc = new LogContext('info', {}, consoleLogSink);\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...args: string[]\n): Promise<void> {\n assert(args.length >= 2, `expected [fileMode, workerIndex, ...flags]`);\n const fileMode = v.parse(args[0], replicaFileModeSchema);\n const workerIndex = Number(args[1]);\n installProfileHandler(parent, `syncer-${workerIndex}`, workerIndex);\n const config = getNormalizedZeroConfig({env, argv: args.slice(2)});\n\n startOtelAuto(\n createLogContext(config, 'syncer', workerIndex, false),\n 'syncer',\n workerIndex,\n );\n lc = createLogContext(config, 'syncer', workerIndex);\n initEventSink(lc, config);\n\n const {cvr, upstream, enableCrudMutations} = config;\n\n const replicaFile = replicaFileName(config.replica.file, fileMode);\n registerSQLiteCorruptionDiagnosticTarget(\n {\n debugName: 'syncer replica',\n dbPath: replicaFile,\n },\n config.sqliteCorruptionChecks,\n );\n lc.debug?.(`running view-syncer on ${replicaFile}`);\n\n const cvrDB = await connectPgClient(lc, cvr.db, `sync-worker-${pid}-cvr`, {\n max: must(cvr.maxConnsPerWorker, 'cvr.maxConnsPerWorker must be set'),\n });\n\n const upstreamDB =\n enableCrudMutations && upstream.type === 'pg'\n ? await connectPgClient(lc, upstream.db, `sync-worker-${pid}-upstream`, {\n max: must(\n upstream.maxConnsPerWorker,\n 'upstream.maxConnsPerWorker must be set',\n ),\n })\n : undefined;\n\n const dbWarmup = Promise.allSettled([\n warmupConnections(lc, cvrDB, 'cvr'),\n upstreamDB ? warmupConnections(lc, upstreamDB, 'upstream') : promiseVoid,\n ]);\n\n const tmpDir = config.storageDBTmpDir ?? tmpdir();\n const operatorStorage = DatabaseStorage.create(\n lc,\n path.join(tmpDir, `sync-worker-${randomUUID()}`),\n );\n const writeAuthzStorage = DatabaseStorage.create(\n lc,\n path.join(tmpDir, `mutagen-${randomUUID()}`),\n );\n\n const shard = getShardID(config);\n const customQueryConfig = getCustomQueryConfig(config);\n const pushConfig =\n config.push.url === undefined && config.mutate.url === undefined\n ? undefined\n : {\n ...config.push,\n ...config.mutate,\n url: must(\n config.push.url ?? config.mutate.url,\n 'No push or mutate URL configured',\n ),\n };\n\n /** @deprecated used in JWT validation */\n let validateLegacyJWT: ValidateLegacyJWT | undefined = undefined;\n\n const tokenOptions = tokenConfigOptions(config.auth ?? {});\n if (tokenOptions.length === 1) {\n validateLegacyJWT = async (token, {userID}) => {\n if (!userID) {\n throw new ProtocolErrorWithLevel(\n {\n kind: 'Unauthorized',\n message: 'UserID is required for JWT validation.',\n origin: 'zeroCache',\n },\n 'warn',\n );\n }\n\n const decoded = await verifyToken(config.auth, token, {\n subject: userID,\n ...(config.auth?.issuer && {issuer: config.auth.issuer}),\n ...(config.auth?.audience && {\n audience: config.auth.audience,\n }),\n });\n return {\n type: 'jwt',\n raw: token,\n decoded,\n };\n };\n }\n\n // Shared by all of the view-syncers on this worker so that the row reads\n // performed when advancing their pipelines are done once per worker rather\n // than once per client group.\n const snapshotRowCache =\n config.snapshotRowCacheSize > 0\n ? new SnapshotRowCache(config.snapshotRowCacheSize)\n : undefined;\n\n // Shared by all of the view-syncers on this worker, which each hold their\n // own copy of the changes they are advancing through.\n const deferredWritesBudget = config.deferIvmWrites\n ? DeferredWritesBudget.forHeapProportion(\n config.deferIvmWritesHeapProportion,\n )\n : undefined;\n if (deferredWritesBudget) {\n lc.info?.(\n `Deferred IVM writes may hold up to ${deferredWritesBudget.maxRows} ` +\n `rows (~${(deferredWritesBudget.maxBytes / 1024 ** 2).toFixed(2)} MB) ` +\n `across client groups`,\n );\n getOrCreateGauge(\n 'sync',\n 'ivm.deferred-writes-reserved-rows',\n 'Rows reserved by the client groups of a sync worker to hold IVM ' +\n 'changes in memory (deferIvmWrites)',\n ).addCallback(o => o.observe(deferredWritesBudget.reservedRows));\n getOrCreateGauge('sync', 'ivm.deferred-writes-held-bytes', {\n description:\n 'Estimated bytes of the IVM changes that the client groups of a sync ' +\n 'worker hold in memory (deferIvmWrites)',\n unit: 'By',\n }).addCallback(o => o.observe(deferredWritesBudget.heldBytes));\n }\n\n // Shared by all of the view-syncers on this worker, whose batches of\n // changes together are held to a share of the worker's time.\n const batchPacer = config.costBasedSyncEnabled\n ? new BatchPacer({\n shareTarget: config.syncUpdateShareTarget,\n maxIntervalMs: config.syncMaxBatchIntervalMs,\n lc,\n })\n : undefined;\n if (batchPacer) {\n getOrCreateGauge('sync', 'worker-busy-share', {\n description:\n 'Share of its time that a sync worker was busy over the last few ' +\n 'seconds, which the pacing of its client groups holds to a target',\n unit: '1',\n }).addCallback(o => o.observe(batchPacer.share));\n getOrCreateGauge('sync', 'batch-wait-multiplier', {\n description:\n 'How many times the processing time of a batch of changes the ' +\n 'client groups of a sync worker wait before their next batch',\n unit: '1',\n }).addCallback(o => o.observe(batchPacer.multiplier));\n }\n\n // Shared by all of the view-syncers on this worker, so that the work done\n // for each query shape is logged once per worker per interval.\n const queryStats =\n config.log.queryStatsIntervalSeconds > 0 ? new QueryStats() : undefined;\n const stopQueryStats = queryStats?.start(\n lc\n .withContext('taskID', config.taskID)\n .withContext('component', 'view-syncer'),\n config.log.queryStatsIntervalSeconds * 1000,\n );\n\n const viewSyncerFactory = (\n id: string,\n sub: Subscription<ReplicaState>,\n drainCoordinator: DrainCoordinator,\n ) => {\n const logger = lc\n .withContext('taskID', config.taskID)\n .withContext('component', 'view-syncer')\n .withContext('appID', shard.appID)\n .withContext('shardNum', shard.shardNum)\n .withContext('clientGroupID', id)\n .withContext('instance', randomID());\n\n const customQueryTransformer =\n customQueryConfig && new CustomQueryTransformer(logger, shard);\n const connContextManager = new ConnectionContextManagerImpl(\n logger,\n config.auth.revalidateIntervalSeconds,\n config.auth.retransformIntervalSeconds,\n customQueryConfig,\n pushConfig,\n validateLegacyJWT,\n );\n\n lc.debug?.(\n `creating view syncer. Query Planner Enabled: ${config.enableQueryPlanner}`,\n );\n\n const inspectorDelegate = new InspectorDelegate(customQueryTransformer);\n\n const priorityOpRunningYieldThresholdMs = Math.max(\n config.yieldThresholdMs / 4,\n 2,\n );\n const normalYieldThresholdMs = Math.max(config.yieldThresholdMs, 2);\n\n return new ViewSyncerService(\n config,\n logger,\n shard,\n config.taskID,\n id,\n cvrDB,\n new PipelineDriver(\n logger,\n config.log,\n new Snapshotter(\n logger,\n replicaFile,\n shard,\n undefined,\n snapshotRowCache,\n ),\n shard,\n operatorStorage.createClientGroupStorage(id),\n id,\n inspectorDelegate,\n () =>\n isPriorityOpRunning()\n ? priorityOpRunningYieldThresholdMs\n : normalYieldThresholdMs,\n config.enableQueryPlanner,\n config,\n deferredWritesBudget,\n queryStats,\n ),\n sub,\n drainCoordinator,\n config.log.slowHydrateThreshold,\n inspectorDelegate,\n connContextManager,\n customQueryTransformer,\n runPriorityOp,\n undefined,\n undefined,\n undefined,\n undefined,\n batchPacer,\n );\n };\n\n const mutagenFactory = upstreamDB\n ? (id: string) =>\n new MutagenService(\n lc\n .withContext('component', 'mutagen')\n .withContext('clientGroupID', id),\n shard,\n id,\n upstreamDB,\n config,\n writeAuthzStorage,\n )\n : undefined;\n\n const pusherFactory =\n pushConfig === undefined\n ? undefined\n : (id: string, connContextManager: ConnectionContextManager) =>\n new PusherService(\n config,\n lc.withContext('clientGroupID', id),\n id,\n connContextManager,\n );\n\n const syncer = new Syncer(\n lc,\n config,\n viewSyncerFactory,\n mutagenFactory,\n pusherFactory,\n parent,\n validateLegacyJWT,\n );\n\n startAnonymousTelemetry(lc, config);\n\n void dbWarmup.then(() => parent.send(['ready', {ready: true}]));\n\n try {\n return await runUntilKilled(lc, parent, syncer);\n } finally {\n // Logs the work done since the last interval.\n stopQueryStats?.();\n }\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(\n () => lc,\n () => runWorker(must(parentWorker), process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":"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"}
|