@rocicorp/zero 1.2.0 → 1.3.0-canary.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/analyze-query/src/bin-analyze.js +25 -25
- package/out/analyze-query/src/bin-analyze.js.map +1 -1
- package/out/ast-to-zql/src/ast-to-zql.d.ts.map +1 -1
- package/out/ast-to-zql/src/ast-to-zql.js +2 -1
- package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
- package/out/replicache/src/btree/node.d.ts.map +1 -1
- package/out/replicache/src/btree/node.js +2 -2
- package/out/replicache/src/btree/node.js.map +1 -1
- package/out/replicache/src/connection-loop.js +3 -3
- package/out/replicache/src/connection-loop.js.map +1 -1
- package/out/replicache/src/deleted-clients.d.ts +0 -4
- package/out/replicache/src/deleted-clients.d.ts.map +1 -1
- package/out/replicache/src/deleted-clients.js +1 -1
- package/out/replicache/src/deleted-clients.js.map +1 -1
- package/out/replicache/src/hash.d.ts.map +1 -1
- package/out/replicache/src/hash.js.map +1 -1
- package/out/replicache/src/process-scheduler.d.ts.map +1 -1
- package/out/replicache/src/process-scheduler.js.map +1 -1
- package/out/replicache/src/request-idle.js +1 -1
- package/out/replicache/src/request-idle.js.map +1 -1
- package/out/replicache/src/sync/patch.d.ts +1 -1
- package/out/replicache/src/sync/patch.d.ts.map +1 -1
- package/out/replicache/src/sync/patch.js +1 -1
- package/out/replicache/src/sync/patch.js.map +1 -1
- package/out/shared/src/arrays.d.ts.map +1 -1
- package/out/shared/src/arrays.js +1 -2
- package/out/shared/src/arrays.js.map +1 -1
- package/out/shared/src/bigint-json.js +1 -1
- package/out/shared/src/bigint-json.js.map +1 -1
- package/out/shared/src/btree-set.js +1 -1
- package/out/shared/src/btree-set.js.map +1 -1
- package/out/shared/src/iterables.d.ts +7 -0
- package/out/shared/src/iterables.d.ts.map +1 -1
- package/out/shared/src/iterables.js +10 -1
- package/out/shared/src/iterables.js.map +1 -1
- package/out/shared/src/logging.d.ts.map +1 -1
- package/out/shared/src/logging.js +10 -9
- package/out/shared/src/logging.js.map +1 -1
- package/out/shared/src/options.js +1 -1
- package/out/shared/src/options.js.map +1 -1
- package/out/shared/src/sorted-entries.d.ts +2 -0
- package/out/shared/src/sorted-entries.d.ts.map +1 -0
- package/out/shared/src/sorted-entries.js +9 -0
- package/out/shared/src/sorted-entries.js.map +1 -0
- package/out/shared/src/tdigest-schema.d.ts.map +1 -1
- package/out/shared/src/tdigest-schema.js.map +1 -1
- package/out/shared/src/tdigest.d.ts.map +1 -1
- package/out/shared/src/tdigest.js +7 -7
- package/out/shared/src/tdigest.js.map +1 -1
- package/out/shared/src/valita.d.ts.map +1 -1
- package/out/shared/src/valita.js +1 -1
- package/out/shared/src/valita.js.map +1 -1
- package/out/z2s/src/sql.d.ts +2 -2
- package/out/z2s/src/sql.d.ts.map +1 -1
- package/out/z2s/src/sql.js +3 -3
- package/out/z2s/src/sql.js.map +1 -1
- package/out/zero/package.js +6 -7
- package/out/zero/package.js.map +1 -1
- package/out/zero/src/pg.js +1 -1
- package/out/zero/src/server.js +1 -1
- package/out/zero-cache/src/auth/auth.d.ts +8 -26
- package/out/zero-cache/src/auth/auth.d.ts.map +1 -1
- package/out/zero-cache/src/auth/auth.js +57 -82
- package/out/zero-cache/src/auth/auth.js.map +1 -1
- package/out/zero-cache/src/auth/jwt.d.ts +3 -3
- package/out/zero-cache/src/auth/jwt.d.ts.map +1 -1
- package/out/zero-cache/src/auth/jwt.js.map +1 -1
- package/out/zero-cache/src/auth/load-permissions.js +1 -1
- package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +38 -2
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +56 -1
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/custom/fetch.d.ts +2 -9
- package/out/zero-cache/src/custom/fetch.d.ts.map +1 -1
- package/out/zero-cache/src/custom/fetch.js +10 -4
- package/out/zero-cache/src/custom/fetch.js.map +1 -1
- package/out/zero-cache/src/custom-queries/transform-query.d.ts +20 -9
- package/out/zero-cache/src/custom-queries/transform-query.d.ts.map +1 -1
- package/out/zero-cache/src/custom-queries/transform-query.js +74 -37
- package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
- package/out/zero-cache/src/db/migration-lite.d.ts.map +1 -1
- package/out/zero-cache/src/db/migration-lite.js +1 -1
- package/out/zero-cache/src/db/migration-lite.js.map +1 -1
- package/out/zero-cache/src/db/migration.d.ts.map +1 -1
- package/out/zero-cache/src/db/migration.js +1 -1
- package/out/zero-cache/src/db/migration.js.map +1 -1
- package/out/zero-cache/src/db/pg-copy-binary.d.ts +101 -0
- package/out/zero-cache/src/db/pg-copy-binary.d.ts.map +1 -0
- package/out/zero-cache/src/db/pg-copy-binary.js +381 -0
- package/out/zero-cache/src/db/pg-copy-binary.js.map +1 -0
- package/out/zero-cache/src/db/transaction-pool.d.ts.map +1 -1
- package/out/zero-cache/src/db/transaction-pool.js +3 -0
- package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
- package/out/zero-cache/src/db/warmup.d.ts.map +1 -1
- package/out/zero-cache/src/db/warmup.js +3 -1
- package/out/zero-cache/src/db/warmup.js.map +1 -1
- package/out/zero-cache/src/server/anonymous-otel-start.d.ts.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.d.ts.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +5 -2
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/inspector-delegate.d.ts +2 -2
- package/out/zero-cache/src/server/inspector-delegate.d.ts.map +1 -1
- package/out/zero-cache/src/server/inspector-delegate.js +4 -4
- package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
- package/out/zero-cache/src/server/main.js +1 -1
- package/out/zero-cache/src/server/main.js.map +1 -1
- package/out/zero-cache/src/server/reaper.d.ts.map +1 -1
- package/out/zero-cache/src/server/reaper.js +4 -1
- package/out/zero-cache/src/server/reaper.js.map +1 -1
- package/out/zero-cache/src/server/runner/run-worker.js +1 -1
- package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/server/syncer.js +41 -20
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/server/worker-urls.d.ts.map +1 -1
- package/out/zero-cache/src/server/worker-urls.js +2 -1
- package/out/zero-cache/src/server/worker-urls.js.map +1 -1
- package/out/zero-cache/src/services/change-source/change-source.d.ts +4 -0
- package/out/zero-cache/src/services/change-source/change-source.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js +3 -2
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +5 -2
- package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.js +13 -4
- package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +3 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js +91 -9
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js +2 -2
- package/out/zero-cache/src/services/change-source/pg/schema/shard.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-streamer-service.js +3 -0
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
- package/out/zero-cache/src/services/life-cycle.d.ts +5 -4
- package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
- package/out/zero-cache/src/services/life-cycle.js +11 -11
- package/out/zero-cache/src/services/life-cycle.js.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.js +5 -5
- package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.d.ts +20 -20
- package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.js +91 -104
- package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.js +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
- package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-schema.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-schema.js +4 -3
- 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.d.ts +168 -0
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.js +385 -0
- package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/cvr-store.js +2 -2
- package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js +5 -4
- package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts +2 -3
- package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js +3 -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.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +5 -3
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/row-record-cache.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/row-record-cache.js +13 -7
- package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +3 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js +6 -9
- package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +24 -26
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +236 -124
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/lite.d.ts.map +1 -1
- package/out/zero-cache/src/types/lite.js +3 -2
- package/out/zero-cache/src/types/lite.js.map +1 -1
- package/out/zero-cache/src/types/pg-types.js +4 -1
- package/out/zero-cache/src/types/pg-types.js.map +1 -1
- package/out/zero-cache/src/types/pg-versions.d.ts +3 -0
- package/out/zero-cache/src/types/pg-versions.d.ts.map +1 -0
- package/out/zero-cache/src/types/pg-versions.js +7 -0
- package/out/zero-cache/src/types/pg-versions.js.map +1 -0
- package/out/zero-cache/src/types/pg.d.ts.map +1 -1
- package/out/zero-cache/src/types/pg.js +6 -1
- package/out/zero-cache/src/types/pg.js.map +1 -1
- package/out/zero-cache/src/types/subscription.d.ts.map +1 -1
- package/out/zero-cache/src/types/subscription.js +2 -2
- package/out/zero-cache/src/types/subscription.js.map +1 -1
- package/out/zero-cache/src/workers/connect-params.d.ts +1 -1
- package/out/zero-cache/src/workers/connect-params.d.ts.map +1 -1
- package/out/zero-cache/src/workers/connect-params.js +1 -1
- package/out/zero-cache/src/workers/connect-params.js.map +1 -1
- package/out/zero-cache/src/workers/connection.js +2 -2
- package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts +2 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js +64 -38
- package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.d.ts +2 -1
- package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/workers/syncer.js +70 -31
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/connection.d.ts +4 -4
- package/out/zero-client/src/client/connection.d.ts.map +1 -1
- package/out/zero-client/src/client/connection.js.map +1 -1
- package/out/zero-client/src/client/http-string.d.ts.map +1 -1
- package/out/zero-client/src/client/http-string.js.map +1 -1
- package/out/zero-client/src/client/metrics.d.ts.map +1 -1
- package/out/zero-client/src/client/metrics.js +2 -1
- package/out/zero-client/src/client/metrics.js.map +1 -1
- package/out/zero-client/src/client/options.d.ts +30 -5
- package/out/zero-client/src/client/options.d.ts.map +1 -1
- package/out/zero-client/src/client/options.js.map +1 -1
- package/out/zero-client/src/client/server-option.js +1 -1
- package/out/zero-client/src/client/server-option.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero-poke-handler.d.ts.map +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.d.ts +4 -3
- package/out/zero-client/src/client/zero.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.js +33 -11
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-pg/src/mod.js +1 -1
- package/out/zero-protocol/src/ast.d.ts.map +1 -1
- package/out/zero-protocol/src/ast.js.map +1 -1
- package/out/zero-protocol/src/change-desired-queries.d.ts +4 -0
- package/out/zero-protocol/src/change-desired-queries.d.ts.map +1 -1
- package/out/zero-protocol/src/change-desired-queries.js +4 -1
- package/out/zero-protocol/src/change-desired-queries.js.map +1 -1
- package/out/zero-protocol/src/connect.d.ts +4 -0
- package/out/zero-protocol/src/connect.d.ts.map +1 -1
- package/out/zero-protocol/src/connect.js +2 -1
- package/out/zero-protocol/src/connect.js.map +1 -1
- package/out/zero-protocol/src/primary-key.d.ts.map +1 -1
- package/out/zero-protocol/src/primary-key.js.map +1 -1
- package/out/zero-protocol/src/protocol-version.d.ts +1 -1
- package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
- package/out/zero-protocol/src/protocol-version.js.map +1 -1
- package/out/zero-protocol/src/push.d.ts +4 -0
- package/out/zero-protocol/src/push.d.ts.map +1 -1
- package/out/zero-protocol/src/push.js +2 -1
- package/out/zero-protocol/src/push.js.map +1 -1
- package/out/zero-protocol/src/up.d.ts +3 -0
- package/out/zero-protocol/src/up.d.ts.map +1 -1
- package/out/zero-react/src/zero-provider.d.ts.map +1 -1
- package/out/zero-react/src/zero-provider.js +11 -5
- package/out/zero-react/src/zero-provider.js.map +1 -1
- package/out/zero-schema/src/name-mapper.js +1 -1
- package/out/zero-schema/src/name-mapper.js.map +1 -1
- package/out/zero-server/src/mod.js +1 -1
- package/out/zero-server/src/process-mutations.d.ts.map +1 -1
- package/out/zero-server/src/process-mutations.js +2 -1
- package/out/zero-server/src/process-mutations.js.map +1 -1
- package/out/zero-server/src/push-processor.d.ts +1 -0
- package/out/zero-server/src/push-processor.d.ts.map +1 -1
- package/out/zero-server/src/push-processor.js +3 -2
- package/out/zero-server/src/push-processor.js.map +1 -1
- package/out/zero-solid/src/use-zero.d.ts.map +1 -1
- package/out/zero-solid/src/use-zero.js +8 -9
- package/out/zero-solid/src/use-zero.js.map +1 -1
- package/out/zql/src/builder/like.js +2 -1
- package/out/zql/src/builder/like.js.map +1 -1
- package/out/zql/src/ivm/data.d.ts.map +1 -1
- package/out/zql/src/ivm/data.js +6 -15
- package/out/zql/src/ivm/data.js.map +1 -1
- package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
- package/out/zql/src/ivm/memory-source.js +14 -8
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/query/complete-ordering.js +1 -1
- package/out/zql/src/query/complete-ordering.js.map +1 -1
- package/out/zql/src/query/query-impl.d.ts.map +1 -1
- package/out/zql/src/query/query-impl.js +2 -2
- package/out/zql/src/query/query-impl.js.map +1 -1
- package/out/zql/src/query/query-registry.d.ts.map +1 -1
- package/out/zql/src/query/query-registry.js +2 -1
- package/out/zql/src/query/query-registry.js.map +1 -1
- package/out/zql/src/query/ttl.js +1 -1
- package/out/zql/src/query/ttl.js.map +1 -1
- package/out/zqlite/src/internal/sql.d.ts +2 -2
- package/out/zqlite/src/internal/sql.d.ts.map +1 -1
- package/out/zqlite/src/internal/sql.js +1 -2
- package/out/zqlite/src/internal/sql.js.map +1 -1
- package/out/zqlite/src/sqlite-cost-model.d.ts +1 -1
- package/out/zqlite/src/sqlite-cost-model.d.ts.map +1 -1
- package/out/zqlite/src/sqlite-cost-model.js +1 -1
- package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
- package/out/zqlite/src/sqlite-stat-fanout.js +1 -1
- package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
- package/out/zqlite/src/table-source.d.ts.map +1 -1
- package/out/zqlite/src/table-source.js +8 -12
- package/out/zqlite/src/table-source.js.map +1 -1
- package/package.json +6 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"warmup.js","names":[],"sources":["../../../../../zero-cache/src/db/warmup.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {PostgresDB} from '../types/pg.ts';\n\nexport async function warmupConnections(\n lc: LogContext,\n db: PostgresDB,\n name: string,\n) {\n const {
|
|
1
|
+
{"version":3,"file":"warmup.js","names":[],"sources":["../../../../../zero-cache/src/db/warmup.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {PostgresDB} from '../types/pg.ts';\n\nconst MAX_WARMUP_CONNECTIONS = 5;\n\nexport async function warmupConnections(\n lc: LogContext,\n db: PostgresDB,\n name: string,\n) {\n const {host} = db.options;\n const max = Math.min(db.options.max, MAX_WARMUP_CONNECTIONS);\n await Promise.allSettled(\n Array.from({length: max}, () => db`SELECT 1`.simple().execute()),\n );\n const start = performance.now();\n const pingTimes = await Promise.all(\n Array.from({length: Math.min(max, 5)}, () =>\n db`SELECT 2`.simple().then(\n () => performance.now() - start,\n () => performance.now() - start,\n ),\n ),\n );\n const average = pingTimes.reduce((l, r) => l + r, 0) / pingTimes.length;\n const log = average >= 10 ? 'warn' : 'info';\n lc[log]?.(`average ping to ${name} db@${host}: ${average.toFixed(2)} ms`);\n if (log === 'warn') {\n lc.warn?.(`ideal db ping time is < 5 ms`);\n }\n}\n"],"mappings":";AAGA,IAAM,yBAAyB;AAE/B,eAAsB,kBACpB,IACA,IACA,MACA;CACA,MAAM,EAAC,SAAQ,GAAG;CAClB,MAAM,MAAM,KAAK,IAAI,GAAG,QAAQ,KAAK,uBAAuB;AAC5D,OAAM,QAAQ,WACZ,MAAM,KAAK,EAAC,QAAQ,KAAI,QAAQ,EAAE,WAAW,QAAQ,CAAC,SAAS,CAAC,CACjE;CACD,MAAM,QAAQ,YAAY,KAAK;CAC/B,MAAM,YAAY,MAAM,QAAQ,IAC9B,MAAM,KAAK,EAAC,QAAQ,KAAK,IAAI,KAAK,EAAE,EAAC,QACnC,EAAE,WAAW,QAAQ,CAAC,WACd,YAAY,KAAK,GAAG,aACpB,YAAY,KAAK,GAAG,MAC3B,CACF,CACF;CACD,MAAM,UAAU,UAAU,QAAQ,GAAG,MAAM,IAAI,GAAG,EAAE,GAAG,UAAU;CACjE,MAAM,MAAM,WAAW,KAAK,SAAS;AACrC,IAAG,OAAO,mBAAmB,KAAK,MAAM,KAAK,IAAI,QAAQ,QAAQ,EAAE,CAAC,KAAK;AACzE,KAAI,QAAQ,OACV,IAAG,OAAO,+BAA+B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anonymous-otel-start.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/anonymous-otel-start.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAOjD,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,0BAA0B,CAAC;AAGlC,MAAM,MAAM,WAAW,GAAG;IACxB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;
|
|
1
|
+
{"version":3,"file":"anonymous-otel-start.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/anonymous-otel-start.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAOjD,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,0BAA0B,CAAC;AAGlC,MAAM,MAAM,WAAW,GAAG;IACxB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAkfF,eAAO,MAAM,uBAAuB,GAAI,KAAK,UAAU,EAAE,SAAS,UAAU,SAC/C,CAAC;AAC9B,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,GAAG,QAAQ,EAAE,cAAS,SAC1B,CAAC;AACxC,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,GAAG,QAAQ,EAAE,cAAS,SAC1B,CAAC;AACrC,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,SACX,CAAC;AACpC,eAAO,MAAM,uBAAuB,YACC,CAAC;AACtC,eAAO,MAAM,yBAAyB,YACC,CAAC;AACxC,eAAO,MAAM,2BAA2B,GAAI,QAAQ,MAAM,MAAM,SACjB,CAAC;AAChD,eAAO,MAAM,oBAAoB,GAAI,QAAQ,MAAM,WAAW,SACtB,CAAC;AACzC,eAAO,MAAM,0BAA0B,YAA6B,CAAC"}
|
|
@@ -11,6 +11,7 @@ import { execSync } from "child_process";
|
|
|
11
11
|
import { randomUUID } from "crypto";
|
|
12
12
|
import { dirname, join } from "path";
|
|
13
13
|
//#region ../zero-cache/src/server/anonymous-otel-start.ts
|
|
14
|
+
var hostNameRe = /^[a-f0-9]{12}$/;
|
|
14
15
|
var AnonymousTelemetryManager = class AnonymousTelemetryManager {
|
|
15
16
|
static #instance;
|
|
16
17
|
#starting = false;
|
|
@@ -277,7 +278,7 @@ var AnonymousTelemetryManager = class AnonymousTelemetryManager {
|
|
|
277
278
|
if (existsSync("/.dockerenv")) return true;
|
|
278
279
|
if (existsSync("/usr/local/bin/docker-entrypoint.sh")) return true;
|
|
279
280
|
if (process.env.KUBERNETES_SERVICE_HOST) return true;
|
|
280
|
-
if (process.env.DOCKER_CONTAINER_ID || process.env.HOSTNAME?.match(
|
|
281
|
+
if (process.env.DOCKER_CONTAINER_ID || process.env.HOSTNAME?.match(hostNameRe)) return true;
|
|
281
282
|
if (existsSync("/proc/1/cgroup")) {
|
|
282
283
|
const cgroup = readFileSync("/proc/1/cgroup", "utf8");
|
|
283
284
|
if (cgroup.includes("docker") || cgroup.includes("kubepods") || cgroup.includes("containerd")) return true;
|
|
@@ -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 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 {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 {h64} from '../../../shared/src/hash.js';\nimport {\n getServerVersion,\n getZeroConfig,\n type ZeroConfig,\n} from '../config/zero-config.js';\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\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(/^[a-f0-9]{12}$/)\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":["#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 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 {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 {h64} from '../../../shared/src/hash.js';\nimport {\n getServerVersion,\n getZeroConfig,\n type ZeroConfig,\n} from '../config/zero-config.js';\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.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/change-streamer.ts"],"names":[],"mappings":"AA0BA,OAAO,EAGL,KAAK,MAAM,EACZ,MAAM,uBAAuB,CAAC;AAK/B,wBAA8B,SAAS,CACrC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,GAAG,IAAI,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"change-streamer.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/change-streamer.ts"],"names":[],"mappings":"AA0BA,OAAO,EAGL,KAAK,MAAM,EACZ,MAAM,uBAAuB,CAAC;AAK/B,wBAA8B,SAAS,CACrC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,GAAG,IAAI,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,IAAI,CAAC,CAyJf"}
|
|
@@ -37,13 +37,16 @@ async function runWorker(parent, env, ...args) {
|
|
|
37
37
|
max: change.maxConns,
|
|
38
38
|
connection: { ["application_name"]: CHANGE_STREAMER_APP_NAME }
|
|
39
39
|
}, { sendStringAsJson: true });
|
|
40
|
-
warmupConnections(lc, changeDB, "change");
|
|
40
|
+
warmupConnections(lc, changeDB, "change").catch(() => {});
|
|
41
41
|
const { autoReset, replicationLag } = config;
|
|
42
42
|
const shard = getShardConfig(config);
|
|
43
43
|
let changeStreamer;
|
|
44
44
|
const context = getServerContext(config);
|
|
45
45
|
for (const first of [true, false]) try {
|
|
46
|
-
const { changeSource, subscriptionState } = upstream.type === "pg" ? await initializePostgresChangeSource(lc, upstream.db, shard, replica.file,
|
|
46
|
+
const { changeSource, subscriptionState } = upstream.type === "pg" ? await initializePostgresChangeSource(lc, upstream.db, shard, replica.file, {
|
|
47
|
+
...initialSync,
|
|
48
|
+
replicationSlotFailover: upstream.pgReplicationSlotFailover
|
|
49
|
+
}, context, replicationLag.reportIntervalMs) : await initializeCustomChangeSource(lc, upstream.db, shard, replica.file, context);
|
|
47
50
|
changeStreamer = await initializeStreamer(lc, shard, taskID, address, protocol, changeDB, changeSource, ReplicationStatusPublisher.forReplicaFile(replica.file), subscriptionState, autoReset ?? false, backPressureLimitHeapProportion, flowControlConsensusPaddingSeconds, setTimeout);
|
|
48
51
|
break;
|
|
49
52
|
} catch (e) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"change-streamer.js","names":[],"sources":["../../../../../zero-cache/src/server/change-streamer.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.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 {warmupConnections} from '../db/warmup.ts';\nimport {initEventSink, publishCriticalEvent} from '../observability/events.ts';\nimport {upgradeReplica} from '../services/change-source/common/replica-schema.ts';\nimport {initializeCustomChangeSource} from '../services/change-source/custom/change-source.ts';\nimport {initializePostgresChangeSource} from '../services/change-source/pg/change-source.ts';\nimport {BackupMonitor} from '../services/change-streamer/backup-monitor.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 {ReplicaMonitor} from '../services/change-streamer/replica-monitor.ts';\nimport {\n AutoResetSignal,\n CHANGE_STREAMER_APP_NAME,\n} from '../services/change-streamer/schema/tables.ts';\nimport {exitAfter, runUntilKilled} from '../services/life-cycle.ts';\nimport {\n replicationStatusError,\n ReplicationStatusPublisher,\n} from '../services/replicator/replication-status.ts';\nimport {pgClient} from '../types/pg.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {getShardConfig} from '../types/shards.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...args: string[]\n): Promise<void> {\n assert(args.length > 0, `parent startMs not specified`);\n const parentStartMs = parseInt(args[0]);\n\n const config = getNormalizedZeroConfig({env, argv: args.slice(1)});\n const {\n taskID,\n changeStreamer: {\n port,\n address,\n protocol,\n startupDelayMs,\n backPressureLimitHeapProportion,\n flowControlConsensusPaddingSeconds,\n },\n upstream,\n change,\n replica,\n initialSync,\n litestream,\n } = config;\n\n startOtelAuto(createLogContext(config, {worker: 'change-streamer'}, false));\n const lc = createLogContext(config, {worker: 'change-streamer'}, true);\n initEventSink(lc, config);\n\n // Kick off DB connection warmup in the background.\n const changeDB = pgClient(\n lc,\n change.db,\n {\n max: change.maxConns,\n connection: {['application_name']: CHANGE_STREAMER_APP_NAME},\n },\n {sendStringAsJson: true},\n );\n void warmupConnections(lc, changeDB, 'change');\n\n const {autoReset, replicationLag} = config;\n const shard = getShardConfig(config);\n\n let changeStreamer: ChangeStreamerService | undefined;\n\n const context = getServerContext(config);\n\n for (const first of [true, false]) {\n try {\n // Note: This performs initial sync of the replica if necessary.\n const {changeSource, subscriptionState} =\n upstream.type === 'pg'\n ? await initializePostgresChangeSource(\n lc,\n upstream.db,\n shard,\n replica.file,\n initialSync,\n context,\n replicationLag.reportIntervalMs,\n )\n : await initializeCustomChangeSource(\n lc,\n upstream.db,\n shard,\n replica.file,\n context,\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 autoReset ?? false,\n backPressureLimitHeapProportion,\n flowControlConsensusPaddingSeconds,\n setTimeout,\n );\n break;\n } catch (e) {\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 continue; // execute again with a fresh initial-sync\n }\n await publishCriticalEvent(\n lc,\n replicationStatusError(lc, 'Initializing', e),\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 // Perform any upgrades to the replica in case it was restored from an\n // earlier version. Note that this upgrade is done by the replicator worker\n // as well (in both the replication-manager and the view-syncer), but the\n // change-streamer independently reads the replica, and it is fine run the\n // upgrade logic redundantly since it is idempotent.\n await upgradeReplica(lc, 'change-streamer-init', replica.file);\n\n const {backupURL, port: metricsPort} = litestream;\n const monitor = backupURL\n ? new BackupMonitor(\n lc,\n replica.file,\n backupURL,\n `http://localhost:${metricsPort}/metrics`,\n changeStreamer,\n // The time between when the zero-cache was started to when the\n // change-streamer is ready to start serves as the initial delay for\n // watermark cleanup (as it either includes a similar replica\n // restoration/preparation step, or an initial-sync, which\n // generally takes longer).\n //\n // Consider: Also account for permanent volumes?\n Date.now() - parentStartMs,\n )\n : new ReplicaMonitor(lc, replica.file, changeStreamer);\n\n const changeStreamerWebServer = new ChangeStreamerHttpServer(\n lc,\n config,\n {port, startupDelayMs},\n parent,\n changeStreamer,\n monitor instanceof BackupMonitor ? monitor : null,\n );\n\n parent.send(['ready', {ready: true}]);\n\n // Note: The changeStreamer itself is not started here; it is started by the\n // changeStreamerWebServer.\n return runUntilKilled(lc, parent, changeStreamerWebServer, monitor);\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(() =>\n runWorker(must(parentWorker), process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAmCA,eAA8B,UAC5B,QACA,KACA,GAAG,MACY;AACf,QAAO,KAAK,SAAS,GAAG,+BAA+B;CACvD,MAAM,gBAAgB,SAAS,KAAK,GAAG;CAEvC,MAAM,SAAS,wBAAwB;EAAC;EAAK,MAAM,KAAK,MAAM,EAAE;EAAC,CAAC;CAClE,MAAM,EACJ,QACA,gBAAgB,EACd,MACA,SACA,UACA,gBACA,iCACA,sCAEF,UACA,QACA,SACA,aACA,eACE;AAEJ,eAAc,iBAAiB,QAAQ,EAAC,QAAQ,mBAAkB,EAAE,MAAM,CAAC;CAC3E,MAAM,KAAK,iBAAiB,QAAQ,EAAC,QAAQ,mBAAkB,EAAE,KAAK;AACtE,eAAc,IAAI,OAAO;CAGzB,MAAM,WAAW,SACf,IACA,OAAO,IACP;EACE,KAAK,OAAO;EACZ,YAAY,GAAE,qBAAqB,0BAAyB;EAC7D,EACD,EAAC,kBAAkB,MAAK,CACzB;AACI,mBAAkB,IAAI,UAAU,SAAS;
|
|
1
|
+
{"version":3,"file":"change-streamer.js","names":[],"sources":["../../../../../zero-cache/src/server/change-streamer.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.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 {warmupConnections} from '../db/warmup.ts';\nimport {initEventSink, publishCriticalEvent} from '../observability/events.ts';\nimport {upgradeReplica} from '../services/change-source/common/replica-schema.ts';\nimport {initializeCustomChangeSource} from '../services/change-source/custom/change-source.ts';\nimport {initializePostgresChangeSource} from '../services/change-source/pg/change-source.ts';\nimport {BackupMonitor} from '../services/change-streamer/backup-monitor.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 {ReplicaMonitor} from '../services/change-streamer/replica-monitor.ts';\nimport {\n AutoResetSignal,\n CHANGE_STREAMER_APP_NAME,\n} from '../services/change-streamer/schema/tables.ts';\nimport {exitAfter, runUntilKilled} from '../services/life-cycle.ts';\nimport {\n replicationStatusError,\n ReplicationStatusPublisher,\n} from '../services/replicator/replication-status.ts';\nimport {pgClient} from '../types/pg.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {getShardConfig} from '../types/shards.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...args: string[]\n): Promise<void> {\n assert(args.length > 0, `parent startMs not specified`);\n const parentStartMs = parseInt(args[0]);\n\n const config = getNormalizedZeroConfig({env, argv: args.slice(1)});\n const {\n taskID,\n changeStreamer: {\n port,\n address,\n protocol,\n startupDelayMs,\n backPressureLimitHeapProportion,\n flowControlConsensusPaddingSeconds,\n },\n upstream,\n change,\n replica,\n initialSync,\n litestream,\n } = config;\n\n startOtelAuto(createLogContext(config, {worker: 'change-streamer'}, false));\n const lc = createLogContext(config, {worker: 'change-streamer'}, true);\n initEventSink(lc, config);\n\n // Kick off DB connection warmup in the background.\n const changeDB = pgClient(\n lc,\n change.db,\n {\n max: change.maxConns,\n connection: {['application_name']: CHANGE_STREAMER_APP_NAME},\n },\n {sendStringAsJson: true},\n );\n void warmupConnections(lc, changeDB, 'change').catch(() => {});\n\n const {autoReset, replicationLag} = config;\n const shard = getShardConfig(config);\n\n let changeStreamer: ChangeStreamerService | undefined;\n\n const context = getServerContext(config);\n\n for (const first of [true, false]) {\n try {\n // Note: This performs initial sync of the replica if necessary.\n const {changeSource, subscriptionState} =\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 },\n context,\n replicationLag.reportIntervalMs,\n )\n : await initializeCustomChangeSource(\n lc,\n upstream.db,\n shard,\n replica.file,\n context,\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 autoReset ?? false,\n backPressureLimitHeapProportion,\n flowControlConsensusPaddingSeconds,\n setTimeout,\n );\n break;\n } catch (e) {\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 continue; // execute again with a fresh initial-sync\n }\n await publishCriticalEvent(\n lc,\n replicationStatusError(lc, 'Initializing', e),\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 // Perform any upgrades to the replica in case it was restored from an\n // earlier version. Note that this upgrade is done by the replicator worker\n // as well (in both the replication-manager and the view-syncer), but the\n // change-streamer independently reads the replica, and it is fine run the\n // upgrade logic redundantly since it is idempotent.\n await upgradeReplica(lc, 'change-streamer-init', replica.file);\n\n const {backupURL, port: metricsPort} = litestream;\n const monitor = backupURL\n ? new BackupMonitor(\n lc,\n replica.file,\n backupURL,\n `http://localhost:${metricsPort}/metrics`,\n changeStreamer,\n // The time between when the zero-cache was started to when the\n // change-streamer is ready to start serves as the initial delay for\n // watermark cleanup (as it either includes a similar replica\n // restoration/preparation step, or an initial-sync, which\n // generally takes longer).\n //\n // Consider: Also account for permanent volumes?\n Date.now() - parentStartMs,\n )\n : new ReplicaMonitor(lc, replica.file, changeStreamer);\n\n const changeStreamerWebServer = new ChangeStreamerHttpServer(\n lc,\n config,\n {port, startupDelayMs},\n parent,\n changeStreamer,\n monitor instanceof BackupMonitor ? monitor : null,\n );\n\n parent.send(['ready', {ready: true}]);\n\n // Note: The changeStreamer itself is not started here; it is started by the\n // changeStreamerWebServer.\n return runUntilKilled(lc, parent, changeStreamerWebServer, monitor);\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(() =>\n runWorker(must(parentWorker), process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAmCA,eAA8B,UAC5B,QACA,KACA,GAAG,MACY;AACf,QAAO,KAAK,SAAS,GAAG,+BAA+B;CACvD,MAAM,gBAAgB,SAAS,KAAK,GAAG;CAEvC,MAAM,SAAS,wBAAwB;EAAC;EAAK,MAAM,KAAK,MAAM,EAAE;EAAC,CAAC;CAClE,MAAM,EACJ,QACA,gBAAgB,EACd,MACA,SACA,UACA,gBACA,iCACA,sCAEF,UACA,QACA,SACA,aACA,eACE;AAEJ,eAAc,iBAAiB,QAAQ,EAAC,QAAQ,mBAAkB,EAAE,MAAM,CAAC;CAC3E,MAAM,KAAK,iBAAiB,QAAQ,EAAC,QAAQ,mBAAkB,EAAE,KAAK;AACtE,eAAc,IAAI,OAAO;CAGzB,MAAM,WAAW,SACf,IACA,OAAO,IACP;EACE,KAAK,OAAO;EACZ,YAAY,GAAE,qBAAqB,0BAAyB;EAC7D,EACD,EAAC,kBAAkB,MAAK,CACzB;AACI,mBAAkB,IAAI,UAAU,SAAS,CAAC,YAAY,GAAG;CAE9D,MAAM,EAAC,WAAW,mBAAkB;CACpC,MAAM,QAAQ,eAAe,OAAO;CAEpC,IAAI;CAEJ,MAAM,UAAU,iBAAiB,OAAO;AAExC,MAAK,MAAM,SAAS,CAAC,MAAM,MAAM,CAC/B,KAAI;EAEF,MAAM,EAAC,cAAc,sBACnB,SAAS,SAAS,OACd,MAAM,+BACJ,IACA,SAAS,IACT,OACA,QAAQ,MACR;GACE,GAAG;GACH,yBAAyB,SAAS;GACnC,EACD,SACA,eAAe,iBAChB,GACD,MAAM,6BACJ,IACA,SAAS,IACT,OACA,QAAQ,MACR,QACD;AAKP,mBAAiB,MAAM,mBACrB,IACA,OACA,QACA,SACA,UACA,UACA,cATA,2BAA2B,eAAe,QAAQ,KAAK,EAWvD,mBACA,aAAa,OACb,iCACA,oCACA,WACD;AACD;UACO,GAAG;AACV,MAAI,SAAS,aAAa,iBAAiB;AACzC,MAAG,OAAO,qBAAqB,QAAQ,QAAQ,EAAE;AAGjD,gBAAa,QAAQ,KAAK;AAC1B;;AAEF,QAAM,qBACJ,IACA,uBAAuB,IAAI,gBAAgB,EAAE,CAC9C;AACD,MAAI,aAAa,kBACf,OAAM,IAAI,MACR,qCAAqC,QAAQ,KAAK,0CAClD,EAAC,OAAO,GAAE,CACX;AAEH,QAAM;;AAIV,QAAO,gBAAgB,mDAAmD;AAO1E,OAAM,eAAe,IAAI,wBAAwB,QAAQ,KAAK;CAE9D,MAAM,EAAC,WAAW,MAAM,gBAAe;CACvC,MAAM,UAAU,YACZ,IAAI,cACF,IACA,QAAQ,MACR,WACA,oBAAoB,YAAY,WAChC,gBAQA,KAAK,KAAK,GAAG,cACd,GACD,IAAI,eAAe,IAAI,QAAQ,MAAM,eAAe;CAExD,MAAM,0BAA0B,IAAI,yBAClC,IACA,QACA;EAAC;EAAM;EAAe,EACtB,QACA,gBACA,mBAAmB,gBAAgB,UAAU,KAC9C;AAED,QAAO,KAAK,CAAC,SAAS,EAAC,OAAO,MAAK,CAAC,CAAC;AAIrC,QAAO,eAAe,IAAI,QAAQ,yBAAyB,QAAQ;;AAIrE,IAAI,CAAC,mBAAmB,CACjB,iBACH,UAAU,KAAK,aAAa,EAAE,QAAQ,KAAK,GAAG,QAAQ,KAAK,MAAM,EAAE,CAAC,CACrE"}
|
|
@@ -4,7 +4,7 @@ import type { AST } from '../../../zero-protocol/src/ast.ts';
|
|
|
4
4
|
import type { ServerMetrics as ServerMetricsJSON } from '../../../zero-protocol/src/inspect-down.ts';
|
|
5
5
|
import { type MetricMap, type MetricsDelegate } from '../../../zql/src/query/metrics-delegate.ts';
|
|
6
6
|
import type { CustomQueryTransformer } from '../custom-queries/transform-query.ts';
|
|
7
|
-
import type {
|
|
7
|
+
import type { ConnectionContext } from '../services/view-syncer/connection-context-manager.ts';
|
|
8
8
|
/**
|
|
9
9
|
* Server-side metrics collected for queries during materialization and update.
|
|
10
10
|
* These metrics are reported via the inspector and complement client-side metrics.
|
|
@@ -38,7 +38,7 @@ export declare class InspectorDelegate implements MetricsDelegate {
|
|
|
38
38
|
* CustomQueryTransformer. This is primarily used by the inspector to transform
|
|
39
39
|
* queries for analysis.
|
|
40
40
|
*/
|
|
41
|
-
transformCustomQuery(name: string, args: readonly ReadonlyJSONValue[],
|
|
41
|
+
transformCustomQuery(name: string, args: readonly ReadonlyJSONValue[], ctx: ConnectionContext): Promise<AST>;
|
|
42
42
|
}
|
|
43
43
|
export {};
|
|
44
44
|
//# sourceMappingURL=inspector-delegate.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inspector-delegate.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/inspector-delegate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;AAEnE,OAAO,EAAC,OAAO,EAAC,MAAM,gCAAgC,CAAC;AACvD,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,mCAAmC,CAAC;AAC3D,OAAO,KAAK,EAAC,aAAa,IAAI,iBAAiB,EAAC,MAAM,4CAA4C,CAAC;AAEnG,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,eAAe,EACrB,MAAM,4CAA4C,CAAC;AAEpD,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,sCAAsC,CAAC;AACjF,OAAO,KAAK,EAAC,
|
|
1
|
+
{"version":3,"file":"inspector-delegate.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/inspector-delegate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;AAEnE,OAAO,EAAC,OAAO,EAAC,MAAM,gCAAgC,CAAC;AACvD,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,mCAAmC,CAAC;AAC3D,OAAO,KAAK,EAAC,aAAa,IAAI,iBAAiB,EAAC,MAAM,4CAA4C,CAAC;AAEnG,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,eAAe,EACrB,MAAM,4CAA4C,CAAC;AAEpD,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,sCAAsC,CAAC;AACjF,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,uDAAuD,CAAC;AAI7F;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,8BAA8B,EAAE,OAAO,CAAC;IACxC,qBAAqB,EAAE,OAAO,CAAC;CAChC,CAAC;AAEF,KAAK,aAAa,GAAG,MAAM,CAAC;AAQ5B,qBAAa,iBAAkB,YAAW,eAAe;;gBAM3C,sBAAsB,EAAE,sBAAsB,GAAG,SAAS;IAItE,SAAS,CAAC,CAAC,SAAS,MAAM,SAAS,EACjC,MAAM,EAAE,CAAC,EACT,KAAK,EAAE,MAAM,EACb,GAAG,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,GACpB,IAAI;IAYP,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;IAKjE,cAAc;;;;IAId,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS;IAIhD,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAKlC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI;IAIzC;;;OAGG;IACH,eAAe,CAAC,aAAa,EAAE,aAAa,GAAG,OAAO;IAMtD,gBAAgB,CAAC,aAAa,EAAE,aAAa,GAAG,IAAI;IAIpD,kBAAkB,CAAC,aAAa,EAAE,aAAa;IAI/C;;;;OAIG;IACG,oBAAoB,CACxB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,SAAS,iBAAiB,EAAE,EAClC,GAAG,EAAE,iBAAiB,GACrB,OAAO,CAAC,GAAG,CAAC;CAuChB"}
|
|
@@ -65,7 +65,7 @@ var InspectorDelegate = class {
|
|
|
65
65
|
* CustomQueryTransformer. This is primarily used by the inspector to transform
|
|
66
66
|
* queries for analysis.
|
|
67
67
|
*/
|
|
68
|
-
async transformCustomQuery(name, args,
|
|
68
|
+
async transformCustomQuery(name, args, ctx) {
|
|
69
69
|
assert(this.#customQueryTransformer, "Custom query transformation requested but no CustomQueryTransformer is configured");
|
|
70
70
|
const queries = [{
|
|
71
71
|
id: hashOfNameAndArgs(name, args),
|
|
@@ -74,9 +74,9 @@ var InspectorDelegate = class {
|
|
|
74
74
|
args,
|
|
75
75
|
clientState: {}
|
|
76
76
|
}];
|
|
77
|
-
const results = await this.#customQueryTransformer.transform(
|
|
78
|
-
if ("kind" in results) throw new ProtocolErrorWithLevel(results, "warn");
|
|
79
|
-
const result = results[0];
|
|
77
|
+
const results = await this.#customQueryTransformer.transform(ctx, queries);
|
|
78
|
+
if ("kind" in results.result) throw new ProtocolErrorWithLevel(results.result, "warn");
|
|
79
|
+
const result = results.result[0];
|
|
80
80
|
if (!result) throw new Error("No transformation result returned");
|
|
81
81
|
if ("error" in result) {
|
|
82
82
|
const message = result.message ?? "Unknown application error from custom query";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inspector-delegate.js","names":["#globalMetrics","#perQueryServerMetrics","#queryIDToAST","#customQueryTransformer"],"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 {mapValues} from '../../../shared/src/objects.ts';\nimport {TDigest} from '../../../shared/src/tdigest.ts';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport type {ServerMetrics as ServerMetricsJSON} 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 {
|
|
1
|
+
{"version":3,"file":"inspector-delegate.js","names":["#globalMetrics","#perQueryServerMetrics","#queryIDToAST","#customQueryTransformer"],"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 {mapValues} from '../../../shared/src/objects.ts';\nimport {TDigest} from '../../../shared/src/tdigest.ts';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport type {ServerMetrics as ServerMetricsJSON} 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 */\nexport type ServerMetrics = {\n 'query-materialization-server': TDigest;\n 'query-update-server': TDigest;\n};\n\ntype ClientGroupID = string;\n\n/**\n * Set of authenticated client group IDs. We keep this outside of the class to\n * share this state across all instances of the InspectorDelegate.\n */\nconst authenticatedClientGroupIDs = new Set<ClientGroupID>();\n\nexport class InspectorDelegate implements MetricsDelegate {\n readonly #globalMetrics: ServerMetrics = newMetrics();\n readonly #perQueryServerMetrics = new Map<string, ServerMetrics>();\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 let serverMetrics = this.#perQueryServerMetrics.get(queryID);\n if (!serverMetrics) {\n serverMetrics = newMetrics();\n this.#perQueryServerMetrics.set(queryID, serverMetrics);\n }\n serverMetrics[metric].add(value);\n this.#globalMetrics[metric].add(value);\n }\n\n getMetricsJSONForQuery(queryID: string): ServerMetricsJSON | null {\n const serverMetrics = this.#perQueryServerMetrics.get(queryID);\n return serverMetrics ? mapValues(serverMetrics, v => v.toJSON()) : null;\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.#perQueryServerMetrics.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 setAuthenticated(clientGroupID: ClientGroupID): void {\n authenticatedClientGroupIDs.add(clientGroupID);\n }\n\n clearAuthenticated(clientGroupID: ClientGroupID) {\n authenticatedClientGroupIDs.delete(clientGroupID);\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":";;;;;;;;;;;;AAiCA,IAAM,8CAA8B,IAAI,KAAoB;AAE5D,IAAa,oBAAb,MAA0D;CACxD,iBAAyC,YAAY;CACrD,yCAAkC,IAAI,KAA4B;CAClE,gCAA2C,IAAI,KAAK;CACpD;CAEA,YAAY,wBAA4D;AACtE,QAAA,yBAA+B;;CAGjC,UACE,QACA,OACA,GAAG,MACG;AACN,SAAO,eAAe,OAAO,EAAE,0BAA0B,SAAS;EAClE,MAAM,UAAU,KAAK;EACrB,IAAI,gBAAgB,MAAA,sBAA4B,IAAI,QAAQ;AAC5D,MAAI,CAAC,eAAe;AAClB,mBAAgB,YAAY;AAC5B,SAAA,sBAA4B,IAAI,SAAS,cAAc;;AAEzD,gBAAc,QAAQ,IAAI,MAAM;AAChC,QAAA,cAAoB,QAAQ,IAAI,MAAM;;CAGxC,uBAAuB,SAA2C;EAChE,MAAM,gBAAgB,MAAA,sBAA4B,IAAI,QAAQ;AAC9D,SAAO,gBAAgB,UAAU,gBAAe,MAAK,EAAE,QAAQ,CAAC,GAAG;;CAGrE,iBAAiB;AACf,SAAO,UAAU,MAAA,gBAAqB,MAAK,EAAE,QAAQ,CAAC;;CAGxD,eAAe,SAAkC;AAC/C,SAAO,MAAA,aAAmB,IAAI,QAAQ;;CAGxC,YAAY,SAAuB;AACjC,QAAA,sBAA4B,OAAO,QAAQ;AAC3C,QAAA,aAAmB,OAAO,QAAQ;;CAGpC,SAAS,SAAiB,KAAgB;AACxC,QAAA,aAAmB,IAAI,SAAS,IAAI;;;;;;CAOtC,gBAAgB,eAAuC;AACrD,SACE,mBAAmB,IAAI,4BAA4B,IAAI,cAAc;;CAIzE,iBAAiB,eAAoC;AACnD,8BAA4B,IAAI,cAAc;;CAGhD,mBAAmB,eAA8B;AAC/C,8BAA4B,OAAO,cAAc;;;;;;;CAQnD,MAAM,qBACJ,MACA,MACA,KACc;AACd,SACE,MAAA,wBACA,oFACD;EAID,MAAM,UAA+B,CACnC;GACE,IAHY,kBAAkB,MAAM,KAAK;GAIzC,MAAM;GACN;GACA;GACA,aAAa,EAAE;GAChB,CACF;EAED,MAAM,UAAU,MAAM,MAAA,uBAA6B,UAAU,KAAK,QAAQ;AAE1E,MAAI,UAAU,QAAQ,OACpB,OAAM,IAAI,uBAAuB,QAAQ,QAAQ,OAAO;EAG1D,MAAM,SAAS,QAAQ,OAAO;AAC9B,MAAI,CAAC,OACH,OAAM,IAAI,MAAM,oCAAoC;AAGtD,MAAI,WAAW,QAAQ;GACrB,MAAM,UACJ,OAAO,WAAW;AACpB,SAAM,IAAI,MACR,mCAAmC,KAAK,IAAI,OAAO,MAAM,KAAK,QAAQ,GAAG,KAAK,UAAU,OAAO,QAAQ,GACxG;;AAGH,SAAO,OAAO;;;AAIlB,SAAS,aAA4B;AACnC,QAAO;EACL,gCAAgC,IAAI,SAAS;EAC7C,uBAAuB,IAAI,SAAS;EACrC"}
|
|
@@ -20,7 +20,7 @@ async function runWorker(parent, env) {
|
|
|
20
20
|
initEventSink(lc, config);
|
|
21
21
|
const processes = new ProcessManager(lc, parent);
|
|
22
22
|
const { numSyncWorkers: numSyncers } = config;
|
|
23
|
-
if (config.upstream.maxConns < numSyncers) throw new Error(`Insufficient upstream connections (${config.upstream.maxConns}) for ${numSyncers} syncers.Increase ZERO_UPSTREAM_MAX_CONNS or decrease ZERO_NUM_SYNC_WORKERS (which defaults to available cores).`);
|
|
23
|
+
if (config.enableCrudMutations && config.upstream.maxConns < numSyncers) throw new Error(`Insufficient upstream connections (${config.upstream.maxConns}) for ${numSyncers} syncers.Increase ZERO_UPSTREAM_MAX_CONNS or decrease ZERO_NUM_SYNC_WORKERS (which defaults to available cores).`);
|
|
24
24
|
if (config.cvr.maxConns < numSyncers) throw new Error(`Insufficient cvr connections (${config.cvr.maxConns}) for ${numSyncers} syncers.Increase ZERO_CVR_MAX_CONNS or decrease ZERO_NUM_SYNC_WORKERS (which defaults to available cores).`);
|
|
25
25
|
const internalFlags = numSyncers === 0 ? [] : [
|
|
26
26
|
"--upstream-max-conns-per-worker",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","names":[],"sources":["../../../../../zero-cache/src/server/main.ts"],"sourcesContent":["import {resolver} from '@rocicorp/resolver';\nimport path from 'node:path';\nimport {must} from '../../../shared/src/must.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {\n exitAfter,\n ProcessManager,\n runUntilKilled,\n type WorkerType,\n} from '../services/life-cycle.ts';\nimport {\n restoreReplica,\n startReplicaBackupProcess,\n} from '../services/litestream/commands.ts';\nimport {\n childWorker,\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {\n createNotifierFrom,\n handleSubscriptionsFrom,\n type ReplicaFileMode,\n subscribeTo,\n} from '../workers/replicator.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\nimport {WorkerDispatcher} from './worker-dispatcher.ts';\nimport {\n CHANGE_STREAMER_URL,\n MUTATOR_URL,\n REAPER_URL,\n REPLICATOR_URL,\n SYNCER_URL,\n} from './worker-urls.ts';\n\nconst clientConnectionBifurcated = false;\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n): Promise<void> {\n const startMs = Date.now();\n const config = getNormalizedZeroConfig({env});\n\n startOtelAuto(createLogContext(config, {worker: 'dispatcher'}, false));\n const lc = createLogContext(config, {worker: 'dispatcher'}, true);\n initEventSink(lc, config);\n\n const processes = new ProcessManager(lc, parent);\n\n const {numSyncWorkers: numSyncers} = config;\n if (config.upstream.maxConns < numSyncers) {\n throw new Error(\n `Insufficient upstream connections (${config.upstream.maxConns}) for ${numSyncers} syncers.` +\n `Increase ZERO_UPSTREAM_MAX_CONNS or decrease ZERO_NUM_SYNC_WORKERS (which defaults to available cores).`,\n );\n }\n if (config.cvr.maxConns < numSyncers) {\n throw new Error(\n `Insufficient cvr connections (${config.cvr.maxConns}) for ${numSyncers} syncers.` +\n `Increase ZERO_CVR_MAX_CONNS or decrease ZERO_NUM_SYNC_WORKERS (which defaults to available cores).`,\n );\n }\n\n const internalFlags: string[] =\n numSyncers === 0\n ? []\n : [\n '--upstream-max-conns-per-worker',\n String(Math.floor(config.upstream.maxConns / numSyncers)),\n '--cvr-max-conns-per-worker',\n String(Math.floor(config.cvr.maxConns / numSyncers)),\n ];\n\n function loadWorker(\n moduleUrl: URL,\n type: WorkerType,\n id?: string | number,\n ...args: string[]\n ): Worker {\n const worker = childWorker(moduleUrl, env, ...args, ...internalFlags);\n const name = path.basename(moduleUrl.pathname) + (id ? ` (${id})` : '');\n return processes.addWorker(worker, type, name);\n }\n\n const {\n taskID,\n changeStreamer: {mode: changeStreamerMode, uri: changeStreamerURI},\n litestream,\n } = config;\n const runChangeStreamer =\n changeStreamerMode === 'dedicated' && changeStreamerURI === undefined;\n\n let restoreStart = new Date();\n if (litestream.backupURL || (litestream.executable && !runChangeStreamer)) {\n try {\n restoreStart = await restoreReplica(lc, config);\n } catch (e) {\n if (runChangeStreamer) {\n // If the restore failed, e.g. due to a corrupt backup, the\n // replication-manager recovers by re-syncing.\n lc.error?.('error restoring backup. resyncing the replica.', e);\n } else {\n // View-syncers, on the other hand, have no option other than to retry\n // until a valid backup has been published. This is achieved by\n // shutting down and letting the container runner retry with its\n // configured policy.\n throw e;\n }\n }\n }\n\n const {promise: changeStreamerReady, resolve: changeStreamerStarted} =\n resolver();\n const changeStreamer = runChangeStreamer\n ? loadWorker(\n CHANGE_STREAMER_URL,\n 'supporting',\n undefined,\n String(restoreStart.getTime()),\n ).once('message', changeStreamerStarted)\n : (changeStreamerStarted() ?? undefined);\n\n const {promise: reaperReady, resolve: reaperStarted} = resolver();\n if (numSyncers > 0) {\n loadWorker(REAPER_URL, 'supporting').once('message', reaperStarted);\n } else {\n reaperStarted();\n }\n\n // Wait for the change-streamer to be ready to guarantee that a replica\n // file is present.\n await changeStreamerReady;\n\n if (runChangeStreamer && litestream.backupURL) {\n // Start a backup replicator and corresponding litestream backup process.\n const {promise: backupReady, resolve} = resolver();\n const mode: ReplicaFileMode = 'backup';\n loadWorker(REPLICATOR_URL, 'supporting', mode, mode).once(\n // Wait for the Replicator's first message (i.e. \"ready\") before starting\n // litestream backup in order to avoid contending on the lock when the\n // replicator first prepares the db file.\n 'message',\n () => {\n processes.addSubprocess(\n startReplicaBackupProcess(lc, config),\n 'supporting',\n 'litestream',\n );\n resolve();\n },\n );\n await backupReady;\n }\n\n // Before starting the view-syncers, ensure that the reaper has started\n // up, indicating that any CVR db migrations have been performed.\n await reaperReady;\n\n const syncers: Worker[] = [];\n if (numSyncers) {\n const mode: ReplicaFileMode =\n runChangeStreamer && litestream.backupURL ? 'serving-copy' : 'serving';\n const {promise: replicaReady, resolve} = resolver();\n const replicator = loadWorker(\n REPLICATOR_URL,\n 'supporting',\n mode,\n mode,\n ).once('message', () => {\n subscribeTo(lc, replicator);\n resolve();\n });\n await replicaReady;\n\n const notifier = createNotifierFrom(lc, replicator);\n for (let i = 0; i < numSyncers; i++) {\n syncers.push(loadWorker(SYNCER_URL, 'user-facing', i + 1, mode));\n }\n syncers.forEach(syncer => handleSubscriptionsFrom(lc, syncer, notifier));\n }\n let mutator: Worker | undefined;\n if (clientConnectionBifurcated) {\n mutator = loadWorker(MUTATOR_URL, 'supporting', 'mutator');\n }\n\n lc.info?.('waiting for workers to be ready ...');\n const logWaiting = setInterval(\n () => lc.info?.(`still waiting for ${processes.initializing().join(', ')}`),\n 10_000,\n );\n await processes.allWorkersReady();\n clearInterval(logWaiting);\n lc.info?.(`all workers ready (${Date.now() - startMs} ms)`);\n\n parent.send(['ready', {ready: true}]);\n\n try {\n await runUntilKilled(\n lc,\n parent,\n new WorkerDispatcher(\n lc,\n taskID,\n parent,\n syncers,\n mutator,\n changeStreamer,\n ),\n );\n } catch (err) {\n processes.logErrorAndExit(err, 'dispatcher');\n }\n\n await processes.done();\n}\n\nif (!singleProcessMode()) {\n void exitAfter(() => runWorker(must(parentWorker), process.env));\n}\n"],"mappings":";;;;;;;;;;;;;;AAwCA,eAA8B,UAC5B,QACA,KACe;CACf,MAAM,UAAU,KAAK,KAAK;CAC1B,MAAM,SAAS,wBAAwB,EAAC,KAAI,CAAC;AAE7C,eAAc,iBAAiB,QAAQ,EAAC,QAAQ,cAAa,EAAE,MAAM,CAAC;CACtE,MAAM,KAAK,iBAAiB,QAAQ,EAAC,QAAQ,cAAa,EAAE,KAAK;AACjE,eAAc,IAAI,OAAO;CAEzB,MAAM,YAAY,IAAI,eAAe,IAAI,OAAO;CAEhD,MAAM,EAAC,gBAAgB,eAAc;AACrC,KAAI,OAAO,SAAS,WAAW,
|
|
1
|
+
{"version":3,"file":"main.js","names":[],"sources":["../../../../../zero-cache/src/server/main.ts"],"sourcesContent":["import {resolver} from '@rocicorp/resolver';\nimport path from 'node:path';\nimport {must} from '../../../shared/src/must.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {\n exitAfter,\n ProcessManager,\n runUntilKilled,\n type WorkerType,\n} from '../services/life-cycle.ts';\nimport {\n restoreReplica,\n startReplicaBackupProcess,\n} from '../services/litestream/commands.ts';\nimport {\n childWorker,\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {\n createNotifierFrom,\n handleSubscriptionsFrom,\n type ReplicaFileMode,\n subscribeTo,\n} from '../workers/replicator.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\nimport {WorkerDispatcher} from './worker-dispatcher.ts';\nimport {\n CHANGE_STREAMER_URL,\n MUTATOR_URL,\n REAPER_URL,\n REPLICATOR_URL,\n SYNCER_URL,\n} from './worker-urls.ts';\n\nconst clientConnectionBifurcated = false;\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n): Promise<void> {\n const startMs = Date.now();\n const config = getNormalizedZeroConfig({env});\n\n startOtelAuto(createLogContext(config, {worker: 'dispatcher'}, false));\n const lc = createLogContext(config, {worker: 'dispatcher'}, true);\n initEventSink(lc, config);\n\n const processes = new ProcessManager(lc, parent);\n\n const {numSyncWorkers: numSyncers} = config;\n if (config.enableCrudMutations && config.upstream.maxConns < numSyncers) {\n throw new Error(\n `Insufficient upstream connections (${config.upstream.maxConns}) for ${numSyncers} syncers.` +\n `Increase ZERO_UPSTREAM_MAX_CONNS or decrease ZERO_NUM_SYNC_WORKERS (which defaults to available cores).`,\n );\n }\n if (config.cvr.maxConns < numSyncers) {\n throw new Error(\n `Insufficient cvr connections (${config.cvr.maxConns}) for ${numSyncers} syncers.` +\n `Increase ZERO_CVR_MAX_CONNS or decrease ZERO_NUM_SYNC_WORKERS (which defaults to available cores).`,\n );\n }\n\n const internalFlags: string[] =\n numSyncers === 0\n ? []\n : [\n '--upstream-max-conns-per-worker',\n String(Math.floor(config.upstream.maxConns / numSyncers)),\n '--cvr-max-conns-per-worker',\n String(Math.floor(config.cvr.maxConns / numSyncers)),\n ];\n\n function loadWorker(\n moduleUrl: URL,\n type: WorkerType,\n id?: string | number,\n ...args: string[]\n ): Worker {\n const worker = childWorker(moduleUrl, env, ...args, ...internalFlags);\n const name = path.basename(moduleUrl.pathname) + (id ? ` (${id})` : '');\n return processes.addWorker(worker, type, name);\n }\n\n const {\n taskID,\n changeStreamer: {mode: changeStreamerMode, uri: changeStreamerURI},\n litestream,\n } = config;\n const runChangeStreamer =\n changeStreamerMode === 'dedicated' && changeStreamerURI === undefined;\n\n let restoreStart = new Date();\n if (litestream.backupURL || (litestream.executable && !runChangeStreamer)) {\n try {\n restoreStart = await restoreReplica(lc, config);\n } catch (e) {\n if (runChangeStreamer) {\n // If the restore failed, e.g. due to a corrupt backup, the\n // replication-manager recovers by re-syncing.\n lc.error?.('error restoring backup. resyncing the replica.', e);\n } else {\n // View-syncers, on the other hand, have no option other than to retry\n // until a valid backup has been published. This is achieved by\n // shutting down and letting the container runner retry with its\n // configured policy.\n throw e;\n }\n }\n }\n\n const {promise: changeStreamerReady, resolve: changeStreamerStarted} =\n resolver();\n const changeStreamer = runChangeStreamer\n ? loadWorker(\n CHANGE_STREAMER_URL,\n 'supporting',\n undefined,\n String(restoreStart.getTime()),\n ).once('message', changeStreamerStarted)\n : (changeStreamerStarted() ?? undefined);\n\n const {promise: reaperReady, resolve: reaperStarted} = resolver();\n if (numSyncers > 0) {\n loadWorker(REAPER_URL, 'supporting').once('message', reaperStarted);\n } else {\n reaperStarted();\n }\n\n // Wait for the change-streamer to be ready to guarantee that a replica\n // file is present.\n await changeStreamerReady;\n\n if (runChangeStreamer && litestream.backupURL) {\n // Start a backup replicator and corresponding litestream backup process.\n const {promise: backupReady, resolve} = resolver();\n const mode: ReplicaFileMode = 'backup';\n loadWorker(REPLICATOR_URL, 'supporting', mode, mode).once(\n // Wait for the Replicator's first message (i.e. \"ready\") before starting\n // litestream backup in order to avoid contending on the lock when the\n // replicator first prepares the db file.\n 'message',\n () => {\n processes.addSubprocess(\n startReplicaBackupProcess(lc, config),\n 'supporting',\n 'litestream',\n );\n resolve();\n },\n );\n await backupReady;\n }\n\n // Before starting the view-syncers, ensure that the reaper has started\n // up, indicating that any CVR db migrations have been performed.\n await reaperReady;\n\n const syncers: Worker[] = [];\n if (numSyncers) {\n const mode: ReplicaFileMode =\n runChangeStreamer && litestream.backupURL ? 'serving-copy' : 'serving';\n const {promise: replicaReady, resolve} = resolver();\n const replicator = loadWorker(\n REPLICATOR_URL,\n 'supporting',\n mode,\n mode,\n ).once('message', () => {\n subscribeTo(lc, replicator);\n resolve();\n });\n await replicaReady;\n\n const notifier = createNotifierFrom(lc, replicator);\n for (let i = 0; i < numSyncers; i++) {\n syncers.push(loadWorker(SYNCER_URL, 'user-facing', i + 1, mode));\n }\n syncers.forEach(syncer => handleSubscriptionsFrom(lc, syncer, notifier));\n }\n let mutator: Worker | undefined;\n if (clientConnectionBifurcated) {\n mutator = loadWorker(MUTATOR_URL, 'supporting', 'mutator');\n }\n\n lc.info?.('waiting for workers to be ready ...');\n const logWaiting = setInterval(\n () => lc.info?.(`still waiting for ${processes.initializing().join(', ')}`),\n 10_000,\n );\n await processes.allWorkersReady();\n clearInterval(logWaiting);\n lc.info?.(`all workers ready (${Date.now() - startMs} ms)`);\n\n parent.send(['ready', {ready: true}]);\n\n try {\n await runUntilKilled(\n lc,\n parent,\n new WorkerDispatcher(\n lc,\n taskID,\n parent,\n syncers,\n mutator,\n changeStreamer,\n ),\n );\n } catch (err) {\n processes.logErrorAndExit(err, 'dispatcher');\n }\n\n await processes.done();\n}\n\nif (!singleProcessMode()) {\n void exitAfter(() => runWorker(must(parentWorker), process.env));\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reaper.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/reaper.ts"],"names":[],"mappings":"AAQA,OAAO,EAGL,KAAK,MAAM,EACZ,MAAM,uBAAuB,CAAC;AAQ/B,wBAA8B,SAAS,CACrC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,GAAG,IAAI,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"reaper.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/reaper.ts"],"names":[],"mappings":"AAQA,OAAO,EAGL,KAAK,MAAM,EACZ,MAAM,uBAAuB,CAAC;AAQ/B,wBAA8B,SAAS,CACrC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,GAAG,IAAI,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,IAAI,CAAC,CAgCf"}
|
|
@@ -24,7 +24,10 @@ async function runWorker(parent, env, ...argv) {
|
|
|
24
24
|
startAnonymousTelemetry(lc, config);
|
|
25
25
|
const { cvr } = config;
|
|
26
26
|
const shard = getShardID(config);
|
|
27
|
-
const cvrDB = pgClient(lc, cvr.db, {
|
|
27
|
+
const cvrDB = pgClient(lc, cvr.db, {
|
|
28
|
+
max: 1,
|
|
29
|
+
connection: { ["application_name"]: `zero-sync-cvr-purger` }
|
|
30
|
+
});
|
|
28
31
|
await initViewSyncerSchema(lc, cvrDB, shard);
|
|
29
32
|
parent.send(["ready", { ready: true }]);
|
|
30
33
|
return runUntilKilled(lc, parent, new CVRPurger(lc, cvrDB, shard, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reaper.js","names":[],"sources":["../../../../../zero-cache/src/server/reaper.ts"],"sourcesContent":["import {must} from '../../../shared/src/must.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {exitAfter, runUntilKilled} from '../services/life-cycle.ts';\nimport {CVRPurger} from '../services/view-syncer/cvr-purger.ts';\nimport {ActiveUsersGauge} from '../services/view-syncer/active-users-gauge.ts';\nimport {initViewSyncerSchema} from '../services/view-syncer/schema/init.ts';\nimport {pgClient} from '../types/pg.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {getShardID} from '../types/shards.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\nimport {startAnonymousTelemetry} from './anonymous-otel-start.ts';\n\nconst MS_PER_HOUR = 1000 * 60 * 60;\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...argv: string[]\n): Promise<void> {\n const config = getNormalizedZeroConfig({env, argv});\n\n startOtelAuto(createLogContext(config, {worker: 'reaper'}, false));\n const lc = createLogContext(config, {worker: 'reaper'}, true);\n initEventSink(lc, config);\n startAnonymousTelemetry(lc, config);\n\n const {cvr} = config;\n const shard = getShardID(config);\n const cvrDB = pgClient(lc, cvr.db, {\n connection: {['application_name']: `zero-sync-cvr-purger`},\n });\n await initViewSyncerSchema(lc, cvrDB, shard);\n parent.send(['ready', {ready: true}]);\n\n return runUntilKilled(\n lc,\n parent,\n new CVRPurger(lc, cvrDB, shard, {\n inactivityThresholdMs:\n cvr.garbageCollectionInactivityThresholdHours * MS_PER_HOUR,\n initialBatchSize: cvr.garbageCollectionInitialBatchSize,\n initialIntervalMs: cvr.garbageCollectionInitialIntervalSeconds * 1000,\n }),\n // Periodically computes and exports active users gauge to anonymous telemetry\n new ActiveUsersGauge(lc, cvrDB, shard, {\n // Default 10minutes refresh; can be made configurable later if needed\n updateIntervalMs: 10 * 60 * 1000,\n }),\n );\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(() =>\n runWorker(must(parentWorker), process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAkBA,IAAM,cAAc,MAAO,KAAK;AAEhC,eAA8B,UAC5B,QACA,KACA,GAAG,MACY;CACf,MAAM,SAAS,wBAAwB;EAAC;EAAK;EAAK,CAAC;AAEnD,eAAc,iBAAiB,QAAQ,EAAC,QAAQ,UAAS,EAAE,MAAM,CAAC;CAClE,MAAM,KAAK,iBAAiB,QAAQ,EAAC,QAAQ,UAAS,EAAE,KAAK;AAC7D,eAAc,IAAI,OAAO;AACzB,yBAAwB,IAAI,OAAO;CAEnC,MAAM,EAAC,QAAO;CACd,MAAM,QAAQ,WAAW,OAAO;CAChC,MAAM,QAAQ,SAAS,IAAI,IAAI,IAAI
|
|
1
|
+
{"version":3,"file":"reaper.js","names":[],"sources":["../../../../../zero-cache/src/server/reaper.ts"],"sourcesContent":["import {must} from '../../../shared/src/must.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {exitAfter, runUntilKilled} from '../services/life-cycle.ts';\nimport {CVRPurger} from '../services/view-syncer/cvr-purger.ts';\nimport {ActiveUsersGauge} from '../services/view-syncer/active-users-gauge.ts';\nimport {initViewSyncerSchema} from '../services/view-syncer/schema/init.ts';\nimport {pgClient} from '../types/pg.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {getShardID} from '../types/shards.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\nimport {startAnonymousTelemetry} from './anonymous-otel-start.ts';\n\nconst MS_PER_HOUR = 1000 * 60 * 60;\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...argv: string[]\n): Promise<void> {\n const config = getNormalizedZeroConfig({env, argv});\n\n startOtelAuto(createLogContext(config, {worker: 'reaper'}, false));\n const lc = createLogContext(config, {worker: 'reaper'}, true);\n initEventSink(lc, config);\n startAnonymousTelemetry(lc, config);\n\n const {cvr} = config;\n const shard = getShardID(config);\n const cvrDB = pgClient(lc, cvr.db, {\n max: 1,\n connection: {['application_name']: `zero-sync-cvr-purger`},\n });\n await initViewSyncerSchema(lc, cvrDB, shard);\n parent.send(['ready', {ready: true}]);\n\n return runUntilKilled(\n lc,\n parent,\n new CVRPurger(lc, cvrDB, shard, {\n inactivityThresholdMs:\n cvr.garbageCollectionInactivityThresholdHours * MS_PER_HOUR,\n initialBatchSize: cvr.garbageCollectionInitialBatchSize,\n initialIntervalMs: cvr.garbageCollectionInitialIntervalSeconds * 1000,\n }),\n // Periodically computes and exports active users gauge to anonymous telemetry\n new ActiveUsersGauge(lc, cvrDB, shard, {\n // Default 10minutes refresh; can be made configurable later if needed\n updateIntervalMs: 10 * 60 * 1000,\n }),\n );\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(() =>\n runWorker(must(parentWorker), process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAkBA,IAAM,cAAc,MAAO,KAAK;AAEhC,eAA8B,UAC5B,QACA,KACA,GAAG,MACY;CACf,MAAM,SAAS,wBAAwB;EAAC;EAAK;EAAK,CAAC;AAEnD,eAAc,iBAAiB,QAAQ,EAAC,QAAQ,UAAS,EAAE,MAAM,CAAC;CAClE,MAAM,KAAK,iBAAiB,QAAQ,EAAC,QAAQ,UAAS,EAAE,KAAK;AAC7D,eAAc,IAAI,OAAO;AACzB,yBAAwB,IAAI,OAAO;CAEnC,MAAM,EAAC,QAAO;CACd,MAAM,QAAQ,WAAW,OAAO;CAChC,MAAM,QAAQ,SAAS,IAAI,IAAI,IAAI;EACjC,KAAK;EACL,YAAY,GAAE,qBAAqB,wBAAuB;EAC3D,CAAC;AACF,OAAM,qBAAqB,IAAI,OAAO,MAAM;AAC5C,QAAO,KAAK,CAAC,SAAS,EAAC,OAAO,MAAK,CAAC,CAAC;AAErC,QAAO,eACL,IACA,QACA,IAAI,UAAU,IAAI,OAAO,OAAO;EAC9B,uBACE,IAAI,4CAA4C;EAClD,kBAAkB,IAAI;EACtB,mBAAmB,IAAI,0CAA0C;EAClE,CAAC,EAEF,IAAI,iBAAiB,IAAI,OAAO,OAAO,EAErC,kBAAkB,MAAU,KAC7B,CAAC,CACH;;AAIH,IAAI,CAAC,mBAAmB,CACjB,iBACH,UAAU,KAAK,aAAa,EAAE,QAAQ,KAAK,GAAG,QAAQ,KAAK,MAAM,EAAE,CAAC,CACrE"}
|
|
@@ -28,7 +28,7 @@ async function runWorker(parent, env) {
|
|
|
28
28
|
const { port, lazyStartup } = config;
|
|
29
29
|
const serverVersion = getServerVersion(config);
|
|
30
30
|
lc.info?.(`starting server${!serverVersion ? "" : `@${serverVersion}`} `, {
|
|
31
|
-
protocolVersion:
|
|
31
|
+
protocolVersion: 50,
|
|
32
32
|
taskID: config.taskID,
|
|
33
33
|
app: config.app,
|
|
34
34
|
shard: config.shard,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncer.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/syncer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"syncer.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/syncer.ts"],"names":[],"mappings":"AA4BA,OAAO,EAGL,KAAK,MAAM,EACZ,MAAM,uBAAuB,CAAC;AAmC/B,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,GAAG,IAAI,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,IAAI,CAAC,CAoMf"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { assert } from "../../../shared/src/asserts.js";
|
|
2
|
+
import { promiseVoid } from "../../../shared/src/resolved-promises.js";
|
|
2
3
|
import { parse } from "../../../shared/src/valita.js";
|
|
3
4
|
import { must } from "../../../shared/src/must.js";
|
|
4
5
|
import { parentWorker, singleProcessMode } from "../types/processes.js";
|
|
@@ -16,11 +17,13 @@ import { initEventSink } from "../observability/events.js";
|
|
|
16
17
|
import { replicaFileModeSchema, replicaFileName } from "../workers/replicator.js";
|
|
17
18
|
import { startAnonymousTelemetry } from "./anonymous-otel-start.js";
|
|
18
19
|
import { DatabaseStorage } from "../../../zqlite/src/database-storage.js";
|
|
19
|
-
import {
|
|
20
|
+
import { ProtocolErrorWithLevel } from "../types/error-with-level.js";
|
|
20
21
|
import { CustomQueryTransformer } from "../custom-queries/transform-query.js";
|
|
21
22
|
import { MutagenService } from "../services/mutagen/mutagen.js";
|
|
22
23
|
import { PusherService } from "../services/mutagen/pusher.js";
|
|
24
|
+
import { ConnectionContextManagerImpl } from "../services/view-syncer/connection-context-manager.js";
|
|
23
25
|
import { ViewSyncerService } from "../services/view-syncer/view-syncer.js";
|
|
26
|
+
import { tokenConfigOptions, verifyToken } from "../auth/jwt.js";
|
|
24
27
|
import { Syncer } from "../workers/syncer.js";
|
|
25
28
|
import { InspectorDelegate } from "./inspector-delegate.js";
|
|
26
29
|
import { isPriorityOpRunning, runPriorityOp } from "./priority-op.js";
|
|
@@ -37,6 +40,8 @@ function getCustomQueryConfig(config) {
|
|
|
37
40
|
if (!queryConfig?.url) return;
|
|
38
41
|
return {
|
|
39
42
|
url: queryConfig.url,
|
|
43
|
+
apiKey: queryConfig.apiKey,
|
|
44
|
+
allowedClientHeaders: queryConfig.allowedClientHeaders,
|
|
40
45
|
forwardCookies: queryConfig.forwardCookies ?? false
|
|
41
46
|
};
|
|
42
47
|
}
|
|
@@ -50,41 +55,57 @@ function runWorker(parent, env, ...args) {
|
|
|
50
55
|
initEventSink(lc, config);
|
|
51
56
|
assert(args.length > 0, `replicator mode not specified`);
|
|
52
57
|
const fileMode = parse(args[0], replicaFileModeSchema);
|
|
53
|
-
const { cvr, upstream } = config;
|
|
54
|
-
assert(cvr.maxConnsPerWorker, "cvr.maxConnsPerWorker must be set");
|
|
55
|
-
assert(upstream.maxConnsPerWorker, "upstream.maxConnsPerWorker must be set");
|
|
58
|
+
const { cvr, upstream, enableCrudMutations } = config;
|
|
56
59
|
const replicaFile = replicaFileName(config.replica.file, fileMode);
|
|
57
60
|
lc.debug?.(`running view-syncer on ${replicaFile}`);
|
|
58
61
|
const cvrDB = pgClient(lc, cvr.db, {
|
|
59
|
-
max: cvr.maxConnsPerWorker,
|
|
62
|
+
max: must(cvr.maxConnsPerWorker, "cvr.maxConnsPerWorker must be set"),
|
|
60
63
|
connection: { ["application_name"]: `zero-sync-worker-${pid}-cvr` }
|
|
61
64
|
});
|
|
62
|
-
const upstreamDB = pgClient(lc, upstream.db, {
|
|
63
|
-
max: upstream.maxConnsPerWorker,
|
|
65
|
+
const upstreamDB = enableCrudMutations ? pgClient(lc, upstream.db, {
|
|
66
|
+
max: must(upstream.maxConnsPerWorker, "upstream.maxConnsPerWorker must be set"),
|
|
64
67
|
connection: { ["application_name"]: `zero-sync-worker-${pid}-upstream` }
|
|
65
|
-
});
|
|
66
|
-
const dbWarmup = Promise.allSettled([warmupConnections(lc, cvrDB, "cvr"), warmupConnections(lc, upstreamDB, "upstream")]);
|
|
68
|
+
}) : void 0;
|
|
69
|
+
const dbWarmup = Promise.allSettled([warmupConnections(lc, cvrDB, "cvr"), upstreamDB ? warmupConnections(lc, upstreamDB, "upstream") : promiseVoid]);
|
|
67
70
|
const tmpDir = config.storageDBTmpDir ?? tmpdir();
|
|
68
71
|
const operatorStorage = DatabaseStorage.create(lc, path.join(tmpDir, `sync-worker-${randomUUID()}`));
|
|
69
72
|
const writeAuthzStorage = DatabaseStorage.create(lc, path.join(tmpDir, `mutagen-${randomUUID()}`));
|
|
70
73
|
const shard = getShardID(config);
|
|
71
|
-
const
|
|
74
|
+
const customQueryConfig = getCustomQueryConfig(config);
|
|
75
|
+
const pushConfig = config.push.url === void 0 && config.mutate.url === void 0 ? void 0 : {
|
|
76
|
+
...config.push,
|
|
77
|
+
...config.mutate,
|
|
78
|
+
url: must(config.push.url ?? config.mutate.url, "No push or mutate URL configured")
|
|
79
|
+
};
|
|
80
|
+
/** @deprecated used in JWT validation */
|
|
81
|
+
let validateLegacyJWT = void 0;
|
|
82
|
+
if (tokenConfigOptions(config.auth ?? {}).length === 1) validateLegacyJWT = async (token, { userID }) => {
|
|
83
|
+
if (!userID) throw new ProtocolErrorWithLevel({
|
|
84
|
+
kind: "Unauthorized",
|
|
85
|
+
message: "UserID is required for JWT validation.",
|
|
86
|
+
origin: "zeroCache"
|
|
87
|
+
}, "warn");
|
|
88
|
+
return {
|
|
89
|
+
type: "jwt",
|
|
90
|
+
raw: token,
|
|
91
|
+
decoded: await verifyToken(config.auth, token, {
|
|
92
|
+
subject: userID,
|
|
93
|
+
...config.auth?.issuer && { issuer: config.auth.issuer },
|
|
94
|
+
...config.auth?.audience && { audience: config.auth.audience }
|
|
95
|
+
})
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
const viewSyncerFactory = (id, sub, drainCoordinator) => {
|
|
72
99
|
const logger = lc.withContext("component", "view-syncer").withContext("clientGroupID", id).withContext("instance", randomID());
|
|
100
|
+
const customQueryTransformer = customQueryConfig && new CustomQueryTransformer(logger, shard);
|
|
101
|
+
const contextManager = new ConnectionContextManagerImpl(logger, config.auth.revalidateIntervalSeconds, config.auth.retransformIntervalSeconds, customQueryConfig, pushConfig, validateLegacyJWT);
|
|
73
102
|
lc.debug?.(`creating view syncer. Query Planner Enabled: ${config.enableQueryPlanner}`);
|
|
74
|
-
const customQueryConfig = getCustomQueryConfig(config);
|
|
75
|
-
const customQueryTransformer = customQueryConfig && new CustomQueryTransformer(logger, customQueryConfig, shard);
|
|
76
103
|
const inspectorDelegate = new InspectorDelegate(customQueryTransformer);
|
|
77
104
|
const priorityOpRunningYieldThresholdMs = Math.max(config.yieldThresholdMs / 4, 2);
|
|
78
105
|
const normalYieldThresholdMs = Math.max(config.yieldThresholdMs, 2);
|
|
79
|
-
|
|
80
|
-
return new ViewSyncerService(config, logger, shard, config.taskID, id, cvrDB, new PipelineDriver(logger, config.log, new Snapshotter(logger, replicaFile, shard), shard, operatorStorage.createClientGroupStorage(id), id, inspectorDelegate, () => isPriorityOpRunning() ? priorityOpRunningYieldThresholdMs : normalYieldThresholdMs, config.enableQueryPlanner, config), sub, drainCoordinator, config.log.slowHydrateThreshold, inspectorDelegate, customQueryTransformer, runPriorityOp, authSession);
|
|
106
|
+
return new ViewSyncerService(config, logger, shard, config.taskID, id, cvrDB, new PipelineDriver(logger, config.log, new Snapshotter(logger, replicaFile, shard), shard, operatorStorage.createClientGroupStorage(id), id, inspectorDelegate, () => isPriorityOpRunning() ? priorityOpRunningYieldThresholdMs : normalYieldThresholdMs, config.enableQueryPlanner, config), sub, drainCoordinator, config.log.slowHydrateThreshold, inspectorDelegate, contextManager, customQueryTransformer, runPriorityOp);
|
|
81
107
|
};
|
|
82
|
-
const
|
|
83
|
-
const syncer = new Syncer(lc, config, viewSyncerFactory, mutagenFactory, config.push.url === void 0 && config.mutate.url === void 0 ? void 0 : (id) => new PusherService(config, {
|
|
84
|
-
...config.push,
|
|
85
|
-
...config.mutate,
|
|
86
|
-
url: must(config.push.url ?? config.mutate.url, "No push or mutate URL configured")
|
|
87
|
-
}, lc.withContext("clientGroupID", id), id), parent);
|
|
108
|
+
const syncer = new Syncer(lc, config, viewSyncerFactory, upstreamDB ? (id) => new MutagenService(lc.withContext("component", "mutagen").withContext("clientGroupID", id), shard, id, upstreamDB, config, writeAuthzStorage) : void 0, pushConfig === void 0 ? void 0 : (id, contextManager) => new PusherService(config, lc.withContext("clientGroupID", id), id, contextManager), parent, validateLegacyJWT);
|
|
88
109
|
startAnonymousTelemetry(lc, config);
|
|
89
110
|
dbWarmup.then(() => parent.send(["ready", { ready: true }]));
|
|
90
111
|
return runUntilKilled(lc, parent, syncer);
|