@rocicorp/zero 0.26.0-canary.8 → 0.26.0
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/ast-to-zql/src/ast-to-zql.d.ts.map +1 -1
- package/out/ast-to-zql/src/ast-to-zql.js +16 -27
- package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
- package/out/otel/src/log-options.d.ts +2 -2
- package/out/replicache/src/bg-interval.d.ts.map +1 -1
- package/out/replicache/src/bg-interval.js +3 -0
- package/out/replicache/src/bg-interval.js.map +1 -1
- package/out/shared/src/arrays.js +1 -1
- package/out/shared/src/arrays.js.map +1 -1
- package/out/shared/src/browser-env.js +0 -4
- package/out/shared/src/browser-env.js.map +1 -1
- package/out/shared/src/btree-set.js +4 -1
- package/out/shared/src/btree-set.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/queue.js +1 -1
- package/out/shared/src/queue.js.map +1 -1
- package/out/z2s/src/compiler.d.ts.map +1 -1
- package/out/z2s/src/compiler.js +13 -11
- package/out/z2s/src/compiler.js.map +1 -1
- package/out/zero/package.json.js +1 -1
- package/out/zero/src/react.js +1 -3
- package/out/zero/src/react.js.map +1 -1
- package/out/zero-cache/src/auth/read-authorizer.js +0 -7
- package/out/zero-cache/src/auth/read-authorizer.js.map +1 -1
- package/out/zero-cache/src/config/network.d.ts +3 -2
- package/out/zero-cache/src/config/network.d.ts.map +1 -1
- package/out/zero-cache/src/config/network.js +9 -2
- package/out/zero-cache/src/config/network.js.map +1 -1
- package/out/zero-cache/src/config/server-context.d.ts +16 -0
- package/out/zero-cache/src/config/server-context.d.ts.map +1 -0
- package/out/zero-cache/src/config/server-context.js +32 -0
- package/out/zero-cache/src/config/server-context.js.map +1 -0
- package/out/zero-cache/src/config/zero-config.d.ts +3 -3
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +2 -6
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/db/migration.d.ts.map +1 -1
- package/out/zero-cache/src/db/migration.js +40 -51
- package/out/zero-cache/src/db/migration.js.map +1 -1
- package/out/zero-cache/src/db/run-transaction.d.ts +17 -0
- package/out/zero-cache/src/db/run-transaction.d.ts.map +1 -0
- package/out/zero-cache/src/db/run-transaction.js +24 -0
- package/out/zero-cache/src/db/run-transaction.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 -3
- package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
- package/out/zero-cache/src/scripts/decommission.d.ts +1 -1
- package/out/zero-cache/src/scripts/deploy-permissions.js +2 -1
- package/out/zero-cache/src/scripts/deploy-permissions.js.map +1 -1
- package/out/zero-cache/src/scripts/permissions.d.ts +1 -1
- package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +6 -2
- package/out/zero-cache/src/server/change-streamer.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/runner/run-worker.d.ts.map +1 -1
- package/out/zero-cache/src/server/runner/run-worker.js +7 -3
- package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js +4 -4
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.js +11 -0
- package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.d.ts +5 -2
- 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 +6 -6
- 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 +6 -4
- 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 +130 -43
- package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/decommission.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/decommission.js +2 -1
- package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +4 -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 +35 -10
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/init.js +10 -0
- package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +6 -3
- package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js +19 -10
- package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts +1 -0
- package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/data.js +4 -2
- package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +3 -0
- package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/status.d.ts +6 -4
- package/out/zero-cache/src/services/change-source/protocol/current/status.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/status.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts +2 -2
- package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js +30 -12
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +23 -3
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +1 -0
- package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/forwarder.js +1 -1
- package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.js +12 -4
- package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.d.ts +11 -2
- package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js +80 -42
- package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.d.ts +1 -1
- package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.js +2 -1
- package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/mutagen.js +22 -17
- package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/replication-state.d.ts +10 -1
- package/out/zero-cache/src/services/replicator/schema/replication-state.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/replication-state.js +49 -9
- package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
- package/out/zero-cache/src/services/running-state.d.ts +1 -0
- package/out/zero-cache/src/services/running-state.d.ts.map +1 -1
- package/out/zero-cache/src/services/running-state.js +3 -0
- package/out/zero-cache/src/services/running-state.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-purger.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-purger.js +32 -28
- package/out/zero-cache/src/services/view-syncer/cvr-purger.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.js +329 -155
- 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 +387 -345
- package/out/zero-cache/src/services/view-syncer/cvr.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 +68 -16
- 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 -8
- package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/tracer.d.ts +2 -0
- package/out/zero-cache/src/services/view-syncer/tracer.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/tracer.js +7 -0
- package/out/zero-cache/src/services/view-syncer/tracer.js.map +1 -0
- 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 +58 -43
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/pg.js +0 -4
- package/out/zero-cache/src/types/pg.js.map +1 -1
- package/out/zero-cache/src/types/streams.d.ts +3 -1
- package/out/zero-cache/src/types/streams.d.ts.map +1 -1
- package/out/zero-cache/src/types/streams.js +1 -1
- package/out/zero-cache/src/types/streams.js.map +1 -1
- package/out/zero-cache/src/types/subscription.d.ts +7 -1
- package/out/zero-cache/src/types/subscription.d.ts.map +1 -1
- package/out/zero-cache/src/types/subscription.js +8 -2
- package/out/zero-cache/src/types/subscription.js.map +1 -1
- package/out/zero-client/src/client/options.d.ts +7 -7
- package/out/zero-client/src/client/options.d.ts.map +1 -1
- package/out/zero-client/src/client/options.js.map +1 -1
- package/out/zero-client/src/client/query-manager.js +1 -1
- package/out/zero-client/src/client/query-manager.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero-poke-handler.d.ts +5 -5
- package/out/zero-client/src/client/zero-poke-handler.d.ts.map +1 -1
- package/out/zero-client/src/client/zero-poke-handler.js +15 -17
- package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
- package/out/zero-client/src/client/zero.d.ts +6 -2
- package/out/zero-client/src/client/zero.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.js +44 -8
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-client/src/mod.d.ts +1 -1
- package/out/zero-client/src/mod.d.ts.map +1 -1
- package/out/zero-protocol/src/ast.d.ts +2 -9
- package/out/zero-protocol/src/ast.d.ts.map +1 -1
- package/out/zero-protocol/src/ast.js +15 -32
- package/out/zero-protocol/src/ast.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 +5 -2
- package/out/zero-protocol/src/protocol-version.js.map +1 -1
- package/out/zero-react/src/mod.d.ts +0 -2
- package/out/zero-react/src/mod.d.ts.map +1 -1
- package/out/zero-react/src/use-query.d.ts +6 -6
- package/out/zero-react/src/use-query.d.ts.map +1 -1
- package/out/zero-react/src/use-query.js +9 -2
- package/out/zero-react/src/use-query.js.map +1 -1
- package/out/zero-react/src/zero-provider.d.ts +5 -5
- package/out/zero-react/src/zero-provider.d.ts.map +1 -1
- package/out/zero-react/src/zero-provider.js.map +1 -1
- package/out/zero-solid/src/solid-view.d.ts +0 -42
- package/out/zero-solid/src/solid-view.d.ts.map +1 -1
- package/out/zero-solid/src/solid-view.js +1 -1
- package/out/zero-solid/src/solid-view.js.map +1 -1
- package/out/zero-solid/src/use-query.d.ts +4 -4
- package/out/zero-solid/src/use-query.d.ts.map +1 -1
- package/out/zero-solid/src/use-query.js.map +1 -1
- package/out/zero-solid/src/use-zero.d.ts +5 -5
- package/out/zero-solid/src/use-zero.d.ts.map +1 -1
- package/out/zero-solid/src/use-zero.js.map +1 -1
- package/out/zero-types/src/default-types.d.ts +2 -0
- package/out/zero-types/src/default-types.d.ts.map +1 -1
- package/out/zql/src/builder/builder.d.ts.map +1 -1
- package/out/zql/src/builder/builder.js +6 -48
- package/out/zql/src/builder/builder.js.map +1 -1
- package/out/zql/src/builder/filter.d.ts.map +1 -1
- package/out/zql/src/builder/filter.js +0 -1
- package/out/zql/src/builder/filter.js.map +1 -1
- package/out/zql/src/ivm/array-view.d.ts.map +1 -1
- package/out/zql/src/ivm/array-view.js +6 -57
- package/out/zql/src/ivm/array-view.js.map +1 -1
- package/out/zql/src/ivm/view-apply-change.d.ts +3 -50
- package/out/zql/src/ivm/view-apply-change.d.ts.map +1 -1
- package/out/zql/src/ivm/view-apply-change.js +105 -358
- package/out/zql/src/ivm/view-apply-change.js.map +1 -1
- package/out/zql/src/mutate/mutator-registry.d.ts +3 -3
- package/out/zql/src/mutate/mutator-registry.d.ts.map +1 -1
- package/out/zql/src/mutate/mutator-registry.js.map +1 -1
- package/out/zql/src/planner/planner-builder.d.ts.map +1 -1
- package/out/zql/src/planner/planner-builder.js +1 -2
- package/out/zql/src/planner/planner-builder.js.map +1 -1
- package/out/zql/src/query/complete-ordering.js +0 -6
- package/out/zql/src/query/complete-ordering.js.map +1 -1
- package/out/zql/src/query/expression.d.ts +2 -19
- package/out/zql/src/query/expression.d.ts.map +1 -1
- package/out/zql/src/query/expression.js +6 -50
- package/out/zql/src/query/expression.js.map +1 -1
- package/out/zql/src/query/query-delegate-base.js +3 -1
- package/out/zql/src/query/query-delegate-base.js.map +1 -1
- package/out/zql/src/query/query-impl.d.ts.map +1 -1
- package/out/zql/src/query/query-impl.js +8 -12
- package/out/zql/src/query/query-impl.js.map +1 -1
- package/out/zql/src/query/query-internals.js.map +1 -1
- package/out/zql/src/query/query-registry.d.ts +3 -3
- package/out/zql/src/query/query-registry.d.ts.map +1 -1
- package/out/zql/src/query/query-registry.js.map +1 -1
- package/out/zql/src/query/query.d.ts +28 -5
- package/out/zql/src/query/query.d.ts.map +1 -1
- package/out/zqlite/src/query-builder.d.ts +0 -2
- package/out/zqlite/src/query-builder.d.ts.map +1 -1
- package/out/zqlite/src/query-builder.js.map +1 -1
- package/out/zqlite/src/resolve-scalar-subqueries.d.ts +10 -2
- package/out/zqlite/src/resolve-scalar-subqueries.d.ts.map +1 -1
- package/out/zqlite/src/resolve-scalar-subqueries.js +41 -9
- package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
- package/out/zqlite/src/sqlite-cost-model.d.ts.map +1 -1
- package/out/zqlite/src/sqlite-cost-model.js +0 -1
- package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
- package/package.json +3 -5
- package/out/zero-cache/src/services/change-source/custom/sync-schema.d.ts +0 -4
- package/out/zero-cache/src/services/change-source/custom/sync-schema.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/custom/sync-schema.js +0 -14
- package/out/zero-cache/src/services/change-source/custom/sync-schema.js.map +0 -1
- package/out/zero-cache/src/services/change-source/pg/sync-schema.d.ts +0 -5
- package/out/zero-cache/src/services/change-source/pg/sync-schema.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/pg/sync-schema.js +0 -14
- package/out/zero-cache/src/services/change-source/pg/sync-schema.js.map +0 -1
- package/out/zero-react/src/paging-reducer.d.ts +0 -61
- package/out/zero-react/src/paging-reducer.d.ts.map +0 -1
- package/out/zero-react/src/paging-reducer.js +0 -77
- package/out/zero-react/src/paging-reducer.js.map +0 -1
- package/out/zero-react/src/use-rows.d.ts +0 -39
- package/out/zero-react/src/use-rows.d.ts.map +0 -1
- package/out/zero-react/src/use-rows.js +0 -130
- package/out/zero-react/src/use-rows.js.map +0 -1
- package/out/zero-react/src/use-zero-virtualizer.d.ts +0 -122
- package/out/zero-react/src/use-zero-virtualizer.d.ts.map +0 -1
- package/out/zero-react/src/use-zero-virtualizer.js +0 -342
- package/out/zero-react/src/use-zero-virtualizer.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backfill-manager.js","sources":["../../../../../../../zero-cache/src/services/change-source/common/backfill-manager.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {assert} from '../../../../../shared/src/asserts.ts';\nimport {stringify} from '../../../../../shared/src/bigint-json.ts';\nimport {CustomKeyMap} from '../../../../../shared/src/custom-key-map.ts';\nimport {must} from '../../../../../shared/src/must.ts';\nimport {randInt} from '../../../../../shared/src/rand.ts';\nimport {JSON_STRINGIFIED, type JSONFormat} from '../../../types/lite.ts';\nimport {\n stateVersionFromString,\n stateVersionToString,\n} from '../../../types/state-version.ts';\nimport type {\n BackfillCompleted,\n BackfillRequest,\n ChangeStreamMessage,\n Identifier,\n MessageBackfill,\n} from '../protocol/current.ts';\nimport type {\n Cancelable,\n ChangeStreamMultiplexer,\n Listener,\n} from './change-stream-multiplexer.ts';\n\nfunction tableKey({schema, name}: Identifier) {\n return `${schema}.${name}`;\n}\n\ntype BackfillStreamer = (\n req: BackfillRequest,\n) => AsyncGenerator<MessageBackfill | BackfillCompleted>;\n\ntype RunningBackfillState = {\n request: BackfillRequest;\n canceledReason?: string | undefined;\n minWatermark: string;\n};\n\nconst MIN_BACKOFF_INTERVAL_MS = 2_000;\nconst MAX_BACKOFF_INTERVAL_MS = 60_000;\n\ntype AwaitingStatusWatermark = {\n watermark: string;\n reached: () => void;\n};\n\n/**\n * The BackfillManager initiates backfills for BackfillRequests from the\n * change-streamer (i.e. unfinished backfills from previous sessions)\n * or for new backfills signaled by `create-table` or `add-column` messages\n * from the change-source.\n *\n * The BackfillManager registers itself as a change stream listener in order\n * to track necessary backfills, and potentially invalidate the in-progress\n * backfill (e.g. due to a schema change) so that it can be retried at a\n * new snapshot.\n *\n * The manager also handles low priority streaming of the backfill messages\n * using the {@link ChangeStreamMultiplexer}, implementing a policy of always\n * releasing its reservation if another producer (i.e. the main change stream)\n * has messages to stream.\n */\nexport class BackfillManager implements Cancelable, Listener {\n readonly #lc: LogContext;\n\n /**\n * Tracks the metadata of required backfills based on schema changes\n * and initial backfill requests.\n */\n readonly #requiredBackfills = new CustomKeyMap<Identifier, BackfillRequest>(\n tableKey,\n );\n readonly #changeStreamer: ChangeStreamMultiplexer;\n readonly #backfillStreamer: BackfillStreamer;\n readonly #jsonFormat: JSONFormat;\n\n /**\n * The current running backfill. The backfill request is always also in\n * `#requiredBackfills` (technically, it can be a subset of what's in\n * `#requiredBackfills`); the request is removed from `#requiredBackfills`\n * upon completion.\n */\n #runningBackfill: RunningBackfillState | null = null;\n\n /** The last seen watermark in the change stream. */\n #lastStatusWatermark: string | null = null;\n\n readonly #awaitingStatusWatermarks: AwaitingStatusWatermark[] = [];\n\n /** The watermark of the current transaction in the change stream. */\n #currentTxWatermark: string | null = null;\n\n constructor(\n lc: LogContext,\n changeStreamer: ChangeStreamMultiplexer,\n backfillStreamer: BackfillStreamer,\n jsonFormat: JSONFormat = JSON_STRINGIFIED,\n minBackoffMs = MIN_BACKOFF_INTERVAL_MS,\n maxBackoffMs = MAX_BACKOFF_INTERVAL_MS,\n ) {\n this.#lc = lc.withContext('component', 'backfill-manager');\n this.#changeStreamer = changeStreamer;\n this.#backfillStreamer = backfillStreamer;\n this.#jsonFormat = jsonFormat;\n this.#minBackoffMs = minBackoffMs;\n this.#maxBackoffMs = maxBackoffMs;\n this.#retryDelayMs = minBackoffMs;\n }\n\n run(lastWatermark: string, initialRequests: BackfillRequest[]) {\n this.#lc.info?.(\n `starting backfill manager with ${initialRequests.length} initial requests`,\n {requests: initialRequests},\n );\n this.#lastStatusWatermark = lastWatermark;\n initialRequests.forEach(req =>\n this.#setRequiredBackfill('initial-request', req),\n );\n this.#checkAndStartBackfill();\n }\n\n #setLastStatusWatermark({watermark}: {watermark: string}) {\n // Only allow the watermark to move forward. This prevents a backfill\n // transaction (whose watermark is unrelated to change-stream state)\n // from moving the watermark backwards.\n if ((this.#lastStatusWatermark ?? '') < watermark) {\n this.#lastStatusWatermark = watermark;\n for (let i = this.#awaitingStatusWatermarks.length - 1; i >= 0; i--) {\n const awaiting = this.#awaitingStatusWatermarks[i];\n if (watermark >= awaiting.watermark) {\n awaiting.reached();\n this.#awaitingStatusWatermarks.splice(i, 1);\n }\n }\n }\n }\n\n #changeStreamReached(\n lc: LogContext,\n watermark: string,\n ): Promise<void> | null {\n if ((this.#lastStatusWatermark ?? '') < watermark) {\n const {promise, resolve: reached} = resolver();\n this.#awaitingStatusWatermarks.push({watermark, reached});\n lc.info?.(\n `waiting for change stream (at ${this.#lastStatusWatermark}) to reach ${watermark}`,\n );\n return promise;\n }\n return null;\n }\n\n readonly #minBackoffMs: number;\n readonly #maxBackoffMs: number;\n #retryDelayMs: number;\n #backfillRetryTimer: NodeJS.Timeout | undefined;\n\n #checkAndStartBackfill() {\n if (\n !this.#backfillRetryTimer &&\n !this.#runningBackfill &&\n this.#requiredBackfills.size\n ) {\n // Pick a random backfill to avoid head-of-line blocking by a\n // problematic backfill (e.g. awaiting a primary key). This is\n // simpler that adding logic to classify (and declassify)\n // problematic backfills.\n const candidates = [...this.#requiredBackfills.values()];\n const request = candidates[randInt(0, candidates.length - 1)];\n const state = {request, minWatermark: ''};\n const lc = this.#lc.withContext('table', request.table.name);\n\n this.#runningBackfill = state;\n void this.#runBackfill(lc, state)\n .then(() => {\n this.#stopRunningBackfill('backfill exited', state);\n this.#retryDelayMs = this.#minBackoffMs; // reset on success\n })\n // For unexpected errors (e.g. upstream replication slot\n // unavailability), retry with exponential backoff.\n .catch(e => {\n this.#stopRunningBackfill(String(e), state);\n this.#retryBackfillWithBackoff(e);\n });\n }\n }\n\n #retryBackfillWithBackoff(e: unknown) {\n const log = this.#retryDelayMs === this.#maxBackoffMs ? 'error' : 'warn';\n this.#lc[log]?.(\n `Error running backfill. Retrying in ${this.#retryDelayMs} ms`,\n e,\n );\n this.#backfillRetryTimer = setTimeout(() => {\n this.#backfillRetryTimer = undefined;\n this.#checkAndStartBackfill();\n }, this.#retryDelayMs);\n\n this.#retryDelayMs = Math.min(this.#retryDelayMs * 2, this.#maxBackoffMs);\n }\n\n async #runBackfill(lc: LogContext, state: RunningBackfillState) {\n const changeStream = this.#changeStreamer; // Purely for readability\n\n // backfillTx is set if and only if a changeStreamer reservation has been\n // acquired and the backfill stream is inside a transaction.\n let backfillTx: string | null = null;\n\n /**\n * @returns the new tx watermark, or null if backfill was cancelled\n */\n const beginTxFor = async (\n msg: MessageBackfill | BackfillCompleted,\n ): Promise<string | null> => {\n assert(backfillTx === null);\n const lastWatermark = await changeStream.reserve('backfill');\n\n // After obtaining the changeStream reservation, check if the stream\n // had changes that resulted in invalidating / canceling this backfill.\n if (\n state.canceledReason ||\n (msg.tag === 'backfill' && msg.watermark < state.minWatermark)\n ) {\n if (state.canceledReason === undefined) {\n assert(msg.tag === 'backfill'); // TypeScript should have figured this out.\n this.#stopRunningBackfill(\n `row key change at ${state.minWatermark} ` +\n `postdates backfill watermark at ${msg.watermark}`,\n state,\n );\n }\n changeStream.release(lastWatermark);\n return null;\n }\n\n const {major, minor = 0n} = stateVersionFromString(lastWatermark);\n const tx = stateVersionToString({\n major,\n minor: BigInt(minor) + 1n,\n });\n\n void changeStream.push([\n 'begin',\n {tag: 'begin', json: this.#jsonFormat},\n {commitWatermark: tx},\n ]);\n return (backfillTx = tx);\n };\n\n const commitTx = () => {\n if (backfillTx) {\n void changeStream.push([\n 'commit',\n {tag: 'commit'},\n {watermark: backfillTx},\n ]);\n changeStream.release(backfillTx);\n }\n backfillTx = null;\n };\n\n for await (const msg of this.#backfillStreamer(state.request)) {\n // Before sending `backfill-completed`, the main replication stream\n // may need to catch up.\n const mustWaitBeforeFlush =\n msg.tag === 'backfill-completed' &&\n this.#changeStreamReached(lc, msg.watermark);\n\n // If necessary, yield the reservation to the main stream.\n if (\n backfillTx &&\n (changeStream.waiterDelay() > 0 || mustWaitBeforeFlush)\n ) {\n commitTx();\n }\n\n await mustWaitBeforeFlush;\n\n if (\n msg.tag === 'backfill' &&\n msg.rowValues.length > 0 &&\n msg.relation.rowKey.columns.length === 0\n ) {\n throw new MissingRowKeyError(state.request);\n }\n\n // Reserve the changeStreamer if not in a transaction.\n if ((backfillTx ??= await beginTxFor(msg)) === null) {\n lc.info?.(\n `backfill stream canceled: ${state.canceledReason}`,\n state.request,\n );\n this.#checkAndStartBackfill(); // start the next backfill if present\n return; // this backfill is canceled\n }\n\n // `await` to allow the change streamer to exert back pressure\n // on backfills.\n await changeStream.push(['data', msg]);\n }\n\n // Flush any final tx and release the stream.\n backfillTx && commitTx();\n lc.debug?.(`backfill stream exited`, state.canceledReason ?? '');\n }\n\n #backfillRunningFor(table: Identifier): RunningBackfillState | null {\n const state = this.#runningBackfill;\n return state?.request.table.schema === table.schema &&\n state.request.table.name === table.name\n ? state\n : null;\n }\n\n /**\n * Stops the running backfill for the specified `reason`. If `instance` is\n * specified, the running backfill is stopped only if it is that instance.\n * This allows the running backfill itself to clear backfill state without\n * accidentally stopping a different (e.g. subsequent) backfill.\n */\n #stopRunningBackfill(reason?: string, instance?: RunningBackfillState) {\n const backfill = this.#runningBackfill;\n if (backfill && backfill === (instance ?? backfill)) {\n backfill.canceledReason = reason;\n this.#runningBackfill = null;\n reason && this.#lc.info?.(`canceling backfill:`, reason);\n }\n }\n\n #setRequiredBackfill(source: string, req: BackfillRequest) {\n const action = this.#requiredBackfills.has(req.table) ? 'updated' : 'added';\n this.#lc.info?.(`Backfill ${action}: ${source}`, {backfill: req});\n this.#requiredBackfills.set(req.table, req);\n }\n\n #deleteRequiredBackfill(source: string, id: Identifier) {\n const req = this.#requiredBackfills.get(id);\n if (req) {\n const action = source === 'backfill-completed' ? 'completed' : 'dropped';\n this.#lc.info?.(`Backfill ${action}: ${source}`, {backfill: req});\n this.#requiredBackfills.delete(id);\n }\n }\n\n /**\n * Implements {@link Listener.onChange()}, invoked by the\n * {@link ChangeStreamMultiplexer}.\n */\n onChange(message: ChangeStreamMessage): void {\n if (message[0] === 'begin') {\n this.#currentTxWatermark = message[2].commitWatermark;\n return;\n }\n if (message[0] === 'commit') {\n this.#currentTxWatermark = null;\n this.#setLastStatusWatermark(message[2]);\n // Every commit is a candidate for starting the next backfill\n // (if one is not currently running).\n this.#checkAndStartBackfill();\n return;\n }\n if (message[0] === 'status') {\n this.#setLastStatusWatermark(message[2]);\n return;\n }\n if (message[0] !== 'data') {\n return;\n }\n const change = message[1];\n const {tag} = change;\n switch (tag) {\n case 'update-table-metadata': {\n const {table, new: metadata} = change;\n const backfillRequest = this.#requiredBackfills.get(table);\n if (backfillRequest) {\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n table: {...backfillRequest.table, metadata},\n });\n if (this.#backfillRunningFor(table)) {\n this.#stopRunningBackfill(`TableMetadata updated`);\n }\n }\n break;\n }\n case 'create-table': {\n const {\n spec: {schema, name},\n metadata = null,\n backfill,\n } = change;\n\n if (backfill) {\n this.#setRequiredBackfill(tag, {\n table: {schema, name, metadata},\n columns: backfill,\n });\n }\n break;\n }\n case 'rename-table': {\n const {old, new: newTable} = change;\n const backfillRequest = this.#requiredBackfills.get(old);\n if (backfillRequest) {\n const {schema, name} = newTable;\n this.#deleteRequiredBackfill(tag, old);\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n table: {...backfillRequest.table, schema, name},\n });\n if (this.#backfillRunningFor(old)) {\n this.#stopRunningBackfill(`table renamed`);\n }\n }\n break;\n }\n case 'drop-table': {\n const {id} = change;\n const backfillRequest = this.#requiredBackfills.get(id);\n if (backfillRequest) {\n this.#deleteRequiredBackfill(tag, id);\n if (this.#backfillRunningFor(id)) {\n this.#stopRunningBackfill(`table dropped`);\n }\n }\n break;\n }\n case 'add-column': {\n const {\n table,\n tableMetadata: metadata = null,\n column,\n backfill,\n } = change;\n if (backfill) {\n const backfillRequest = this.#requiredBackfills.get(table);\n if (!backfillRequest) {\n this.#setRequiredBackfill(tag, {\n table: {...table, metadata},\n columns: {[column.name]: backfill},\n });\n } else {\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n table: {...backfillRequest.table, metadata},\n columns: {\n ...backfillRequest.columns,\n [column.name]: backfill,\n },\n });\n // Note: The running backfill need not be canceled if a\n // new column is added. The new column will be backfilled\n // by its own stream after the current backfill completes.\n }\n }\n break;\n }\n case 'update-column': {\n const {\n table,\n old: {name: oldName},\n new: {name: newName},\n } = change;\n if (oldName !== newName) {\n const backfillRequest = this.#requiredBackfills.get(table);\n if (backfillRequest && oldName in backfillRequest.columns) {\n const {[oldName]: colSpec, ...otherCols} = backfillRequest.columns;\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n columns: {...otherCols, [newName]: colSpec},\n });\n const backfill = this.#backfillRunningFor(table);\n if (backfill && oldName in backfill.request.columns) {\n this.#stopRunningBackfill(`column renamed`);\n }\n }\n }\n break;\n }\n case 'drop-column': {\n const {table, column} = change;\n const backfillRequest = this.#requiredBackfills.get(table);\n if (backfillRequest && column in backfillRequest.columns) {\n const {[column]: _excluded, ...remaining} = backfillRequest.columns;\n if (Object.keys(remaining).length === 0) {\n this.#deleteRequiredBackfill(tag, table);\n } else {\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n columns: remaining,\n });\n }\n const backfill = this.#backfillRunningFor(table);\n if (backfill && column in backfill.request.columns) {\n this.#stopRunningBackfill(`column dropped`);\n }\n }\n break;\n }\n case 'update': {\n const {relation, key, new: row} = change;\n const backfill = this.#backfillRunningFor(relation);\n const txWatermark = must(this.#currentTxWatermark, `not in a tx`);\n if (backfill?.request.table.metadata && key !== null) {\n // A corner case that backfill is unable to correctly handle is\n // when a row's key changes; this is decomposed into a delete\n // of the old key and a set of the new key in the replica change\n // log, at which point the backfill algorithm assumes that the\n // (old) row is deleted but does not know to backfill the new row.\n // In these corner cases, the current backfill is canceled and\n // retried if its version precedes this update.\n for (const col of Object.keys(\n backfill.request.table.metadata.rowKey,\n )) {\n if (key[col] !== row[col]) {\n backfill.minWatermark = txWatermark;\n this.#lc.info?.(\n `key for row as changed (col: ${col}). ` +\n `backfill data must not predate ${backfill.minWatermark}`,\n );\n break;\n }\n }\n }\n break;\n }\n case 'backfill-completed': {\n const {relation, columns} = change;\n const backfillRequest = this.#requiredBackfills.get(relation);\n assert(\n backfillRequest,\n () => `No BackfillRequest completed backfill ${stringify(change)}`,\n );\n const remaining = Object.entries(backfillRequest.columns).filter(\n ([col]) =>\n !(columns.includes(col) || relation.rowKey.columns.includes(col)),\n );\n if (remaining.length === 0) {\n this.#deleteRequiredBackfill(tag, relation);\n } else {\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n columns: Object.fromEntries(remaining),\n });\n }\n // Technically the backfill is already stopping, but this method\n // cleans up the state that tracks it.\n this.#stopRunningBackfill();\n break;\n }\n }\n }\n\n cancel(): void {\n this.#stopRunningBackfill(`change stream canceled`);\n clearTimeout(this.#backfillRetryTimer);\n }\n}\n\nabstract class BackfillStreamError extends Error {\n constructor(bf: BackfillRequest, msg: string, cause?: unknown) {\n super(\n `Cannot backfill ${bf.table.schema}.${bf.table.name}` +\n `[${Object.keys(bf.columns).join(',')}]: ${msg}`,\n {cause},\n );\n }\n}\n\n/**\n * Background: The zero-cache supports replication of tables without a\n * PRIMARY KEY to facilitate the onboarding process. These rows can be\n * INSERT'ed, but postgres will rightfully prohibit UPDATEs and DELETEs\n * on such tables because the rows cannot be identified by a key. Supporting\n * this mode of replication allows the user to \"fix\" the setup by adding the\n * primary key, after which the table can be published downstream without\n * requiring a resync of the data.\n *\n * In terms of backfill, however, non-empty tables without a row key **cannot**\n * be backfilled, because backfill retries would result in writing duplicating\n * rows. (Empty tables, on the other hand, are fine because there is no data\n * to be deduped.)\n *\n * The MissingRowKeyError is used to signal that the table cannot be backfilled\n * in its current state. For simplicity, it is handled like runtime errors and\n * retried with backoff, with which it can eventually succeed if (1) a primary\n * key is added or (2) the table is emptied, e.g. via a TRUNCATE.\n */\nclass MissingRowKeyError extends BackfillStreamError {\n readonly name = 'MissingRowKeyError';\n\n constructor(bf: BackfillRequest, cause?: unknown) {\n super(bf, `\"${bf.table.name}\" is missing a PRIMARY KEY`, cause);\n }\n}\n\n/**\n * Error type for backfill stream implementations to throw indicating that\n * the backfill request failed to due a schema incompatibility error. This\n * type of error does not need exponential backoff, as the retry happens\n * naturally once the invalidating schema change is processed and committed.\n */\nexport class SchemaIncompatibilityError extends BackfillStreamError {\n readonly name = 'SchemaIncompatibilityError';\n\n constructor(bf: BackfillRequest, msg: string, cause?: unknown) {\n super(bf, msg, cause);\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"backfill-manager.js","sources":["../../../../../../../zero-cache/src/services/change-source/common/backfill-manager.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {assert} from '../../../../../shared/src/asserts.ts';\nimport {stringify} from '../../../../../shared/src/bigint-json.ts';\nimport {CustomKeyMap} from '../../../../../shared/src/custom-key-map.ts';\nimport {must} from '../../../../../shared/src/must.ts';\nimport {randInt} from '../../../../../shared/src/rand.ts';\nimport {JSON_STRINGIFIED, type JSONFormat} from '../../../types/lite.ts';\nimport {\n stateVersionFromString,\n stateVersionToString,\n} from '../../../types/state-version.ts';\nimport type {\n BackfillCompleted,\n BackfillRequest,\n ChangeStreamMessage,\n Identifier,\n MessageBackfill,\n} from '../protocol/current.ts';\nimport type {\n Cancelable,\n ChangeStreamMultiplexer,\n Listener,\n} from './change-stream-multiplexer.ts';\n\nfunction tableKey({schema, name}: Identifier) {\n return `${schema}.${name}`;\n}\n\ntype BackfillStreamer = (\n req: BackfillRequest,\n) => AsyncGenerator<MessageBackfill | BackfillCompleted>;\n\ntype RunningBackfillState = {\n request: BackfillRequest;\n canceledReason?: string | undefined;\n minWatermark: string;\n};\n\nconst MIN_BACKOFF_INTERVAL_MS = 2_000;\nconst MAX_BACKOFF_INTERVAL_MS = 60_000;\n\ntype AwaitingStatusWatermark = {\n watermark: string;\n reached: () => void;\n};\n\n/**\n * The BackfillManager initiates backfills for BackfillRequests from the\n * change-streamer (i.e. unfinished backfills from previous sessions)\n * or for new backfills signaled by `create-table` or `add-column` messages\n * from the change-source.\n *\n * The BackfillManager registers itself as a change stream listener in order\n * to track necessary backfills, and potentially invalidate the in-progress\n * backfill (e.g. due to a schema change) so that it can be retried at a\n * new snapshot.\n *\n * The manager also handles low priority streaming of the backfill messages\n * using the {@link ChangeStreamMultiplexer}, implementing a policy of always\n * releasing its reservation if another producer (i.e. the main change stream)\n * has messages to stream.\n */\nexport class BackfillManager implements Cancelable, Listener {\n readonly #lc: LogContext;\n\n /**\n * Tracks the metadata of required backfills based on schema changes\n * and initial backfill requests.\n */\n readonly #requiredBackfills = new CustomKeyMap<Identifier, BackfillRequest>(\n tableKey,\n );\n readonly #changeStreamer: ChangeStreamMultiplexer;\n readonly #backfillStreamer: BackfillStreamer;\n readonly #jsonFormat: JSONFormat;\n\n /**\n * The current running backfill. The backfill request is always also in\n * `#requiredBackfills` (technically, it can be a subset of what's in\n * `#requiredBackfills`); the request is removed from `#requiredBackfills`\n * upon completion.\n */\n #runningBackfill: RunningBackfillState | null = null;\n\n /** The last seen watermark in the change stream. */\n #lastStatusWatermark: string | null = null;\n\n readonly #awaitingStatusWatermarks: AwaitingStatusWatermark[] = [];\n\n /** The watermark of the current transaction in the change stream. */\n #currentTxWatermark: string | null = null;\n\n constructor(\n lc: LogContext,\n changeStreamer: ChangeStreamMultiplexer,\n backfillStreamer: BackfillStreamer,\n jsonFormat: JSONFormat = JSON_STRINGIFIED,\n minBackoffMs = MIN_BACKOFF_INTERVAL_MS,\n maxBackoffMs = MAX_BACKOFF_INTERVAL_MS,\n ) {\n this.#lc = lc.withContext('component', 'backfill-manager');\n this.#changeStreamer = changeStreamer;\n this.#backfillStreamer = backfillStreamer;\n this.#jsonFormat = jsonFormat;\n this.#minBackoffMs = minBackoffMs;\n this.#maxBackoffMs = maxBackoffMs;\n this.#retryDelayMs = minBackoffMs;\n }\n\n run(lastWatermark: string, initialRequests: BackfillRequest[]) {\n this.#lc.info?.(\n `starting backfill manager with ${initialRequests.length} initial requests`,\n {requests: initialRequests},\n );\n this.#lastStatusWatermark = lastWatermark;\n initialRequests.forEach(req =>\n this.#setRequiredBackfill('initial-request', req),\n );\n this.#checkAndStartBackfill();\n }\n\n #setLastStatusWatermark({watermark}: {watermark: string}) {\n // Only allow the watermark to move forward. This prevents a backfill\n // transaction (whose watermark is unrelated to change-stream state)\n // from moving the watermark backwards.\n if ((this.#lastStatusWatermark ?? '') < watermark) {\n this.#lastStatusWatermark = watermark;\n for (let i = this.#awaitingStatusWatermarks.length - 1; i >= 0; i--) {\n const awaiting = this.#awaitingStatusWatermarks[i];\n if (watermark >= awaiting.watermark) {\n awaiting.reached();\n this.#awaitingStatusWatermarks.splice(i, 1);\n }\n }\n }\n }\n\n #changeStreamReached(\n lc: LogContext,\n watermark: string,\n ): Promise<void> | null {\n if ((this.#lastStatusWatermark ?? '') < watermark) {\n const {promise, resolve: reached} = resolver();\n this.#awaitingStatusWatermarks.push({watermark, reached});\n lc.info?.(\n `waiting for change stream (at ${this.#lastStatusWatermark}) to reach ${watermark}`,\n );\n return promise;\n }\n return null;\n }\n\n readonly #minBackoffMs: number;\n readonly #maxBackoffMs: number;\n #retryDelayMs: number;\n #backfillRetryTimer: NodeJS.Timeout | undefined;\n\n #checkAndStartBackfill() {\n if (\n !this.#backfillRetryTimer &&\n !this.#runningBackfill &&\n this.#requiredBackfills.size\n ) {\n // Pick a random backfill to avoid head-of-line blocking by a\n // problematic backfill (e.g. awaiting a primary key). This is\n // simpler that adding logic to classify (and declassify)\n // problematic backfills.\n const candidates = [...this.#requiredBackfills.values()];\n const request = candidates[randInt(0, candidates.length - 1)];\n const state = {request, minWatermark: ''};\n const lc = this.#lc.withContext('table', request.table.name);\n\n this.#runningBackfill = state;\n void this.#runBackfill(lc, state)\n .then(() => {\n this.#stopRunningBackfill('backfill exited', state);\n this.#retryDelayMs = this.#minBackoffMs; // reset on success\n })\n // For unexpected errors (e.g. upstream replication slot\n // unavailability), retry with exponential backoff.\n .catch(e => {\n this.#stopRunningBackfill(String(e), state);\n this.#retryBackfillWithBackoff(e);\n });\n }\n }\n\n #retryBackfillWithBackoff(e: unknown) {\n const log = this.#retryDelayMs === this.#maxBackoffMs ? 'error' : 'warn';\n this.#lc[log]?.(\n `Error running backfill. Retrying in ${this.#retryDelayMs} ms`,\n e,\n );\n this.#backfillRetryTimer = setTimeout(() => {\n this.#backfillRetryTimer = undefined;\n this.#checkAndStartBackfill();\n }, this.#retryDelayMs);\n\n this.#retryDelayMs = Math.min(this.#retryDelayMs * 2, this.#maxBackoffMs);\n }\n\n async #runBackfill(lc: LogContext, state: RunningBackfillState) {\n const changeStream = this.#changeStreamer; // Purely for readability\n\n // backfillTx is set if and only if a changeStreamer reservation has been\n // acquired and the backfill stream is inside a transaction.\n let backfillTx: string | null = null;\n\n /**\n * @returns the new tx watermark, or null if backfill was cancelled\n */\n const beginTxFor = async (\n msg: MessageBackfill | BackfillCompleted,\n ): Promise<string | null> => {\n assert(backfillTx === null, 'Expected no active backfill transaction');\n const lastWatermark = await changeStream.reserve('backfill');\n\n // After obtaining the changeStream reservation, check if the stream\n // had changes that resulted in invalidating / canceling this backfill.\n if (\n state.canceledReason ||\n (msg.tag === 'backfill' && msg.watermark < state.minWatermark)\n ) {\n if (state.canceledReason === undefined) {\n assert(msg.tag === 'backfill', 'Expected backfill message tag'); // TypeScript should have figured this out.\n this.#stopRunningBackfill(\n `row key change at ${state.minWatermark} ` +\n `postdates backfill watermark at ${msg.watermark}`,\n state,\n );\n }\n changeStream.release(lastWatermark);\n return null;\n }\n\n const {major, minor = 0n} = stateVersionFromString(lastWatermark);\n const tx = stateVersionToString({\n major,\n minor: BigInt(minor) + 1n,\n });\n\n void changeStream.push([\n 'begin',\n {tag: 'begin', json: this.#jsonFormat, skipAck: true},\n {commitWatermark: tx},\n ]);\n return (backfillTx = tx);\n };\n\n const commitTx = () => {\n if (backfillTx) {\n void changeStream.push([\n 'commit',\n {tag: 'commit'},\n {watermark: backfillTx},\n ]);\n changeStream.release(backfillTx);\n }\n backfillTx = null;\n };\n\n for await (const msg of this.#backfillStreamer(state.request)) {\n // Before sending `backfill-completed`, the main replication stream\n // may need to catch up.\n const mustWaitBeforeFlush =\n msg.tag === 'backfill-completed' &&\n this.#changeStreamReached(lc, msg.watermark);\n\n // If necessary, yield the reservation to the main stream.\n if (\n backfillTx &&\n (changeStream.waiterDelay() > 0 || mustWaitBeforeFlush)\n ) {\n commitTx();\n }\n\n mustWaitBeforeFlush && (await mustWaitBeforeFlush);\n\n if (\n msg.tag === 'backfill' &&\n msg.rowValues.length > 0 &&\n msg.relation.rowKey.columns.length === 0\n ) {\n throw new MissingRowKeyError(state.request);\n }\n\n // Reserve the changeStreamer if not in a transaction.\n if ((backfillTx ??= await beginTxFor(msg)) === null) {\n lc.info?.(\n `backfill stream canceled: ${state.canceledReason}`,\n state.request,\n );\n this.#checkAndStartBackfill(); // start the next backfill if present\n return; // this backfill is canceled\n }\n\n // `await` to allow the change streamer to exert back pressure\n // on backfills.\n await changeStream.push(['data', msg]);\n }\n\n // Flush any final tx and release the stream.\n backfillTx && commitTx();\n lc.debug?.(`backfill stream exited`, state.canceledReason ?? '');\n }\n\n #backfillRunningFor(table: Identifier): RunningBackfillState | null {\n const state = this.#runningBackfill;\n return state?.request.table.schema === table.schema &&\n state.request.table.name === table.name\n ? state\n : null;\n }\n\n /**\n * Stops the running backfill for the specified `reason`. If `instance` is\n * specified, the running backfill is stopped only if it is that instance.\n * This allows the running backfill itself to clear backfill state without\n * accidentally stopping a different (e.g. subsequent) backfill.\n */\n #stopRunningBackfill(reason?: string, instance?: RunningBackfillState) {\n const backfill = this.#runningBackfill;\n if (backfill && backfill === (instance ?? backfill)) {\n backfill.canceledReason = reason;\n this.#runningBackfill = null;\n reason && this.#lc.info?.(`canceling backfill:`, reason);\n }\n }\n\n #setRequiredBackfill(source: string, req: BackfillRequest) {\n const action = this.#requiredBackfills.has(req.table) ? 'updated' : 'added';\n this.#lc.info?.(`Backfill ${action}: ${source}`, {backfill: req});\n this.#requiredBackfills.set(req.table, req);\n }\n\n #deleteRequiredBackfill(source: string, id: Identifier) {\n const req = this.#requiredBackfills.get(id);\n if (req) {\n const action = source === 'backfill-completed' ? 'completed' : 'dropped';\n this.#lc.info?.(`Backfill ${action}: ${source}`, {backfill: req});\n this.#requiredBackfills.delete(id);\n }\n }\n\n /**\n * Implements {@link Listener.onChange()}, invoked by the\n * {@link ChangeStreamMultiplexer}.\n */\n onChange(message: ChangeStreamMessage): void {\n if (message[0] === 'begin') {\n this.#currentTxWatermark = message[2].commitWatermark;\n return;\n }\n if (message[0] === 'commit') {\n this.#currentTxWatermark = null;\n this.#setLastStatusWatermark(message[2]);\n // Every commit is a candidate for starting the next backfill\n // (if one is not currently running).\n this.#checkAndStartBackfill();\n return;\n }\n if (message[0] === 'status') {\n this.#setLastStatusWatermark(message[2]);\n return;\n }\n if (message[0] !== 'data') {\n return;\n }\n const change = message[1];\n const {tag} = change;\n switch (tag) {\n case 'update-table-metadata': {\n const {table, new: metadata} = change;\n const backfillRequest = this.#requiredBackfills.get(table);\n if (backfillRequest) {\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n table: {...backfillRequest.table, metadata},\n });\n if (this.#backfillRunningFor(table)) {\n this.#stopRunningBackfill(`TableMetadata updated`);\n }\n }\n break;\n }\n case 'create-table': {\n const {\n spec: {schema, name},\n metadata = null,\n backfill,\n } = change;\n\n if (backfill) {\n this.#setRequiredBackfill(tag, {\n table: {schema, name, metadata},\n columns: backfill,\n });\n }\n break;\n }\n case 'rename-table': {\n const {old, new: newTable} = change;\n const backfillRequest = this.#requiredBackfills.get(old);\n if (backfillRequest) {\n const {schema, name} = newTable;\n this.#deleteRequiredBackfill(tag, old);\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n table: {...backfillRequest.table, schema, name},\n });\n if (this.#backfillRunningFor(old)) {\n this.#stopRunningBackfill(`table renamed`);\n }\n }\n break;\n }\n case 'drop-table': {\n const {id} = change;\n const backfillRequest = this.#requiredBackfills.get(id);\n if (backfillRequest) {\n this.#deleteRequiredBackfill(tag, id);\n if (this.#backfillRunningFor(id)) {\n this.#stopRunningBackfill(`table dropped`);\n }\n }\n break;\n }\n case 'add-column': {\n const {\n table,\n tableMetadata: metadata = null,\n column,\n backfill,\n } = change;\n if (backfill) {\n const backfillRequest = this.#requiredBackfills.get(table);\n if (!backfillRequest) {\n this.#setRequiredBackfill(tag, {\n table: {...table, metadata},\n columns: {[column.name]: backfill},\n });\n } else {\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n table: {...backfillRequest.table, metadata},\n columns: {\n ...backfillRequest.columns,\n [column.name]: backfill,\n },\n });\n // Note: The running backfill need not be canceled if a\n // new column is added. The new column will be backfilled\n // by its own stream after the current backfill completes.\n }\n }\n break;\n }\n case 'update-column': {\n const {\n table,\n old: {name: oldName},\n new: {name: newName},\n } = change;\n if (oldName !== newName) {\n const backfillRequest = this.#requiredBackfills.get(table);\n if (backfillRequest && oldName in backfillRequest.columns) {\n const {[oldName]: colSpec, ...otherCols} = backfillRequest.columns;\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n columns: {...otherCols, [newName]: colSpec},\n });\n const backfill = this.#backfillRunningFor(table);\n if (backfill && oldName in backfill.request.columns) {\n this.#stopRunningBackfill(`column renamed`);\n }\n }\n }\n break;\n }\n case 'drop-column': {\n const {table, column} = change;\n const backfillRequest = this.#requiredBackfills.get(table);\n if (backfillRequest && column in backfillRequest.columns) {\n const {[column]: _excluded, ...remaining} = backfillRequest.columns;\n if (Object.keys(remaining).length === 0) {\n this.#deleteRequiredBackfill(tag, table);\n } else {\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n columns: remaining,\n });\n }\n const backfill = this.#backfillRunningFor(table);\n if (backfill && column in backfill.request.columns) {\n this.#stopRunningBackfill(`column dropped`);\n }\n }\n break;\n }\n case 'update': {\n const {relation, key, new: row} = change;\n const backfill = this.#backfillRunningFor(relation);\n const txWatermark = must(this.#currentTxWatermark, `not in a tx`);\n if (backfill?.request.table.metadata && key !== null) {\n // A corner case that backfill is unable to correctly handle is\n // when a row's key changes; this is decomposed into a delete\n // of the old key and a set of the new key in the replica change\n // log, at which point the backfill algorithm assumes that the\n // (old) row is deleted but does not know to backfill the new row.\n // In these corner cases, the current backfill is canceled and\n // retried if its version precedes this update.\n for (const col of Object.keys(\n backfill.request.table.metadata.rowKey,\n )) {\n if (key[col] !== row[col]) {\n backfill.minWatermark = txWatermark;\n this.#lc.info?.(\n `key for row as changed (col: ${col}). ` +\n `backfill data must not predate ${backfill.minWatermark}`,\n );\n break;\n }\n }\n }\n break;\n }\n case 'backfill-completed': {\n const {relation, columns} = change;\n const backfillRequest = this.#requiredBackfills.get(relation);\n assert(\n backfillRequest,\n () => `No BackfillRequest completed backfill ${stringify(change)}`,\n );\n const remaining = Object.entries(backfillRequest.columns).filter(\n ([col]) =>\n !(columns.includes(col) || relation.rowKey.columns.includes(col)),\n );\n if (remaining.length === 0) {\n this.#deleteRequiredBackfill(tag, relation);\n } else {\n this.#setRequiredBackfill(tag, {\n ...backfillRequest,\n columns: Object.fromEntries(remaining),\n });\n }\n // Technically the backfill is already stopping, but this method\n // cleans up the state that tracks it.\n this.#stopRunningBackfill();\n break;\n }\n }\n }\n\n cancel(): void {\n this.#stopRunningBackfill(`change stream canceled`);\n clearTimeout(this.#backfillRetryTimer);\n }\n}\n\nabstract class BackfillStreamError extends Error {\n constructor(bf: BackfillRequest, msg: string, cause?: unknown) {\n super(\n `Cannot backfill ${bf.table.schema}.${bf.table.name}` +\n `[${Object.keys(bf.columns).join(',')}]: ${msg}`,\n {cause},\n );\n }\n}\n\n/**\n * Background: The zero-cache supports replication of tables without a\n * PRIMARY KEY to facilitate the onboarding process. These rows can be\n * INSERT'ed, but postgres will rightfully prohibit UPDATEs and DELETEs\n * on such tables because the rows cannot be identified by a key. Supporting\n * this mode of replication allows the user to \"fix\" the setup by adding the\n * primary key, after which the table can be published downstream without\n * requiring a resync of the data.\n *\n * In terms of backfill, however, non-empty tables without a row key **cannot**\n * be backfilled, because backfill retries would result in writing duplicating\n * rows. (Empty tables, on the other hand, are fine because there is no data\n * to be deduped.)\n *\n * The MissingRowKeyError is used to signal that the table cannot be backfilled\n * in its current state. For simplicity, it is handled like runtime errors and\n * retried with backoff, with which it can eventually succeed if (1) a primary\n * key is added or (2) the table is emptied, e.g. via a TRUNCATE.\n */\nclass MissingRowKeyError extends BackfillStreamError {\n readonly name = 'MissingRowKeyError';\n\n constructor(bf: BackfillRequest, cause?: unknown) {\n super(bf, `\"${bf.table.name}\" is missing a PRIMARY KEY`, cause);\n }\n}\n\n/**\n * Error type for backfill stream implementations to throw indicating that\n * the backfill request failed due to a schema incompatibility error. This\n * type of error does not need exponential backoff, as the retry happens\n * naturally once the invalidating schema change is processed and committed.\n */\nexport class SchemaIncompatibilityError extends BackfillStreamError {\n readonly name = 'SchemaIncompatibilityError';\n\n constructor(bf: BackfillRequest, msg: string, cause?: unknown) {\n super(bf, msg, cause);\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replica-schema.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/change-source/common/replica-schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,iCAAiC,CAAC;AAE9D,OAAO,EAEL,KAAK,uBAAuB,EAE7B,MAAM,+BAA+B,CAAC;AASvC,wBAAsB,WAAW,CAC/B,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,GAC3D,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,iBAgBf;AAED,eAAO,MAAM,+BAA+B,0UAW3C,CAAC;AAEF,eAAO,MAAM,oBAAoB,8XAUhC,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,
|
|
1
|
+
{"version":3,"file":"replica-schema.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/change-source/common/replica-schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,iCAAiC,CAAC;AAE9D,OAAO,EAEL,KAAK,uBAAuB,EAE7B,MAAM,+BAA+B,CAAC;AASvC,wBAAsB,WAAW,CAC/B,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,GAC3D,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,iBAgBf;AAED,eAAO,MAAM,+BAA+B,0UAW3C,CAAC;AAEF,eAAO,MAAM,oBAAoB,8XAUhC,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,uBA4EvC,CAAC"}
|
|
@@ -124,6 +124,17 @@ const schemaVersionMigrationMap = {
|
|
|
124
124
|
` + CREATE_TABLE_METADATA_TABLE
|
|
125
125
|
);
|
|
126
126
|
}
|
|
127
|
+
},
|
|
128
|
+
10: {
|
|
129
|
+
migrateSchema: (_, db) => {
|
|
130
|
+
db.exec(
|
|
131
|
+
/*sql*/
|
|
132
|
+
`
|
|
133
|
+
ALTER TABLE "_zero.replicationConfig"
|
|
134
|
+
ADD COLUMN "initialSyncContext" TEXT DEFAULT '{}';
|
|
135
|
+
`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
127
138
|
}
|
|
128
139
|
};
|
|
129
140
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replica-schema.js","sources":["../../../../../../../zero-cache/src/services/change-source/common/replica-schema.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {SqliteError} from '@rocicorp/zero-sqlite3';\nimport type {Database} from '../../../../../zqlite/src/db.ts';\nimport {listTables} from '../../../db/lite-tables.ts';\nimport {\n runSchemaMigrations,\n type IncrementalMigrationMap,\n type Migration,\n} from '../../../db/migration-lite.ts';\nimport {AutoResetSignal} from '../../change-streamer/schema/tables.ts';\nimport {populateFromExistingTables} from '../../replicator/schema/column-metadata.ts';\nimport {\n CREATE_RUNTIME_EVENTS_TABLE,\n recordEvent,\n} from '../../replicator/schema/replication-state.ts';\nimport {CREATE_TABLE_METADATA_TABLE} from '../../replicator/schema/table-metadata.ts';\n\nexport async function initReplica(\n log: LogContext,\n debugName: string,\n dbPath: string,\n initialSync: (lc: LogContext, tx: Database) => Promise<void>,\n): Promise<void> {\n const setupMigration: Migration = {\n migrateSchema: (log, tx) => initialSync(log, tx),\n minSafeVersion: 1,\n };\n\n try {\n await runSchemaMigrations(\n log,\n debugName,\n dbPath,\n setupMigration,\n schemaVersionMigrationMap,\n );\n } catch (e) {\n if (e instanceof SqliteError && e.code === 'SQLITE_CORRUPT') {\n throw new AutoResetSignal(e.message);\n }\n throw e;\n }\n}\n\nexport async function upgradeReplica(\n log: LogContext,\n debugName: string,\n dbPath: string,\n) {\n await runSchemaMigrations(\n log,\n debugName,\n dbPath,\n // setupMigration should never be invoked\n {\n migrateSchema: () => {\n throw new Error(\n 'This should only be called for already synced replicas',\n );\n },\n },\n schemaVersionMigrationMap,\n );\n}\n\nexport const CREATE_V6_COLUMN_METADATA_TABLE = /*sql*/ `\n CREATE TABLE \"_zero.column_metadata\" (\n table_name TEXT NOT NULL,\n column_name TEXT NOT NULL,\n upstream_type TEXT NOT NULL,\n is_not_null INTEGER NOT NULL,\n is_enum INTEGER NOT NULL,\n is_array INTEGER NOT NULL,\n character_max_length INTEGER,\n PRIMARY KEY (table_name, column_name)\n );\n`;\n\nexport const CREATE_V7_CHANGE_LOG = /*sql*/ `\n CREATE TABLE \"_zero.changeLog2\" (\n \"stateVersion\" TEXT NOT NULL,\n \"pos\" INT NOT NULL,\n \"table\" TEXT NOT NULL,\n \"rowKey\" TEXT NOT NULL,\n \"op\" TEXT NOT NULL,\n PRIMARY KEY(\"stateVersion\", \"pos\"),\n UNIQUE(\"table\", \"rowKey\")\n );\n`;\n\nexport const schemaVersionMigrationMap: IncrementalMigrationMap = {\n // There's no incremental migration from v1. Just reset the replica.\n 4: {\n migrateSchema: () => {\n throw new AutoResetSignal('upgrading replica to new schema');\n },\n minSafeVersion: 3,\n },\n\n 5: {\n migrateSchema: (_, db) => {\n db.exec(CREATE_RUNTIME_EVENTS_TABLE);\n },\n migrateData: (_, db) => {\n recordEvent(db, 'upgrade');\n },\n },\n\n // Revised in the migration to v8 because the v6 code was incomplete.\n 6: {},\n\n 7: {\n migrateSchema: (_, db) => {\n // Note: The original \"changeLog\" table is kept so that the replica file\n // is compatible with older zero-caches. However, it is truncated for\n // space savings (since historic changes were never read).\n db.exec(`DELETE FROM \"_zero.changeLog\"`);\n // First version of changeLog2\n db.exec(CREATE_V7_CHANGE_LOG);\n },\n },\n\n 8: {\n migrateSchema: (_, db) => {\n const tableExists = db\n .prepare(\n `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = '_zero.column_metadata'`,\n )\n .get();\n\n if (!tableExists) {\n db.exec(CREATE_V6_COLUMN_METADATA_TABLE);\n }\n },\n migrateData: (_, db) => {\n // Re-populate the ColumnMetadataStore; the original migration\n // at v6 was incomplete, as covered replicas migrated from earlier\n // versions but did not initialize the table for new replicas.\n db.exec(/*sql*/ `DELETE FROM \"_zero.column_metadata\"`);\n\n const tables = listTables(db, false);\n populateFromExistingTables(db, tables);\n },\n },\n\n 9: {\n migrateSchema: (_, db) => {\n db.exec(\n /*sql*/ `\n ALTER TABLE \"_zero.changeLog2\" \n ADD COLUMN \"backfillingColumnVersions\" TEXT DEFAULT '{}';\n ALTER TABLE \"_zero.column_metadata\"\n ADD COLUMN backfill TEXT;\n ` + CREATE_TABLE_METADATA_TABLE,\n );\n },\n },\n};\n"],"names":["log"],"mappings":";;;;;;;AAiBA,eAAsB,YACpB,KACA,WACA,QACA,aACe;AACf,QAAM,iBAA4B;AAAA,IAChC,eAAe,CAACA,MAAK,OAAO,YAAYA,MAAK,EAAE;AAAA,IAC/C,gBAAgB;AAAA,EAAA;AAGlB,MAAI;AACF,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ,SAAS,GAAG;AACV,QAAI,aAAa,eAAe,EAAE,SAAS,kBAAkB;AAC3D,YAAM,IAAI,gBAAgB,EAAE,OAAO;AAAA,IACrC;AACA,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,eACpB,KACA,WACA,QACA;AACA,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,MACE,eAAe,MAAM;AACnB,cAAM,IAAI;AAAA,UACR;AAAA,QAAA;AAAA,MAEJ;AAAA,IAAA;AAAA,IAEF;AAAA,EAAA;AAEJ;AAEO,MAAM;AAAA;AAAA,EAA0C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAahD,MAAM;AAAA;AAAA,EAA+B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYrC,MAAM,4BAAqD;AAAA;AAAA,EAEhE,GAAG;AAAA,IACD,eAAe,MAAM;AACnB,YAAM,IAAI,gBAAgB,iCAAiC;AAAA,IAC7D;AAAA,IACA,gBAAgB;AAAA,EAAA;AAAA,EAGlB,GAAG;AAAA,IACD,eAAe,CAAC,GAAG,OAAO;AACxB,SAAG,KAAK,2BAA2B;AAAA,IACrC;AAAA,IACA,aAAa,CAAC,GAAG,OAAO;AACtB,kBAAY,IAAI,SAAS;AAAA,IAC3B;AAAA,EAAA;AAAA;AAAA,EAIF,GAAG,CAAA;AAAA,EAEH,GAAG;AAAA,IACD,eAAe,CAAC,GAAG,OAAO;AAIxB,SAAG,KAAK,+BAA+B;AAEvC,SAAG,KAAK,oBAAoB;AAAA,IAC9B;AAAA,EAAA;AAAA,EAGF,GAAG;AAAA,IACD,eAAe,CAAC,GAAG,OAAO;AACxB,YAAM,cAAc,GACjB;AAAA,QACC;AAAA,MAAA,EAED,IAAA;AAEH,UAAI,CAAC,aAAa;AAChB,WAAG,KAAK,+BAA+B;AAAA,MACzC;AAAA,IACF;AAAA,IACA,aAAa,CAAC,GAAG,OAAO;AAItB,SAAG;AAAA;AAAA,QAAa;AAAA,MAAA;AAEhB,YAAM,SAAS,WAAW,IAAI,KAAK;AACnC,iCAA2B,IAAI,MAAM;AAAA,IACvC;AAAA,EAAA;AAAA,EAGF,GAAG;AAAA,IACD,eAAe,CAAC,GAAG,OAAO;AACxB,SAAG;AAAA;AAAA,QACO;AAAA;AAAA;AAAA;AAAA;AAAA,UAKN;AAAA,MAAA;AAAA,IAEN;AAAA,EAAA;AAEJ;"}
|
|
1
|
+
{"version":3,"file":"replica-schema.js","sources":["../../../../../../../zero-cache/src/services/change-source/common/replica-schema.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {SqliteError} from '@rocicorp/zero-sqlite3';\nimport type {Database} from '../../../../../zqlite/src/db.ts';\nimport {listTables} from '../../../db/lite-tables.ts';\nimport {\n runSchemaMigrations,\n type IncrementalMigrationMap,\n type Migration,\n} from '../../../db/migration-lite.ts';\nimport {AutoResetSignal} from '../../change-streamer/schema/tables.ts';\nimport {populateFromExistingTables} from '../../replicator/schema/column-metadata.ts';\nimport {\n CREATE_RUNTIME_EVENTS_TABLE,\n recordEvent,\n} from '../../replicator/schema/replication-state.ts';\nimport {CREATE_TABLE_METADATA_TABLE} from '../../replicator/schema/table-metadata.ts';\n\nexport async function initReplica(\n log: LogContext,\n debugName: string,\n dbPath: string,\n initialSync: (lc: LogContext, tx: Database) => Promise<void>,\n): Promise<void> {\n const setupMigration: Migration = {\n migrateSchema: (log, tx) => initialSync(log, tx),\n minSafeVersion: 1,\n };\n\n try {\n await runSchemaMigrations(\n log,\n debugName,\n dbPath,\n setupMigration,\n schemaVersionMigrationMap,\n );\n } catch (e) {\n if (e instanceof SqliteError && e.code === 'SQLITE_CORRUPT') {\n throw new AutoResetSignal(e.message);\n }\n throw e;\n }\n}\n\nexport async function upgradeReplica(\n log: LogContext,\n debugName: string,\n dbPath: string,\n) {\n await runSchemaMigrations(\n log,\n debugName,\n dbPath,\n // setupMigration should never be invoked\n {\n migrateSchema: () => {\n throw new Error(\n 'This should only be called for already synced replicas',\n );\n },\n },\n schemaVersionMigrationMap,\n );\n}\n\nexport const CREATE_V6_COLUMN_METADATA_TABLE = /*sql*/ `\n CREATE TABLE \"_zero.column_metadata\" (\n table_name TEXT NOT NULL,\n column_name TEXT NOT NULL,\n upstream_type TEXT NOT NULL,\n is_not_null INTEGER NOT NULL,\n is_enum INTEGER NOT NULL,\n is_array INTEGER NOT NULL,\n character_max_length INTEGER,\n PRIMARY KEY (table_name, column_name)\n );\n`;\n\nexport const CREATE_V7_CHANGE_LOG = /*sql*/ `\n CREATE TABLE \"_zero.changeLog2\" (\n \"stateVersion\" TEXT NOT NULL,\n \"pos\" INT NOT NULL,\n \"table\" TEXT NOT NULL,\n \"rowKey\" TEXT NOT NULL,\n \"op\" TEXT NOT NULL,\n PRIMARY KEY(\"stateVersion\", \"pos\"),\n UNIQUE(\"table\", \"rowKey\")\n );\n`;\n\nexport const schemaVersionMigrationMap: IncrementalMigrationMap = {\n // There's no incremental migration from v1. Just reset the replica.\n 4: {\n migrateSchema: () => {\n throw new AutoResetSignal('upgrading replica to new schema');\n },\n minSafeVersion: 3,\n },\n\n 5: {\n migrateSchema: (_, db) => {\n db.exec(CREATE_RUNTIME_EVENTS_TABLE);\n },\n migrateData: (_, db) => {\n recordEvent(db, 'upgrade');\n },\n },\n\n // Revised in the migration to v8 because the v6 code was incomplete.\n 6: {},\n\n 7: {\n migrateSchema: (_, db) => {\n // Note: The original \"changeLog\" table is kept so that the replica file\n // is compatible with older zero-caches. However, it is truncated for\n // space savings (since historic changes were never read).\n db.exec(`DELETE FROM \"_zero.changeLog\"`);\n // First version of changeLog2\n db.exec(CREATE_V7_CHANGE_LOG);\n },\n },\n\n 8: {\n migrateSchema: (_, db) => {\n const tableExists = db\n .prepare(\n `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = '_zero.column_metadata'`,\n )\n .get();\n\n if (!tableExists) {\n db.exec(CREATE_V6_COLUMN_METADATA_TABLE);\n }\n },\n migrateData: (_, db) => {\n // Re-populate the ColumnMetadataStore; the original migration\n // at v6 was incomplete, as covered replicas migrated from earlier\n // versions but did not initialize the table for new replicas.\n db.exec(/*sql*/ `DELETE FROM \"_zero.column_metadata\"`);\n\n const tables = listTables(db, false);\n populateFromExistingTables(db, tables);\n },\n },\n\n 9: {\n migrateSchema: (_, db) => {\n db.exec(\n /*sql*/ `\n ALTER TABLE \"_zero.changeLog2\" \n ADD COLUMN \"backfillingColumnVersions\" TEXT DEFAULT '{}';\n ALTER TABLE \"_zero.column_metadata\"\n ADD COLUMN backfill TEXT;\n ` + CREATE_TABLE_METADATA_TABLE,\n );\n },\n },\n\n 10: {\n migrateSchema: (_, db) => {\n db.exec(/*sql*/ `\n ALTER TABLE \"_zero.replicationConfig\" \n ADD COLUMN \"initialSyncContext\" TEXT DEFAULT '{}';\n `);\n },\n },\n};\n"],"names":["log"],"mappings":";;;;;;;AAiBA,eAAsB,YACpB,KACA,WACA,QACA,aACe;AACf,QAAM,iBAA4B;AAAA,IAChC,eAAe,CAACA,MAAK,OAAO,YAAYA,MAAK,EAAE;AAAA,IAC/C,gBAAgB;AAAA,EAAA;AAGlB,MAAI;AACF,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ,SAAS,GAAG;AACV,QAAI,aAAa,eAAe,EAAE,SAAS,kBAAkB;AAC3D,YAAM,IAAI,gBAAgB,EAAE,OAAO;AAAA,IACrC;AACA,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,eACpB,KACA,WACA,QACA;AACA,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,MACE,eAAe,MAAM;AACnB,cAAM,IAAI;AAAA,UACR;AAAA,QAAA;AAAA,MAEJ;AAAA,IAAA;AAAA,IAEF;AAAA,EAAA;AAEJ;AAEO,MAAM;AAAA;AAAA,EAA0C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAahD,MAAM;AAAA;AAAA,EAA+B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYrC,MAAM,4BAAqD;AAAA;AAAA,EAEhE,GAAG;AAAA,IACD,eAAe,MAAM;AACnB,YAAM,IAAI,gBAAgB,iCAAiC;AAAA,IAC7D;AAAA,IACA,gBAAgB;AAAA,EAAA;AAAA,EAGlB,GAAG;AAAA,IACD,eAAe,CAAC,GAAG,OAAO;AACxB,SAAG,KAAK,2BAA2B;AAAA,IACrC;AAAA,IACA,aAAa,CAAC,GAAG,OAAO;AACtB,kBAAY,IAAI,SAAS;AAAA,IAC3B;AAAA,EAAA;AAAA;AAAA,EAIF,GAAG,CAAA;AAAA,EAEH,GAAG;AAAA,IACD,eAAe,CAAC,GAAG,OAAO;AAIxB,SAAG,KAAK,+BAA+B;AAEvC,SAAG,KAAK,oBAAoB;AAAA,IAC9B;AAAA,EAAA;AAAA,EAGF,GAAG;AAAA,IACD,eAAe,CAAC,GAAG,OAAO;AACxB,YAAM,cAAc,GACjB;AAAA,QACC;AAAA,MAAA,EAED,IAAA;AAEH,UAAI,CAAC,aAAa;AAChB,WAAG,KAAK,+BAA+B;AAAA,MACzC;AAAA,IACF;AAAA,IACA,aAAa,CAAC,GAAG,OAAO;AAItB,SAAG;AAAA;AAAA,QAAa;AAAA,MAAA;AAEhB,YAAM,SAAS,WAAW,IAAI,KAAK;AACnC,iCAA2B,IAAI,MAAM;AAAA,IACvC;AAAA,EAAA;AAAA,EAGF,GAAG;AAAA,IACD,eAAe,CAAC,GAAG,OAAO;AACxB,SAAG;AAAA;AAAA,QACO;AAAA;AAAA;AAAA;AAAA;AAAA,UAKN;AAAA,MAAA;AAAA,IAEN;AAAA,EAAA;AAAA,EAGF,IAAI;AAAA,IACF,eAAe,CAAC,GAAG,OAAO;AACxB,SAAG;AAAA;AAAA,QAAa;AAAA;AAAA;AAAA;AAAA,MAAA;AAAA,IAIlB;AAAA,EAAA;AAEJ;"}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import type { LogContext } from '@rocicorp/logger';
|
|
2
|
+
import { type JSONObject } from '../../../../../shared/src/bigint-json.ts';
|
|
2
3
|
import { Database } from '../../../../../zqlite/src/db.ts';
|
|
3
4
|
import type { ShardConfig } from '../../../types/shards.ts';
|
|
4
5
|
import { type SubscriptionState } from '../../replicator/schema/replication-state.ts';
|
|
5
6
|
import type { ChangeSource } from '../change-source.ts';
|
|
7
|
+
/** Server context to store with the initial sync metadata for debugging. */
|
|
8
|
+
export type ServerContext = JSONObject;
|
|
6
9
|
/**
|
|
7
10
|
* Initializes a Custom change source before streaming changes from the
|
|
8
11
|
* corresponding logical replication stream.
|
|
9
12
|
*/
|
|
10
|
-
export declare function initializeCustomChangeSource(lc: LogContext, upstreamURI: string, shard: ShardConfig, replicaDbFile: string): Promise<{
|
|
13
|
+
export declare function initializeCustomChangeSource(lc: LogContext, upstreamURI: string, shard: ShardConfig, replicaDbFile: string, context: ServerContext): Promise<{
|
|
11
14
|
subscriptionState: SubscriptionState;
|
|
12
15
|
changeSource: ChangeSource;
|
|
13
16
|
}>;
|
|
@@ -22,5 +25,5 @@ export declare function initializeCustomChangeSource(lc: LogContext, upstreamURI
|
|
|
22
25
|
* at initial sync time is the `replicaVersion` (and starting
|
|
23
26
|
* version for all initially-synced rows).
|
|
24
27
|
*/
|
|
25
|
-
export declare function initialSync(lc: LogContext, shard: ShardConfig, tx: Database, upstreamURI: string): Promise<void>;
|
|
28
|
+
export declare function initialSync(lc: LogContext, shard: ShardConfig, tx: Database, upstreamURI: string, context: ServerContext): Promise<void>;
|
|
26
29
|
//# sourceMappingURL=change-source.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"change-source.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/change-source/custom/change-source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"change-source.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/change-source/custom/change-source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAGjD,OAAO,EAEL,KAAK,UAAU,EAChB,MAAM,0CAA0C,CAAC;AAGlD,OAAO,EAAC,QAAQ,EAAC,MAAM,iCAAiC,CAAC;AAGzD,OAAO,KAAK,EAAC,WAAW,EAAU,MAAM,0BAA0B,CAAC;AAQnE,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,8CAA8C,CAAC;AACtD,OAAO,KAAK,EAAC,YAAY,EAAe,MAAM,qBAAqB,CAAC;AAQpE,4EAA4E;AAC5E,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC;AAEvC;;;GAGG;AACH,wBAAsB,4BAA4B,CAChD,EAAE,EAAE,UAAU,EACd,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,WAAW,EAClB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC;IAAC,iBAAiB,EAAE,iBAAiB,CAAC;IAAC,YAAY,EAAE,YAAY,CAAA;CAAC,CAAC,CA+B7E;AAmED;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAC/B,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,WAAW,EAClB,EAAE,EAAE,QAAQ,EACZ,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,aAAa,iBAqFvB"}
|
|
@@ -10,16 +10,15 @@ import { AutoResetSignal } from "../../change-streamer/schema/tables.js";
|
|
|
10
10
|
import { ChangeProcessor } from "../../replicator/change-processor.js";
|
|
11
11
|
import { ReplicationStatusPublisher } from "../../replicator/replication-status.js";
|
|
12
12
|
import { getSubscriptionState, createReplicationStateTables, initReplicationState } from "../../replicator/schema/replication-state.js";
|
|
13
|
+
import { initReplica } from "../common/replica-schema.js";
|
|
13
14
|
import { changeStreamMessageSchema } from "../protocol/current/downstream.js";
|
|
14
15
|
import "../protocol/current/upstream.js";
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
await initSyncSchema(
|
|
16
|
+
async function initializeCustomChangeSource(lc, upstreamURI, shard, replicaDbFile, context) {
|
|
17
|
+
await initReplica(
|
|
18
18
|
lc,
|
|
19
19
|
`replica-${shard.appID}-${shard.shardNum}`,
|
|
20
|
-
shard,
|
|
21
20
|
replicaDbFile,
|
|
22
|
-
upstreamURI
|
|
21
|
+
(log, tx) => initialSync(log, shard, tx, upstreamURI, context)
|
|
23
22
|
);
|
|
24
23
|
const replica = new Database(lc, replicaDbFile);
|
|
25
24
|
const subscriptionState = getSubscriptionState(new StatementRunner(replica));
|
|
@@ -92,7 +91,7 @@ class CustomChangeSource {
|
|
|
92
91
|
return { changes: instream, acks: outstream };
|
|
93
92
|
}
|
|
94
93
|
}
|
|
95
|
-
async function initialSync(lc, shard, tx, upstreamURI) {
|
|
94
|
+
async function initialSync(lc, shard, tx, upstreamURI, context) {
|
|
96
95
|
const { appID: id, publications } = shard;
|
|
97
96
|
const changeSource = new CustomChangeSource(lc, upstreamURI, shard, {
|
|
98
97
|
replicaVersion: "",
|
|
@@ -129,6 +128,7 @@ async function initialSync(lc, shard, tx, upstreamURI) {
|
|
|
129
128
|
tx,
|
|
130
129
|
[...publications].sort(),
|
|
131
130
|
commitWatermark,
|
|
131
|
+
context,
|
|
132
132
|
false
|
|
133
133
|
);
|
|
134
134
|
processor.processMessage(lc, change);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"change-source.js","sources":["../../../../../../../zero-cache/src/services/change-source/custom/change-source.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {WebSocket} from 'ws';\nimport {assert, unreachable} from '../../../../../shared/src/asserts.ts';\nimport {stringify} from '../../../../../shared/src/bigint-json.ts';\nimport {deepEqual} from '../../../../../shared/src/json.ts';\nimport type {SchemaValue} from '../../../../../zero-schema/src/table-schema.ts';\nimport {Database} from '../../../../../zqlite/src/db.ts';\nimport {computeZqlSpecs} from '../../../db/lite-tables.ts';\nimport {StatementRunner} from '../../../db/statements.ts';\nimport type {ShardConfig, ShardID} from '../../../types/shards.ts';\nimport {stream} from '../../../types/streams.ts';\nimport {\n AutoResetSignal,\n type ReplicationConfig,\n} from '../../change-streamer/schema/tables.ts';\nimport {ChangeProcessor} from '../../replicator/change-processor.ts';\nimport {ReplicationStatusPublisher} from '../../replicator/replication-status.ts';\nimport {\n createReplicationStateTables,\n getSubscriptionState,\n initReplicationState,\n type SubscriptionState,\n} from '../../replicator/schema/replication-state.ts';\nimport type {ChangeSource, ChangeStream} from '../change-source.ts';\nimport {changeStreamMessageSchema} from '../protocol/current/downstream.ts';\nimport {\n type BackfillRequest,\n type ChangeSourceUpstream,\n} from '../protocol/current/upstream.ts';\nimport {initSyncSchema} from './sync-schema.ts';\n\n/**\n * Initializes a Custom change source before streaming changes from the\n * corresponding logical replication stream.\n */\nexport async function initializeCustomChangeSource(\n lc: LogContext,\n upstreamURI: string,\n shard: ShardConfig,\n replicaDbFile: string,\n): Promise<{subscriptionState: SubscriptionState; changeSource: ChangeSource}> {\n await initSyncSchema(\n lc,\n `replica-${shard.appID}-${shard.shardNum}`,\n shard,\n replicaDbFile,\n upstreamURI,\n );\n\n const replica = new Database(lc, replicaDbFile);\n const subscriptionState = getSubscriptionState(new StatementRunner(replica));\n replica.close();\n\n if (shard.publications.length) {\n // Verify that the publications match what has been synced.\n const requested = [...shard.publications].sort();\n const replicated = subscriptionState.publications.sort();\n if (!deepEqual(requested, replicated)) {\n throw new Error(\n `Invalid ShardConfig. Requested publications [${requested}] do not match synced publications: [${replicated}]`,\n );\n }\n }\n\n const changeSource = new CustomChangeSource(\n lc,\n upstreamURI,\n shard,\n subscriptionState,\n );\n\n return {subscriptionState, changeSource};\n}\n\nclass CustomChangeSource implements ChangeSource {\n readonly #lc: LogContext;\n readonly #upstreamUri: string;\n readonly #shard: ShardID;\n readonly #replicationConfig: ReplicationConfig;\n\n constructor(\n lc: LogContext,\n upstreamUri: string,\n shard: ShardID,\n replicationConfig: ReplicationConfig,\n ) {\n this.#lc = lc.withContext('component', 'change-source');\n this.#upstreamUri = upstreamUri;\n this.#shard = shard;\n this.#replicationConfig = replicationConfig;\n }\n\n initialSync(): ChangeStream {\n return this.#startStream();\n }\n\n startStream(\n clientWatermark: string,\n backfillRequests: BackfillRequest[] = [],\n ): Promise<ChangeStream> {\n if (backfillRequests?.length) {\n throw new Error(\n 'backfill is yet not supported for custom change sources',\n );\n }\n return Promise.resolve(this.#startStream(clientWatermark));\n }\n\n #startStream(clientWatermark?: string): ChangeStream {\n const {publications, replicaVersion} = this.#replicationConfig;\n const {appID, shardNum} = this.#shard;\n const url = new URL(this.#upstreamUri);\n url.searchParams.set('appID', appID);\n url.searchParams.set('shardNum', String(shardNum));\n for (const pub of publications) {\n url.searchParams.append('publications', pub);\n }\n if (clientWatermark) {\n assert(\n replicaVersion.length,\n 'replicaVersion is required when clientWatermark is set',\n );\n url.searchParams.set('lastWatermark', clientWatermark);\n url.searchParams.set('replicaVersion', replicaVersion);\n }\n\n const ws = new WebSocket(url);\n const {instream, outstream} = stream(\n this.#lc,\n ws,\n changeStreamMessageSchema,\n // Upstream acks coalesce. If upstream exhibits back-pressure,\n // only the last ACK is kept / buffered.\n {coalesce: (curr: ChangeSourceUpstream) => curr},\n );\n return {changes: instream, acks: outstream};\n }\n}\n\n/**\n * Initial sync for a custom change source makes a request to the\n * change source endpoint with no `replicaVersion` or `lastWatermark`.\n * The initial transaction returned by the endpoint is treated as\n * the initial sync, and the commit watermark of that transaction\n * becomes the `replicaVersion` of the initialized replica.\n *\n * Note that this is equivalent to how the LSN of the Postgres WAL\n * at initial sync time is the `replicaVersion` (and starting\n * version for all initially-synced rows).\n */\nexport async function initialSync(\n lc: LogContext,\n shard: ShardConfig,\n tx: Database,\n upstreamURI: string,\n) {\n const {appID: id, publications} = shard;\n const changeSource = new CustomChangeSource(lc, upstreamURI, shard, {\n replicaVersion: '', // ignored for initialSync()\n publications,\n });\n const {changes} = changeSource.initialSync();\n\n createReplicationStateTables(tx);\n const processor = new ChangeProcessor(\n new StatementRunner(tx),\n 'initial-sync',\n (_, err) => {\n throw err;\n },\n );\n\n const statusPublisher = new ReplicationStatusPublisher(tx);\n try {\n let num = 0;\n for await (const change of changes) {\n const [tag] = change;\n switch (tag) {\n case 'begin': {\n const {commitWatermark} = change[2];\n lc.info?.(\n `initial sync of shard ${id} at replicaVersion ${commitWatermark}`,\n );\n statusPublisher.publish(\n lc,\n 'Initializing',\n `Copying upstream tables at version ${commitWatermark}`,\n 5000,\n );\n initReplicationState(\n tx,\n [...publications].sort(),\n commitWatermark,\n false,\n );\n processor.processMessage(lc, change);\n break;\n }\n case 'data':\n processor.processMessage(lc, change);\n if (++num % 1000 === 0) {\n lc.debug?.(`processed ${num} changes`);\n }\n break;\n case 'commit':\n processor.processMessage(lc, change);\n validateInitiallySyncedData(lc, tx, shard);\n lc.info?.(`finished initial-sync of ${num} changes`);\n return;\n\n case 'status':\n break; // Ignored\n // @ts-expect-error: falls through if the tag is not 'reset-required\n case 'control': {\n const {tag, message} = change[1];\n if (tag === 'reset-required') {\n throw new AutoResetSignal(\n message ?? 'auto-reset signaled by change source',\n );\n }\n }\n // falls through\n case 'rollback':\n throw new Error(\n `unexpected message during initial-sync: ${stringify(change)}`,\n );\n default:\n unreachable(change);\n }\n }\n throw new Error(\n `change source ${upstreamURI} closed before initial-sync completed`,\n );\n } catch (e) {\n await statusPublisher.publishAndThrowError(lc, 'Initializing', e);\n } finally {\n statusPublisher.stop();\n }\n}\n\n// Verify that the upstream tables expected by the sync logic\n// have been properly initialized.\nfunction getRequiredTables({\n appID,\n shardNum,\n}: ShardID): Record<string, Record<string, SchemaValue>> {\n return {\n [`${appID}_${shardNum}.clients`]: {\n clientGroupID: {type: 'string'},\n clientID: {type: 'string'},\n lastMutationID: {type: 'number'},\n userID: {type: 'string'},\n },\n [`${appID}_${shardNum}.mutations`]: {\n clientGroupID: {type: 'string'},\n clientID: {type: 'string'},\n mutationID: {type: 'number'},\n mutation: {type: 'json'},\n },\n [`${appID}.permissions`]: {\n permissions: {type: 'json'},\n hash: {type: 'string'},\n },\n };\n}\n\nfunction validateInitiallySyncedData(\n lc: LogContext,\n db: Database,\n shard: ShardID,\n) {\n const tables = computeZqlSpecs(lc, db, {includeBackfillingColumns: true});\n const required = getRequiredTables(shard);\n for (const [name, columns] of Object.entries(required)) {\n const table = tables.get(name)?.zqlSpec;\n if (!table) {\n throw new Error(\n `Upstream is missing the \"${name}\" table. (Found ${[\n ...tables.keys(),\n ]})` +\n `Please ensure that each table has a unique index over one ` +\n `or more non-null columns.`,\n );\n }\n for (const [col, {type}] of Object.entries(columns)) {\n const found = table[col];\n if (!found) {\n throw new Error(\n `Upstream \"${table}\" table is missing the \"${col}\" column`,\n );\n }\n if (found.type !== type) {\n throw new Error(\n `Upstream \"${table}.${col}\" column is a ${found.type} type but must be a ${type} type.`,\n );\n }\n }\n }\n}\n"],"names":["tag"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"change-source.js","sources":["../../../../../../../zero-cache/src/services/change-source/custom/change-source.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {WebSocket} from 'ws';\nimport {assert, unreachable} from '../../../../../shared/src/asserts.ts';\nimport {\n stringify,\n type JSONObject,\n} from '../../../../../shared/src/bigint-json.ts';\nimport {deepEqual} from '../../../../../shared/src/json.ts';\nimport type {SchemaValue} from '../../../../../zero-schema/src/table-schema.ts';\nimport {Database} from '../../../../../zqlite/src/db.ts';\nimport {computeZqlSpecs} from '../../../db/lite-tables.ts';\nimport {StatementRunner} from '../../../db/statements.ts';\nimport type {ShardConfig, ShardID} from '../../../types/shards.ts';\nimport {stream} from '../../../types/streams.ts';\nimport {\n AutoResetSignal,\n type ReplicationConfig,\n} from '../../change-streamer/schema/tables.ts';\nimport {ChangeProcessor} from '../../replicator/change-processor.ts';\nimport {ReplicationStatusPublisher} from '../../replicator/replication-status.ts';\nimport {\n createReplicationStateTables,\n getSubscriptionState,\n initReplicationState,\n type SubscriptionState,\n} from '../../replicator/schema/replication-state.ts';\nimport type {ChangeSource, ChangeStream} from '../change-source.ts';\nimport {initReplica} from '../common/replica-schema.ts';\nimport {changeStreamMessageSchema} from '../protocol/current/downstream.ts';\nimport {\n type BackfillRequest,\n type ChangeSourceUpstream,\n} from '../protocol/current/upstream.ts';\n\n/** Server context to store with the initial sync metadata for debugging. */\nexport type ServerContext = JSONObject;\n\n/**\n * Initializes a Custom change source before streaming changes from the\n * corresponding logical replication stream.\n */\nexport async function initializeCustomChangeSource(\n lc: LogContext,\n upstreamURI: string,\n shard: ShardConfig,\n replicaDbFile: string,\n context: ServerContext,\n): Promise<{subscriptionState: SubscriptionState; changeSource: ChangeSource}> {\n await initReplica(\n lc,\n `replica-${shard.appID}-${shard.shardNum}`,\n replicaDbFile,\n (log, tx) => initialSync(log, shard, tx, upstreamURI, context),\n );\n\n const replica = new Database(lc, replicaDbFile);\n const subscriptionState = getSubscriptionState(new StatementRunner(replica));\n replica.close();\n\n if (shard.publications.length) {\n // Verify that the publications match what has been synced.\n const requested = [...shard.publications].sort();\n const replicated = subscriptionState.publications.sort();\n if (!deepEqual(requested, replicated)) {\n throw new Error(\n `Invalid ShardConfig. Requested publications [${requested}] do not match synced publications: [${replicated}]`,\n );\n }\n }\n\n const changeSource = new CustomChangeSource(\n lc,\n upstreamURI,\n shard,\n subscriptionState,\n );\n\n return {subscriptionState, changeSource};\n}\n\nclass CustomChangeSource implements ChangeSource {\n readonly #lc: LogContext;\n readonly #upstreamUri: string;\n readonly #shard: ShardID;\n readonly #replicationConfig: ReplicationConfig;\n\n constructor(\n lc: LogContext,\n upstreamUri: string,\n shard: ShardID,\n replicationConfig: ReplicationConfig,\n ) {\n this.#lc = lc.withContext('component', 'change-source');\n this.#upstreamUri = upstreamUri;\n this.#shard = shard;\n this.#replicationConfig = replicationConfig;\n }\n\n initialSync(): ChangeStream {\n return this.#startStream();\n }\n\n startStream(\n clientWatermark: string,\n backfillRequests: BackfillRequest[] = [],\n ): Promise<ChangeStream> {\n if (backfillRequests?.length) {\n throw new Error(\n 'backfill is yet not supported for custom change sources',\n );\n }\n return Promise.resolve(this.#startStream(clientWatermark));\n }\n\n #startStream(clientWatermark?: string): ChangeStream {\n const {publications, replicaVersion} = this.#replicationConfig;\n const {appID, shardNum} = this.#shard;\n const url = new URL(this.#upstreamUri);\n url.searchParams.set('appID', appID);\n url.searchParams.set('shardNum', String(shardNum));\n for (const pub of publications) {\n url.searchParams.append('publications', pub);\n }\n if (clientWatermark) {\n assert(\n replicaVersion.length,\n 'replicaVersion is required when clientWatermark is set',\n );\n url.searchParams.set('lastWatermark', clientWatermark);\n url.searchParams.set('replicaVersion', replicaVersion);\n }\n\n const ws = new WebSocket(url);\n const {instream, outstream} = stream(\n this.#lc,\n ws,\n changeStreamMessageSchema,\n // Upstream acks coalesce. If upstream exhibits back-pressure,\n // only the last ACK is kept / buffered.\n {coalesce: (curr: ChangeSourceUpstream) => curr},\n );\n return {changes: instream, acks: outstream};\n }\n}\n\n/**\n * Initial sync for a custom change source makes a request to the\n * change source endpoint with no `replicaVersion` or `lastWatermark`.\n * The initial transaction returned by the endpoint is treated as\n * the initial sync, and the commit watermark of that transaction\n * becomes the `replicaVersion` of the initialized replica.\n *\n * Note that this is equivalent to how the LSN of the Postgres WAL\n * at initial sync time is the `replicaVersion` (and starting\n * version for all initially-synced rows).\n */\nexport async function initialSync(\n lc: LogContext,\n shard: ShardConfig,\n tx: Database,\n upstreamURI: string,\n context: ServerContext,\n) {\n const {appID: id, publications} = shard;\n const changeSource = new CustomChangeSource(lc, upstreamURI, shard, {\n replicaVersion: '', // ignored for initialSync()\n publications,\n });\n const {changes} = changeSource.initialSync();\n\n createReplicationStateTables(tx);\n const processor = new ChangeProcessor(\n new StatementRunner(tx),\n 'initial-sync',\n (_, err) => {\n throw err;\n },\n );\n\n const statusPublisher = new ReplicationStatusPublisher(tx);\n try {\n let num = 0;\n for await (const change of changes) {\n const [tag] = change;\n switch (tag) {\n case 'begin': {\n const {commitWatermark} = change[2];\n lc.info?.(\n `initial sync of shard ${id} at replicaVersion ${commitWatermark}`,\n );\n statusPublisher.publish(\n lc,\n 'Initializing',\n `Copying upstream tables at version ${commitWatermark}`,\n 5000,\n );\n initReplicationState(\n tx,\n [...publications].sort(),\n commitWatermark,\n context,\n false,\n );\n processor.processMessage(lc, change);\n break;\n }\n case 'data':\n processor.processMessage(lc, change);\n if (++num % 1000 === 0) {\n lc.debug?.(`processed ${num} changes`);\n }\n break;\n case 'commit':\n processor.processMessage(lc, change);\n validateInitiallySyncedData(lc, tx, shard);\n lc.info?.(`finished initial-sync of ${num} changes`);\n return;\n\n case 'status':\n break; // Ignored\n // @ts-expect-error: falls through if the tag is not 'reset-required\n case 'control': {\n const {tag, message} = change[1];\n if (tag === 'reset-required') {\n throw new AutoResetSignal(\n message ?? 'auto-reset signaled by change source',\n );\n }\n }\n // falls through\n case 'rollback':\n throw new Error(\n `unexpected message during initial-sync: ${stringify(change)}`,\n );\n default:\n unreachable(change);\n }\n }\n throw new Error(\n `change source ${upstreamURI} closed before initial-sync completed`,\n );\n } catch (e) {\n await statusPublisher.publishAndThrowError(lc, 'Initializing', e);\n } finally {\n statusPublisher.stop();\n }\n}\n\n// Verify that the upstream tables expected by the sync logic\n// have been properly initialized.\nfunction getRequiredTables({\n appID,\n shardNum,\n}: ShardID): Record<string, Record<string, SchemaValue>> {\n return {\n [`${appID}_${shardNum}.clients`]: {\n clientGroupID: {type: 'string'},\n clientID: {type: 'string'},\n lastMutationID: {type: 'number'},\n userID: {type: 'string'},\n },\n [`${appID}_${shardNum}.mutations`]: {\n clientGroupID: {type: 'string'},\n clientID: {type: 'string'},\n mutationID: {type: 'number'},\n mutation: {type: 'json'},\n },\n [`${appID}.permissions`]: {\n permissions: {type: 'json'},\n hash: {type: 'string'},\n },\n };\n}\n\nfunction validateInitiallySyncedData(\n lc: LogContext,\n db: Database,\n shard: ShardID,\n) {\n const tables = computeZqlSpecs(lc, db, {includeBackfillingColumns: true});\n const required = getRequiredTables(shard);\n for (const [name, columns] of Object.entries(required)) {\n const table = tables.get(name)?.zqlSpec;\n if (!table) {\n throw new Error(\n `Upstream is missing the \"${name}\" table. (Found ${[\n ...tables.keys(),\n ]})` +\n `Please ensure that each table has a unique index over one ` +\n `or more non-null columns.`,\n );\n }\n for (const [col, {type}] of Object.entries(columns)) {\n const found = table[col];\n if (!found) {\n throw new Error(\n `Upstream \"${table}\" table is missing the \"${col}\" column`,\n );\n }\n if (found.type !== type) {\n throw new Error(\n `Upstream \"${table}.${col}\" column is a ${found.type} type but must be a ${type} type.`,\n );\n }\n }\n }\n}\n"],"names":["tag"],"mappings":"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"}
|
|
@@ -5,21 +5,23 @@ import { type ShardConfig } from '../../../types/shards.ts';
|
|
|
5
5
|
import type { Sink } from '../../../types/streams.ts';
|
|
6
6
|
import { type SubscriptionState } from '../../replicator/schema/replication-state.ts';
|
|
7
7
|
import type { ChangeSource } from '../change-source.ts';
|
|
8
|
-
import { type
|
|
8
|
+
import { type Listener } from '../common/change-stream-multiplexer.ts';
|
|
9
|
+
import type { ChangeStreamMessage } from '../protocol/current/downstream.ts';
|
|
10
|
+
import { type InitialSyncOptions, type ServerContext } from './initial-sync.ts';
|
|
9
11
|
import type { MessageRelation as PostgresRelation } from './logical-replication/pgoutput.types.ts';
|
|
10
12
|
/**
|
|
11
13
|
* Initializes a Postgres change source, including the initial sync of the
|
|
12
14
|
* replica, before streaming changes from the corresponding logical replication
|
|
13
15
|
* stream.
|
|
14
16
|
*/
|
|
15
|
-
export declare function initializePostgresChangeSource(lc: LogContext, upstreamURI: string, shard: ShardConfig, replicaDbFile: string, syncOptions: InitialSyncOptions): Promise<{
|
|
17
|
+
export declare function initializePostgresChangeSource(lc: LogContext, upstreamURI: string, shard: ShardConfig, replicaDbFile: string, syncOptions: InitialSyncOptions, context: ServerContext): Promise<{
|
|
16
18
|
subscriptionState: SubscriptionState;
|
|
17
19
|
changeSource: ChangeSource;
|
|
18
20
|
}>;
|
|
19
|
-
export declare class Acker {
|
|
21
|
+
export declare class Acker implements Listener {
|
|
20
22
|
#private;
|
|
21
23
|
constructor(acks: Sink<bigint>);
|
|
22
|
-
|
|
24
|
+
onChange(change: ChangeStreamMessage): void;
|
|
23
25
|
ack(watermark: LexiVersion): void;
|
|
24
26
|
}
|
|
25
27
|
export declare function relationDifferent(a: PublishedTableSpec, b: PostgresRelation): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"change-source.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/change-source/pg/change-source.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"change-source.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/change-source/pg/change-source.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAqBjD,OAAO,KAAK,EAAa,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AAEzE,OAAO,EAAC,KAAK,WAAW,EAAC,MAAM,gCAAgC,CAAC;AAEhE,OAAO,EAEL,KAAK,WAAW,EAEjB,MAAM,0BAA0B,CAAC;AAKlC,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,2BAA2B,CAAC;AAEpD,OAAO,EAEL,KAAK,iBAAiB,EAEvB,MAAM,8CAA8C,CAAC;AACtD,OAAO,KAAK,EAAC,YAAY,EAAe,MAAM,qBAAqB,CAAC;AAEpE,OAAO,EAEL,KAAK,QAAQ,EACd,MAAM,wCAAwC,CAAC;AAUhD,OAAO,KAAK,EAEV,mBAAmB,EAEpB,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,aAAa,EACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAGV,eAAe,IAAI,gBAAgB,EACpC,MAAM,yCAAyC,CAAC;AAuBjD;;;;GAIG;AACH,wBAAsB,8BAA8B,CAClD,EAAE,EAAE,UAAU,EACd,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,WAAW,EAClB,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,kBAAkB,EAC/B,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC;IAAC,iBAAiB,EAAE,iBAAiB,CAAC;IAAC,YAAY,EAAE,YAAY,CAAA;CAAC,CAAC,CAqC7E;AA8YD,qBAAa,KAAM,YAAW,QAAQ;;gBAIxB,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;IAI9B,QAAQ,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI;IAgC3C,GAAG,CAAC,SAAS,EAAE,WAAW;CAoB3B;AA0gBD,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,kBAAkB,EAAE,CAAC,EAAE,gBAAgB,WAwB3E"}
|