@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":"events.js","names":[],"sources":["../../../../../zero-cache/src/observability/events.ts"],"sourcesContent":["import {gzip} from 'node:zlib';\nimport type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {CloudEvent, emitterFor, httpTransport} from 'cloudevents';\nimport {nanoid} from 'nanoid';\nimport {stringify} from '../../../shared/src/bigint-json.ts';\nimport {isJSONValue, type JSONObject} from '../../../shared/src/json.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport {sleep} from '../../../shared/src/sleep.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {type ZeroEvent} from '../../../zero-events/src/index.ts';\nimport type {NormalizedZeroConfig} from '../config/normalize.ts';\n\nconst MAX_PUBLISH_ATTEMPTS = 6;\nconst INITIAL_PUBLISH_BACKOFF_MS = 500;\n\ntype PublisherFn = (lc: LogContext, event: ZeroEvent) => Promise<void>;\n\nlet publishFn: PublisherFn = (lc, {type}) => {\n lc.warn?.(\n `Cannot publish \"${type}\" event before initEventSink(). ` +\n `This is only expected in unit tests.`,\n );\n return promiseVoid;\n};\n\nconst attributeValueSchema = v.union(v.string(), v.number(), v.boolean());\n\nconst eventSchema = v.record(attributeValueSchema);\n\ntype PartialEvent = v.Infer<typeof eventSchema>;\n\n// Note: This conforms to the format of the knative K_CE_OVERRIDES binding:\n// https://github.com/knative/eventing/blob/main/docs/spec/sources.md#sinkbinding\nconst extensionsObjectSchema = v.object({extensions: eventSchema});\n\nasync function base64gzip(str: string): Promise<string> {\n const {promise: gzipped, resolve, reject} = resolver<Buffer>();\n gzip(Buffer.from(str), (err, buf) => (err ? reject(err) : resolve(buf)));\n return (await gzipped).toString('base64');\n}\n\n/**\n * Initializes a per-process event sink according to the cloud event\n * parameters in the ZeroConfig. This must be called at the beginning\n * of the process, before any ZeroEvents are generated / published.\n */\nexport function initEventSink(\n lc: LogContext,\n {taskID, cloudEvent}: Pick<NormalizedZeroConfig, 'taskID' | 'cloudEvent'>,\n) {\n if (!cloudEvent.sinkEnv) {\n // The default implementation just outputs the events to logs.\n publishFn = (lc, event) => {\n lc.info?.(`ZeroEvent: ${event.type}`, event);\n return promiseVoid;\n };\n return;\n }\n\n let overrides: PartialEvent = {};\n\n if (cloudEvent.extensionOverridesEnv) {\n const strVal = must(process.env[cloudEvent.extensionOverridesEnv]);\n const {extensions} = v.parse(JSON.parse(strVal), extensionsObjectSchema);\n overrides = extensions;\n }\n\n async function createCloudEvent(event: ZeroEvent) {\n const {type, time} = event;\n const json = stringify(event);\n const data = await base64gzip(json);\n\n return new CloudEvent({\n id: nanoid(),\n source: taskID,\n type,\n time,\n // Pass `data` as text/plain to prevent intermediaries from\n // base64-decoding it. It is the responsibility of the final processor\n // to recognize that datacontentencoding === \"gzip\" and unpack the\n // `data` accordingly before parsing it.\n datacontenttype: 'text/plain',\n datacontentencoding: 'gzip',\n data,\n ...overrides,\n });\n }\n\n const sinkURI = must(process.env[cloudEvent.sinkEnv]);\n const emit = emitterFor(httpTransport(sinkURI));\n lc.debug?.(`Publishing ZeroEvents to ${sinkURI}`);\n\n publishFn = async (lc, event) => {\n let cloudEvent: CloudEvent<string>;\n try {\n cloudEvent = await createCloudEvent(event);\n } catch (e) {\n lc.error?.(`Error creating CloudEvent ${event.type}`, e);\n return;\n }\n lc.debug?.(`Publishing CloudEvent: ${cloudEvent.type}`);\n\n for (let i = 0; i < MAX_PUBLISH_ATTEMPTS; i++) {\n if (i > 0) {\n // exponential backoff on retries\n await sleep(INITIAL_PUBLISH_BACKOFF_MS * 2 ** (i - 1));\n }\n try {\n await emit(cloudEvent);\n // Avoid logging the (possibly large and) unreadable data field.\n const {data: _, ...event} = cloudEvent;\n lc.info?.(`Published CloudEvent: ${cloudEvent.type}`, {event});\n return;\n } catch (e) {\n lc.warn?.(`Error publishing ${cloudEvent.type} (attempt ${i + 1})`, e);\n }\n }\n };\n}\n\nexport function initEventSinkForTesting(sink: ZeroEvent[], now = new Date()) {\n publishFn = (lc, event) => {\n lc.info?.(`Testing event sink received ${event.type} event`, event);\n // Replace the default Date.now() with the test instance for determinism.\n sink.push({...event, time: now.toISOString()});\n return promiseVoid;\n };\n}\n\nexport function publishEvent<E extends ZeroEvent>(lc: LogContext, event: E) {\n void publishFn(lc, event);\n}\n\nexport async function publishCriticalEvent<E extends ZeroEvent>(\n lc: LogContext,\n event: E,\n) {\n await publishFn(lc, event);\n}\n\nexport function makeErrorDetails(e: unknown): JSONObject {\n const err = e instanceof Error ? e : new Error(String(e));\n const errorDetails: JSONObject = {\n name: err.name,\n message: err.message,\n stack: err.stack,\n cause: err.cause ? makeErrorDetails(err.cause) : undefined,\n };\n // Include any enumerable properties (e.g. of Error subtypes).\n for (const [field, value] of Object.entries(err)) {\n if (isJSONValue(value, [])) {\n errorDetails[field] = value;\n }\n }\n return errorDetails;\n}\n"],"mappings":";;;;;;;;;;;AAcA,IAAM,uBAAuB;AAC7B,IAAM,6BAA6B;AAInC,IAAI,aAA0B,IAAI,EAAC,WAAU;CAC3C,GAAG,OACD,mBAAmB,KAAK,qEAE1B;CACA,OAAO;AACT;AAEA,IAAM,
|
|
1
|
+
{"version":3,"file":"events.js","names":["v","v.parse"],"sources":["../../../../../zero-cache/src/observability/events.ts"],"sourcesContent":["import {gzip} from 'node:zlib';\nimport type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {CloudEvent, emitterFor, httpTransport} from 'cloudevents';\nimport {nanoid} from 'nanoid';\nimport {stringify} from '../../../shared/src/bigint-json.ts';\nimport {isJSONValue, type JSONObject} from '../../../shared/src/json.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport {sleep} from '../../../shared/src/sleep.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {type ZeroEvent} from '../../../zero-events/src/index.ts';\nimport type {NormalizedZeroConfig} from '../config/normalize.ts';\n\nconst MAX_PUBLISH_ATTEMPTS = 6;\nconst INITIAL_PUBLISH_BACKOFF_MS = 500;\n\ntype PublisherFn = (lc: LogContext, event: ZeroEvent) => Promise<void>;\n\nlet publishFn: PublisherFn = (lc, {type}) => {\n lc.warn?.(\n `Cannot publish \"${type}\" event before initEventSink(). ` +\n `This is only expected in unit tests.`,\n );\n return promiseVoid;\n};\n\nconst attributeValueSchema = v.union(v.string(), v.number(), v.boolean());\n\nconst eventSchema = v.record(attributeValueSchema);\n\ntype PartialEvent = v.Infer<typeof eventSchema>;\n\n// Note: This conforms to the format of the knative K_CE_OVERRIDES binding:\n// https://github.com/knative/eventing/blob/main/docs/spec/sources.md#sinkbinding\nconst extensionsObjectSchema = v.object({extensions: eventSchema});\n\nasync function base64gzip(str: string): Promise<string> {\n const {promise: gzipped, resolve, reject} = resolver<Buffer>();\n gzip(Buffer.from(str), (err, buf) => (err ? reject(err) : resolve(buf)));\n return (await gzipped).toString('base64');\n}\n\n/**\n * Initializes a per-process event sink according to the cloud event\n * parameters in the ZeroConfig. This must be called at the beginning\n * of the process, before any ZeroEvents are generated / published.\n */\nexport function initEventSink(\n lc: LogContext,\n {taskID, cloudEvent}: Pick<NormalizedZeroConfig, 'taskID' | 'cloudEvent'>,\n) {\n if (!cloudEvent.sinkEnv) {\n // The default implementation just outputs the events to logs.\n publishFn = (lc, event) => {\n lc.info?.(`ZeroEvent: ${event.type}`, event);\n return promiseVoid;\n };\n return;\n }\n\n let overrides: PartialEvent = {};\n\n if (cloudEvent.extensionOverridesEnv) {\n const strVal = must(process.env[cloudEvent.extensionOverridesEnv]);\n const {extensions} = v.parse(JSON.parse(strVal), extensionsObjectSchema);\n overrides = extensions;\n }\n\n async function createCloudEvent(event: ZeroEvent) {\n const {type, time} = event;\n const json = stringify(event);\n const data = await base64gzip(json);\n\n return new CloudEvent({\n id: nanoid(),\n source: taskID,\n type,\n time,\n // Pass `data` as text/plain to prevent intermediaries from\n // base64-decoding it. It is the responsibility of the final processor\n // to recognize that datacontentencoding === \"gzip\" and unpack the\n // `data` accordingly before parsing it.\n datacontenttype: 'text/plain',\n datacontentencoding: 'gzip',\n data,\n ...overrides,\n });\n }\n\n const sinkURI = must(process.env[cloudEvent.sinkEnv]);\n const emit = emitterFor(httpTransport(sinkURI));\n lc.debug?.(`Publishing ZeroEvents to ${sinkURI}`);\n\n publishFn = async (lc, event) => {\n let cloudEvent: CloudEvent<string>;\n try {\n cloudEvent = await createCloudEvent(event);\n } catch (e) {\n lc.error?.(`Error creating CloudEvent ${event.type}`, e);\n return;\n }\n lc.debug?.(`Publishing CloudEvent: ${cloudEvent.type}`);\n\n for (let i = 0; i < MAX_PUBLISH_ATTEMPTS; i++) {\n if (i > 0) {\n // exponential backoff on retries\n await sleep(INITIAL_PUBLISH_BACKOFF_MS * 2 ** (i - 1));\n }\n try {\n await emit(cloudEvent);\n // Avoid logging the (possibly large and) unreadable data field.\n const {data: _, ...event} = cloudEvent;\n lc.info?.(`Published CloudEvent: ${cloudEvent.type}`, {event});\n return;\n } catch (e) {\n lc.warn?.(`Error publishing ${cloudEvent.type} (attempt ${i + 1})`, e);\n }\n }\n };\n}\n\nexport function initEventSinkForTesting(sink: ZeroEvent[], now = new Date()) {\n publishFn = (lc, event) => {\n lc.info?.(`Testing event sink received ${event.type} event`, event);\n // Replace the default Date.now() with the test instance for determinism.\n sink.push({...event, time: now.toISOString()});\n return promiseVoid;\n };\n}\n\nexport function publishEvent<E extends ZeroEvent>(lc: LogContext, event: E) {\n void publishFn(lc, event);\n}\n\nexport async function publishCriticalEvent<E extends ZeroEvent>(\n lc: LogContext,\n event: E,\n) {\n await publishFn(lc, event);\n}\n\nexport function makeErrorDetails(e: unknown): JSONObject {\n const err = e instanceof Error ? e : new Error(String(e));\n const errorDetails: JSONObject = {\n name: err.name,\n message: err.message,\n stack: err.stack,\n cause: err.cause ? makeErrorDetails(err.cause) : undefined,\n };\n // Include any enumerable properties (e.g. of Error subtypes).\n for (const [field, value] of Object.entries(err)) {\n if (isJSONValue(value, [])) {\n errorDetails[field] = value;\n }\n }\n return errorDetails;\n}\n"],"mappings":";;;;;;;;;;;AAcA,IAAM,uBAAuB;AAC7B,IAAM,6BAA6B;AAInC,IAAI,aAA0B,IAAI,EAAC,WAAU;CAC3C,GAAG,OACD,mBAAmB,KAAK,qEAE1B;CACA,OAAO;AACT;AAEA,IAAM,uBAAuBA,eAAE,MAAMA,eAAE,OAAO,GAAGA,eAAE,OAAO,GAAGA,eAAE,QAAQ,CAAC;AAExE,IAAM,cAAcA,eAAE,OAAO,oBAAoB;AAMjD,IAAM,yBAAyBA,eAAE,OAAO,EAAC,YAAY,YAAW,CAAC;AAEjE,eAAe,WAAW,KAA8B;CACtD,MAAM,EAAC,SAAS,SAAS,SAAS,WAAU,SAAiB;CAC7D,KAAK,OAAO,KAAK,GAAG,IAAI,KAAK,QAAS,MAAM,OAAO,GAAG,IAAI,QAAQ,GAAG,CAAE;CACvE,QAAQ,MAAM,QAAA,CAAS,SAAS,QAAQ;AAC1C;;;;;;AAOA,SAAgB,cACd,IACA,EAAC,QAAQ,cACT;CACA,IAAI,CAAC,WAAW,SAAS;EAEvB,aAAa,IAAI,UAAU;GACzB,GAAG,OAAO,cAAc,MAAM,QAAQ,KAAK;GAC3C,OAAO;EACT;EACA;CACF;CAEA,IAAI,YAA0B,CAAC;CAE/B,IAAI,WAAW,uBAAuB;EACpC,MAAM,SAAS,KAAK,QAAQ,IAAI,WAAW,sBAAsB;EACjE,MAAM,EAAC,eAAcC,MAAQ,KAAK,MAAM,MAAM,GAAG,sBAAsB;EACvE,YAAY;CACd;CAEA,eAAe,iBAAiB,OAAkB;EAChD,MAAM,EAAC,MAAM,SAAQ;EAErB,MAAM,OAAO,MAAM,WADN,UAAU,KACO,CAAI;EAElC,OAAO,IAAI,WAAW;GACpB,IAAI,OAAO;GACX,QAAQ;GACR;GACA;GAKA,iBAAiB;GACjB,qBAAqB;GACrB;GACA,GAAG;EACL,CAAC;CACH;CAEA,MAAM,UAAU,KAAK,QAAQ,IAAI,WAAW,QAAQ;CACpD,MAAM,OAAO,WAAW,cAAc,OAAO,CAAC;CAC9C,GAAG,QAAQ,4BAA4B,SAAS;CAEhD,YAAY,OAAO,IAAI,UAAU;EAC/B,IAAI;EACJ,IAAI;GACF,aAAa,MAAM,iBAAiB,KAAK;EAC3C,SAAS,GAAG;GACV,GAAG,QAAQ,6BAA6B,MAAM,QAAQ,CAAC;GACvD;EACF;EACA,GAAG,QAAQ,0BAA0B,WAAW,MAAM;EAEtD,KAAK,IAAI,IAAI,GAAG,IAAI,sBAAsB,KAAK;GAC7C,IAAI,IAAI,GAEN,MAAM,MAAM,6BAA6B,MAAM,IAAI,EAAE;GAEvD,IAAI;IACF,MAAM,KAAK,UAAU;IAErB,MAAM,EAAC,MAAM,GAAG,GAAG,UAAS;IAC5B,GAAG,OAAO,yBAAyB,WAAW,QAAQ,EAAC,MAAK,CAAC;IAC7D;GACF,SAAS,GAAG;IACV,GAAG,OAAO,oBAAoB,WAAW,KAAK,YAAY,IAAI,EAAE,IAAI,CAAC;GACvE;EACF;CACF;AACF;AAWA,SAAgB,aAAkC,IAAgB,OAAU;CAC1E,UAAe,IAAI,KAAK;AAC1B;AAEA,eAAsB,qBACpB,IACA,OACA;CACA,MAAM,UAAU,IAAI,KAAK;AAC3B;AAEA,SAAgB,iBAAiB,GAAwB;CACvD,MAAM,MAAM,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC;CACxD,MAAM,eAA2B;EAC/B,MAAM,IAAI;EACV,SAAS,IAAI;EACb,OAAO,IAAI;EACX,OAAO,IAAI,QAAQ,iBAAiB,IAAI,KAAK,IAAI,KAAA;CACnD;CAEA,KAAK,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,GAAG,GAC7C,IAAI,YAAY,OAAO,CAAC,CAAC,GACvB,aAAa,SAAS;CAG1B,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log-throttle.js","names":[
|
|
1
|
+
{"version":3,"file":"log-throttle.js","names":[],"sources":["../../../../../zero-cache/src/observability/log-throttle.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\n\nexport type LogThrottleOptions = {\n /**\n * An admitted key is suppressed for this long. A key that is still\n * occurring after the window is admitted again, so a log that is still\n * relevant keeps showing up (e.g. after the log has rotated) without\n * repeating for every occurrence.\n */\n readonly windowMs: number;\n\n /**\n * The most keys tracked at once. The least recently admitted key is\n * forgotten beyond this, which bounds memory when the key space is\n * unbounded (e.g. dynamically built query shapes).\n */\n readonly maxKeys?: number | undefined;\n\n /**\n * The most occurrences admitted per window across all keys, which bounds\n * log volume when many distinct keys occur at once.\n */\n readonly maxPerWindow?: number | undefined;\n\n readonly now?: (() => number) | undefined;\n};\n\ntype Entry = {\n admittedAt: number;\n suppressed: number;\n};\n\n/**\n * Rate limits a log statement per key, for logs that would otherwise be\n * written for every client group, such as the slow hydration of a popular\n * query.\n *\n * Deduplication alone (logging a key once and never again) is not enough:\n * the one line is lost once the log rotates, and the set of logged keys\n * grows without bound. Instead a key is admitted at most once per\n * {@link LogThrottleOptions.windowMs}, and the occurrences suppressed in\n * between are counted and reported with the next admitted one.\n */\nexport class LogThrottle {\n readonly #windowMs: number;\n readonly #maxKeys: number;\n readonly #maxPerWindow: number;\n readonly #now: () => number;\n\n // Insertion ordered by admission, so the first entry is the least recently\n // admitted key.\n readonly #entries = new Map<string, Entry>();\n #windowStart = -Infinity;\n #admittedInWindow = 0;\n\n constructor({\n windowMs,\n maxKeys = 1000,\n maxPerWindow = 100,\n now = performance.now.bind(performance),\n }: LogThrottleOptions) {\n assert(windowMs >= 0, 'windowMs must be nonnegative');\n assert(maxKeys > 0, 'maxKeys must be positive');\n assert(maxPerWindow > 0, 'maxPerWindow must be positive');\n this.#windowMs = windowMs;\n this.#maxKeys = maxKeys;\n this.#maxPerWindow = maxPerWindow;\n this.#now = now;\n }\n\n /**\n * Records an occurrence of `key`.\n *\n * @returns `undefined` if the occurrence should not be logged, or else the\n * number of occurrences of `key` suppressed since it was last logged.\n */\n admit(key: string): number | undefined {\n const now = this.#now();\n const entry = this.#entries.get(key);\n if (entry && now - entry.admittedAt < this.#windowMs) {\n entry.suppressed++;\n return undefined;\n }\n\n if (now - this.#windowStart >= this.#windowMs) {\n this.#windowStart = now;\n this.#admittedInWindow = 0;\n }\n if (this.#admittedInWindow >= this.#maxPerWindow) {\n // Over the global budget. The occurrence is counted against the key so\n // that it is reported once the key is admitted.\n if (entry) {\n entry.suppressed++;\n } else {\n this.#set(key, {admittedAt: -Infinity, suppressed: 1});\n }\n return undefined;\n }\n\n this.#admittedInWindow++;\n const suppressed = entry?.suppressed ?? 0;\n this.#set(key, {admittedAt: now, suppressed: 0});\n return suppressed;\n }\n\n #set(key: string, entry: Entry): void {\n // Delete first so that the key moves to the end of the insertion order.\n this.#entries.delete(key);\n this.#entries.set(key, entry);\n if (this.#entries.size > this.#maxKeys) {\n const oldest = this.#entries.keys().next();\n if (!oldest.done) {\n this.#entries.delete(oldest.value);\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;AA2CA,IAAa,cAAb,MAAyB;CACvB;CACA;CACA;CACA;CAIA,2BAAoB,IAAI,IAAmB;CAC3C,eAAe;CACf,oBAAoB;CAEpB,YAAY,EACV,UACA,UAAU,KACV,eAAe,KACf,MAAM,YAAY,IAAI,KAAK,WAAW,KACjB;EACrB,OAAO,YAAY,GAAG,8BAA8B;EACpD,OAAO,UAAU,GAAG,0BAA0B;EAC9C,OAAO,eAAe,GAAG,+BAA+B;EACxD,KAAK,YAAY;EACjB,KAAK,WAAW;EAChB,KAAK,gBAAgB;EACrB,KAAK,OAAO;CACd;;;;;;;CAQA,MAAM,KAAiC;EACrC,MAAM,MAAM,KAAK,KAAK;EACtB,MAAM,QAAQ,KAAK,SAAS,IAAI,GAAG;EACnC,IAAI,SAAS,MAAM,MAAM,aAAa,KAAK,WAAW;GACpD,MAAM;GACN;EACF;EAEA,IAAI,MAAM,KAAK,gBAAgB,KAAK,WAAW;GAC7C,KAAK,eAAe;GACpB,KAAK,oBAAoB;EAC3B;EACA,IAAI,KAAK,qBAAqB,KAAK,eAAe;GAGhD,IAAI,OACF,MAAM;QAEN,KAAK,KAAK,KAAK;IAAC,YAAY;IAAW,YAAY;GAAC,CAAC;GAEvD;EACF;EAEA,KAAK;EACL,MAAM,aAAa,OAAO,cAAc;EACxC,KAAK,KAAK,KAAK;GAAC,YAAY;GAAK,YAAY;EAAC,CAAC;EAC/C,OAAO;CACT;CAEA,KAAK,KAAa,OAAoB;EAEpC,KAAK,SAAS,OAAO,GAAG;EACxB,KAAK,SAAS,IAAI,KAAK,KAAK;EAC5B,IAAI,KAAK,SAAS,OAAO,KAAK,UAAU;GACtC,MAAM,SAAS,KAAK,SAAS,KAAK,CAAC,CAAC,KAAK;GACzC,IAAI,CAAC,OAAO,MACV,KAAK,SAAS,OAAO,OAAO,KAAK;EAErC;CACF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metrics.js","names":[],"sources":["../../../../../zero-cache/src/observability/metrics.ts"],"sourcesContent":["import type {\n Counter,\n Histogram,\n Meter,\n MetricOptions,\n ObservableGauge,\n UpDownCounter,\n} from '@opentelemetry/api';\nimport {metrics} from '@opentelemetry/api';\nimport {getOrInsertComputed} from '../../../shared/src/map.ts';\n\n// intentional lazy initialization so it is not started before the SDK is started.\n\nexport type Category =\n | 'replication' // postgres to replica\n | 'replica' // health of replica and litestream backup\n | 'sync' // replica to client\n | 'mutation'\n | 'server';\n\nlet meter: Meter | undefined;\n\ntype Options = MetricOptions & {description: string};\ntype HistogramOptions = Options & {bucketBoundaries: number[]};\n\nexport const NATIVE_HISTOGRAM_INSTRUMENT_NAMES = [\n 'zero.sync.view_syncer_lag',\n 'zero.sync.view_syncer_hydration',\n 'zero.sync.e2e_serving_lag',\n] as const;\n\nexport const LONG_DURATION_HISTOGRAM_BOUNDARIES_S = [\n 1, 2, 5, 10, 30, 60, 120, 300, 600, 1200, 2400, 3600, 7200,\n];\n\nfunction getMeter() {\n if (!meter) {\n meter = metrics.getMeter('zero');\n }\n return meter;\n}\n\nfunction cache<TRet extends object>(): (\n name: string,\n creator: (name: string) => TRet,\n) => TRet {\n const instruments = new Map<string, TRet>();\n return (name: string, creator: (name: string) => TRet) =>\n getOrInsertComputed(instruments, name, creator);\n}\n\nconst upDownCounters = cache<UpDownCounter>();\n\nexport function getOrCreateUpDownCounter(\n category: Category,\n name: string,\n description: string,\n): UpDownCounter;\nexport function getOrCreateUpDownCounter(\n category: Category,\n name: string,\n opts: Options,\n): UpDownCounter;\nexport function getOrCreateUpDownCounter(\n category: Category,\n name: string,\n opts: string | Options,\n): UpDownCounter {\n return upDownCounters(name, name =>\n getMeter().createUpDownCounter(\n `zero.${category}.${name}`,\n typeof opts === 'string' ? {description: opts} : opts,\n ),\n );\n}\n\n/**\n * A latency histogram whose {@link recordMs} method accepts raw millisecond\n * durations and converts them to seconds internally.\n *\n * Use {@link getOrCreateLatencyHistogram} to create one — the unit (`'s'`),\n * bucket boundaries, and ms→s conversion are all baked in\n */\nexport type LatencyHistogram = {\n /**\n * Record a duration. Pass the raw elapsed milliseconds — the conversion to\n * seconds (required by the `unit: 's'` OTel histogram) is handled internally.\n *\n * @param durationMs Elapsed time in **milliseconds** (do NOT pre-divide).\n * @param attributes Optional OTel attributes to attach to the observation.\n */\n recordMs(\n durationMs: number,\n attributes?: Parameters<Histogram['record']>[1],\n ): void;\n};\n\n/**\n * Bucket boundaries (in seconds) for zero's latency histograms.\n *\n * The operational range is 1 ms – 5,000 ms (including customers actively\n * tuning queries). ~2× logarithmic steps give proportionally consistent\n * `histogram_quantile` accuracy regardless of where values cluster within\n * that range. 10,000 ms and 30,000 ms are overflow catchers for truly broken\n * states.\n *\n * 1 ms, 2 ms, 5 ms, 10 ms, 20 ms, 50 ms, 100 ms, 200 ms, 500 ms,\n * 1 s, 2 s, 5 s, 10 s, 30 s\n */\nconst LATENCY_HISTOGRAM_BOUNDARIES_S = [\n 0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5, 10, 30,\n];\n\nconst histograms = cache<Histogram>();\n\n/**\n * Creates a histogram for dimensionless values without applying the\n * millisecond-to-second conversion used by the latency helpers below.\n */\nexport function getOrCreateValueHistogram(\n category: Category,\n name: string,\n opts: HistogramOptions,\n): Histogram {\n const {bucketBoundaries, ...metricOptions} = opts;\n return histograms(name, name =>\n getMeter().createHistogram(`zero.${category}.${name}`, {\n ...metricOptions,\n advice: {\n ...metricOptions.advice,\n explicitBucketBoundaries: bucketBoundaries,\n },\n }),\n );\n}\n\nexport function getOrCreateHistogram(\n category: Category,\n name: string,\n opts: HistogramOptions,\n): LatencyHistogram {\n const {bucketBoundaries, ...metricOptions} = opts;\n const h = histograms(name, name =>\n getMeter().createHistogram(`zero.${category}.${name}`, {\n ...metricOptions,\n advice: {\n ...metricOptions.advice,\n explicitBucketBoundaries: bucketBoundaries,\n },\n }),\n );\n return {\n recordMs: (durationMs, attributes) =>\n h.record(durationMs / 1000, attributes),\n };\n}\n\n// Add instruments created through this helper to\n// NATIVE_HISTOGRAM_INSTRUMENT_NAMES so the SDK exports them as exponential\n// histograms.\nexport function getOrCreateNativeHistogram(\n category: Category,\n name: string,\n opts: Options,\n): LatencyHistogram {\n const h = histograms(name, name =>\n getMeter().createHistogram(`zero.${category}.${name}`, opts),\n );\n return {\n recordMs: (durationMs, attributes) =>\n h.record(durationMs / 1000, attributes),\n };\n}\n\n/**\n * Creates (or retrieves) a latency histogram for the given metric.\n *\n * - `unit` is always `'s'` (seconds), matching the OTel convention.\n * - Bucket boundaries are pre-set for zero's typical operation range\n * (1 ms – 5 s); see {@link LATENCY_HISTOGRAM_BOUNDARIES_S}.\n * - The returned {@link LatencyHistogram} accepts **milliseconds** via\n * `recordMs()`, so callers never need to divide by 1000.\n *\n * @example\n * ```ts\n * readonly #hydrationTime = getOrCreateLatencyHistogram(\n * 'sync', 'hydration-time', 'Time to hydrate a query.',\n * );\n * // ...\n * this.#hydrationTime.recordMs(performance.now() - start);\n * ```\n */\nexport function getOrCreateLatencyHistogram(\n category: Category,\n name: string,\n description: string,\n): LatencyHistogram {\n return getOrCreateHistogram(category, name, {\n description,\n unit: 's',\n bucketBoundaries: LATENCY_HISTOGRAM_BOUNDARIES_S,\n });\n}\n\nconst counters = cache<Counter>();\n\nexport function getOrCreateCounter(\n category: Category,\n name: string,\n description: string,\n): Counter;\nexport function getOrCreateCounter(\n category: Category,\n name: string,\n opts: Options,\n): Counter;\nexport function getOrCreateCounter(\n category: Category,\n name: string,\n opts: string | Options,\n): Counter {\n return counters(name, name =>\n getMeter().createCounter(\n `zero.${category}.${name}`,\n typeof opts === 'string' ? {description: opts} : opts,\n ),\n );\n}\n\nconst gauges = cache<ObservableGauge>();\n\nexport function getOrCreateGauge(\n category: Category,\n name: string,\n description: string,\n): ObservableGauge;\nexport function getOrCreateGauge(\n category: Category,\n name: string,\n opts: Options,\n): ObservableGauge;\nexport function getOrCreateGauge(\n category: Category,\n name: string,\n opts: string | Options,\n): ObservableGauge {\n return gauges(name, name =>\n getMeter().createObservableGauge(\n `zero.${category}.${name}`,\n typeof opts === 'string' ? {description: opts} : opts,\n ),\n );\n}\n"],"mappings":";;;AAoBA,IAAI;AAKJ,IAAa,oCAAoC;CAC/C;CACA;CACA;AACF;AAEA,IAAa,uCAAuC;CAClD;CAAG;CAAG;CAAG;CAAI;CAAI;CAAI;CAAK;CAAK;CAAK;CAAM;CAAM;CAAM;AACxD;AAEA,SAAS,WAAW;CAClB,IAAI,CAAC,OACH,QAAQ,QAAQ,SAAS,MAAM;CAEjC,OAAO;AACT;AAEA,SAAS,QAGC;CACR,MAAM,8BAAc,IAAI,IAAkB;CAC1C,QAAQ,MAAc,YACpB,oBAAoB,aAAa,MAAM,OAAO;AAClD;AAEA,IAAM,iBAAiB,MAAqB;AAY5C,SAAgB,yBACd,UACA,MACA,MACe;CACf,OAAO,eAAe,OAAM,SAC1B,SAAS,
|
|
1
|
+
{"version":3,"file":"metrics.js","names":[],"sources":["../../../../../zero-cache/src/observability/metrics.ts"],"sourcesContent":["import type {\n Counter,\n Histogram,\n Meter,\n MetricOptions,\n ObservableGauge,\n UpDownCounter,\n} from '@opentelemetry/api';\nimport {metrics} from '@opentelemetry/api';\nimport {getOrInsertComputed} from '../../../shared/src/map.ts';\n\n// intentional lazy initialization so it is not started before the SDK is started.\n\nexport type Category =\n | 'replication' // postgres to replica\n | 'replica' // health of replica and litestream backup\n | 'sync' // replica to client\n | 'mutation'\n | 'server';\n\nlet meter: Meter | undefined;\n\ntype Options = MetricOptions & {description: string};\ntype HistogramOptions = Options & {bucketBoundaries: number[]};\n\nexport const NATIVE_HISTOGRAM_INSTRUMENT_NAMES = [\n 'zero.sync.view_syncer_lag',\n 'zero.sync.view_syncer_hydration',\n 'zero.sync.e2e_serving_lag',\n] as const;\n\nexport const LONG_DURATION_HISTOGRAM_BOUNDARIES_S = [\n 1, 2, 5, 10, 30, 60, 120, 300, 600, 1200, 2400, 3600, 7200,\n];\n\nfunction getMeter() {\n if (!meter) {\n meter = metrics.getMeter('zero');\n }\n return meter;\n}\n\nfunction cache<TRet extends object>(): (\n name: string,\n creator: (name: string) => TRet,\n) => TRet {\n const instruments = new Map<string, TRet>();\n return (name: string, creator: (name: string) => TRet) =>\n getOrInsertComputed(instruments, name, creator);\n}\n\nconst upDownCounters = cache<UpDownCounter>();\n\nexport function getOrCreateUpDownCounter(\n category: Category,\n name: string,\n description: string,\n): UpDownCounter;\nexport function getOrCreateUpDownCounter(\n category: Category,\n name: string,\n opts: Options,\n): UpDownCounter;\nexport function getOrCreateUpDownCounter(\n category: Category,\n name: string,\n opts: string | Options,\n): UpDownCounter {\n return upDownCounters(name, name =>\n getMeter().createUpDownCounter(\n `zero.${category}.${name}`,\n typeof opts === 'string' ? {description: opts} : opts,\n ),\n );\n}\n\n/**\n * A latency histogram whose {@link recordMs} method accepts raw millisecond\n * durations and converts them to seconds internally.\n *\n * Use {@link getOrCreateLatencyHistogram} to create one — the unit (`'s'`),\n * bucket boundaries, and ms→s conversion are all baked in\n */\nexport type LatencyHistogram = {\n /**\n * Record a duration. Pass the raw elapsed milliseconds — the conversion to\n * seconds (required by the `unit: 's'` OTel histogram) is handled internally.\n *\n * @param durationMs Elapsed time in **milliseconds** (do NOT pre-divide).\n * @param attributes Optional OTel attributes to attach to the observation.\n */\n recordMs(\n durationMs: number,\n attributes?: Parameters<Histogram['record']>[1],\n ): void;\n};\n\n/**\n * Bucket boundaries (in seconds) for zero's latency histograms.\n *\n * The operational range is 1 ms – 5,000 ms (including customers actively\n * tuning queries). ~2× logarithmic steps give proportionally consistent\n * `histogram_quantile` accuracy regardless of where values cluster within\n * that range. 10,000 ms and 30,000 ms are overflow catchers for truly broken\n * states.\n *\n * 1 ms, 2 ms, 5 ms, 10 ms, 20 ms, 50 ms, 100 ms, 200 ms, 500 ms,\n * 1 s, 2 s, 5 s, 10 s, 30 s\n */\nconst LATENCY_HISTOGRAM_BOUNDARIES_S = [\n 0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5, 10, 30,\n];\n\nconst histograms = cache<Histogram>();\n\n/**\n * Creates a histogram for dimensionless values without applying the\n * millisecond-to-second conversion used by the latency helpers below.\n */\nexport function getOrCreateValueHistogram(\n category: Category,\n name: string,\n opts: HistogramOptions,\n): Histogram {\n const {bucketBoundaries, ...metricOptions} = opts;\n return histograms(name, name =>\n getMeter().createHistogram(`zero.${category}.${name}`, {\n ...metricOptions,\n advice: {\n ...metricOptions.advice,\n explicitBucketBoundaries: bucketBoundaries,\n },\n }),\n );\n}\n\nexport function getOrCreateHistogram(\n category: Category,\n name: string,\n opts: HistogramOptions,\n): LatencyHistogram {\n const {bucketBoundaries, ...metricOptions} = opts;\n const h = histograms(name, name =>\n getMeter().createHistogram(`zero.${category}.${name}`, {\n ...metricOptions,\n advice: {\n ...metricOptions.advice,\n explicitBucketBoundaries: bucketBoundaries,\n },\n }),\n );\n return {\n recordMs: (durationMs, attributes) =>\n h.record(durationMs / 1000, attributes),\n };\n}\n\n// Add instruments created through this helper to\n// NATIVE_HISTOGRAM_INSTRUMENT_NAMES so the SDK exports them as exponential\n// histograms.\nexport function getOrCreateNativeHistogram(\n category: Category,\n name: string,\n opts: Options,\n): LatencyHistogram {\n const h = histograms(name, name =>\n getMeter().createHistogram(`zero.${category}.${name}`, opts),\n );\n return {\n recordMs: (durationMs, attributes) =>\n h.record(durationMs / 1000, attributes),\n };\n}\n\n/**\n * Creates (or retrieves) a latency histogram for the given metric.\n *\n * - `unit` is always `'s'` (seconds), matching the OTel convention.\n * - Bucket boundaries are pre-set for zero's typical operation range\n * (1 ms – 5 s); see {@link LATENCY_HISTOGRAM_BOUNDARIES_S}.\n * - The returned {@link LatencyHistogram} accepts **milliseconds** via\n * `recordMs()`, so callers never need to divide by 1000.\n *\n * @example\n * ```ts\n * readonly #hydrationTime = getOrCreateLatencyHistogram(\n * 'sync', 'hydration-time', 'Time to hydrate a query.',\n * );\n * // ...\n * this.#hydrationTime.recordMs(performance.now() - start);\n * ```\n */\nexport function getOrCreateLatencyHistogram(\n category: Category,\n name: string,\n description: string,\n): LatencyHistogram {\n return getOrCreateHistogram(category, name, {\n description,\n unit: 's',\n bucketBoundaries: LATENCY_HISTOGRAM_BOUNDARIES_S,\n });\n}\n\nconst counters = cache<Counter>();\n\nexport function getOrCreateCounter(\n category: Category,\n name: string,\n description: string,\n): Counter;\nexport function getOrCreateCounter(\n category: Category,\n name: string,\n opts: Options,\n): Counter;\nexport function getOrCreateCounter(\n category: Category,\n name: string,\n opts: string | Options,\n): Counter {\n return counters(name, name =>\n getMeter().createCounter(\n `zero.${category}.${name}`,\n typeof opts === 'string' ? {description: opts} : opts,\n ),\n );\n}\n\nconst gauges = cache<ObservableGauge>();\n\nexport function getOrCreateGauge(\n category: Category,\n name: string,\n description: string,\n): ObservableGauge;\nexport function getOrCreateGauge(\n category: Category,\n name: string,\n opts: Options,\n): ObservableGauge;\nexport function getOrCreateGauge(\n category: Category,\n name: string,\n opts: string | Options,\n): ObservableGauge {\n return gauges(name, name =>\n getMeter().createObservableGauge(\n `zero.${category}.${name}`,\n typeof opts === 'string' ? {description: opts} : opts,\n ),\n );\n}\n"],"mappings":";;;AAoBA,IAAI;AAKJ,IAAa,oCAAoC;CAC/C;CACA;CACA;AACF;AAEA,IAAa,uCAAuC;CAClD;CAAG;CAAG;CAAG;CAAI;CAAI;CAAI;CAAK;CAAK;CAAK;CAAM;CAAM;CAAM;AACxD;AAEA,SAAS,WAAW;CAClB,IAAI,CAAC,OACH,QAAQ,QAAQ,SAAS,MAAM;CAEjC,OAAO;AACT;AAEA,SAAS,QAGC;CACR,MAAM,8BAAc,IAAI,IAAkB;CAC1C,QAAQ,MAAc,YACpB,oBAAoB,aAAa,MAAM,OAAO;AAClD;AAEA,IAAM,iBAAiB,MAAqB;AAY5C,SAAgB,yBACd,UACA,MACA,MACe;CACf,OAAO,eAAe,OAAM,SAC1B,SAAS,CAAC,CAAC,oBACT,QAAQ,SAAS,GAAG,QACpB,OAAO,SAAS,WAAW,EAAC,aAAa,KAAI,IAAI,IACnD,CACF;AACF;;;;;;;;;;;;;AAmCA,IAAM,iCAAiC;CACrC;CAAO;CAAO;CAAO;CAAM;CAAM;CAAM;CAAK;CAAK;CAAK;CAAG;CAAG;CAAG;CAAI;AACrE;AAEA,IAAM,aAAa,MAAiB;;;;;AAMpC,SAAgB,0BACd,UACA,MACA,MACW;CACX,MAAM,EAAC,kBAAkB,GAAG,kBAAiB;CAC7C,OAAO,WAAW,OAAM,SACtB,SAAS,CAAC,CAAC,gBAAgB,QAAQ,SAAS,GAAG,QAAQ;EACrD,GAAG;EACH,QAAQ;GACN,GAAG,cAAc;GACjB,0BAA0B;EAC5B;CACF,CAAC,CACH;AACF;AAEA,SAAgB,qBACd,UACA,MACA,MACkB;CAClB,MAAM,EAAC,kBAAkB,GAAG,kBAAiB;CAC7C,MAAM,IAAI,WAAW,OAAM,SACzB,SAAS,CAAC,CAAC,gBAAgB,QAAQ,SAAS,GAAG,QAAQ;EACrD,GAAG;EACH,QAAQ;GACN,GAAG,cAAc;GACjB,0BAA0B;EAC5B;CACF,CAAC,CACH;CACA,OAAO,EACL,WAAW,YAAY,eACrB,EAAE,OAAO,aAAa,KAAM,UAAU,EAC1C;AACF;AAKA,SAAgB,2BACd,UACA,MACA,MACkB;CAClB,MAAM,IAAI,WAAW,OAAM,SACzB,SAAS,CAAC,CAAC,gBAAgB,QAAQ,SAAS,GAAG,QAAQ,IAAI,CAC7D;CACA,OAAO,EACL,WAAW,YAAY,eACrB,EAAE,OAAO,aAAa,KAAM,UAAU,EAC1C;AACF;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,4BACd,UACA,MACA,aACkB;CAClB,OAAO,qBAAqB,UAAU,MAAM;EAC1C;EACA,MAAM;EACN,kBAAkB;CACpB,CAAC;AACH;AAEA,IAAM,WAAW,MAAe;AAYhC,SAAgB,mBACd,UACA,MACA,MACS;CACT,OAAO,SAAS,OAAM,SACpB,SAAS,CAAC,CAAC,cACT,QAAQ,SAAS,GAAG,QACpB,OAAO,SAAS,WAAW,EAAC,aAAa,KAAI,IAAI,IACnD,CACF;AACF;AAEA,IAAM,SAAS,MAAuB;AAYtC,SAAgB,iBACd,UACA,MACA,MACiB;CACjB,OAAO,OAAO,OAAM,SAClB,SAAS,CAAC,CAAC,sBACT,QAAQ,SAAS,GAAG,QACpB,OAAO,SAAS,WAAW,EAAC,aAAa,KAAI,IAAI,IACnD,CACF;AACF"}
|
|
@@ -48,7 +48,8 @@ async function validatePermissions(db, permissions) {
|
|
|
48
48
|
colorConsole.warn(`Upstream is missing expected publications "${[...missing]}".\nYou may need to re-initialize your replica.\nDeploying ${app} permissions without validating against published tables/columns.`);
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
|
-
const
|
|
51
|
+
const tablesToColumns = new Map(tables.map((t) => [liteTableName(t), Object.keys(t.columns)]));
|
|
52
|
+
const validate = validator(tablesToColumns);
|
|
52
53
|
try {
|
|
53
54
|
for (const [table, perms] of Object.entries(permissions?.tables ?? {})) {
|
|
54
55
|
const validateRule = ([_, cond]) => {
|
|
@@ -100,7 +101,8 @@ async function deployPermissions(upstreamURI, permissions, force) {
|
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
103
|
async function writePermissionsFile(perms, file, format) {
|
|
103
|
-
|
|
104
|
+
const contents = format === "sql" ? `UPDATE ${ident(app)}.permissions SET permissions = ${literal(JSON.stringify(perms))};` : JSON.stringify(perms, null, format === "pretty" ? 2 : 0);
|
|
105
|
+
await writeFile(file, contents);
|
|
104
106
|
colorConsole.info(`Wrote ${format} permissions to ${config.output.file}`);
|
|
105
107
|
}
|
|
106
108
|
var ret = await loadSchemaAndPermissions(config.schema.path, true);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy-permissions.js","names":[],"sources":["../../../../../zero-cache/src/scripts/deploy-permissions.ts"],"sourcesContent":["import '../../../shared/src/dotenv.ts';\n\nimport {writeFile} from 'node:fs/promises';\nimport {ident as id, literal} from 'pg-format';\nimport {colorConsole, createLogContext} from '../../../shared/src/logging.ts';\nimport {parseOptions} from '../../../shared/src/options.ts';\nimport {difference} from '../../../shared/src/set-utils.ts';\nimport {mapCondition} from '../../../zero-protocol/src/ast.ts';\nimport {\n type AssetPermissions,\n type PermissionsConfig,\n type Rule,\n} from '../../../zero-schema/src/compiled-permissions.ts';\nimport {validator} from '../../../zero-schema/src/name-mapper.ts';\nimport {ZERO_ENV_VAR_PREFIX} from '../config/zero-config.ts';\nimport {runTx} from '../db/run-transaction.ts';\nimport {getPublicationInfo} from '../services/change-source/pg/schema/published.ts';\nimport {\n ensureGlobalTables,\n SHARD_CONFIG_TABLE,\n} from '../services/change-source/pg/schema/shard.ts';\nimport {liteTableName} from '../types/names.ts';\nimport {pgClient, type PostgresDB} from '../types/pg.ts';\nimport {appSchema, getShardID, upstreamSchema} from '../types/shards.ts';\nimport {\n deployPermissionsOptions,\n loadSchemaAndPermissions,\n} from './permissions.ts';\n\nconst config = parseOptions(deployPermissionsOptions, {\n argv: process.argv.slice(2),\n envNamePrefix: ZERO_ENV_VAR_PREFIX,\n});\n\nconst shard = getShardID(config);\nconst app = appSchema(shard);\n\nconst lc = createLogContext(config);\n\nasync function validatePermissions(\n db: PostgresDB,\n permissions: PermissionsConfig,\n) {\n const schema = upstreamSchema(shard);\n\n // Check if the shardConfig table has been initialized.\n const result = await db`\n SELECT relname FROM pg_class\n JOIN pg_namespace ON relnamespace = pg_namespace.oid\n WHERE nspname = ${schema} AND relname = ${SHARD_CONFIG_TABLE}`;\n if (result.length === 0) {\n colorConsole.warn(\n `zero-cache has not yet initialized the upstream database.\\n` +\n `Deploying ${app} permissions without validating against published tables/columns.`,\n );\n return;\n }\n\n // Get the publications for the shard\n const config = await db<{publications: string[]}[]>`\n SELECT publications FROM ${db(schema + '.' + SHARD_CONFIG_TABLE)}\n `;\n if (config.length === 0) {\n colorConsole.warn(\n `zero-cache has not yet initialized the upstream database.\\n` +\n `Deploying ${app} permissions without validating against published tables/columns.`,\n );\n return;\n }\n colorConsole.info(\n `Validating permissions against tables and columns published for \"${app}\".`,\n );\n\n const [{publications: shardPublications}] = config;\n const {tables, publications} = await getPublicationInfo(\n db,\n shardPublications,\n );\n const pubnames = publications.map(p => p.pubname);\n const missing = difference(new Set(shardPublications), new Set(pubnames));\n if (missing.size) {\n colorConsole.warn(\n `Upstream is missing expected publications \"${[...missing]}\".\\n` +\n `You may need to re-initialize your replica.\\n` +\n `Deploying ${app} permissions without validating against published tables/columns.`,\n );\n return;\n }\n const tablesToColumns = new Map(\n tables.map(t => [liteTableName(t), Object.keys(t.columns)]),\n );\n const validate = validator(tablesToColumns);\n try {\n for (const [table, perms] of Object.entries(permissions?.tables ?? {})) {\n const validateRule = ([_, cond]: Rule) => {\n mapCondition(cond, table, validate);\n };\n const validateAsset = (asset: AssetPermissions | undefined) => {\n asset?.select?.forEach(validateRule);\n asset?.delete?.forEach(validateRule);\n asset?.insert?.forEach(validateRule);\n asset?.update?.preMutation?.forEach(validateRule);\n asset?.update?.postMutation?.forEach(validateRule);\n };\n validateAsset(perms.row);\n if (perms.cell) {\n Object.values(perms.cell).forEach(validateAsset);\n }\n }\n } catch (e) {\n failWithMessage(String(e));\n }\n}\n\nfunction failWithMessage(msg: string) {\n colorConsole.error(msg);\n colorConsole.info('\\nUse --force to deploy at your own risk.\\n');\n process.exit(-1);\n}\n\nasync function deployPermissions(\n upstreamURI: string,\n permissions: PermissionsConfig,\n force: boolean,\n) {\n const db = pgClient(lc, upstreamURI, 'deploy-permissions');\n const {host, port} = db.options;\n colorConsole.debug(`Connecting to upstream@${host}:${port}`);\n try {\n await ensureGlobalTables(db, shard);\n\n const {hash, changed} = await runTx(db, async tx => {\n if (force) {\n colorConsole.warn(`--force specified. Skipping validation.`);\n } else {\n await validatePermissions(tx, permissions);\n }\n\n const {appID} = shard;\n colorConsole.info(\n `Deploying permissions for --app-id \"${appID}\" to upstream@${db.options.host}`,\n );\n const [{hash: beforeHash}] = await tx<{hash: string}[]>`\n SELECT hash from ${tx(app)}.permissions`;\n const [{hash}] = await tx<{hash: string}[]>`\n UPDATE ${tx(app)}.permissions SET ${db({permissions})} RETURNING hash`;\n\n return {hash: hash.substring(0, 7), changed: beforeHash !== hash};\n });\n if (changed) {\n colorConsole.info(`Deployed new permissions (hash=${hash})`);\n } else {\n colorConsole.info(`Permissions unchanged (hash=${hash})`);\n }\n } finally {\n await db.end();\n }\n}\n\nasync function writePermissionsFile(\n perms: PermissionsConfig,\n file: string,\n format: 'sql' | 'json' | 'pretty',\n) {\n const contents =\n format === 'sql'\n ? `UPDATE ${id(app)}.permissions SET permissions = ${literal(\n JSON.stringify(perms),\n )};`\n : JSON.stringify(perms, null, format === 'pretty' ? 2 : 0);\n await writeFile(file, contents);\n colorConsole.info(`Wrote ${format} permissions to ${config.output.file}`);\n}\n\nconst ret = await loadSchemaAndPermissions(config.schema.path, true);\nif (!ret || Object.keys(ret?.permissions ?? {}).length === 0) {\n colorConsole.warn(\n `No permissions found at ${config.schema.path}, so could not deploy ` +\n `permissions. Replicating data, but no tables will be syncable. ` +\n `Create a schema file with permissions to be able to sync data.`,\n );\n} else {\n colorConsole.warn(\n `Permissions are deprecated and will be removed in an upcoming release. See: https://zero.rocicorp.dev/docs/auth.`,\n );\n\n const {permissions} = ret;\n if (config.output.file) {\n await writePermissionsFile(\n permissions,\n config.output.file,\n config.output.format,\n );\n } else if (config.upstream.type !== 'pg') {\n colorConsole.warn(\n `Permissions deployment is not supported for ${config.upstream.type} upstreams`,\n );\n process.exit(-1);\n } else if (config.upstream.db) {\n await deployPermissions(config.upstream.db, permissions, config.force);\n } else {\n colorConsole.error(`No --output-file or --upstream-db specified`);\n // Shows the usage text.\n parseOptions(deployPermissionsOptions, {\n argv: ['--help'],\n envNamePrefix: ZERO_ENV_VAR_PREFIX,\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"deploy-permissions.js","names":["id"],"sources":["../../../../../zero-cache/src/scripts/deploy-permissions.ts"],"sourcesContent":["import '../../../shared/src/dotenv.ts';\n\nimport {writeFile} from 'node:fs/promises';\nimport {ident as id, literal} from 'pg-format';\nimport {colorConsole, createLogContext} from '../../../shared/src/logging.ts';\nimport {parseOptions} from '../../../shared/src/options.ts';\nimport {difference} from '../../../shared/src/set-utils.ts';\nimport {mapCondition} from '../../../zero-protocol/src/ast.ts';\nimport {\n type AssetPermissions,\n type PermissionsConfig,\n type Rule,\n} from '../../../zero-schema/src/compiled-permissions.ts';\nimport {validator} from '../../../zero-schema/src/name-mapper.ts';\nimport {ZERO_ENV_VAR_PREFIX} from '../config/zero-config.ts';\nimport {runTx} from '../db/run-transaction.ts';\nimport {getPublicationInfo} from '../services/change-source/pg/schema/published.ts';\nimport {\n ensureGlobalTables,\n SHARD_CONFIG_TABLE,\n} from '../services/change-source/pg/schema/shard.ts';\nimport {liteTableName} from '../types/names.ts';\nimport {pgClient, type PostgresDB} from '../types/pg.ts';\nimport {appSchema, getShardID, upstreamSchema} from '../types/shards.ts';\nimport {\n deployPermissionsOptions,\n loadSchemaAndPermissions,\n} from './permissions.ts';\n\nconst config = parseOptions(deployPermissionsOptions, {\n argv: process.argv.slice(2),\n envNamePrefix: ZERO_ENV_VAR_PREFIX,\n});\n\nconst shard = getShardID(config);\nconst app = appSchema(shard);\n\nconst lc = createLogContext(config);\n\nasync function validatePermissions(\n db: PostgresDB,\n permissions: PermissionsConfig,\n) {\n const schema = upstreamSchema(shard);\n\n // Check if the shardConfig table has been initialized.\n const result = await db`\n SELECT relname FROM pg_class\n JOIN pg_namespace ON relnamespace = pg_namespace.oid\n WHERE nspname = ${schema} AND relname = ${SHARD_CONFIG_TABLE}`;\n if (result.length === 0) {\n colorConsole.warn(\n `zero-cache has not yet initialized the upstream database.\\n` +\n `Deploying ${app} permissions without validating against published tables/columns.`,\n );\n return;\n }\n\n // Get the publications for the shard\n const config = await db<{publications: string[]}[]>`\n SELECT publications FROM ${db(schema + '.' + SHARD_CONFIG_TABLE)}\n `;\n if (config.length === 0) {\n colorConsole.warn(\n `zero-cache has not yet initialized the upstream database.\\n` +\n `Deploying ${app} permissions without validating against published tables/columns.`,\n );\n return;\n }\n colorConsole.info(\n `Validating permissions against tables and columns published for \"${app}\".`,\n );\n\n const [{publications: shardPublications}] = config;\n const {tables, publications} = await getPublicationInfo(\n db,\n shardPublications,\n );\n const pubnames = publications.map(p => p.pubname);\n const missing = difference(new Set(shardPublications), new Set(pubnames));\n if (missing.size) {\n colorConsole.warn(\n `Upstream is missing expected publications \"${[...missing]}\".\\n` +\n `You may need to re-initialize your replica.\\n` +\n `Deploying ${app} permissions without validating against published tables/columns.`,\n );\n return;\n }\n const tablesToColumns = new Map(\n tables.map(t => [liteTableName(t), Object.keys(t.columns)]),\n );\n const validate = validator(tablesToColumns);\n try {\n for (const [table, perms] of Object.entries(permissions?.tables ?? {})) {\n const validateRule = ([_, cond]: Rule) => {\n mapCondition(cond, table, validate);\n };\n const validateAsset = (asset: AssetPermissions | undefined) => {\n asset?.select?.forEach(validateRule);\n asset?.delete?.forEach(validateRule);\n asset?.insert?.forEach(validateRule);\n asset?.update?.preMutation?.forEach(validateRule);\n asset?.update?.postMutation?.forEach(validateRule);\n };\n validateAsset(perms.row);\n if (perms.cell) {\n Object.values(perms.cell).forEach(validateAsset);\n }\n }\n } catch (e) {\n failWithMessage(String(e));\n }\n}\n\nfunction failWithMessage(msg: string) {\n colorConsole.error(msg);\n colorConsole.info('\\nUse --force to deploy at your own risk.\\n');\n process.exit(-1);\n}\n\nasync function deployPermissions(\n upstreamURI: string,\n permissions: PermissionsConfig,\n force: boolean,\n) {\n const db = pgClient(lc, upstreamURI, 'deploy-permissions');\n const {host, port} = db.options;\n colorConsole.debug(`Connecting to upstream@${host}:${port}`);\n try {\n await ensureGlobalTables(db, shard);\n\n const {hash, changed} = await runTx(db, async tx => {\n if (force) {\n colorConsole.warn(`--force specified. Skipping validation.`);\n } else {\n await validatePermissions(tx, permissions);\n }\n\n const {appID} = shard;\n colorConsole.info(\n `Deploying permissions for --app-id \"${appID}\" to upstream@${db.options.host}`,\n );\n const [{hash: beforeHash}] = await tx<{hash: string}[]>`\n SELECT hash from ${tx(app)}.permissions`;\n const [{hash}] = await tx<{hash: string}[]>`\n UPDATE ${tx(app)}.permissions SET ${db({permissions})} RETURNING hash`;\n\n return {hash: hash.substring(0, 7), changed: beforeHash !== hash};\n });\n if (changed) {\n colorConsole.info(`Deployed new permissions (hash=${hash})`);\n } else {\n colorConsole.info(`Permissions unchanged (hash=${hash})`);\n }\n } finally {\n await db.end();\n }\n}\n\nasync function writePermissionsFile(\n perms: PermissionsConfig,\n file: string,\n format: 'sql' | 'json' | 'pretty',\n) {\n const contents =\n format === 'sql'\n ? `UPDATE ${id(app)}.permissions SET permissions = ${literal(\n JSON.stringify(perms),\n )};`\n : JSON.stringify(perms, null, format === 'pretty' ? 2 : 0);\n await writeFile(file, contents);\n colorConsole.info(`Wrote ${format} permissions to ${config.output.file}`);\n}\n\nconst ret = await loadSchemaAndPermissions(config.schema.path, true);\nif (!ret || Object.keys(ret?.permissions ?? {}).length === 0) {\n colorConsole.warn(\n `No permissions found at ${config.schema.path}, so could not deploy ` +\n `permissions. Replicating data, but no tables will be syncable. ` +\n `Create a schema file with permissions to be able to sync data.`,\n );\n} else {\n colorConsole.warn(\n `Permissions are deprecated and will be removed in an upcoming release. See: https://zero.rocicorp.dev/docs/auth.`,\n );\n\n const {permissions} = ret;\n if (config.output.file) {\n await writePermissionsFile(\n permissions,\n config.output.file,\n config.output.format,\n );\n } else if (config.upstream.type !== 'pg') {\n colorConsole.warn(\n `Permissions deployment is not supported for ${config.upstream.type} upstreams`,\n );\n process.exit(-1);\n } else if (config.upstream.db) {\n await deployPermissions(config.upstream.db, permissions, config.force);\n } else {\n colorConsole.error(`No --output-file or --upstream-db specified`);\n // Shows the usage text.\n parseOptions(deployPermissionsOptions, {\n argv: ['--help'],\n envNamePrefix: ZERO_ENV_VAR_PREFIX,\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.js","names":[],"sources":["../../../../../zero-cache/src/scripts/permissions.ts"],"sourcesContent":["import {existsSync} from 'node:fs';\nimport {basename, dirname, join, relative, resolve, sep} from 'node:path';\nimport {fileURLToPath} from 'node:url';\nimport {logOptions} from '../../../otel/src/log-options.ts';\nimport {colorConsole} from '../../../shared/src/logging.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {\n permissionsConfigSchema,\n type PermissionsConfig,\n} from '../../../zero-schema/src/compiled-permissions.ts';\nimport {isSchemaConfig} from '../../../zero-schema/src/schema-config.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport {appOptions, shardOptions, zeroOptions} from '../config/zero-config.ts';\n\nexport const deployPermissionsOptions = {\n schema: {\n path: {\n type: v.string().optional(() => 'schema.ts'),\n desc: ['Relative path to the file containing the schema definition.'],\n alias: 'p',\n },\n },\n\n upstream: {\n db: {\n type: v.string().optional(),\n desc: [\n `The upstream Postgres database to deploy permissions to.`,\n `This is ignored if an {bold output-file} is specified.`,\n ],\n },\n\n type: zeroOptions.upstream.type,\n },\n\n app: {id: appOptions.id},\n\n shard: shardOptions,\n\n log: logOptions,\n\n output: {\n file: {\n type: v.string().optional(),\n desc: [\n `Outputs the permissions to a file with the requested {bold output-format}.`,\n ],\n },\n\n format: {\n type: v.literalUnion('sql', 'json', 'pretty').optional(() => 'sql'),\n desc: [\n `The desired format of the output file.`,\n ``,\n `A {bold sql} file can be executed via \"psql -f <file.sql>\", or \"\\\\\\\\i <file.sql>\"`,\n `from within the psql console, or copied and pasted into a migration script.`,\n ``,\n `The {bold json} and {bold pretty} formats are available for non-pg backends`,\n `and general debugging.`,\n ],\n },\n },\n\n force: {\n type: v.boolean().optional(() => false),\n desc: [`Deploy to upstream without validation. Use at your own risk.`],\n alias: 'f',\n },\n};\n\nexport async function loadSchemaAndPermissions(\n schemaPath: string,\n allowMissing: true,\n): Promise<{schema: Schema; permissions: PermissionsConfig} | undefined>;\nexport async function loadSchemaAndPermissions(\n schemaPath: string,\n allowMissing?: false,\n): Promise<{schema: Schema; permissions: PermissionsConfig}>;\nexport async function loadSchemaAndPermissions(\n schemaPath: string,\n allowMissing: boolean | undefined,\n): Promise<{schema: Schema; permissions: PermissionsConfig} | undefined> {\n const typeModuleErrorMessage = () =>\n `\\n\\nYou may need to add \\` \"type\": \"module\" \\` to the package.json file for ${schemaPath}.\\n`;\n\n colorConsole.info(`Loading schema from ${schemaPath}`);\n\n const dir = dirname(fileURLToPath(import.meta.url));\n const absoluteSchemaPath = resolve(schemaPath);\n const relativeDir = relative(dir, dirname(absoluteSchemaPath));\n let relativePath =\n relativeDir.length && relativeDir !== '.'\n ? join(relativeDir, basename(absoluteSchemaPath))\n : `.${sep}${basename(absoluteSchemaPath)}`;\n\n // tsImport doesn't expect to receive slashes in the Windows format when running\n // on Windows. They need to be converted to *nix format.\n relativePath = relativePath.replace(/\\\\/g, '/');\n\n if (!existsSync(absoluteSchemaPath)) {\n if (allowMissing) {\n return undefined;\n }\n colorConsole.error(`Schema file ${schemaPath} does not exist.`);\n process.exit(1);\n }\n\n let module;\n try {\n module = await import(relativePath);\n } catch (e) {\n colorConsole.error(\n `Failed to load zero schema from ${absoluteSchemaPath}` +\n typeModuleErrorMessage(),\n );\n throw e;\n }\n\n if (!isSchemaConfig(module)) {\n colorConsole.error(\n `Schema file ${schemaPath} must export [schema].` +\n typeModuleErrorMessage(),\n );\n process.exit(1);\n }\n try {\n const schemaConfig = module;\n const perms =\n await (schemaConfig.permissions as unknown as Promise<unknown>);\n const {schema} = schemaConfig;\n\n if (perms) {\n colorConsole.warn?.(\n 'Permissions are deprecated and will be removed in an upcoming release. See: https://zero.rocicorp.dev/docs/auth.',\n );\n }\n\n return {\n schema,\n permissions: v.parse(perms ?? {}, permissionsConfigSchema),\n };\n } catch (e) {\n colorConsole.error(`Failed to parse Permissions object`);\n throw e;\n }\n}\n"],"mappings":";;;;;;;;;;AAcA,IAAa,2BAA2B;CACtC,QAAQ,EACN,MAAM;EACJ,
|
|
1
|
+
{"version":3,"file":"permissions.js","names":["v","v.literalUnion","v.parse"],"sources":["../../../../../zero-cache/src/scripts/permissions.ts"],"sourcesContent":["import {existsSync} from 'node:fs';\nimport {basename, dirname, join, relative, resolve, sep} from 'node:path';\nimport {fileURLToPath} from 'node:url';\nimport {logOptions} from '../../../otel/src/log-options.ts';\nimport {colorConsole} from '../../../shared/src/logging.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {\n permissionsConfigSchema,\n type PermissionsConfig,\n} from '../../../zero-schema/src/compiled-permissions.ts';\nimport {isSchemaConfig} from '../../../zero-schema/src/schema-config.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport {appOptions, shardOptions, zeroOptions} from '../config/zero-config.ts';\n\nexport const deployPermissionsOptions = {\n schema: {\n path: {\n type: v.string().optional(() => 'schema.ts'),\n desc: ['Relative path to the file containing the schema definition.'],\n alias: 'p',\n },\n },\n\n upstream: {\n db: {\n type: v.string().optional(),\n desc: [\n `The upstream Postgres database to deploy permissions to.`,\n `This is ignored if an {bold output-file} is specified.`,\n ],\n },\n\n type: zeroOptions.upstream.type,\n },\n\n app: {id: appOptions.id},\n\n shard: shardOptions,\n\n log: logOptions,\n\n output: {\n file: {\n type: v.string().optional(),\n desc: [\n `Outputs the permissions to a file with the requested {bold output-format}.`,\n ],\n },\n\n format: {\n type: v.literalUnion('sql', 'json', 'pretty').optional(() => 'sql'),\n desc: [\n `The desired format of the output file.`,\n ``,\n `A {bold sql} file can be executed via \"psql -f <file.sql>\", or \"\\\\\\\\i <file.sql>\"`,\n `from within the psql console, or copied and pasted into a migration script.`,\n ``,\n `The {bold json} and {bold pretty} formats are available for non-pg backends`,\n `and general debugging.`,\n ],\n },\n },\n\n force: {\n type: v.boolean().optional(() => false),\n desc: [`Deploy to upstream without validation. Use at your own risk.`],\n alias: 'f',\n },\n};\n\nexport async function loadSchemaAndPermissions(\n schemaPath: string,\n allowMissing: true,\n): Promise<{schema: Schema; permissions: PermissionsConfig} | undefined>;\nexport async function loadSchemaAndPermissions(\n schemaPath: string,\n allowMissing?: false,\n): Promise<{schema: Schema; permissions: PermissionsConfig}>;\nexport async function loadSchemaAndPermissions(\n schemaPath: string,\n allowMissing: boolean | undefined,\n): Promise<{schema: Schema; permissions: PermissionsConfig} | undefined> {\n const typeModuleErrorMessage = () =>\n `\\n\\nYou may need to add \\` \"type\": \"module\" \\` to the package.json file for ${schemaPath}.\\n`;\n\n colorConsole.info(`Loading schema from ${schemaPath}`);\n\n const dir = dirname(fileURLToPath(import.meta.url));\n const absoluteSchemaPath = resolve(schemaPath);\n const relativeDir = relative(dir, dirname(absoluteSchemaPath));\n let relativePath =\n relativeDir.length && relativeDir !== '.'\n ? join(relativeDir, basename(absoluteSchemaPath))\n : `.${sep}${basename(absoluteSchemaPath)}`;\n\n // tsImport doesn't expect to receive slashes in the Windows format when running\n // on Windows. They need to be converted to *nix format.\n relativePath = relativePath.replace(/\\\\/g, '/');\n\n if (!existsSync(absoluteSchemaPath)) {\n if (allowMissing) {\n return undefined;\n }\n colorConsole.error(`Schema file ${schemaPath} does not exist.`);\n process.exit(1);\n }\n\n let module;\n try {\n module = await import(relativePath);\n } catch (e) {\n colorConsole.error(\n `Failed to load zero schema from ${absoluteSchemaPath}` +\n typeModuleErrorMessage(),\n );\n throw e;\n }\n\n if (!isSchemaConfig(module)) {\n colorConsole.error(\n `Schema file ${schemaPath} must export [schema].` +\n typeModuleErrorMessage(),\n );\n process.exit(1);\n }\n try {\n const schemaConfig = module;\n const perms =\n await (schemaConfig.permissions as unknown as Promise<unknown>);\n const {schema} = schemaConfig;\n\n if (perms) {\n colorConsole.warn?.(\n 'Permissions are deprecated and will be removed in an upcoming release. See: https://zero.rocicorp.dev/docs/auth.',\n );\n }\n\n return {\n schema,\n permissions: v.parse(perms ?? {}, permissionsConfigSchema),\n };\n } catch (e) {\n colorConsole.error(`Failed to parse Permissions object`);\n throw e;\n }\n}\n"],"mappings":";;;;;;;;;;AAcA,IAAa,2BAA2B;CACtC,QAAQ,EACN,MAAM;EACJ,MAAMA,eAAE,OAAO,CAAC,CAAC,eAAe,WAAW;EAC3C,MAAM,CAAC,6DAA6D;EACpE,OAAO;CACT,EACF;CAEA,UAAU;EACR,IAAI;GACF,MAAMA,eAAE,OAAO,CAAC,CAAC,SAAS;GAC1B,MAAM,CACJ,4DACA,wDACF;EACF;EAEA,MAAM,YAAY,SAAS;CAC7B;CAEA,KAAK,EAAC,IAAI,WAAW,GAAE;CAEvB,OAAO;CAEP,KAAK;CAEL,QAAQ;EACN,MAAM;GACJ,MAAMA,eAAE,OAAO,CAAC,CAAC,SAAS;GAC1B,MAAM,CACJ,4EACF;EACF;EAEA,QAAQ;GACN,MAAMC,aAAe,OAAO,QAAQ,QAAQ,CAAC,CAAC,eAAe,KAAK;GAClE,MAAM;IACJ;IACA;IACA;IACA;IACA;IACA;IACA;GACF;EACF;CACF;CAEA,OAAO;EACL,MAAMD,eAAE,QAAQ,CAAC,CAAC,eAAe,KAAK;EACtC,MAAM,CAAC,8DAA8D;EACrE,OAAO;CACT;AACF;AAUA,eAAsB,yBACpB,YACA,cACuE;CACvE,MAAM,+BACJ,+EAA+E,WAAW;CAE5F,aAAa,KAAK,uBAAuB,YAAY;CAErD,MAAM,MAAM,QAAQ,cAAc,YAAY,GAAG,CAAC;CAClD,MAAM,qBAAqB,QAAQ,UAAU;CAC7C,MAAM,cAAc,SAAS,KAAK,QAAQ,kBAAkB,CAAC;CAC7D,IAAI,eACF,YAAY,UAAU,gBAAgB,MAClC,KAAK,aAAa,SAAS,kBAAkB,CAAC,IAC9C,IAAI,MAAM,SAAS,kBAAkB;CAI3C,eAAe,aAAa,QAAQ,OAAO,GAAG;CAE9C,IAAI,CAAC,WAAW,kBAAkB,GAAG;EACnC,IAAI,cACF;EAEF,aAAa,MAAM,eAAe,WAAW,iBAAiB;EAC9D,QAAQ,KAAK,CAAC;CAChB;CAEA,IAAI;CACJ,IAAI;EACF,SAAS,MAAM,OAAO;CACxB,SAAS,GAAG;EACV,aAAa,MACX,mCAAmC,uBACjC,uBAAuB,CAC3B;EACA,MAAM;CACR;CAEA,IAAI,CAAC,eAAe,MAAM,GAAG;EAC3B,aAAa,MACX,eAAe,WAAW,0BACxB,uBAAuB,CAC3B;EACA,QAAQ,KAAK,CAAC;CAChB;CACA,IAAI;EACF,MAAM,eAAe;EACrB,MAAM,QACJ,MAAO,aAAa;EACtB,MAAM,EAAC,WAAU;EAEjB,IAAI,OACF,aAAa,OACX,kHACF;EAGF,OAAO;GACL;GACA,aAAaE,MAAQ,SAAS,CAAC,GAAG,uBAAuB;EAC3D;CACF,SAAS,GAAG;EACV,aAAa,MAAM,oCAAoC;EACvD,MAAM;CACR;AACF"}
|
|
@@ -256,7 +256,8 @@ var AnonymousTelemetryManager = class AnonymousTelemetryManager {
|
|
|
256
256
|
try {
|
|
257
257
|
if (this.#isInContainer()) return "container";
|
|
258
258
|
const fsidPath = join(homedir(), ".rocicorp", "fsid");
|
|
259
|
-
|
|
259
|
+
const fsidDir = dirname(fsidPath);
|
|
260
|
+
mkdirSync(fsidDir, { recursive: true });
|
|
260
261
|
const newId = randomUUID();
|
|
261
262
|
try {
|
|
262
263
|
writeFileSync(fsidPath, newId, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anonymous-otel-start.js","names":["#instance","#processId","#lc","#starting","#config","#viewSyncerCount","#cachedAttributes","#run","#stopped","#getAttributes","#meterProvider","#meter","#setupMetrics","#activeUsersGetter","#totalCrudMutations","#totalCustomMutations","#totalCrudQueries","#totalCustomQueries","#totalRowsSynced","#totalConnectionsSuccess","#totalConnectionsAttempted","#activeClientGroupsGetter","#getPlatform","#getGitProjectId","#getOrSetFsID","#findUp","#isInContainer"],"sources":["../../../../../zero-cache/src/server/anonymous-otel-start.ts"],"sourcesContent":["import {execSync} from 'child_process';\nimport {randomUUID} from 'crypto';\nimport {existsSync, mkdirSync, readFileSync, writeFileSync} from 'fs';\nimport {homedir, platform} from 'os';\nimport {dirname, join} from 'path';\nimport type {ObservableResult} from '@opentelemetry/api';\nimport {type Meter} from '@opentelemetry/api';\nimport {OTLPMetricExporter} from '@opentelemetry/exporter-metrics-otlp-http';\nimport {resourceFromAttributes} from '@opentelemetry/resources';\nimport {\n MeterProvider,\n PeriodicExportingMetricReader,\n} from '@opentelemetry/sdk-metrics';\nimport type {LogContext} from '@rocicorp/logger';\nimport {h64} from '../../../shared/src/hash.ts';\nimport {\n getServerVersion,\n getZeroConfig,\n type ZeroConfig,\n} from '../config/zero-config.ts';\nimport {setupOtelDiagnosticLogger} from './otel-diag-logger.ts';\n\nexport type ActiveUsers = {\n active_users_last_day: number;\n users_1da: number;\n users_7da: number;\n users_30da: number;\n users_1da_legacy: number;\n users_7da_legacy: number;\n users_30da_legacy: number;\n};\n\nconst hostNameRe = /^[a-f0-9]{12}$/;\n\nclass AnonymousTelemetryManager {\n static #instance: AnonymousTelemetryManager;\n #starting = false;\n #stopped = false;\n #meter!: Meter;\n #meterProvider!: MeterProvider;\n #totalCrudMutations = 0;\n #totalCustomMutations = 0;\n #totalCrudQueries = 0;\n #totalCustomQueries = 0;\n #totalRowsSynced = 0;\n #totalConnectionsSuccess = 0;\n #totalConnectionsAttempted = 0;\n #activeClientGroupsGetter: (() => number) | undefined;\n #activeUsersGetter: (() => ActiveUsers) | undefined;\n #lc: LogContext | undefined;\n #config: ZeroConfig | undefined;\n #processId: string;\n #cachedAttributes: Record<string, string> | undefined;\n #viewSyncerCount = 1;\n\n private constructor() {\n this.#processId = randomUUID();\n }\n\n static getInstance(): AnonymousTelemetryManager {\n if (!AnonymousTelemetryManager.#instance) {\n AnonymousTelemetryManager.#instance = new AnonymousTelemetryManager();\n }\n return AnonymousTelemetryManager.#instance;\n }\n\n start(lc?: LogContext, config?: ZeroConfig) {\n this.#lc = lc;\n\n // Set up OpenTelemetry diagnostic logger if not already configured\n setupOtelDiagnosticLogger(lc);\n\n if (!config) {\n try {\n config = getZeroConfig();\n } catch (e) {\n this.#lc?.info?.('telemetry: disabled - unable to parse config', e);\n return;\n }\n }\n\n if (process.env.DO_NOT_TRACK) {\n this.#lc?.info?.(\n 'telemetry: disabled - DO_NOT_TRACK environment variable is set',\n );\n return;\n }\n\n if (!config.enableTelemetry) {\n this.#lc?.info?.('telemetry: disabled - enableTelemetry is false');\n return;\n }\n\n if (this.#starting) {\n return;\n }\n\n this.#starting = true;\n this.#config = config;\n this.#viewSyncerCount = config.numSyncWorkers ?? 1;\n this.#cachedAttributes = undefined;\n\n this.#lc?.info?.(`telemetry: starting in 1 minute`);\n\n // Delay telemetry startup by 1 minute to avoid potential boot loop issues\n setTimeout(() => this.#run(), 60000);\n }\n\n #run() {\n if (this.#stopped) {\n return;\n }\n\n const resource = resourceFromAttributes(this.#getAttributes());\n\n // Add a random jitter to the export interval to avoid all view-syncers exporting at the same time\n const exportIntervalMillis =\n 60000 * this.#viewSyncerCount + Math.floor(Math.random() * 10000);\n const readers = [\n new PeriodicExportingMetricReader({\n exportIntervalMillis,\n exporter: new OTLPMetricExporter({\n url: 'https://metrics.rocicorp.dev',\n timeoutMillis: 30000,\n }),\n }),\n ];\n\n // Uncomment this to debug metrics exports.\n\n // readers.push(\n // new PeriodicExportingMetricReader({\n // exportIntervalMillis,\n // exporter: new ConsoleMetricExporter(),\n // }),\n // );\n\n this.#meterProvider = new MeterProvider({resource, readers});\n this.#meter = this.#meterProvider.getMeter('zero-anonymous-telemetry');\n\n this.#setupMetrics();\n this.#lc?.info?.(\n `telemetry: started (exports every ${exportIntervalMillis / 1000} seconds for ${this.#viewSyncerCount} view-syncers)`,\n );\n }\n\n #setupMetrics() {\n // Observable gauges\n const uptimeGauge = this.#meter.createObservableGauge('zero.uptime', {\n description: 'System uptime in seconds',\n unit: 'seconds',\n });\n\n // Observable counters\n const uptimeCounter = this.#meter.createObservableCounter(\n 'zero.uptime_counter',\n {\n description: 'System uptime in seconds',\n unit: 'seconds',\n },\n );\n const crudMutationsCounter = this.#meter.createObservableCounter(\n 'zero.crud_mutations_processed',\n {\n description: 'Total number of CRUD mutations processed',\n },\n );\n const customMutationsCounter = this.#meter.createObservableCounter(\n 'zero.custom_mutations_processed',\n {\n description: 'Total number of custom mutations processed',\n },\n );\n const totalMutationsCounter = this.#meter.createObservableCounter(\n 'zero.mutations_processed',\n {\n description: 'Total number of mutations processed',\n },\n );\n const crudQueriesCounter = this.#meter.createObservableCounter(\n 'zero.crud_queries_processed',\n {\n description: 'Total number of CRUD queries processed',\n },\n );\n const customQueriesCounter = this.#meter.createObservableCounter(\n 'zero.custom_queries_processed',\n {\n description: 'Total number of custom queries processed',\n },\n );\n const totalQueriesCounter = this.#meter.createObservableCounter(\n 'zero.queries_processed',\n {\n description: 'Total number of queries processed',\n },\n );\n const rowsSyncedCounter = this.#meter.createObservableCounter(\n 'zero.rows_synced',\n {\n description: 'Total number of rows synced',\n },\n );\n\n // Observable counters for connections\n const connectionsSuccessCounter = this.#meter.createObservableCounter(\n 'zero.connections_success',\n {\n description: 'Total number of successful connections',\n },\n );\n\n const connectionsAttemptedCounter = this.#meter.createObservableCounter(\n 'zero.connections_attempted',\n {\n description: 'Total number of attempted connections',\n },\n );\n\n const activeClientGroupsGauge = this.#meter.createObservableGauge(\n 'zero.gauge_active_client_groups',\n {\n description: 'Number of currently active client groups',\n },\n );\n\n const attrs = this.#getAttributes();\n const active =\n (metric: keyof ActiveUsers) => (result: ObservableResult) => {\n const actives = this.#activeUsersGetter?.();\n if (actives) {\n const value = actives[metric];\n result.observe(value, attrs);\n }\n };\n this.#meter\n .createObservableGauge('zero.active_users_last_day', {\n description: 'Count of CVR instances active in the last 24h',\n })\n .addCallback(active('active_users_last_day'));\n this.#meter\n .createObservableGauge('zero.users_1da', {\n description: 'Count of 1-day active profiles',\n })\n .addCallback(active('users_1da'));\n this.#meter\n .createObservableGauge('zero.users_7da', {\n description: 'Count of 7-day active profiles',\n })\n .addCallback(active('users_7da'));\n this.#meter\n .createObservableGauge('zero.users_30da', {\n description: 'Count of 30-day active profiles',\n })\n .addCallback(active('users_30da'));\n this.#meter\n .createObservableGauge('zero.users_1da_legacy', {\n description: 'Count of 1-day active profiles with CVR fallback',\n })\n .addCallback(active('users_1da_legacy'));\n this.#meter\n .createObservableGauge('zero.users_7da_legacy', {\n description: 'Count of 7-day active profiles with CVR fallback',\n })\n .addCallback(active('users_7da_legacy'));\n this.#meter\n .createObservableGauge('zero.users_30da_legacy', {\n description: 'Count of 30-day active profiles with CVR fallback',\n })\n .addCallback(active('users_30da_legacy'));\n\n // Callbacks\n uptimeGauge.addCallback((result: ObservableResult) => {\n const uptimeSeconds = Math.floor(process.uptime());\n result.observe(uptimeSeconds, attrs);\n });\n uptimeCounter.addCallback((result: ObservableResult) => {\n const uptimeSeconds = Math.floor(process.uptime());\n result.observe(uptimeSeconds, attrs);\n });\n crudMutationsCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalCrudMutations, attrs);\n });\n customMutationsCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalCustomMutations, attrs);\n });\n totalMutationsCounter.addCallback((result: ObservableResult) => {\n const totalMutations =\n this.#totalCrudMutations + this.#totalCustomMutations;\n result.observe(totalMutations, attrs);\n });\n crudQueriesCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalCrudQueries, attrs);\n });\n customQueriesCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalCustomQueries, attrs);\n });\n totalQueriesCounter.addCallback((result: ObservableResult) => {\n const totalQueries = this.#totalCrudQueries + this.#totalCustomQueries;\n result.observe(totalQueries, attrs);\n });\n rowsSyncedCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalRowsSynced, attrs);\n });\n connectionsSuccessCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalConnectionsSuccess, attrs);\n });\n connectionsAttemptedCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalConnectionsAttempted, attrs);\n });\n activeClientGroupsGauge.addCallback((result: ObservableResult) => {\n const activeClientGroups = this.#activeClientGroupsGetter?.() ?? 0;\n result.observe(activeClientGroups, attrs);\n });\n }\n\n recordMutation(type: 'crud' | 'custom', count = 1) {\n if (type === 'crud') {\n this.#totalCrudMutations += count;\n } else {\n this.#totalCustomMutations += count;\n }\n }\n\n recordQuery(type: 'crud' | 'custom', count = 1) {\n if (type === 'crud') {\n this.#totalCrudQueries += count;\n } else {\n this.#totalCustomQueries += count;\n }\n }\n\n recordRowsSynced(count: number) {\n this.#totalRowsSynced += count;\n }\n\n recordConnectionSuccess() {\n this.#totalConnectionsSuccess++;\n }\n\n recordConnectionAttempted() {\n this.#totalConnectionsAttempted++;\n }\n\n setActiveClientGroupsGetter(getter: () => number) {\n this.#activeClientGroupsGetter = getter;\n }\n\n setActiveUsersGetter(getter: () => ActiveUsers) {\n this.#activeUsersGetter = getter;\n }\n\n shutdown() {\n this.#stopped = true;\n if (this.#meterProvider) {\n this.#lc?.info?.('telemetry: shutting down');\n void this.#meterProvider.shutdown();\n }\n }\n\n #getAttributes() {\n if (!this.#cachedAttributes) {\n this.#cachedAttributes = {\n 'zero.app.id': h64(this.#config?.upstream.db || 'unknown').toString(),\n 'zero.machine.os': platform(),\n 'zero.telemetry.type': 'anonymous',\n 'zero.infra.platform': this.#getPlatform(),\n 'zero.version': getServerVersion(this.#config),\n 'zero.task.id': this.#config?.taskID || 'unknown',\n 'zero.project.id': this.#getGitProjectId(),\n 'zero.process.id': this.#processId,\n 'zero.fs.id': this.#getOrSetFsID(),\n };\n this.#lc?.debug?.(\n `telemetry: cached attributes=${JSON.stringify(this.#cachedAttributes)}`,\n );\n }\n return this.#cachedAttributes;\n }\n\n #getPlatform(): string {\n if (process.env.ZERO_ON_CLOUD_ZERO) return 'cloudzero';\n if (process.env.FLY_APP_NAME || process.env.FLY_REGION) return 'fly.io';\n if (\n process.env.ECS_CONTAINER_METADATA_URI_V4 ||\n process.env.ECS_CONTAINER_METADATA_URI ||\n process.env.AWS_EXECUTION_ENV\n ) {\n return 'aws';\n }\n if (process.env.RAILWAY_ENV || process.env.RAILWAY_STATIC_URL) {\n return 'railway';\n }\n if (process.env.RENDER || process.env.RENDER_SERVICE_ID) return 'render';\n if (\n process.env.GCP_PROJECT ||\n process.env.GCLOUD_PROJECT ||\n process.env.GOOGLE_CLOUD_PROJECT\n ) {\n return 'gcp';\n }\n if (process.env.COOLIFY_URL || process.env.COOLIFY_CONTAINER_NAME) {\n return 'coolify';\n }\n if (process.env.CONTAINER_APP_REVISION) return 'azure';\n if (process.env.FLIGHTCONTROL || process.env.FC_URL) return 'flightcontrol';\n return 'unknown';\n }\n\n #findUp(startDir: string, target: string): string | null {\n let dir = startDir;\n while (dir !== dirname(dir)) {\n if (existsSync(join(dir, target))) return dir;\n dir = dirname(dir);\n }\n return null;\n }\n\n #getGitProjectId(): string {\n try {\n const cwd = process.cwd();\n const gitRoot = this.#findUp(cwd, '.git');\n if (!gitRoot) {\n return 'unknown';\n }\n\n const rootCommitHash = execSync('git rev-list --max-parents=0 HEAD -1', {\n cwd: gitRoot,\n encoding: 'utf8',\n timeout: 1000,\n stdio: ['ignore', 'pipe', 'ignore'], // Suppress stderr\n }).trim();\n\n return rootCommitHash.length === 40 ? rootCommitHash : 'unknown';\n } catch (error) {\n // execSync throws a child_process.SpawnSyncReturns-shaped error with an\n // output property (an array with stdio buffers) and sometimes a killed\n // property that references back to the error itself — making it\n // circular and causing stringify() in the Logger logic to throw.\n const details =\n error instanceof Error\n ? {message: error.message, name: error.name, stack: error.stack}\n : String(error);\n this.#lc?.debug?.('telemetry: unable to get Git root commit:', details);\n return 'unknown';\n }\n }\n\n #getOrSetFsID(): string {\n try {\n if (this.#isInContainer()) {\n return 'container';\n }\n const fsidPath = join(homedir(), '.rocicorp', 'fsid');\n const fsidDir = dirname(fsidPath);\n\n mkdirSync(fsidDir, {recursive: true});\n\n // Always try atomic file creation first - this eliminates any race conditions\n const newId = randomUUID();\n try {\n writeFileSync(fsidPath, newId, {encoding: 'utf8', flag: 'wx'});\n return newId;\n } catch (writeError) {\n if ((writeError as NodeJS.ErrnoException).code === 'EEXIST') {\n const existingId = readFileSync(fsidPath, 'utf8').trim();\n return existingId;\n }\n throw writeError;\n }\n } catch (error) {\n this.#lc?.debug?.(\n 'telemetry: unable to get or set filesystem ID:',\n error,\n );\n return 'unknown';\n }\n }\n\n #isInContainer(): boolean {\n try {\n if (process.env.ZERO_IN_CONTAINER) {\n return true;\n }\n\n if (existsSync('/.dockerenv')) {\n return true;\n }\n\n if (existsSync('/usr/local/bin/docker-entrypoint.sh')) {\n return true;\n }\n\n if (process.env.KUBERNETES_SERVICE_HOST) {\n return true;\n }\n\n if (\n process.env.DOCKER_CONTAINER_ID ||\n process.env.HOSTNAME?.match(hostNameRe)\n ) {\n return true;\n }\n\n if (existsSync('/proc/1/cgroup')) {\n const cgroup = readFileSync('/proc/1/cgroup', 'utf8');\n if (\n cgroup.includes('docker') ||\n cgroup.includes('kubepods') ||\n cgroup.includes('containerd')\n ) {\n return true;\n }\n }\n\n return false;\n } catch (error) {\n this.#lc?.debug?.(\n 'telemetry: unable to detect container environment:',\n error,\n );\n return false;\n }\n }\n}\n\nconst manager = () => AnonymousTelemetryManager.getInstance();\n\nexport const startAnonymousTelemetry = (lc?: LogContext, config?: ZeroConfig) =>\n manager().start(lc, config);\nexport const recordMutation = (type: 'crud' | 'custom', count = 1) =>\n manager().recordMutation(type, count);\nexport const recordQuery = (type: 'crud' | 'custom', count = 1) =>\n manager().recordQuery(type, count);\nexport const recordRowsSynced = (count: number) =>\n manager().recordRowsSynced(count);\nexport const recordConnectionSuccess = () =>\n manager().recordConnectionSuccess();\nexport const recordConnectionAttempted = () =>\n manager().recordConnectionAttempted();\nexport const setActiveClientGroupsGetter = (getter: () => number) =>\n manager().setActiveClientGroupsGetter(getter);\nexport const setActiveUsersGetter = (getter: () => ActiveUsers) =>\n manager().setActiveUsersGetter(getter);\nexport const shutdownAnonymousTelemetry = () => manager().shutdown();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"anonymous-otel-start.js","names":[],"sources":["../../../../../zero-cache/src/server/anonymous-otel-start.ts"],"sourcesContent":["import {execSync} from 'child_process';\nimport {randomUUID} from 'crypto';\nimport {existsSync, mkdirSync, readFileSync, writeFileSync} from 'fs';\nimport {homedir, platform} from 'os';\nimport {dirname, join} from 'path';\nimport type {ObservableResult} from '@opentelemetry/api';\nimport {type Meter} from '@opentelemetry/api';\nimport {OTLPMetricExporter} from '@opentelemetry/exporter-metrics-otlp-http';\nimport {resourceFromAttributes} from '@opentelemetry/resources';\nimport {\n MeterProvider,\n PeriodicExportingMetricReader,\n} from '@opentelemetry/sdk-metrics';\nimport type {LogContext} from '@rocicorp/logger';\nimport {h64} from '../../../shared/src/hash.ts';\nimport {\n getServerVersion,\n getZeroConfig,\n type ZeroConfig,\n} from '../config/zero-config.ts';\nimport {setupOtelDiagnosticLogger} from './otel-diag-logger.ts';\n\nexport type ActiveUsers = {\n active_users_last_day: number;\n users_1da: number;\n users_7da: number;\n users_30da: number;\n users_1da_legacy: number;\n users_7da_legacy: number;\n users_30da_legacy: number;\n};\n\nconst hostNameRe = /^[a-f0-9]{12}$/;\n\nclass AnonymousTelemetryManager {\n static #instance: AnonymousTelemetryManager;\n #starting = false;\n #stopped = false;\n #meter!: Meter;\n #meterProvider!: MeterProvider;\n #totalCrudMutations = 0;\n #totalCustomMutations = 0;\n #totalCrudQueries = 0;\n #totalCustomQueries = 0;\n #totalRowsSynced = 0;\n #totalConnectionsSuccess = 0;\n #totalConnectionsAttempted = 0;\n #activeClientGroupsGetter: (() => number) | undefined;\n #activeUsersGetter: (() => ActiveUsers) | undefined;\n #lc: LogContext | undefined;\n #config: ZeroConfig | undefined;\n #processId: string;\n #cachedAttributes: Record<string, string> | undefined;\n #viewSyncerCount = 1;\n\n private constructor() {\n this.#processId = randomUUID();\n }\n\n static getInstance(): AnonymousTelemetryManager {\n if (!AnonymousTelemetryManager.#instance) {\n AnonymousTelemetryManager.#instance = new AnonymousTelemetryManager();\n }\n return AnonymousTelemetryManager.#instance;\n }\n\n start(lc?: LogContext, config?: ZeroConfig) {\n this.#lc = lc;\n\n // Set up OpenTelemetry diagnostic logger if not already configured\n setupOtelDiagnosticLogger(lc);\n\n if (!config) {\n try {\n config = getZeroConfig();\n } catch (e) {\n this.#lc?.info?.('telemetry: disabled - unable to parse config', e);\n return;\n }\n }\n\n if (process.env.DO_NOT_TRACK) {\n this.#lc?.info?.(\n 'telemetry: disabled - DO_NOT_TRACK environment variable is set',\n );\n return;\n }\n\n if (!config.enableTelemetry) {\n this.#lc?.info?.('telemetry: disabled - enableTelemetry is false');\n return;\n }\n\n if (this.#starting) {\n return;\n }\n\n this.#starting = true;\n this.#config = config;\n this.#viewSyncerCount = config.numSyncWorkers ?? 1;\n this.#cachedAttributes = undefined;\n\n this.#lc?.info?.(`telemetry: starting in 1 minute`);\n\n // Delay telemetry startup by 1 minute to avoid potential boot loop issues\n setTimeout(() => this.#run(), 60000);\n }\n\n #run() {\n if (this.#stopped) {\n return;\n }\n\n const resource = resourceFromAttributes(this.#getAttributes());\n\n // Add a random jitter to the export interval to avoid all view-syncers exporting at the same time\n const exportIntervalMillis =\n 60000 * this.#viewSyncerCount + Math.floor(Math.random() * 10000);\n const readers = [\n new PeriodicExportingMetricReader({\n exportIntervalMillis,\n exporter: new OTLPMetricExporter({\n url: 'https://metrics.rocicorp.dev',\n timeoutMillis: 30000,\n }),\n }),\n ];\n\n // Uncomment this to debug metrics exports.\n\n // readers.push(\n // new PeriodicExportingMetricReader({\n // exportIntervalMillis,\n // exporter: new ConsoleMetricExporter(),\n // }),\n // );\n\n this.#meterProvider = new MeterProvider({resource, readers});\n this.#meter = this.#meterProvider.getMeter('zero-anonymous-telemetry');\n\n this.#setupMetrics();\n this.#lc?.info?.(\n `telemetry: started (exports every ${exportIntervalMillis / 1000} seconds for ${this.#viewSyncerCount} view-syncers)`,\n );\n }\n\n #setupMetrics() {\n // Observable gauges\n const uptimeGauge = this.#meter.createObservableGauge('zero.uptime', {\n description: 'System uptime in seconds',\n unit: 'seconds',\n });\n\n // Observable counters\n const uptimeCounter = this.#meter.createObservableCounter(\n 'zero.uptime_counter',\n {\n description: 'System uptime in seconds',\n unit: 'seconds',\n },\n );\n const crudMutationsCounter = this.#meter.createObservableCounter(\n 'zero.crud_mutations_processed',\n {\n description: 'Total number of CRUD mutations processed',\n },\n );\n const customMutationsCounter = this.#meter.createObservableCounter(\n 'zero.custom_mutations_processed',\n {\n description: 'Total number of custom mutations processed',\n },\n );\n const totalMutationsCounter = this.#meter.createObservableCounter(\n 'zero.mutations_processed',\n {\n description: 'Total number of mutations processed',\n },\n );\n const crudQueriesCounter = this.#meter.createObservableCounter(\n 'zero.crud_queries_processed',\n {\n description: 'Total number of CRUD queries processed',\n },\n );\n const customQueriesCounter = this.#meter.createObservableCounter(\n 'zero.custom_queries_processed',\n {\n description: 'Total number of custom queries processed',\n },\n );\n const totalQueriesCounter = this.#meter.createObservableCounter(\n 'zero.queries_processed',\n {\n description: 'Total number of queries processed',\n },\n );\n const rowsSyncedCounter = this.#meter.createObservableCounter(\n 'zero.rows_synced',\n {\n description: 'Total number of rows synced',\n },\n );\n\n // Observable counters for connections\n const connectionsSuccessCounter = this.#meter.createObservableCounter(\n 'zero.connections_success',\n {\n description: 'Total number of successful connections',\n },\n );\n\n const connectionsAttemptedCounter = this.#meter.createObservableCounter(\n 'zero.connections_attempted',\n {\n description: 'Total number of attempted connections',\n },\n );\n\n const activeClientGroupsGauge = this.#meter.createObservableGauge(\n 'zero.gauge_active_client_groups',\n {\n description: 'Number of currently active client groups',\n },\n );\n\n const attrs = this.#getAttributes();\n const active =\n (metric: keyof ActiveUsers) => (result: ObservableResult) => {\n const actives = this.#activeUsersGetter?.();\n if (actives) {\n const value = actives[metric];\n result.observe(value, attrs);\n }\n };\n this.#meter\n .createObservableGauge('zero.active_users_last_day', {\n description: 'Count of CVR instances active in the last 24h',\n })\n .addCallback(active('active_users_last_day'));\n this.#meter\n .createObservableGauge('zero.users_1da', {\n description: 'Count of 1-day active profiles',\n })\n .addCallback(active('users_1da'));\n this.#meter\n .createObservableGauge('zero.users_7da', {\n description: 'Count of 7-day active profiles',\n })\n .addCallback(active('users_7da'));\n this.#meter\n .createObservableGauge('zero.users_30da', {\n description: 'Count of 30-day active profiles',\n })\n .addCallback(active('users_30da'));\n this.#meter\n .createObservableGauge('zero.users_1da_legacy', {\n description: 'Count of 1-day active profiles with CVR fallback',\n })\n .addCallback(active('users_1da_legacy'));\n this.#meter\n .createObservableGauge('zero.users_7da_legacy', {\n description: 'Count of 7-day active profiles with CVR fallback',\n })\n .addCallback(active('users_7da_legacy'));\n this.#meter\n .createObservableGauge('zero.users_30da_legacy', {\n description: 'Count of 30-day active profiles with CVR fallback',\n })\n .addCallback(active('users_30da_legacy'));\n\n // Callbacks\n uptimeGauge.addCallback((result: ObservableResult) => {\n const uptimeSeconds = Math.floor(process.uptime());\n result.observe(uptimeSeconds, attrs);\n });\n uptimeCounter.addCallback((result: ObservableResult) => {\n const uptimeSeconds = Math.floor(process.uptime());\n result.observe(uptimeSeconds, attrs);\n });\n crudMutationsCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalCrudMutations, attrs);\n });\n customMutationsCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalCustomMutations, attrs);\n });\n totalMutationsCounter.addCallback((result: ObservableResult) => {\n const totalMutations =\n this.#totalCrudMutations + this.#totalCustomMutations;\n result.observe(totalMutations, attrs);\n });\n crudQueriesCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalCrudQueries, attrs);\n });\n customQueriesCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalCustomQueries, attrs);\n });\n totalQueriesCounter.addCallback((result: ObservableResult) => {\n const totalQueries = this.#totalCrudQueries + this.#totalCustomQueries;\n result.observe(totalQueries, attrs);\n });\n rowsSyncedCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalRowsSynced, attrs);\n });\n connectionsSuccessCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalConnectionsSuccess, attrs);\n });\n connectionsAttemptedCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalConnectionsAttempted, attrs);\n });\n activeClientGroupsGauge.addCallback((result: ObservableResult) => {\n const activeClientGroups = this.#activeClientGroupsGetter?.() ?? 0;\n result.observe(activeClientGroups, attrs);\n });\n }\n\n recordMutation(type: 'crud' | 'custom', count = 1) {\n if (type === 'crud') {\n this.#totalCrudMutations += count;\n } else {\n this.#totalCustomMutations += count;\n }\n }\n\n recordQuery(type: 'crud' | 'custom', count = 1) {\n if (type === 'crud') {\n this.#totalCrudQueries += count;\n } else {\n this.#totalCustomQueries += count;\n }\n }\n\n recordRowsSynced(count: number) {\n this.#totalRowsSynced += count;\n }\n\n recordConnectionSuccess() {\n this.#totalConnectionsSuccess++;\n }\n\n recordConnectionAttempted() {\n this.#totalConnectionsAttempted++;\n }\n\n setActiveClientGroupsGetter(getter: () => number) {\n this.#activeClientGroupsGetter = getter;\n }\n\n setActiveUsersGetter(getter: () => ActiveUsers) {\n this.#activeUsersGetter = getter;\n }\n\n shutdown() {\n this.#stopped = true;\n if (this.#meterProvider) {\n this.#lc?.info?.('telemetry: shutting down');\n void this.#meterProvider.shutdown();\n }\n }\n\n #getAttributes() {\n if (!this.#cachedAttributes) {\n this.#cachedAttributes = {\n 'zero.app.id': h64(this.#config?.upstream.db || 'unknown').toString(),\n 'zero.machine.os': platform(),\n 'zero.telemetry.type': 'anonymous',\n 'zero.infra.platform': this.#getPlatform(),\n 'zero.version': getServerVersion(this.#config),\n 'zero.task.id': this.#config?.taskID || 'unknown',\n 'zero.project.id': this.#getGitProjectId(),\n 'zero.process.id': this.#processId,\n 'zero.fs.id': this.#getOrSetFsID(),\n };\n this.#lc?.debug?.(\n `telemetry: cached attributes=${JSON.stringify(this.#cachedAttributes)}`,\n );\n }\n return this.#cachedAttributes;\n }\n\n #getPlatform(): string {\n if (process.env.ZERO_ON_CLOUD_ZERO) return 'cloudzero';\n if (process.env.FLY_APP_NAME || process.env.FLY_REGION) return 'fly.io';\n if (\n process.env.ECS_CONTAINER_METADATA_URI_V4 ||\n process.env.ECS_CONTAINER_METADATA_URI ||\n process.env.AWS_EXECUTION_ENV\n ) {\n return 'aws';\n }\n if (process.env.RAILWAY_ENV || process.env.RAILWAY_STATIC_URL) {\n return 'railway';\n }\n if (process.env.RENDER || process.env.RENDER_SERVICE_ID) return 'render';\n if (\n process.env.GCP_PROJECT ||\n process.env.GCLOUD_PROJECT ||\n process.env.GOOGLE_CLOUD_PROJECT\n ) {\n return 'gcp';\n }\n if (process.env.COOLIFY_URL || process.env.COOLIFY_CONTAINER_NAME) {\n return 'coolify';\n }\n if (process.env.CONTAINER_APP_REVISION) return 'azure';\n if (process.env.FLIGHTCONTROL || process.env.FC_URL) return 'flightcontrol';\n return 'unknown';\n }\n\n #findUp(startDir: string, target: string): string | null {\n let dir = startDir;\n while (dir !== dirname(dir)) {\n if (existsSync(join(dir, target))) return dir;\n dir = dirname(dir);\n }\n return null;\n }\n\n #getGitProjectId(): string {\n try {\n const cwd = process.cwd();\n const gitRoot = this.#findUp(cwd, '.git');\n if (!gitRoot) {\n return 'unknown';\n }\n\n const rootCommitHash = execSync('git rev-list --max-parents=0 HEAD -1', {\n cwd: gitRoot,\n encoding: 'utf8',\n timeout: 1000,\n stdio: ['ignore', 'pipe', 'ignore'], // Suppress stderr\n }).trim();\n\n return rootCommitHash.length === 40 ? rootCommitHash : 'unknown';\n } catch (error) {\n // execSync throws a child_process.SpawnSyncReturns-shaped error with an\n // output property (an array with stdio buffers) and sometimes a killed\n // property that references back to the error itself — making it\n // circular and causing stringify() in the Logger logic to throw.\n const details =\n error instanceof Error\n ? {message: error.message, name: error.name, stack: error.stack}\n : String(error);\n this.#lc?.debug?.('telemetry: unable to get Git root commit:', details);\n return 'unknown';\n }\n }\n\n #getOrSetFsID(): string {\n try {\n if (this.#isInContainer()) {\n return 'container';\n }\n const fsidPath = join(homedir(), '.rocicorp', 'fsid');\n const fsidDir = dirname(fsidPath);\n\n mkdirSync(fsidDir, {recursive: true});\n\n // Always try atomic file creation first - this eliminates any race conditions\n const newId = randomUUID();\n try {\n writeFileSync(fsidPath, newId, {encoding: 'utf8', flag: 'wx'});\n return newId;\n } catch (writeError) {\n if ((writeError as NodeJS.ErrnoException).code === 'EEXIST') {\n const existingId = readFileSync(fsidPath, 'utf8').trim();\n return existingId;\n }\n throw writeError;\n }\n } catch (error) {\n this.#lc?.debug?.(\n 'telemetry: unable to get or set filesystem ID:',\n error,\n );\n return 'unknown';\n }\n }\n\n #isInContainer(): boolean {\n try {\n if (process.env.ZERO_IN_CONTAINER) {\n return true;\n }\n\n if (existsSync('/.dockerenv')) {\n return true;\n }\n\n if (existsSync('/usr/local/bin/docker-entrypoint.sh')) {\n return true;\n }\n\n if (process.env.KUBERNETES_SERVICE_HOST) {\n return true;\n }\n\n if (\n process.env.DOCKER_CONTAINER_ID ||\n process.env.HOSTNAME?.match(hostNameRe)\n ) {\n return true;\n }\n\n if (existsSync('/proc/1/cgroup')) {\n const cgroup = readFileSync('/proc/1/cgroup', 'utf8');\n if (\n cgroup.includes('docker') ||\n cgroup.includes('kubepods') ||\n cgroup.includes('containerd')\n ) {\n return true;\n }\n }\n\n return false;\n } catch (error) {\n this.#lc?.debug?.(\n 'telemetry: unable to detect container environment:',\n error,\n );\n return false;\n }\n }\n}\n\nconst manager = () => AnonymousTelemetryManager.getInstance();\n\nexport const startAnonymousTelemetry = (lc?: LogContext, config?: ZeroConfig) =>\n manager().start(lc, config);\nexport const recordMutation = (type: 'crud' | 'custom', count = 1) =>\n manager().recordMutation(type, count);\nexport const recordQuery = (type: 'crud' | 'custom', count = 1) =>\n manager().recordQuery(type, count);\nexport const recordRowsSynced = (count: number) =>\n manager().recordRowsSynced(count);\nexport const recordConnectionSuccess = () =>\n manager().recordConnectionSuccess();\nexport const recordConnectionAttempted = () =>\n manager().recordConnectionAttempted();\nexport const setActiveClientGroupsGetter = (getter: () => number) =>\n manager().setActiveClientGroupsGetter(getter);\nexport const setActiveUsersGetter = (getter: () => ActiveUsers) =>\n manager().setActiveUsersGetter(getter);\nexport const shutdownAnonymousTelemetry = () => manager().shutdown();\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"change-streamer.js","names":[],"sources":["../../../../../zero-cache/src/server/change-streamer.ts"],"sourcesContent":["import {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport {DatabaseInitError} from '../../../zqlite/src/db.ts';\nimport {getServerContext} from '../config/server-context.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {deleteLiteDB} from '../db/delete-lite-db.ts';\nimport {registerSQLiteCorruptionDiagnosticTarget} from '../db/sqlite-corruption.ts';\nimport {warmupConnections} from '../db/warmup.ts';\nimport {initEventSink, publishCriticalEvent} from '../observability/events.ts';\nimport {getOrCreateGauge} from '../observability/metrics.ts';\nimport {InitCleanup} from '../services/change-source/common/init-cleanup.ts';\nimport type {PgChangeLogPurgeLocker} from '../services/change-source/common/replica-restore.ts';\nimport {initializeCustomChangeSource} from '../services/change-source/custom/change-source.ts';\nimport {initializePostgresChangeSource} from '../services/change-source/pg/change-source-init.ts';\nimport {createBackupCleanupMonitor} from '../services/change-streamer/backup-cleanup-monitor-factory.ts';\nimport {ChangeStreamerHttpServer} from '../services/change-streamer/change-streamer-http.ts';\nimport {initializeStreamer} from '../services/change-streamer/change-streamer-service.ts';\nimport type {ChangeStreamerService} from '../services/change-streamer/change-streamer.ts';\nimport {DEFAULT_READINESS_GRACE_PERIOD_MS} from '../services/change-streamer/restore-admission.ts';\nimport {initChangeStreamerSchema} from '../services/change-streamer/schema/init.ts';\nimport {AutoResetSignal} from '../services/change-streamer/schema/tables.ts';\nimport {\n PurgeLocker,\n type PurgeLock,\n} from '../services/change-streamer/storer.ts';\nimport {\n exitAfter,\n runUntilKilled,\n SupportingProcesses,\n} from '../services/life-cycle.ts';\nimport {deleteLitestreamMetaDir} from '../services/litestream/commands.ts';\nimport {\n changeLogFileName,\n deleteChangeLogDB,\n} from '../services/replicator/change-log-db.ts';\nimport {\n replicationStatusError,\n ReplicationStatusPublisher,\n} from '../services/replicator/replication-status.ts';\nimport {sqliteFileBytes} from '../services/replicator/sqlite-change-log-observability.ts';\nimport {warnIfPostgresUnpatched} from '../types/pg-patch.ts';\nimport {connectPgClient} from '../types/pg.ts';\nimport {\n broadcastWorker,\n childWorker,\n parentWorker,\n singleProcessMode,\n type ProfileResponseMessage,\n type Worker,\n} from '../types/processes.ts';\nimport {installProfileHandler} from '../types/profiler.ts';\nimport {getShardConfig} from '../types/shards.ts';\nimport type {ReplicaFileMode} from '../workers/replicator.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\nimport {REPLICATOR_URL} from './worker-urls.ts';\n\n// Default LogContext, overridden in runWorker\nlet lc = new LogContext('info', {}, consoleLogSink);\n\n// Errors thrown once the services are running are reported by the services\n// themselves (e.g. as 'Replicating' status events), and are thus not\n// published as 'Initializing' errors.\nlet running = false;\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...argv: string[]\n): Promise<void> {\n installProfileHandler(parent, 'change-streamer');\n const config = getNormalizedZeroConfig({env, argv});\n const {\n taskID,\n changeStreamer: {\n port,\n address,\n protocol,\n startupDelayMs,\n backPressureLimitHeapProportion,\n flowControlConsensusTimeoutProportion,\n flowControlSlowSubscriberGracePeriodSeconds,\n maxConcurrentSwitchoverRestores,\n pgChangeLogEnabled,\n sqliteChangeLogMode,\n sqliteChangeLogReadPercent,\n sqliteChangeLogColdReadPercent,\n sqliteChangeLogComparePercent,\n sqliteChangeLogRetentionMs,\n sqliteChangeLogReadBatchRows,\n sqliteChangeLogPurgeBatchRows,\n sqliteChangeLogBarrierTimeoutMs,\n },\n autoReset,\n replicationLag,\n litestream,\n upstream,\n change,\n replica,\n initialSync,\n keepaliveTimeoutMs,\n sqliteCorruptionChecks,\n } = config;\n\n startOtelAuto(\n createLogContext(config, 'change-streamer', 0, false),\n 'change-streamer',\n 0,\n );\n lc = createLogContext(config, 'change-streamer');\n registerSQLiteCorruptionDiagnosticTarget(\n {\n debugName: 'change-streamer replica',\n dbPath: replica.file,\n },\n sqliteCorruptionChecks,\n );\n initEventSink(lc, config);\n if (upstream.type === 'pg') {\n warnIfPostgresUnpatched(lc);\n }\n\n // Startup-time client used for for change-streamer initialization\n // and handoff / takeover. Steady-state clients are managed by the\n // change-streamer (Storer) implementation.\n const changeDB = await connectPgClient(\n lc,\n change.db,\n 'change-streamer-init',\n {max: 5},\n {sendStringAsJson: true},\n );\n void warmupConnections(lc, changeDB, 'change').catch(() => {});\n\n const shard = getShardConfig(config);\n\n // Ensure the change DB schema is initialized/up-to-date.\n await initChangeStreamerSchema(lc, changeDB, shard);\n\n let purgeLock = null as PurgeLock | null;\n let changeStreamer: ChangeStreamerService | undefined;\n let backupURL: string | undefined;\n\n const context = getServerContext(config);\n const sqliteChangeLogEnabled = sqliteChangeLogMode !== 'off';\n // The modes are cumulative, so `serve` implies `compare`.\n const sqliteChangeLogComparing =\n sqliteChangeLogMode === 'compare' || sqliteChangeLogMode === 'serve';\n if (sqliteChangeLogEnabled) {\n const changeLogFile = changeLogFileName(replica.file);\n registerSQLiteCorruptionDiagnosticTarget(\n {\n debugName: 'change-streamer change-log',\n dbPath: changeLogFile,\n },\n sqliteCorruptionChecks,\n );\n // The log's bytes are accounted for in the process that writes them. This\n // is local disk only — the log is excluded from the litestream backup — and\n // it is a whole-database total, because a table that left the replica left\n // through its wal: the replica's footprint is what shrank and this is where\n // it went.\n getOrCreateGauge('replica', 'sqlite_change_log.file_bytes', {\n description:\n `The SQLite change log's total on-disk footprint: its main db file ` +\n `plus its wal sidecars.`,\n unit: 'By',\n }).addCallback(async o =>\n o.observe(await sqliteFileBytes(lc, changeLogFile)),\n );\n }\n\n let waitForFirstBackupBeforeServing = false;\n let newBackupLineage = false;\n for (const first of [true, false]) {\n // Resources acquired by an initialization attempt (e.g. a claimed\n // replication slot, the purge lock) to be released if the attempt fails.\n const cleanup = new InitCleanup(lc);\n // When restoring from litestream, a lock is acquired to prevent change-log\n // purges. This ensures that (this) change-streamer will be able to resume\n // from the backup. The lock is acquired by the change source initialization\n // just before restoring (i.e. after a replication slot is created, if\n // applicable), and released once this change-streamer takes over the\n // change-log.\n const acquirePurgeLock: PgChangeLogPurgeLocker | undefined =\n pgChangeLogEnabled && litestream.backupURL && litestream.executable\n ? async (slotWatermark?: string) => {\n const lock = await new PurgeLocker(lc, shard, changeDB).acquire(\n slotWatermark,\n );\n purgeLock = lock === 'behind-slot' ? null : lock;\n if (purgeLock) {\n const acquired = purgeLock;\n cleanup.onFailure('purge lock', () => {\n purgeLock = null;\n return acquired.release();\n });\n }\n return lock;\n }\n : undefined;\n\n const restoreOptions = {litestream, acquirePurgeLock, cleanup};\n try {\n // Note: This performs initial sync of the replica if necessary.\n const {\n pgReplicationEpoch: epoch,\n pgReplicationSlotPerReplica: slotPerReplica,\n pgResumeOrphanedSlotGracePeriodMs: inactiveReplicaGracePeriodMs,\n } = upstream;\n const {\n changeSource,\n subscriptionState,\n destinationBackupURL,\n replicaID,\n waitForBackupBeforeServing,\n newBackupLineage: initNewBackupLineage,\n pgChangeLogBehindSlot,\n } = upstream.type === 'pg'\n ? await initializePostgresChangeSource(\n lc,\n upstream.db,\n shard,\n replica.file,\n {\n ...initialSync,\n replicationSlotFailover: upstream.pgReplicationSlotFailover,\n installPartialIndexTriggers: upstream.pgPartialIndexTriggers,\n },\n context,\n replicationLag.reportIntervalMs,\n restoreOptions,\n {\n epoch,\n slotPerReplica,\n inactiveReplicaGracePeriodMs,\n backupV5: litestream.backupUsingV5,\n pgChangeLog: pgChangeLogEnabled,\n },\n upstream.pgStreamInboundTimeoutMs,\n )\n : await initializeCustomChangeSource(\n lc,\n upstream.db,\n shard,\n replica.file,\n context,\n restoreOptions,\n );\n\n const replicationStatusPublisher =\n ReplicationStatusPublisher.forReplicaFile(replica.file);\n\n changeStreamer = await initializeStreamer(\n lc,\n shard,\n taskID,\n address,\n protocol,\n changeDB,\n changeSource,\n replicationStatusPublisher,\n subscriptionState,\n destinationBackupURL\n ? {\n backupURL: destinationBackupURL,\n litestreamVersion: litestream.backupUsingV5 ? 'v5' : 'legacy',\n replicaFile: replica.file,\n }\n : null,\n purgeLock,\n autoReset ?? false,\n {\n pgChangeLogEnabled,\n pgChangeLogBehindSlot: pgChangeLogBehindSlot ?? false,\n backPressureLimitHeapProportion,\n flowControlConsensusTimeoutProportion,\n flowControlSlowSubscriberGracePeriodMs:\n flowControlSlowSubscriberGracePeriodSeconds > 0\n ? flowControlSlowSubscriberGracePeriodSeconds * 1000\n : undefined,\n maxConcurrentSwitchoverRestores,\n // When lag reporting is enabled, a restored view-syncer waits for\n // the next lag report (sent at an interval with ~1 second of\n // jitter) before becoming ready.\n switchoverRestoreReadinessGracePeriodMs:\n (replicationLag.reportIntervalMs > 0\n ? replicationLag.reportIntervalMs + 1000\n : 0) + DEFAULT_READINESS_GRACE_PERIOD_MS,\n statementTimeoutMs: change.statementTimeoutMs,\n changeLogBatchSize: change.logBatchSize,\n sqliteCatchup: {\n changeLogFile: changeLogFileName(replica.file),\n readBatchRows: sqliteChangeLogReadBatchRows,\n barrierTimeoutMs: sqliteChangeLogBarrierTimeoutMs,\n },\n // The presence of these options is the writer's gate. This process\n // performs the restore and the initial sync, so the replica's\n // identity is in hand at the moment the log is opened.\n sqliteChangeLogWriter: sqliteChangeLogEnabled\n ? {\n replicaFile: replica.file,\n identity: {\n // RMv2 supplies the epoch; until then the generation and the\n // replica ID are the whole of the identity.\n epoch: null,\n generation: subscriptionState.replicaVersion,\n replicaID,\n },\n }\n : undefined,\n // The purge scheduler shares the writer's gate -- mode, not the\n // read path -- because `write` mode, with no read selector at all,\n // is the configuration it ships in.\n sqliteChangeLogPurge: sqliteChangeLogEnabled\n ? {\n retentionMs: sqliteChangeLogRetentionMs,\n batchRows: sqliteChangeLogPurgeBatchRows,\n }\n : undefined,\n // Compare mode runs both advisory checks. Postgres remains authoritative.\n sqliteChangeLogCompare:\n pgChangeLogEnabled && sqliteChangeLogComparing\n ? {\n replicaFile: replica.file,\n comparePercent: sqliteChangeLogComparePercent,\n retentionMs: sqliteChangeLogRetentionMs,\n readBatchRows: sqliteChangeLogReadBatchRows,\n }\n : undefined,\n // Slice 11 lands dark by default: serve mode constructs the stable\n // router, while readPercent=0 keeps every catchup on PG and emits\n // eligibility metrics before any canary traffic is enabled.\n sqliteChangeLogServe:\n sqliteChangeLogMode === 'serve'\n ? {\n readPercent: sqliteChangeLogReadPercent,\n coldReadPercent: sqliteChangeLogColdReadPercent,\n retentionMs: sqliteChangeLogRetentionMs,\n }\n : undefined,\n },\n setTimeout,\n );\n backupURL = destinationBackupURL;\n waitForFirstBackupBeforeServing = waitForBackupBeforeServing;\n newBackupLineage = initNewBackupLineage;\n break;\n } catch (e) {\n // Release the resources acquired by the failed attempt, e.g.:\n // * The claimed replication slot, which would otherwise remain active\n // indefinitely, preventing a retry from resuming (or forking) it.\n // * The purge lock. This is safe because the purge lock exists to\n // preserve change-log entries so the new change-streamer can resume\n // from the backup replica's watermark. An AutoResetSignal means we\n // can't resume from the backup replica (e.g. its replication slot is\n // gone), so the change-log entries the lock was protecting are no\n // longer needed. The retry performs a fresh initial sync with a new\n // replication slot, independent of the old change-log. Releasing is\n // also necessary to avoid a self-deadlock when CHANGE_DB ==\n // UPSTREAM_DB: CREATE_REPLICATION_SLOT waits for all older\n // transactions to finish, including this lock's open transaction.\n await cleanup.release();\n if (first && e instanceof AutoResetSignal) {\n lc.warn?.(`resetting replica ${replica.file}`, e);\n // TODO: Make deleteLiteDB work with litestream. It will probably have to be\n // a semantic wipe instead of a file delete.\n deleteLiteDB(replica.file);\n // The change log carries the identity of the replica it was written\n // beside, and the retry performs a fresh initial sync with a new\n // replicaVersion. Reconciliation would catch that on its own (as\n // 'identity-mismatch') but only after the writer opened a file that is\n // known here to be garbage.\n deleteChangeLogDB(replica.file);\n continue; // execute again with a fresh initial-sync\n }\n if (e instanceof DatabaseInitError) {\n throw new Error(\n `Cannot open ZERO_REPLICA_FILE at \"${replica.file}\". Please check that the path is valid.`,\n {cause: e},\n );\n }\n throw e;\n }\n }\n // impossible: upstream must have advanced in order for replication to be stuck.\n assert(changeStreamer, `resetting replica did not advance replicaVersion`);\n\n // The backup-replicator is stopped with the other services, i.e. after the\n // ChangeStreamerHttpServer has drained its subscribers.\n const processes = new SupportingProcesses(lc, parent);\n const profileSubWorkers: Worker[] = [];\n const {promise: replicatorReady, resolve} = resolver();\n if (!backupURL) {\n resolve(); // No backup replicator to wait for, so resolve immediately.\n } else {\n lc.info?.('setting up backup to', backupURL);\n config.litestream.backupURLOverride = backupURL;\n if (newBackupLineage) {\n deleteLitestreamMetaDir(replica.file);\n }\n // Start a backup replicator, which starts up the corresponding\n // litestream backup process.\n const backupReplicator = processes\n .add(\n childWorker(\n REPLICATOR_URL,\n {...env, ZERO_LITESTREAM_BACKUP_URL_OVERRIDE: backupURL},\n 'backup' satisfies ReplicaFileMode,\n ),\n 'backup-replicator',\n )\n .onceMessageType('ready', () => resolve());\n profileSubWorkers.push(backupReplicator);\n // Relay profileResponse messages from backup-replicator up to parent\n backupReplicator.onMessageType<ProfileResponseMessage>(\n 'profileResponse',\n res => parent.send(['profileResponse', res]),\n );\n }\n\n const backupMonitor = createBackupCleanupMonitor({\n lc,\n config,\n replicaFile: replica.file,\n changeStreamer,\n });\n\n let backupReady = promiseVoid;\n if (waitForFirstBackupBeforeServing) {\n const start = performance.now();\n lc.info?.(`awaiting initial backup ...`);\n\n backupReady = backupMonitor.firstBackupReceived().then(() => {\n const elapsed = performance.now() - start;\n lc.info?.(`initial backup confirmed after ${elapsed.toFixed(2)}ms`);\n });\n }\n\n // In RMv2, readiness additionally waits for the backup-replicator to catch\n // up to the replication stream. This is not done in RMv1 (i.e. when the\n // PG change-log is enabled), in which the change-streamer itself is only\n // started after the readinessGate (plus a startup delay), and thus the\n // backup-replicator cannot catch up before then.\n const readinessGate = pgChangeLogEnabled\n ? backupReady\n : Promise.all([backupReady, replicatorReady]);\n\n // Create the broadcast facade once: each broadcastWorker() adds permanent\n // 'message' forwarders to every sub worker, so creating one per /profz\n // request would leak a forwarder per request.\n const profileWorker =\n profileSubWorkers.length > 0\n ? broadcastWorker(profileSubWorkers)\n : undefined;\n const getProfileWorker = profileWorker\n ? () => Promise.resolve(profileWorker)\n : undefined;\n\n const changeStreamerWebServer = new ChangeStreamerHttpServer(\n lc,\n {\n port,\n keepaliveTimeoutMs,\n // The startup delay is only relevant when taking over the PG change-log\n // (RMv1 and RMv1.5), and is disabled for RMv2.\n startupDelayMs: pgChangeLogEnabled ? startupDelayMs : 0,\n readinessGate,\n config,\n getProfileWorker,\n },\n parent,\n changeStreamer,\n );\n\n void readinessGate.then(() => parent.send(['ready', {ready: true}]));\n\n // Note: The changeStreamer itself is not started here; it is started by the\n // changeStreamerWebServer after a delay to ensure that routing\n // routing elements have registered the server before the\n // change-streamer takes over the replication slot.\n // TODO: Remove this delay and start the changeStreamer normally here once\n // transitioned to RMv2, since changeStreamer startup will no longer\n // disrupt an existing task.\n running = true;\n await runUntilKilled(\n lc,\n parent,\n changeStreamerWebServer,\n backupMonitor,\n processes,\n );\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(\n () => lc,\n () =>\n runWorker(\n must(parentWorker),\n process.env,\n ...process.argv.slice(2),\n ).catch(async e => {\n if (!running) {\n await publishCriticalEvent(\n lc,\n replicationStatusError(lc, 'Initializing', e),\n );\n }\n throw e;\n }),\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"change-streamer.js","names":[],"sources":["../../../../../zero-cache/src/server/change-streamer.ts"],"sourcesContent":["import {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport {DatabaseInitError} from '../../../zqlite/src/db.ts';\nimport {getServerContext} from '../config/server-context.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {deleteLiteDB} from '../db/delete-lite-db.ts';\nimport {registerSQLiteCorruptionDiagnosticTarget} from '../db/sqlite-corruption.ts';\nimport {warmupConnections} from '../db/warmup.ts';\nimport {initEventSink, publishCriticalEvent} from '../observability/events.ts';\nimport {getOrCreateGauge} from '../observability/metrics.ts';\nimport {InitCleanup} from '../services/change-source/common/init-cleanup.ts';\nimport type {PgChangeLogPurgeLocker} from '../services/change-source/common/replica-restore.ts';\nimport {initializeCustomChangeSource} from '../services/change-source/custom/change-source.ts';\nimport {initializePostgresChangeSource} from '../services/change-source/pg/change-source-init.ts';\nimport {createBackupCleanupMonitor} from '../services/change-streamer/backup-cleanup-monitor-factory.ts';\nimport {ChangeStreamerHttpServer} from '../services/change-streamer/change-streamer-http.ts';\nimport {initializeStreamer} from '../services/change-streamer/change-streamer-service.ts';\nimport type {ChangeStreamerService} from '../services/change-streamer/change-streamer.ts';\nimport {DEFAULT_READINESS_GRACE_PERIOD_MS} from '../services/change-streamer/restore-admission.ts';\nimport {initChangeStreamerSchema} from '../services/change-streamer/schema/init.ts';\nimport {AutoResetSignal} from '../services/change-streamer/schema/tables.ts';\nimport {\n PurgeLocker,\n type PurgeLock,\n} from '../services/change-streamer/storer.ts';\nimport {\n exitAfter,\n runUntilKilled,\n SupportingProcesses,\n} from '../services/life-cycle.ts';\nimport {deleteLitestreamMetaDir} from '../services/litestream/commands.ts';\nimport {\n changeLogFileName,\n deleteChangeLogDB,\n} from '../services/replicator/change-log-db.ts';\nimport {\n replicationStatusError,\n ReplicationStatusPublisher,\n} from '../services/replicator/replication-status.ts';\nimport {sqliteFileBytes} from '../services/replicator/sqlite-change-log-observability.ts';\nimport {warnIfPostgresUnpatched} from '../types/pg-patch.ts';\nimport {connectPgClient} from '../types/pg.ts';\nimport {\n broadcastWorker,\n childWorker,\n parentWorker,\n singleProcessMode,\n type ProfileResponseMessage,\n type Worker,\n} from '../types/processes.ts';\nimport {installProfileHandler} from '../types/profiler.ts';\nimport {getShardConfig} from '../types/shards.ts';\nimport type {ReplicaFileMode} from '../workers/replicator.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\nimport {REPLICATOR_URL} from './worker-urls.ts';\n\n// Default LogContext, overridden in runWorker\nlet lc = new LogContext('info', {}, consoleLogSink);\n\n// Errors thrown once the services are running are reported by the services\n// themselves (e.g. as 'Replicating' status events), and are thus not\n// published as 'Initializing' errors.\nlet running = false;\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...argv: string[]\n): Promise<void> {\n installProfileHandler(parent, 'change-streamer');\n const config = getNormalizedZeroConfig({env, argv});\n const {\n taskID,\n changeStreamer: {\n port,\n address,\n protocol,\n startupDelayMs,\n backPressureLimitHeapProportion,\n flowControlConsensusTimeoutProportion,\n flowControlSlowSubscriberGracePeriodSeconds,\n maxConcurrentSwitchoverRestores,\n pgChangeLogEnabled,\n sqliteChangeLogMode,\n sqliteChangeLogReadPercent,\n sqliteChangeLogColdReadPercent,\n sqliteChangeLogComparePercent,\n sqliteChangeLogRetentionMs,\n sqliteChangeLogReadBatchRows,\n sqliteChangeLogPurgeBatchRows,\n sqliteChangeLogBarrierTimeoutMs,\n },\n autoReset,\n replicationLag,\n litestream,\n upstream,\n change,\n replica,\n initialSync,\n keepaliveTimeoutMs,\n sqliteCorruptionChecks,\n } = config;\n\n startOtelAuto(\n createLogContext(config, 'change-streamer', 0, false),\n 'change-streamer',\n 0,\n );\n lc = createLogContext(config, 'change-streamer');\n registerSQLiteCorruptionDiagnosticTarget(\n {\n debugName: 'change-streamer replica',\n dbPath: replica.file,\n },\n sqliteCorruptionChecks,\n );\n initEventSink(lc, config);\n if (upstream.type === 'pg') {\n warnIfPostgresUnpatched(lc);\n }\n\n // Startup-time client used for for change-streamer initialization\n // and handoff / takeover. Steady-state clients are managed by the\n // change-streamer (Storer) implementation.\n const changeDB = await connectPgClient(\n lc,\n change.db,\n 'change-streamer-init',\n {max: 5},\n {sendStringAsJson: true},\n );\n void warmupConnections(lc, changeDB, 'change').catch(() => {});\n\n const shard = getShardConfig(config);\n\n // Ensure the change DB schema is initialized/up-to-date.\n await initChangeStreamerSchema(lc, changeDB, shard);\n\n let purgeLock = null as PurgeLock | null;\n let changeStreamer: ChangeStreamerService | undefined;\n let backupURL: string | undefined;\n\n const context = getServerContext(config);\n const sqliteChangeLogEnabled = sqliteChangeLogMode !== 'off';\n // The modes are cumulative, so `serve` implies `compare`.\n const sqliteChangeLogComparing =\n sqliteChangeLogMode === 'compare' || sqliteChangeLogMode === 'serve';\n if (sqliteChangeLogEnabled) {\n const changeLogFile = changeLogFileName(replica.file);\n registerSQLiteCorruptionDiagnosticTarget(\n {\n debugName: 'change-streamer change-log',\n dbPath: changeLogFile,\n },\n sqliteCorruptionChecks,\n );\n // The log's bytes are accounted for in the process that writes them. This\n // is local disk only — the log is excluded from the litestream backup — and\n // it is a whole-database total, because a table that left the replica left\n // through its wal: the replica's footprint is what shrank and this is where\n // it went.\n getOrCreateGauge('replica', 'sqlite_change_log.file_bytes', {\n description:\n `The SQLite change log's total on-disk footprint: its main db file ` +\n `plus its wal sidecars.`,\n unit: 'By',\n }).addCallback(async o =>\n o.observe(await sqliteFileBytes(lc, changeLogFile)),\n );\n }\n\n let waitForFirstBackupBeforeServing = false;\n let newBackupLineage = false;\n for (const first of [true, false]) {\n // Resources acquired by an initialization attempt (e.g. a claimed\n // replication slot, the purge lock) to be released if the attempt fails.\n const cleanup = new InitCleanup(lc);\n // When restoring from litestream, a lock is acquired to prevent change-log\n // purges. This ensures that (this) change-streamer will be able to resume\n // from the backup. The lock is acquired by the change source initialization\n // just before restoring (i.e. after a replication slot is created, if\n // applicable), and released once this change-streamer takes over the\n // change-log.\n const acquirePurgeLock: PgChangeLogPurgeLocker | undefined =\n pgChangeLogEnabled && litestream.backupURL && litestream.executable\n ? async (slotWatermark?: string) => {\n const lock = await new PurgeLocker(lc, shard, changeDB).acquire(\n slotWatermark,\n );\n purgeLock = lock === 'behind-slot' ? null : lock;\n if (purgeLock) {\n const acquired = purgeLock;\n cleanup.onFailure('purge lock', () => {\n purgeLock = null;\n return acquired.release();\n });\n }\n return lock;\n }\n : undefined;\n\n const restoreOptions = {litestream, acquirePurgeLock, cleanup};\n try {\n // Note: This performs initial sync of the replica if necessary.\n const {\n pgReplicationEpoch: epoch,\n pgReplicationSlotPerReplica: slotPerReplica,\n pgResumeOrphanedSlotGracePeriodMs: inactiveReplicaGracePeriodMs,\n } = upstream;\n const {\n changeSource,\n subscriptionState,\n destinationBackupURL,\n replicaID,\n waitForBackupBeforeServing,\n newBackupLineage: initNewBackupLineage,\n pgChangeLogBehindSlot,\n } = upstream.type === 'pg'\n ? await initializePostgresChangeSource(\n lc,\n upstream.db,\n shard,\n replica.file,\n {\n ...initialSync,\n replicationSlotFailover: upstream.pgReplicationSlotFailover,\n installPartialIndexTriggers: upstream.pgPartialIndexTriggers,\n },\n context,\n replicationLag.reportIntervalMs,\n restoreOptions,\n {\n epoch,\n slotPerReplica,\n inactiveReplicaGracePeriodMs,\n backupV5: litestream.backupUsingV5,\n pgChangeLog: pgChangeLogEnabled,\n },\n upstream.pgStreamInboundTimeoutMs,\n )\n : await initializeCustomChangeSource(\n lc,\n upstream.db,\n shard,\n replica.file,\n context,\n restoreOptions,\n );\n\n const replicationStatusPublisher =\n ReplicationStatusPublisher.forReplicaFile(replica.file);\n\n changeStreamer = await initializeStreamer(\n lc,\n shard,\n taskID,\n address,\n protocol,\n changeDB,\n changeSource,\n replicationStatusPublisher,\n subscriptionState,\n destinationBackupURL\n ? {\n backupURL: destinationBackupURL,\n litestreamVersion: litestream.backupUsingV5 ? 'v5' : 'legacy',\n replicaFile: replica.file,\n }\n : null,\n purgeLock,\n autoReset ?? false,\n {\n pgChangeLogEnabled,\n pgChangeLogBehindSlot: pgChangeLogBehindSlot ?? false,\n backPressureLimitHeapProportion,\n flowControlConsensusTimeoutProportion,\n flowControlSlowSubscriberGracePeriodMs:\n flowControlSlowSubscriberGracePeriodSeconds > 0\n ? flowControlSlowSubscriberGracePeriodSeconds * 1000\n : undefined,\n maxConcurrentSwitchoverRestores,\n // When lag reporting is enabled, a restored view-syncer waits for\n // the next lag report (sent at an interval with ~1 second of\n // jitter) before becoming ready.\n switchoverRestoreReadinessGracePeriodMs:\n (replicationLag.reportIntervalMs > 0\n ? replicationLag.reportIntervalMs + 1000\n : 0) + DEFAULT_READINESS_GRACE_PERIOD_MS,\n statementTimeoutMs: change.statementTimeoutMs,\n changeLogBatchSize: change.logBatchSize,\n sqliteCatchup: {\n changeLogFile: changeLogFileName(replica.file),\n readBatchRows: sqliteChangeLogReadBatchRows,\n barrierTimeoutMs: sqliteChangeLogBarrierTimeoutMs,\n },\n // The presence of these options is the writer's gate. This process\n // performs the restore and the initial sync, so the replica's\n // identity is in hand at the moment the log is opened.\n sqliteChangeLogWriter: sqliteChangeLogEnabled\n ? {\n replicaFile: replica.file,\n identity: {\n // RMv2 supplies the epoch; until then the generation and the\n // replica ID are the whole of the identity.\n epoch: null,\n generation: subscriptionState.replicaVersion,\n replicaID,\n },\n }\n : undefined,\n // The purge scheduler shares the writer's gate -- mode, not the\n // read path -- because `write` mode, with no read selector at all,\n // is the configuration it ships in.\n sqliteChangeLogPurge: sqliteChangeLogEnabled\n ? {\n retentionMs: sqliteChangeLogRetentionMs,\n batchRows: sqliteChangeLogPurgeBatchRows,\n }\n : undefined,\n // Compare mode runs both advisory checks. Postgres remains authoritative.\n sqliteChangeLogCompare:\n pgChangeLogEnabled && sqliteChangeLogComparing\n ? {\n replicaFile: replica.file,\n comparePercent: sqliteChangeLogComparePercent,\n retentionMs: sqliteChangeLogRetentionMs,\n readBatchRows: sqliteChangeLogReadBatchRows,\n }\n : undefined,\n // Slice 11 lands dark by default: serve mode constructs the stable\n // router, while readPercent=0 keeps every catchup on PG and emits\n // eligibility metrics before any canary traffic is enabled.\n sqliteChangeLogServe:\n sqliteChangeLogMode === 'serve'\n ? {\n readPercent: sqliteChangeLogReadPercent,\n coldReadPercent: sqliteChangeLogColdReadPercent,\n retentionMs: sqliteChangeLogRetentionMs,\n }\n : undefined,\n },\n setTimeout,\n );\n backupURL = destinationBackupURL;\n waitForFirstBackupBeforeServing = waitForBackupBeforeServing;\n newBackupLineage = initNewBackupLineage;\n break;\n } catch (e) {\n // Release the resources acquired by the failed attempt, e.g.:\n // * The claimed replication slot, which would otherwise remain active\n // indefinitely, preventing a retry from resuming (or forking) it.\n // * The purge lock. This is safe because the purge lock exists to\n // preserve change-log entries so the new change-streamer can resume\n // from the backup replica's watermark. An AutoResetSignal means we\n // can't resume from the backup replica (e.g. its replication slot is\n // gone), so the change-log entries the lock was protecting are no\n // longer needed. The retry performs a fresh initial sync with a new\n // replication slot, independent of the old change-log. Releasing is\n // also necessary to avoid a self-deadlock when CHANGE_DB ==\n // UPSTREAM_DB: CREATE_REPLICATION_SLOT waits for all older\n // transactions to finish, including this lock's open transaction.\n await cleanup.release();\n if (first && e instanceof AutoResetSignal) {\n lc.warn?.(`resetting replica ${replica.file}`, e);\n // TODO: Make deleteLiteDB work with litestream. It will probably have to be\n // a semantic wipe instead of a file delete.\n deleteLiteDB(replica.file);\n // The change log carries the identity of the replica it was written\n // beside, and the retry performs a fresh initial sync with a new\n // replicaVersion. Reconciliation would catch that on its own (as\n // 'identity-mismatch') but only after the writer opened a file that is\n // known here to be garbage.\n deleteChangeLogDB(replica.file);\n continue; // execute again with a fresh initial-sync\n }\n if (e instanceof DatabaseInitError) {\n throw new Error(\n `Cannot open ZERO_REPLICA_FILE at \"${replica.file}\". Please check that the path is valid.`,\n {cause: e},\n );\n }\n throw e;\n }\n }\n // impossible: upstream must have advanced in order for replication to be stuck.\n assert(changeStreamer, `resetting replica did not advance replicaVersion`);\n\n // The backup-replicator is stopped with the other services, i.e. after the\n // ChangeStreamerHttpServer has drained its subscribers.\n const processes = new SupportingProcesses(lc, parent);\n const profileSubWorkers: Worker[] = [];\n const {promise: replicatorReady, resolve} = resolver();\n if (!backupURL) {\n resolve(); // No backup replicator to wait for, so resolve immediately.\n } else {\n lc.info?.('setting up backup to', backupURL);\n config.litestream.backupURLOverride = backupURL;\n if (newBackupLineage) {\n deleteLitestreamMetaDir(replica.file);\n }\n // Start a backup replicator, which starts up the corresponding\n // litestream backup process.\n const backupReplicator = processes\n .add(\n childWorker(\n REPLICATOR_URL,\n {...env, ZERO_LITESTREAM_BACKUP_URL_OVERRIDE: backupURL},\n 'backup' satisfies ReplicaFileMode,\n ),\n 'backup-replicator',\n )\n .onceMessageType('ready', () => resolve());\n profileSubWorkers.push(backupReplicator);\n // Relay profileResponse messages from backup-replicator up to parent\n backupReplicator.onMessageType<ProfileResponseMessage>(\n 'profileResponse',\n res => parent.send(['profileResponse', res]),\n );\n }\n\n const backupMonitor = createBackupCleanupMonitor({\n lc,\n config,\n replicaFile: replica.file,\n changeStreamer,\n });\n\n let backupReady = promiseVoid;\n if (waitForFirstBackupBeforeServing) {\n const start = performance.now();\n lc.info?.(`awaiting initial backup ...`);\n\n backupReady = backupMonitor.firstBackupReceived().then(() => {\n const elapsed = performance.now() - start;\n lc.info?.(`initial backup confirmed after ${elapsed.toFixed(2)}ms`);\n });\n }\n\n // In RMv2, readiness additionally waits for the backup-replicator to catch\n // up to the replication stream. This is not done in RMv1 (i.e. when the\n // PG change-log is enabled), in which the change-streamer itself is only\n // started after the readinessGate (plus a startup delay), and thus the\n // backup-replicator cannot catch up before then.\n const readinessGate = pgChangeLogEnabled\n ? backupReady\n : Promise.all([backupReady, replicatorReady]);\n\n // Create the broadcast facade once: each broadcastWorker() adds permanent\n // 'message' forwarders to every sub worker, so creating one per /profz\n // request would leak a forwarder per request.\n const profileWorker =\n profileSubWorkers.length > 0\n ? broadcastWorker(profileSubWorkers)\n : undefined;\n const getProfileWorker = profileWorker\n ? () => Promise.resolve(profileWorker)\n : undefined;\n\n const changeStreamerWebServer = new ChangeStreamerHttpServer(\n lc,\n {\n port,\n keepaliveTimeoutMs,\n // The startup delay is only relevant when taking over the PG change-log\n // (RMv1 and RMv1.5), and is disabled for RMv2.\n startupDelayMs: pgChangeLogEnabled ? startupDelayMs : 0,\n readinessGate,\n config,\n getProfileWorker,\n },\n parent,\n changeStreamer,\n );\n\n void readinessGate.then(() => parent.send(['ready', {ready: true}]));\n\n // Note: The changeStreamer itself is not started here; it is started by the\n // changeStreamerWebServer after a delay to ensure that routing\n // routing elements have registered the server before the\n // change-streamer takes over the replication slot.\n // TODO: Remove this delay and start the changeStreamer normally here once\n // transitioned to RMv2, since changeStreamer startup will no longer\n // disrupt an existing task.\n running = true;\n await runUntilKilled(\n lc,\n parent,\n changeStreamerWebServer,\n backupMonitor,\n processes,\n );\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(\n () => lc,\n () =>\n runWorker(\n must(parentWorker),\n process.env,\n ...process.argv.slice(2),\n ).catch(async e => {\n if (!running) {\n await publishCriticalEvent(\n lc,\n replicationStatusError(lc, 'Initializing', e),\n );\n }\n throw e;\n }),\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inspector-delegate.js","names":["
|
|
1
|
+
{"version":3,"file":"inspector-delegate.js","names":["hashOfNameAndArgs"],"sources":["../../../../../zero-cache/src/server/inspector-delegate.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {getOrInsertComputed} from '../../../shared/src/map.ts';\nimport {mapValues} from '../../../shared/src/objects.ts';\nimport {TDigest} from '../../../shared/src/tdigest.ts';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport type {QueryServerMetrics as QueryServerMetricsJSON} from '../../../zero-protocol/src/inspect-down.ts';\nimport {hashOfNameAndArgs} from '../../../zero-protocol/src/query-hash.ts';\nimport {\n isServerMetric,\n type MetricMap,\n type MetricsDelegate,\n} from '../../../zql/src/query/metrics-delegate.ts';\nimport {isDevelopmentMode} from '../config/normalize.ts';\nimport type {CustomQueryTransformer} from '../custom-queries/transform-query.ts';\nimport type {ConnectionContext} from '../services/view-syncer/connection-context-manager.ts';\nimport type {CustomQueryRecord} from '../services/view-syncer/schema/types.ts';\nimport {ProtocolErrorWithLevel} from '../types/error-with-level.ts';\n\n/**\n * Server-side metrics collected for queries during materialization and update.\n * These metrics are reported via the inspector and complement client-side metrics.\n * Used for the global aggregate (all queries combined).\n */\nexport type ServerMetrics = {\n 'query-materialization-server': TDigest;\n 'query-update-server': TDigest;\n};\n\ntype ClientGroupID = string;\n\n/**\n * The service that established a client group's authentication, i.e. the\n * ViewSyncer serving it. See {@link InspectorDelegate.clearAuthenticated}.\n */\ntype AuthOwner = object;\n\nconst NO_OWNER: AuthOwner = {};\n\n/**\n * Authenticated client group IDs, each mapped to the owner that authenticated\n * it. We keep this outside of the class to share this state across all\n * instances of the InspectorDelegate.\n */\nconst authenticatedClientGroupIDs = new Map<ClientGroupID, AuthOwner>();\n\n/**\n * `query-update-server` is reported for every change pushed into every\n * pipeline: millions of values a minute while a bulk write fans out across\n * many client groups. Inserting each into a TDigest (two allocations, and a\n * sort every few hundred inserts) cost ~16% of a busy syncer's CPU. Instead,\n * every UPDATE_SAMPLE_RATE-th value of each query is recorded, weighted by\n * the rate, so the digests keep describing the distribution and, to within\n * one sample, the count.\n */\nexport const UPDATE_SAMPLE_RATE = 32;\n\ntype UpdateMetrics = {\n readonly digest: TDigest;\n /** Values reported for the query, recorded or not. */\n reported: number;\n};\n\nfunction newUpdateMetrics(): UpdateMetrics {\n return {digest: new TDigest(), reported: 0};\n}\n\nexport class InspectorDelegate implements MetricsDelegate {\n readonly #globalMetrics: ServerMetrics = newMetrics();\n readonly #perQueryHydrateMs = new Map<string, number>();\n readonly #perQueryUpdateMetrics = new Map<string, UpdateMetrics>();\n readonly #queryIDToAST: Map<string, AST> = new Map();\n readonly #customQueryTransformer: CustomQueryTransformer | undefined;\n\n constructor(customQueryTransformer: CustomQueryTransformer | undefined) {\n this.#customQueryTransformer = customQueryTransformer;\n }\n\n addMetric<K extends keyof MetricMap>(\n metric: K,\n value: number,\n ...args: MetricMap[K]\n ): void {\n assert(isServerMetric(metric), `Invalid server metric: ${metric}`);\n const queryID = args[0];\n if (metric === 'query-materialization-server') {\n this.#perQueryHydrateMs.set(queryID, value);\n this.#globalMetrics[metric].add(value);\n return;\n }\n const update = getOrInsertComputed(\n this.#perQueryUpdateMetrics,\n queryID,\n newUpdateMetrics,\n );\n if (update.reported++ % UPDATE_SAMPLE_RATE === 0) {\n update.digest.add(value, UPDATE_SAMPLE_RATE);\n this.#globalMetrics[metric].add(value, UPDATE_SAMPLE_RATE);\n }\n }\n\n getMetricsJSONForQuery(queryID: string): QueryServerMetricsJSON | null {\n const hydrateMs = this.#perQueryHydrateMs.get(queryID);\n const updateMetrics = this.#perQueryUpdateMetrics.get(queryID);\n if (hydrateMs === undefined && updateMetrics === undefined) {\n return null;\n }\n return {\n 'query-hydration-server-ms': hydrateMs,\n 'query-update-server': (updateMetrics?.digest ?? new TDigest()).toJSON(),\n };\n }\n\n getMetricsJSON() {\n return mapValues(this.#globalMetrics, v => v.toJSON());\n }\n\n getASTForQuery(queryID: string): AST | undefined {\n return this.#queryIDToAST.get(queryID);\n }\n\n removeQuery(queryID: string): void {\n this.#perQueryHydrateMs.delete(queryID);\n this.#perQueryUpdateMetrics.delete(queryID);\n this.#queryIDToAST.delete(queryID);\n }\n\n addQuery(queryID: string, ast: AST): void {\n this.#queryIDToAST.set(queryID, ast);\n }\n\n /**\n * Check if the client is authenticated. We only require authentication once\n * per \"worker\".\n */\n isAuthenticated(clientGroupID: ClientGroupID): boolean {\n return (\n isDevelopmentMode() || authenticatedClientGroupIDs.has(clientGroupID)\n );\n }\n\n /**\n * Marks the client group as authenticated on behalf of `owner`, the service\n * (ViewSyncer) that handled the authentication.\n */\n setAuthenticated(\n clientGroupID: ClientGroupID,\n owner: AuthOwner = NO_OWNER,\n ): void {\n authenticatedClientGroupIDs.set(clientGroupID, owner);\n }\n\n /**\n * Clears the client group's authentication. When `owner` is given, the\n * authentication is only cleared if it was established by that owner: a\n * replacement ViewSyncer can start for the same client group while the\n * previous one is still shutting down, and the previous one's cleanup must\n * not revoke what the replacement has since authenticated.\n */\n clearAuthenticated(clientGroupID: ClientGroupID, owner?: AuthOwner) {\n if (\n owner === undefined ||\n authenticatedClientGroupIDs.get(clientGroupID) === owner\n ) {\n authenticatedClientGroupIDs.delete(clientGroupID);\n }\n }\n\n /**\n * Transforms a single custom query by name and args using the configured\n * CustomQueryTransformer. This is primarily used by the inspector to transform\n * queries for analysis.\n */\n async transformCustomQuery(\n name: string,\n args: readonly ReadonlyJSONValue[],\n ctx: ConnectionContext,\n ): Promise<AST> {\n assert(\n this.#customQueryTransformer,\n 'Custom query transformation requested but no CustomQueryTransformer is configured',\n );\n\n // Create a fake CustomQueryRecord for the single query\n const queryID = hashOfNameAndArgs(name, args);\n const queries: CustomQueryRecord[] = [\n {\n id: queryID,\n type: 'custom',\n name,\n args,\n clientState: {},\n },\n ];\n\n const results = await this.#customQueryTransformer.transform(ctx, queries);\n\n if ('kind' in results.result) {\n throw new ProtocolErrorWithLevel(results.result, 'warn');\n }\n\n const result = results.result[0];\n if (!result) {\n throw new Error('No transformation result returned');\n }\n\n if ('error' in result) {\n const message =\n result.message ?? 'Unknown application error from custom query';\n throw new Error(\n `Error transforming custom query ${name} (${result.error}): ${message} ${JSON.stringify(result.details)}`,\n );\n }\n\n return result.transformedAst;\n }\n}\n\nfunction newMetrics(): ServerMetrics {\n return {\n 'query-materialization-server': new TDigest(),\n 'query-update-server': new TDigest(),\n };\n}\n"],"mappings":";;;;;;;;;;AAqCA,IAAM,WAAsB,CAAC;;;;;;AAO7B,IAAM,8CAA8B,IAAI,IAA8B;AAmBtE,SAAS,mBAAkC;CACzC,OAAO;EAAC,QAAQ,IAAI,QAAQ;EAAG,UAAU;CAAC;AAC5C;AAEA,IAAa,oBAAb,MAA0D;CACxD,iBAAyC,WAAW;CACpD,qCAA8B,IAAI,IAAoB;CACtD,yCAAkC,IAAI,IAA2B;CACjE,gCAA2C,IAAI,IAAI;CACnD;CAEA,YAAY,wBAA4D;EACtE,KAAK,0BAA0B;CACjC;CAEA,UACE,QACA,OACA,GAAG,MACG;EACN,OAAO,eAAe,MAAM,GAAG,0BAA0B,QAAQ;EACjE,MAAM,UAAU,KAAK;EACrB,IAAI,WAAW,gCAAgC;GAC7C,KAAK,mBAAmB,IAAI,SAAS,KAAK;GAC1C,KAAK,eAAe,OAAO,CAAC,IAAI,KAAK;GACrC;EACF;EACA,MAAM,SAAS,oBACb,KAAK,wBACL,SACA,gBACF;EACA,IAAI,OAAO,aAAA,OAAoC,GAAG;GAChD,OAAO,OAAO,IAAI,OAAA,EAAyB;GAC3C,KAAK,eAAe,OAAO,CAAC,IAAI,OAAA,EAAyB;EAC3D;CACF;CAEA,uBAAuB,SAAgD;EACrE,MAAM,YAAY,KAAK,mBAAmB,IAAI,OAAO;EACrD,MAAM,gBAAgB,KAAK,uBAAuB,IAAI,OAAO;EAC7D,IAAI,cAAc,KAAA,KAAa,kBAAkB,KAAA,GAC/C,OAAO;EAET,OAAO;GACL,6BAA6B;GAC7B,wBAAwB,eAAe,UAAU,IAAI,QAAQ,EAAA,CAAG,OAAO;EACzE;CACF;CAEA,iBAAiB;EACf,OAAO,UAAU,KAAK,iBAAgB,MAAK,EAAE,OAAO,CAAC;CACvD;CAEA,eAAe,SAAkC;EAC/C,OAAO,KAAK,cAAc,IAAI,OAAO;CACvC;CAEA,YAAY,SAAuB;EACjC,KAAK,mBAAmB,OAAO,OAAO;EACtC,KAAK,uBAAuB,OAAO,OAAO;EAC1C,KAAK,cAAc,OAAO,OAAO;CACnC;CAEA,SAAS,SAAiB,KAAgB;EACxC,KAAK,cAAc,IAAI,SAAS,GAAG;CACrC;;;;;CAMA,gBAAgB,eAAuC;EACrD,OACE,kBAAkB,KAAK,4BAA4B,IAAI,aAAa;CAExE;;;;;CAMA,iBACE,eACA,QAAmB,UACb;EACN,4BAA4B,IAAI,eAAe,KAAK;CACtD;;;;;;;;CASA,mBAAmB,eAA8B,OAAmB;EAClE,IACE,UAAU,KAAA,KACV,4BAA4B,IAAI,aAAa,MAAM,OAEnD,4BAA4B,OAAO,aAAa;CAEpD;;;;;;CAOA,MAAM,qBACJ,MACA,MACA,KACc;EACd,OACE,KAAK,yBACL,mFACF;EAIA,MAAM,UAA+B,CACnC;GACE,IAHYA,gBAAkB,MAAM,IAGhC;GACJ,MAAM;GACN;GACA;GACA,aAAa,CAAC;EAChB,CACF;EAEA,MAAM,UAAU,MAAM,KAAK,wBAAwB,UAAU,KAAK,OAAO;EAEzE,IAAI,UAAU,QAAQ,QACpB,MAAM,IAAI,uBAAuB,QAAQ,QAAQ,MAAM;EAGzD,MAAM,SAAS,QAAQ,OAAO;EAC9B,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,mCAAmC;EAGrD,IAAI,WAAW,QAAQ;GACrB,MAAM,UACJ,OAAO,WAAW;GACpB,MAAM,IAAI,MACR,mCAAmC,KAAK,IAAI,OAAO,MAAM,KAAK,QAAQ,GAAG,KAAK,UAAU,OAAO,OAAO,GACxG;EACF;EAEA,OAAO,OAAO;CAChB;AACF;AAEA,SAAS,aAA4B;CACnC,OAAO;EACL,gCAAgC,IAAI,QAAQ;EAC5C,uBAAuB,IAAI,QAAQ;CACrC;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logging.js","names":["
|
|
1
|
+
{"version":3,"file":"logging.js","names":["createLogContextShared"],"sources":["../../../../../zero-cache/src/server/logging.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {type Context, type LogLevel, type LogSink} from '@rocicorp/logger';\nimport {otelLogsEnabled} from '../../../otel/src/enabled.ts';\nimport {\n createLogContext as createLogContextShared,\n getLogSink,\n type LogConfig,\n} from '../../../shared/src/logging.ts';\nimport {logLastChanceSQLiteCorruptionDiagnostics} from '../db/sqlite-corruption.ts';\nimport {UNHANDLED_EXCEPTION_ERROR_CODE} from '../services/life-cycle.ts';\nimport {OtelLogSink} from './otel-log-sink.ts';\n\ntype UncaughtExceptionHandler = (\n err: Error,\n origin: NodeJS.UncaughtExceptionOrigin,\n) => Promise<void>;\n\nlet bootstrapUncaughtExceptionHandler: UncaughtExceptionHandler | undefined;\n\nexport function createLogContext(\n {log}: {log: LogConfig},\n worker: string,\n workerIndex = 0,\n includeOtel = true,\n): LogContext {\n const logSink = createLogSink(log, includeOtel);\n const lc = createLogContextShared({log}, {worker, workerIndex}, logSink);\n const handleUncaughtException: UncaughtExceptionHandler = async (\n err,\n origin,\n ) => {\n // Workers create an includeOtel=false context while bootstrapping OTel,\n // then a primary context. Run full corruption checks from the primary\n // handler only.\n try {\n await logUncaughtException(lc, logSink, err, origin, includeOtel);\n } finally {\n process.exit(UNHANDLED_EXCEPTION_ERROR_CODE);\n }\n };\n if (bootstrapUncaughtExceptionHandler) {\n process.off('uncaughtException', bootstrapUncaughtExceptionHandler);\n bootstrapUncaughtExceptionHandler = undefined;\n }\n process.on('uncaughtException', handleUncaughtException);\n if (!includeOtel) {\n bootstrapUncaughtExceptionHandler = handleUncaughtException;\n }\n return lc;\n}\n\nexport async function logUncaughtException(\n lc: LogContext,\n logSink: LogSink,\n err: unknown,\n origin: string,\n includeLastChanceDiagnostics = true,\n): Promise<void> {\n lc.error?.(origin, err);\n if (includeLastChanceDiagnostics) {\n try {\n logLastChanceSQLiteCorruptionDiagnostics(lc, err);\n } catch (diagnosticError) {\n lc.error?.('SQLite corruption last-chance diagnostic failed', {\n error: diagnosticError,\n });\n }\n }\n await logSink.flush?.();\n}\n\nfunction createLogSink(config: LogConfig, includeOtel: boolean): LogSink {\n const sink = getLogSink(config);\n if (includeOtel && otelLogsEnabled()) {\n const otelSink = new OtelLogSink();\n return new CompositeLogSink([otelSink, sink]);\n }\n return sink;\n}\n\nexport class CompositeLogSink implements LogSink {\n readonly #sinks: LogSink[];\n\n constructor(sinks: LogSink[]) {\n this.#sinks = sinks;\n }\n\n log(level: LogLevel, context: Context | undefined, ...args: unknown[]): void {\n for (const sink of this.#sinks) {\n sink.log(level, context, ...args);\n }\n }\n\n async flush(): Promise<void> {\n await Promise.all(this.#sinks.map(sink => sink.flush?.()));\n }\n}\n"],"mappings":";;;;;;;AAiBA,IAAI;AAEJ,SAAgB,iBACd,EAAC,OACD,QACA,cAAc,GACd,cAAc,MACF;CACZ,MAAM,UAAU,cAAc,KAAK,WAAW;CAC9C,MAAM,KAAKA,mBAAuB,EAAC,IAAG,GAAG;EAAC;EAAQ;CAAW,GAAG,OAAO;CACvE,MAAM,0BAAoD,OACxD,KACA,WACG;EAIH,IAAI;GACF,MAAM,qBAAqB,IAAI,SAAS,KAAK,QAAQ,WAAW;EAClE,UAAU;GACR,QAAQ,KAAA,EAAmC;EAC7C;CACF;CACA,IAAI,mCAAmC;EACrC,QAAQ,IAAI,qBAAqB,iCAAiC;EAClE,oCAAoC,KAAA;CACtC;CACA,QAAQ,GAAG,qBAAqB,uBAAuB;CACvD,IAAI,CAAC,aACH,oCAAoC;CAEtC,OAAO;AACT;AAEA,eAAsB,qBACpB,IACA,SACA,KACA,QACA,+BAA+B,MAChB;CACf,GAAG,QAAQ,QAAQ,GAAG;CACtB,IAAI,8BACF,IAAI;EACF,yCAAyC,IAAI,GAAG;CAClD,SAAS,iBAAiB;EACxB,GAAG,QAAQ,mDAAmD,EAC5D,OAAO,gBACT,CAAC;CACH;CAEF,MAAM,QAAQ,QAAQ;AACxB;AAEA,SAAS,cAAc,QAAmB,aAA+B;CACvE,MAAM,OAAO,WAAW,MAAM;CAC9B,IAAI,eAAe,gBAAgB,GAEjC,OAAO,IAAI,iBAAiB,CAAC,IADR,YACQ,GAAU,IAAI,CAAC;CAE9C,OAAO;AACT;AAEA,IAAa,mBAAb,MAAiD;CAC/C;CAEA,YAAY,OAAkB;EAC5B,KAAK,SAAS;CAChB;CAEA,IAAI,OAAiB,SAA8B,GAAG,MAAuB;EAC3E,KAAK,MAAM,QAAQ,KAAK,QACtB,KAAK,IAAI,OAAO,SAAS,GAAG,IAAI;CAEpC;CAEA,MAAM,QAAuB;EAC3B,MAAM,QAAQ,IAAI,KAAK,OAAO,KAAI,SAAQ,KAAK,QAAQ,CAAC,CAAC;CAC3D;AACF"}
|