@rocicorp/zero 1.2.0-canary.0 → 1.2.0-canary.10
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/format.d.ts.map +1 -1
- package/out/ast-to-zql/src/format.js +6 -6
- package/out/ast-to-zql/src/format.js.map +1 -1
- package/out/shared/src/bigint-json.d.ts.map +1 -1
- package/out/shared/src/bigint-json.js.map +1 -1
- package/out/shared/src/btree-set.d.ts.map +1 -1
- package/out/shared/src/btree-set.js +73 -41
- package/out/shared/src/btree-set.js.map +1 -1
- package/out/z2s/src/sql.d.ts +2 -2
- package/out/z2s/src/sql.d.ts.map +1 -1
- package/out/z2s/src/sql.js +2 -3
- package/out/z2s/src/sql.js.map +1 -1
- package/out/zero/package.js +6 -6
- package/out/zero/package.js.map +1 -1
- package/out/zero-cache/src/auth/load-permissions.d.ts +2 -2
- package/out/zero-cache/src/auth/load-permissions.d.ts.map +1 -1
- package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +9 -1
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +21 -3
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/custom/fetch.d.ts +1 -1
- package/out/zero-cache/src/custom/fetch.d.ts.map +1 -1
- package/out/zero-cache/src/custom-queries/transform-query.d.ts.map +1 -1
- package/out/zero-cache/src/custom-queries/transform-query.js +4 -1
- package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
- package/out/zero-cache/src/db/run-transaction.d.ts.map +1 -1
- package/out/zero-cache/src/db/run-transaction.js +2 -2
- package/out/zero-cache/src/db/run-transaction.js.map +1 -1
- package/out/zero-cache/src/db/transaction-pool.d.ts.map +1 -1
- package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
- package/out/zero-cache/src/observability/metrics.d.ts +1 -1
- package/out/zero-cache/src/observability/metrics.d.ts.map +1 -1
- package/out/zero-cache/src/observability/metrics.js.map +1 -1
- package/out/zero-cache/src/server/anonymous-otel-start.d.ts.map +1 -1
- package/out/zero-cache/src/server/anonymous-otel-start.js +6 -1
- package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
- package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +3 -1
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/logging.d.ts.map +1 -1
- package/out/zero-cache/src/server/logging.js +9 -1
- package/out/zero-cache/src/server/logging.js.map +1 -1
- package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/server/replicator.js +28 -1
- package/out/zero-cache/src/server/replicator.js.map +1 -1
- package/out/zero-cache/src/services/change-source/change-source.d.ts +1 -1
- package/out/zero-cache/src/services/change-source/change-source.d.ts.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 +13 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +4 -4
- package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.js +65 -22
- package/out/zero-cache/src/services/change-source/pg/change-source.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-streamer/backup-monitor.d.ts +1 -1
- 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 +31 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +4 -4
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +2 -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 +4 -0
- package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.js +9 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
- package/out/zero-cache/src/services/life-cycle.d.ts +1 -0
- package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
- package/out/zero-cache/src/services/life-cycle.js +2 -2
- package/out/zero-cache/src/services/life-cycle.js.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.js +5 -5
- package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.d.ts +2 -2
- package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.js +7 -4
- package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.js +6 -3
- package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/column-metadata.d.ts +1 -1
- package/out/zero-cache/src/services/replicator/schema/column-metadata.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/replication-state.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/replication-state.js +6 -3
- package/out/zero-cache/src/services/replicator/schema/replication-state.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 +8 -6
- 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 +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.map +1 -1
- package/out/zero-cache/src/services/view-syncer/row-record-cache.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/row-record-cache.js +13 -7
- package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
- 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 +32 -13
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/pg.d.ts +1 -0
- package/out/zero-cache/src/types/pg.d.ts.map +1 -1
- package/out/zero-cache/src/types/pg.js +20 -9
- package/out/zero-cache/src/types/pg.js.map +1 -1
- package/out/zero-cache/src/workers/connection.js +2 -2
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/replicator.d.ts +5 -2
- package/out/zero-cache/src/workers/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/workers/replicator.js +10 -6
- package/out/zero-cache/src/workers/replicator.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero-poke-handler.d.ts.map +1 -1
- package/out/zero-client/src/client/zero-poke-handler.js +6 -2
- package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
- package/out/zero-protocol/src/application-error.d.ts +1 -1
- package/out/zero-protocol/src/application-error.d.ts.map +1 -1
- package/out/zero-protocol/src/application-error.js.map +1 -1
- package/out/zero-types/src/name-mapper.d.ts +1 -0
- package/out/zero-types/src/name-mapper.d.ts.map +1 -1
- package/out/zero-types/src/name-mapper.js +3 -0
- package/out/zero-types/src/name-mapper.js.map +1 -1
- package/out/zql/src/builder/builder.d.ts.map +1 -1
- package/out/zql/src/builder/builder.js +5 -15
- package/out/zql/src/builder/builder.js.map +1 -1
- package/out/zql/src/ivm/memory-source.d.ts +1 -1
- package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
- package/out/zql/src/ivm/memory-source.js +2 -2
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/ivm/take.d.ts.map +1 -1
- package/out/zql/src/ivm/take.js +2 -2
- package/out/zql/src/ivm/take.js.map +1 -1
- package/out/zql/src/ivm/view-apply-change.d.ts.map +1 -1
- package/out/zql/src/ivm/view-apply-change.js +34 -26
- package/out/zql/src/ivm/view-apply-change.js.map +1 -1
- package/out/zql/src/planner/planner-debug.d.ts.map +1 -1
- package/out/zql/src/planner/planner-debug.js.map +1 -1
- package/out/zql/src/query/expression.d.ts +1 -1
- package/out/zql/src/query/expression.d.ts.map +1 -1
- package/out/zql/src/query/expression.js.map +1 -1
- package/out/zql/src/query/query.d.ts +1 -2
- package/out/zql/src/query/query.d.ts.map +1 -1
- package/out/zqlite/src/db.d.ts.map +1 -1
- package/out/zqlite/src/db.js +1 -1
- package/out/zqlite/src/db.js.map +1 -1
- package/out/zqlite/src/internal/sql.d.ts +2 -2
- package/out/zqlite/src/internal/sql.d.ts.map +1 -1
- package/out/zqlite/src/internal/sql.js +1 -2
- package/out/zqlite/src/internal/sql.js.map +1 -1
- package/out/zqlite/src/table-source.d.ts.map +1 -1
- package/out/zqlite/src/table-source.js +7 -11
- package/out/zqlite/src/table-source.js.map +1 -1
- package/package.json +6 -6
- package/out/zql/src/ivm/cap.d.ts +0 -32
- package/out/zql/src/ivm/cap.d.ts.map +0 -1
- package/out/zql/src/ivm/cap.js +0 -226
- package/out/zql/src/ivm/cap.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"column-metadata.js","names":["#instances","#insertStmt","#updateStmt","#clearBackfillStmt","#deleteColumnStmt","#deleteTableStmt","#renameTableStmt","#getColumnStmt","#getTableStmt","#hasTableStmt","#insertMetadata"],"sources":["../../../../../../../zero-cache/src/services/replicator/schema/column-metadata.ts"],"sourcesContent":["/**\n * Column metadata table for storing upstream PostgreSQL schema information.\n *\n * Previously, upstream type metadata was embedded in SQLite column type strings\n * using pipe-delimited notation (e.g., \"int8|NOT_NULL|TEXT_ENUM\"). This caused\n * issues with SQLite type affinity and made schema inspection difficult.\n *\n * This table stores that metadata separately, allowing SQLite columns to use\n * plain type names while preserving all necessary upstream type information.\n */\n\nimport type {Database, Statement} from '../../../../../zqlite/src/db.ts';\nimport {isArrayColumn, isEnumColumn} from '../../../db/pg-to-lite.ts';\nimport type {ColumnSpec, LiteTableSpec} from '../../../db/specs.ts';\nimport {\n isArray as checkIsArray,\n isEnum as checkIsEnum,\n liteTypeString,\n nullableUpstream,\n upstreamDataType,\n} from '../../../types/lite.ts';\nimport type {BackfillID} from '../../change-source/protocol/current.ts';\n\n/**\n * Structured column metadata, replacing the old pipe-delimited string format.\n */\nexport interface ColumnMetadata {\n /** PostgreSQL type name, e.g., 'int8', 'varchar', 'text[]', 'user_role' */\n upstreamType: string;\n isNotNull: boolean;\n isEnum: boolean;\n isArray: boolean;\n /** Maximum character length for varchar/char types */\n characterMaxLength?: number | null;\n isBackfilling: boolean;\n}\n\ntype ColumnMetadataRow = {\n upstream_type: string;\n is_not_null: number;\n is_enum: number;\n is_array: number;\n character_max_length: number | null;\n backfill: string | null;\n};\n\nexport const CREATE_COLUMN_METADATA_TABLE = `\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 backfill TEXT,\n PRIMARY KEY (table_name, column_name)\n );\n`;\n\n/**\n * Efficient column metadata store that prepares all statements upfront.\n * Use this class to avoid re-preparing statements on every operation.\n *\n * Access via `ColumnMetadataStore.getInstance(db)`, which returns `undefined`\n * if the metadata table doesn't exist yet.\n */\nexport class ColumnMetadataStore {\n static #instances = new WeakMap<Database, ColumnMetadataStore>();\n\n readonly #insertStmt: Statement;\n readonly #updateStmt: Statement;\n readonly #clearBackfillStmt: Statement;\n readonly #deleteColumnStmt: Statement;\n readonly #deleteTableStmt: Statement;\n readonly #renameTableStmt: Statement;\n readonly #getColumnStmt: Statement;\n readonly #getTableStmt: Statement;\n readonly #hasTableStmt: Statement;\n\n private constructor(db: Database) {\n this.#insertStmt = db.prepare(`\n INSERT INTO \"_zero.column_metadata\"\n (table_name, column_name, upstream_type, is_not_null, is_enum, is_array, character_max_length, backfill)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n this.#updateStmt = db.prepare(`\n UPDATE \"_zero.column_metadata\"\n SET column_name = ?,\n upstream_type = ?,\n is_not_null = ?,\n is_enum = ?,\n is_array = ?,\n character_max_length = ?\n WHERE table_name = ? AND column_name = ?\n `);\n\n this.#clearBackfillStmt = db.prepare(/*sql*/ `\n UPDATE \"_zero.column_metadata\"\n SET backfill = NULL\n WHERE table_name = ? AND column_name = ?\n `);\n\n this.#deleteColumnStmt = db.prepare(`\n DELETE FROM \"_zero.column_metadata\"\n WHERE table_name = ? AND column_name = ?\n `);\n\n this.#deleteTableStmt = db.prepare(`\n DELETE FROM \"_zero.column_metadata\"\n WHERE table_name = ?\n `);\n\n this.#renameTableStmt = db.prepare(`\n UPDATE \"_zero.column_metadata\"\n SET table_name = ?\n WHERE table_name = ?\n `);\n\n this.#getColumnStmt = db.prepare(`\n SELECT upstream_type, is_not_null, is_enum, is_array, character_max_length, backfill\n FROM \"_zero.column_metadata\"\n WHERE table_name = ? AND column_name = ?\n `);\n\n this.#getTableStmt = db.prepare(`\n SELECT column_name, upstream_type, is_not_null, is_enum, is_array, character_max_length, backfill\n FROM \"_zero.column_metadata\"\n WHERE table_name = ?\n ORDER BY column_name\n `);\n\n this.#hasTableStmt = db.prepare(`\n SELECT 1 FROM sqlite_master\n WHERE type = 'table' AND name = '_zero.column_metadata'\n `);\n }\n\n /**\n * Gets the singleton instance of ColumnMetadataStore for the given database.\n * Returns `undefined` if the metadata table doesn't exist yet.\n */\n static getInstance(db: Database): ColumnMetadataStore | undefined {\n // Check if table exists\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 return undefined;\n }\n\n let instance = ColumnMetadataStore.#instances.get(db);\n if (!instance) {\n instance = new ColumnMetadataStore(db);\n ColumnMetadataStore.#instances.set(db, instance);\n }\n return instance;\n }\n\n insert(\n tableName: string,\n columnName: string,\n spec: ColumnSpec,\n backfill?: BackfillID | undefined,\n ): void {\n const metadata = pgColumnSpecToMetadata(spec);\n this.#insertMetadata(tableName, columnName, metadata, backfill);\n }\n\n #insertMetadata(\n tableName: string,\n columnName: string,\n metadata: Omit<ColumnMetadata, 'isBackfilling'>,\n backfill?: BackfillID | undefined,\n ): void {\n this.#insertStmt.run(\n tableName,\n columnName,\n metadata.upstreamType,\n metadata.isNotNull ? 1 : 0,\n metadata.isEnum ? 1 : 0,\n metadata.isArray ? 1 : 0,\n metadata.characterMaxLength ?? null,\n backfill ? JSON.stringify(backfill) : null,\n );\n }\n\n update(\n tableName: string,\n oldColumnName: string,\n newColumnName: string,\n spec: ColumnSpec,\n ): void {\n const metadata = pgColumnSpecToMetadata(spec);\n this.#updateStmt.run(\n newColumnName,\n metadata.upstreamType,\n metadata.isNotNull ? 1 : 0,\n metadata.isEnum ? 1 : 0,\n metadata.isArray ? 1 : 0,\n metadata.characterMaxLength ?? null,\n tableName,\n oldColumnName,\n );\n }\n\n clearBackfilling(tableName: string, columnName: string): void {\n this.#clearBackfillStmt.run(tableName, columnName);\n }\n\n deleteColumn(tableName: string, columnName: string): void {\n this.#deleteColumnStmt.run(tableName, columnName);\n }\n\n deleteTable(tableName: string): void {\n this.#deleteTableStmt.run(tableName);\n }\n\n renameTable(oldTableName: string, newTableName: string): void {\n this.#renameTableStmt.run(newTableName, oldTableName);\n }\n\n getColumn(tableName: string, columnName: string): ColumnMetadata | undefined {\n const row = this.#getColumnStmt.get(tableName, columnName) as\n | ColumnMetadataRow\n | undefined;\n\n if (!row) {\n return undefined;\n }\n\n return {\n upstreamType: row.upstream_type,\n isNotNull: row.is_not_null !== 0,\n isEnum: row.is_enum !== 0,\n isArray: row.is_array !== 0,\n characterMaxLength: row.character_max_length,\n isBackfilling: row.backfill !== null,\n };\n }\n\n getTable(tableName: string): Map<string, ColumnMetadata> {\n const rows = this.#getTableStmt.all(tableName) as Array<\n ColumnMetadataRow & {column_name: string}\n >;\n\n const metadata = new Map<string, ColumnMetadata>();\n for (const row of rows) {\n metadata.set(row.column_name, {\n upstreamType: row.upstream_type,\n isNotNull: row.is_not_null !== 0,\n isEnum: row.is_enum !== 0,\n isArray: row.is_array !== 0,\n characterMaxLength: row.character_max_length,\n isBackfilling: row.backfill !== null,\n });\n }\n\n return metadata;\n }\n\n hasTable(): boolean {\n const result = this.#hasTableStmt.get();\n return result !== undefined;\n }\n}\n\n/**\n * Populates metadata table from existing tables that use pipe notation.\n * This is used during migration v8 to backfill the metadata table.\n */\nexport function populateFromExistingTables(\n db: Database,\n tables: LiteTableSpec[],\n): void {\n // The backfill column is not relevant here, and does not exist on\n // older versions of the replica.\n const legacyInsertStmt = db.prepare(`\n INSERT INTO \"_zero.column_metadata\"\n (table_name, column_name, upstream_type, is_not_null, is_enum, is_array, character_max_length)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n\n for (const table of tables) {\n for (const [columnName, columnSpec] of Object.entries(table.columns)) {\n const metadata = liteTypeStringToMetadata(\n columnSpec.dataType,\n columnSpec.characterMaximumLength,\n );\n legacyInsertStmt.run(\n table.name,\n columnName,\n metadata.upstreamType,\n metadata.isNotNull ? 1 : 0,\n metadata.isEnum ? 1 : 0,\n metadata.isArray ? 1 : 0,\n metadata.characterMaxLength ?? null,\n );\n }\n }\n}\n\n/**\n * Converts pipe-delimited LiteTypeString to structured ColumnMetadata.\n * This is a compatibility helper for the migration period.\n */\nexport function liteTypeStringToMetadata(\n liteTypeString: string,\n characterMaxLength?: number | null,\n): ColumnMetadata {\n const baseType = upstreamDataType(liteTypeString);\n const isArrayType = checkIsArray(liteTypeString);\n\n // Reconstruct the full upstream type including array notation\n // For new-style arrays like 'text[]', upstreamDataType returns 'text[]'\n // For old-style arrays like 'int4|NOT_NULL[]', upstreamDataType returns 'int4', so we append '[]'\n const fullUpstreamType =\n isArrayType && !baseType.includes('[]') ? `${baseType}[]` : baseType;\n\n return {\n upstreamType: fullUpstreamType,\n isNotNull: !nullableUpstream(liteTypeString),\n isEnum: checkIsEnum(liteTypeString),\n isArray: isArrayType,\n characterMaxLength: characterMaxLength ?? null,\n isBackfilling: false,\n };\n}\n\n/**\n * Converts structured ColumnMetadata back to pipe-delimited LiteTypeString.\n * This is a compatibility helper for the migration period.\n */\nexport function metadataToLiteTypeString(metadata: ColumnMetadata): string {\n return liteTypeString(\n metadata.upstreamType,\n metadata.isNotNull,\n metadata.isEnum,\n metadata.isArray,\n );\n}\n\n/**\n * Converts PostgreSQL ColumnSpec to structured ColumnMetadata.\n * Used during replication to populate the metadata table from upstream schema.\n *\n * Uses the same logic as liteTypeString() and mapPostgresToLiteColumn() via shared helpers.\n */\nexport function pgColumnSpecToMetadata(spec: ColumnSpec): ColumnMetadata {\n return {\n upstreamType: spec.dataType,\n isNotNull: spec.notNull ?? false,\n isEnum: isEnumColumn(spec),\n isArray: isArrayColumn(spec),\n characterMaxLength: spec.characterMaximumLength ?? null,\n isBackfilling: false,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"column-metadata.js","names":["#instances","#insertStmt","#updateStmt","#clearBackfillStmt","#deleteColumnStmt","#deleteTableStmt","#renameTableStmt","#getColumnStmt","#getTableStmt","#hasTableStmt","#insertMetadata"],"sources":["../../../../../../../zero-cache/src/services/replicator/schema/column-metadata.ts"],"sourcesContent":["/**\n * Column metadata table for storing upstream PostgreSQL schema information.\n *\n * Previously, upstream type metadata was embedded in SQLite column type strings\n * using pipe-delimited notation (e.g., \"int8|NOT_NULL|TEXT_ENUM\"). This caused\n * issues with SQLite type affinity and made schema inspection difficult.\n *\n * This table stores that metadata separately, allowing SQLite columns to use\n * plain type names while preserving all necessary upstream type information.\n */\n\nimport type {Database, Statement} from '../../../../../zqlite/src/db.ts';\nimport {isArrayColumn, isEnumColumn} from '../../../db/pg-to-lite.ts';\nimport type {ColumnSpec, LiteTableSpec} from '../../../db/specs.ts';\nimport {\n isArray as checkIsArray,\n isEnum as checkIsEnum,\n liteTypeString,\n nullableUpstream,\n upstreamDataType,\n} from '../../../types/lite.ts';\nimport type {BackfillID} from '../../change-source/protocol/current.ts';\n\n/**\n * Structured column metadata, replacing the old pipe-delimited string format.\n */\nexport interface ColumnMetadata {\n /** PostgreSQL type name, e.g., 'int8', 'varchar', 'text[]', 'user_role' */\n upstreamType: string;\n isNotNull: boolean;\n isEnum: boolean;\n isArray: boolean;\n /** Maximum character length for varchar/char types */\n characterMaxLength?: number | null;\n isBackfilling: boolean;\n}\n\ntype ColumnMetadataRow = {\n upstream_type: string;\n is_not_null: number;\n is_enum: number;\n is_array: number;\n character_max_length: number | null;\n backfill: string | null;\n};\n\nexport const CREATE_COLUMN_METADATA_TABLE = `\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 backfill TEXT,\n PRIMARY KEY (table_name, column_name)\n );\n`;\n\n/**\n * Efficient column metadata store that prepares all statements upfront.\n * Use this class to avoid re-preparing statements on every operation.\n *\n * Access via `ColumnMetadataStore.getInstance(db)`, which returns `undefined`\n * if the metadata table doesn't exist yet.\n */\nexport class ColumnMetadataStore {\n static #instances = new WeakMap<Database, ColumnMetadataStore>();\n\n readonly #insertStmt: Statement;\n readonly #updateStmt: Statement;\n readonly #clearBackfillStmt: Statement;\n readonly #deleteColumnStmt: Statement;\n readonly #deleteTableStmt: Statement;\n readonly #renameTableStmt: Statement;\n readonly #getColumnStmt: Statement;\n readonly #getTableStmt: Statement;\n readonly #hasTableStmt: Statement;\n\n private constructor(db: Database) {\n this.#insertStmt = db.prepare(`\n INSERT INTO \"_zero.column_metadata\"\n (table_name, column_name, upstream_type, is_not_null, is_enum, is_array, character_max_length, backfill)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n this.#updateStmt = db.prepare(`\n UPDATE \"_zero.column_metadata\"\n SET column_name = ?,\n upstream_type = ?,\n is_not_null = ?,\n is_enum = ?,\n is_array = ?,\n character_max_length = ?\n WHERE table_name = ? AND column_name = ?\n `);\n\n this.#clearBackfillStmt = db.prepare(/*sql*/ `\n UPDATE \"_zero.column_metadata\"\n SET backfill = NULL\n WHERE table_name = ? AND column_name = ?\n `);\n\n this.#deleteColumnStmt = db.prepare(`\n DELETE FROM \"_zero.column_metadata\"\n WHERE table_name = ? AND column_name = ?\n `);\n\n this.#deleteTableStmt = db.prepare(`\n DELETE FROM \"_zero.column_metadata\"\n WHERE table_name = ?\n `);\n\n this.#renameTableStmt = db.prepare(`\n UPDATE \"_zero.column_metadata\"\n SET table_name = ?\n WHERE table_name = ?\n `);\n\n this.#getColumnStmt = db.prepare(`\n SELECT upstream_type, is_not_null, is_enum, is_array, character_max_length, backfill\n FROM \"_zero.column_metadata\"\n WHERE table_name = ? AND column_name = ?\n `);\n\n this.#getTableStmt = db.prepare(`\n SELECT column_name, upstream_type, is_not_null, is_enum, is_array, character_max_length, backfill\n FROM \"_zero.column_metadata\"\n WHERE table_name = ?\n ORDER BY column_name\n `);\n\n this.#hasTableStmt = db.prepare(`\n SELECT 1 FROM sqlite_master\n WHERE type = 'table' AND name = '_zero.column_metadata'\n `);\n }\n\n /**\n * Gets the singleton instance of ColumnMetadataStore for the given database.\n * Returns `undefined` if the metadata table doesn't exist yet.\n */\n static getInstance(db: Database): ColumnMetadataStore | undefined {\n // Check if table exists\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 return undefined;\n }\n\n let instance = ColumnMetadataStore.#instances.get(db);\n if (!instance) {\n instance = new ColumnMetadataStore(db);\n ColumnMetadataStore.#instances.set(db, instance);\n }\n return instance;\n }\n\n insert(\n tableName: string,\n columnName: string,\n spec: ColumnSpec,\n backfill?: BackfillID,\n ): void {\n const metadata = pgColumnSpecToMetadata(spec);\n this.#insertMetadata(tableName, columnName, metadata, backfill);\n }\n\n #insertMetadata(\n tableName: string,\n columnName: string,\n metadata: Omit<ColumnMetadata, 'isBackfilling'>,\n backfill?: BackfillID,\n ): void {\n this.#insertStmt.run(\n tableName,\n columnName,\n metadata.upstreamType,\n metadata.isNotNull ? 1 : 0,\n metadata.isEnum ? 1 : 0,\n metadata.isArray ? 1 : 0,\n metadata.characterMaxLength ?? null,\n backfill ? JSON.stringify(backfill) : null,\n );\n }\n\n update(\n tableName: string,\n oldColumnName: string,\n newColumnName: string,\n spec: ColumnSpec,\n ): void {\n const metadata = pgColumnSpecToMetadata(spec);\n this.#updateStmt.run(\n newColumnName,\n metadata.upstreamType,\n metadata.isNotNull ? 1 : 0,\n metadata.isEnum ? 1 : 0,\n metadata.isArray ? 1 : 0,\n metadata.characterMaxLength ?? null,\n tableName,\n oldColumnName,\n );\n }\n\n clearBackfilling(tableName: string, columnName: string): void {\n this.#clearBackfillStmt.run(tableName, columnName);\n }\n\n deleteColumn(tableName: string, columnName: string): void {\n this.#deleteColumnStmt.run(tableName, columnName);\n }\n\n deleteTable(tableName: string): void {\n this.#deleteTableStmt.run(tableName);\n }\n\n renameTable(oldTableName: string, newTableName: string): void {\n this.#renameTableStmt.run(newTableName, oldTableName);\n }\n\n getColumn(tableName: string, columnName: string): ColumnMetadata | undefined {\n const row = this.#getColumnStmt.get(tableName, columnName) as\n | ColumnMetadataRow\n | undefined;\n\n if (!row) {\n return undefined;\n }\n\n return {\n upstreamType: row.upstream_type,\n isNotNull: row.is_not_null !== 0,\n isEnum: row.is_enum !== 0,\n isArray: row.is_array !== 0,\n characterMaxLength: row.character_max_length,\n isBackfilling: row.backfill !== null,\n };\n }\n\n getTable(tableName: string): Map<string, ColumnMetadata> {\n const rows = this.#getTableStmt.all(tableName) as Array<\n ColumnMetadataRow & {column_name: string}\n >;\n\n const metadata = new Map<string, ColumnMetadata>();\n for (const row of rows) {\n metadata.set(row.column_name, {\n upstreamType: row.upstream_type,\n isNotNull: row.is_not_null !== 0,\n isEnum: row.is_enum !== 0,\n isArray: row.is_array !== 0,\n characterMaxLength: row.character_max_length,\n isBackfilling: row.backfill !== null,\n });\n }\n\n return metadata;\n }\n\n hasTable(): boolean {\n const result = this.#hasTableStmt.get();\n return result !== undefined;\n }\n}\n\n/**\n * Populates metadata table from existing tables that use pipe notation.\n * This is used during migration v8 to backfill the metadata table.\n */\nexport function populateFromExistingTables(\n db: Database,\n tables: LiteTableSpec[],\n): void {\n // The backfill column is not relevant here, and does not exist on\n // older versions of the replica.\n const legacyInsertStmt = db.prepare(`\n INSERT INTO \"_zero.column_metadata\"\n (table_name, column_name, upstream_type, is_not_null, is_enum, is_array, character_max_length)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n\n for (const table of tables) {\n for (const [columnName, columnSpec] of Object.entries(table.columns)) {\n const metadata = liteTypeStringToMetadata(\n columnSpec.dataType,\n columnSpec.characterMaximumLength,\n );\n legacyInsertStmt.run(\n table.name,\n columnName,\n metadata.upstreamType,\n metadata.isNotNull ? 1 : 0,\n metadata.isEnum ? 1 : 0,\n metadata.isArray ? 1 : 0,\n metadata.characterMaxLength ?? null,\n );\n }\n }\n}\n\n/**\n * Converts pipe-delimited LiteTypeString to structured ColumnMetadata.\n * This is a compatibility helper for the migration period.\n */\nexport function liteTypeStringToMetadata(\n liteTypeString: string,\n characterMaxLength?: number | null,\n): ColumnMetadata {\n const baseType = upstreamDataType(liteTypeString);\n const isArrayType = checkIsArray(liteTypeString);\n\n // Reconstruct the full upstream type including array notation\n // For new-style arrays like 'text[]', upstreamDataType returns 'text[]'\n // For old-style arrays like 'int4|NOT_NULL[]', upstreamDataType returns 'int4', so we append '[]'\n const fullUpstreamType =\n isArrayType && !baseType.includes('[]') ? `${baseType}[]` : baseType;\n\n return {\n upstreamType: fullUpstreamType,\n isNotNull: !nullableUpstream(liteTypeString),\n isEnum: checkIsEnum(liteTypeString),\n isArray: isArrayType,\n characterMaxLength: characterMaxLength ?? null,\n isBackfilling: false,\n };\n}\n\n/**\n * Converts structured ColumnMetadata back to pipe-delimited LiteTypeString.\n * This is a compatibility helper for the migration period.\n */\nexport function metadataToLiteTypeString(metadata: ColumnMetadata): string {\n return liteTypeString(\n metadata.upstreamType,\n metadata.isNotNull,\n metadata.isEnum,\n metadata.isArray,\n );\n}\n\n/**\n * Converts PostgreSQL ColumnSpec to structured ColumnMetadata.\n * Used during replication to populate the metadata table from upstream schema.\n *\n * Uses the same logic as liteTypeString() and mapPostgresToLiteColumn() via shared helpers.\n */\nexport function pgColumnSpecToMetadata(spec: ColumnSpec): ColumnMetadata {\n return {\n upstreamType: spec.dataType,\n isNotNull: spec.notNull ?? false,\n isEnum: isEnumColumn(spec),\n isArray: isArrayColumn(spec),\n characterMaxLength: spec.characterMaximumLength ?? null,\n isBackfilling: false,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replication-state.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/replicator/schema/replication-state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,0CAA0C,CAAC;AAClD,OAAO,KAAK,CAAC,MAAM,qCAAqC,CAAC;AACzD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,iCAAiC,CAAC;AAC9D,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,2BAA2B,CAAC;AAG/D,OAAO,EAAC,wBAAwB,EAAC,MAAM,gBAAgB,CAAC;AAGxD,OAAO,EAAC,wBAAwB,EAAC,CAAC;AAElC,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAMzD,eAAO,MAAM,2BAA2B,iKAKvC,CAAC;
|
|
1
|
+
{"version":3,"file":"replication-state.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/replicator/schema/replication-state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,0CAA0C,CAAC;AAClD,OAAO,KAAK,CAAC,MAAM,qCAAqC,CAAC;AACzD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,iCAAiC,CAAC;AAC9D,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,2BAA2B,CAAC;AAG/D,OAAO,EAAC,wBAAwB,EAAC,MAAM,gBAAgB,CAAC;AAGxD,OAAO,EAAC,wBAAwB,EAAC,CAAC;AAElC,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAMzD,eAAO,MAAM,2BAA2B,iKAKvC,CAAC;AA0CF,QAAA,MAAM,uBAAuB;;;;EASxB,CAAC;AAEN,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,QAAA,MAAM,iCAAiC;;;;;EAclC,CAAC;AAEN,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAC/C,OAAO,iCAAiC,CACzC,CAAC;AAEF,QAAA,MAAM,sBAAsB;;aAE1B,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,QAAQ,EACZ,YAAY,EAAE,MAAM,EAAE,EACtB,SAAS,EAAE,MAAM,EACjB,kBAAkB,GAAE,UAAe,EACnC,YAAY,UAAO,QAoBpB;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,QAAQ,QAExD;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,QAM5D;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ;;;IAY9C;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,eAAe,GAAG,iBAAiB,CAQ3E;AAED,wBAAgB,8BAA8B,CAC5C,EAAE,EAAE,eAAe,GAClB,2BAA2B,CAS7B;AAED,wBAAgB,0BAA0B,CACxC,EAAE,EAAE,eAAe,EACnB,SAAS,EAAE,MAAM,QAQlB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,eAAe,GAAG,gBAAgB,CAGzE"}
|
|
@@ -17,7 +17,7 @@ var CREATE_RUNTIME_EVENTS_TABLE = `
|
|
|
17
17
|
timestamp TEXT NOT NULL DEFAULT (current_timestamp)
|
|
18
18
|
);
|
|
19
19
|
`;
|
|
20
|
-
var CREATE_REPLICATION_STATE_SCHEMA = "\n CREATE TABLE \"_zero.replicationConfig\" (\n replicaVersion TEXT NOT NULL,\n publications TEXT NOT NULL,\n initialSyncContext TEXT DEFAULT '{}',\n lock INTEGER PRIMARY KEY DEFAULT 1 CHECK (lock=1)\n );\n \n CREATE TABLE \"_zero.replicationState\" (\n stateVersion TEXT NOT NULL,\n lock INTEGER PRIMARY KEY DEFAULT 1 CHECK (lock=1)\n );\n " + CREATE_CHANGELOG_SCHEMA + CREATE_RUNTIME_EVENTS_TABLE + CREATE_COLUMN_METADATA_TABLE + CREATE_TABLE_METADATA_TABLE;
|
|
20
|
+
var CREATE_REPLICATION_STATE_SCHEMA = "\n CREATE TABLE \"_zero.replicationConfig\" (\n replicaVersion TEXT NOT NULL,\n publications TEXT NOT NULL,\n initialSyncContext TEXT DEFAULT '{}',\n lock INTEGER PRIMARY KEY DEFAULT 1 CHECK (lock=1)\n );\n \n CREATE TABLE \"_zero.replicationState\" (\n stateVersion TEXT NOT NULL,\n writeTimeMs INTEGER,\n lock INTEGER PRIMARY KEY DEFAULT 1 CHECK (lock=1)\n );\n " + CREATE_CHANGELOG_SCHEMA + CREATE_RUNTIME_EVENTS_TABLE + CREATE_COLUMN_METADATA_TABLE + CREATE_TABLE_METADATA_TABLE;
|
|
21
21
|
var stringArray = valita_exports.array(valita_exports.string());
|
|
22
22
|
var subscriptionStateSchema = valita_exports.object({
|
|
23
23
|
replicaVersion: valita_exports.string(),
|
|
@@ -45,7 +45,8 @@ function initReplicationState(db, publications, watermark, initialSyncContext =
|
|
|
45
45
|
(replicaVersion, publications, initialSyncContext) VALUES (?, ?, ?)
|
|
46
46
|
`).run(watermark, JSON.stringify(publications.sort()), stringify(initialSyncContext));
|
|
47
47
|
db.prepare(`
|
|
48
|
-
INSERT INTO "_zero.replicationState" (stateVersion)
|
|
48
|
+
INSERT INTO "_zero.replicationState" (stateVersion, writeTimeMs)
|
|
49
|
+
VALUES (?, unixepoch('subsec') * 1000)
|
|
49
50
|
`).run(watermark);
|
|
50
51
|
recordEvent(db, "sync");
|
|
51
52
|
}
|
|
@@ -88,7 +89,9 @@ function getSubscriptionStateAndContext(db) {
|
|
|
88
89
|
`), subscriptionStateAndContextSchema);
|
|
89
90
|
}
|
|
90
91
|
function updateReplicationWatermark(db, watermark) {
|
|
91
|
-
db.run(`
|
|
92
|
+
db.run(`
|
|
93
|
+
UPDATE "_zero.replicationState"
|
|
94
|
+
SET stateVersion=?, writeTimeMs=unixepoch('subsec') * 1000`, watermark);
|
|
92
95
|
}
|
|
93
96
|
function getReplicationState(db) {
|
|
94
97
|
return parse(db.get(`SELECT stateVersion FROM "_zero.replicationState"`), replicationStateSchema);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replication-state.js","names":[],"sources":["../../../../../../../zero-cache/src/services/replicator/schema/replication-state.ts"],"sourcesContent":["/**\n * Replication metadata, used for incremental view maintenance and catchup.\n *\n * These tables are created atomically in {@link setupReplicationTables}\n * after the logical replication handoff when initial data synchronization has completed.\n */\n\nimport {\n jsonObjectSchema,\n stringify,\n type JSONObject,\n} from '../../../../../shared/src/bigint-json.ts';\nimport * as v from '../../../../../shared/src/valita.ts';\nimport type {Database} from '../../../../../zqlite/src/db.ts';\nimport type {StatementRunner} from '../../../db/statements.ts';\nimport {CREATE_CHANGELOG_SCHEMA} from './change-log.ts';\nimport {CREATE_COLUMN_METADATA_TABLE} from './column-metadata.ts';\nimport {ZERO_VERSION_COLUMN_NAME} from './constants.ts';\nimport {CREATE_TABLE_METADATA_TABLE} from './table-metadata.ts';\n\nexport {ZERO_VERSION_COLUMN_NAME};\n\nexport type RuntimeEvent = 'sync' | 'upgrade' | 'vacuum';\n\n// event : The RuntimeEvent. Only one row per event is tracked.\n// Inserting an event will REPLACE any row for the same event.\n// timestamp : SQLite timestamp string, e.g. \"2024-04-12 11:37:46\".\n// Append a `Z` when parsing with `new Date(...)`;\nexport const CREATE_RUNTIME_EVENTS_TABLE = `\n CREATE TABLE \"_zero.runtimeEvents\" (\n event TEXT PRIMARY KEY ON CONFLICT REPLACE,\n timestamp TEXT NOT NULL DEFAULT (current_timestamp)\n );\n`;\n\nconst CREATE_REPLICATION_STATE_SCHEMA =\n // replicaVersion : A value identifying the version at which the initial sync happened, i.e.\n // the version at which all rows were copied, and to `_0_version` was set.\n // This value is used to distinguish data from other replicas (e.g. if a\n // replica is reset or if there are ever multiple replicas).\n // publications : JSON stringified array of publication names\n // initialSyncContext : Metadata related to the context of when and how the replica was initially\n // synced. This corresponds with the same column stored in upstream and is\n // used for debugging replica version mismatches, which can arise from a number\n // of misconfigurations, such as dueling replication-managers, or restores of\n // stale litestream backups.\n // lock : Auto-magic column for enforcing single-row semantics.\n /*sql*/ `\n CREATE TABLE \"_zero.replicationConfig\" (\n replicaVersion TEXT NOT NULL,\n publications TEXT NOT NULL,\n initialSyncContext TEXT DEFAULT '{}',\n lock INTEGER PRIMARY KEY DEFAULT 1 CHECK (lock=1)\n );\n ` +\n // stateVersion : The latest version replicated from upstream, starting with the initial\n // `replicaVersion` and moving forward to each subsequent commit watermark\n // (e.g. corresponding to a Postgres LSN). Versions are represented as\n // lexicographically sortable watermarks (e.g. LexiVersions).\n //\n `\n CREATE TABLE \"_zero.replicationState\" (\n stateVersion TEXT NOT NULL,\n lock INTEGER PRIMARY KEY DEFAULT 1 CHECK (lock=1)\n );\n ` +\n CREATE_CHANGELOG_SCHEMA +\n CREATE_RUNTIME_EVENTS_TABLE +\n CREATE_COLUMN_METADATA_TABLE +\n CREATE_TABLE_METADATA_TABLE;\n\nconst stringArray = v.array(v.string());\n\nconst subscriptionStateSchema = v\n .object({\n replicaVersion: v.string(),\n publications: v.string(),\n watermark: v.string(),\n })\n .map(s => ({\n ...s,\n publications: v.parse(JSON.parse(s.publications), stringArray),\n }));\n\nexport type SubscriptionState = v.Infer<typeof subscriptionStateSchema>;\n\nconst subscriptionStateAndContextSchema = v\n .object({\n replicaVersion: v.string(),\n publications: v.string(),\n initialSyncContext: v.string(),\n watermark: v.string(),\n })\n .map(s => ({\n ...s,\n publications: v.parse(JSON.parse(s.publications), stringArray),\n initialSyncContext: v.parse(\n JSON.parse(s.initialSyncContext),\n jsonObjectSchema,\n ),\n }));\n\nexport type SubscriptionStateAndContext = v.Infer<\n typeof subscriptionStateAndContextSchema\n>;\n\nconst replicationStateSchema = v.object({\n stateVersion: v.string(),\n});\n\nexport type ReplicationState = v.Infer<typeof replicationStateSchema>;\n\nexport function initReplicationState(\n db: Database,\n publications: string[],\n watermark: string,\n initialSyncContext: JSONObject = {},\n createTables = true,\n) {\n if (createTables) {\n createReplicationStateTables(db);\n }\n db.prepare(\n `\n INSERT INTO \"_zero.replicationConfig\" \n (replicaVersion, publications, initialSyncContext) VALUES (?, ?, ?)\n `,\n ).run(\n watermark,\n JSON.stringify(publications.sort()),\n stringify(initialSyncContext),\n );\n db.prepare(
|
|
1
|
+
{"version":3,"file":"replication-state.js","names":[],"sources":["../../../../../../../zero-cache/src/services/replicator/schema/replication-state.ts"],"sourcesContent":["/**\n * Replication metadata, used for incremental view maintenance and catchup.\n *\n * These tables are created atomically in {@link setupReplicationTables}\n * after the logical replication handoff when initial data synchronization has completed.\n */\n\nimport {\n jsonObjectSchema,\n stringify,\n type JSONObject,\n} from '../../../../../shared/src/bigint-json.ts';\nimport * as v from '../../../../../shared/src/valita.ts';\nimport type {Database} from '../../../../../zqlite/src/db.ts';\nimport type {StatementRunner} from '../../../db/statements.ts';\nimport {CREATE_CHANGELOG_SCHEMA} from './change-log.ts';\nimport {CREATE_COLUMN_METADATA_TABLE} from './column-metadata.ts';\nimport {ZERO_VERSION_COLUMN_NAME} from './constants.ts';\nimport {CREATE_TABLE_METADATA_TABLE} from './table-metadata.ts';\n\nexport {ZERO_VERSION_COLUMN_NAME};\n\nexport type RuntimeEvent = 'sync' | 'upgrade' | 'vacuum';\n\n// event : The RuntimeEvent. Only one row per event is tracked.\n// Inserting an event will REPLACE any row for the same event.\n// timestamp : SQLite timestamp string, e.g. \"2024-04-12 11:37:46\".\n// Append a `Z` when parsing with `new Date(...)`;\nexport const CREATE_RUNTIME_EVENTS_TABLE = `\n CREATE TABLE \"_zero.runtimeEvents\" (\n event TEXT PRIMARY KEY ON CONFLICT REPLACE,\n timestamp TEXT NOT NULL DEFAULT (current_timestamp)\n );\n`;\n\nconst CREATE_REPLICATION_STATE_SCHEMA =\n // replicaVersion : A value identifying the version at which the initial sync happened, i.e.\n // the version at which all rows were copied, and to `_0_version` was set.\n // This value is used to distinguish data from other replicas (e.g. if a\n // replica is reset or if there are ever multiple replicas).\n // publications : JSON stringified array of publication names\n // initialSyncContext : Metadata related to the context of when and how the replica was initially\n // synced. This corresponds with the same column stored in upstream and is\n // used for debugging replica version mismatches, which can arise from a number\n // of misconfigurations, such as dueling replication-managers, or restores of\n // stale litestream backups.\n // lock : Auto-magic column for enforcing single-row semantics.\n /*sql*/ `\n CREATE TABLE \"_zero.replicationConfig\" (\n replicaVersion TEXT NOT NULL,\n publications TEXT NOT NULL,\n initialSyncContext TEXT DEFAULT '{}',\n lock INTEGER PRIMARY KEY DEFAULT 1 CHECK (lock=1)\n );\n ` +\n // stateVersion : The latest version replicated from upstream, starting with the initial\n // `replicaVersion` and moving forward to each subsequent commit watermark\n // (e.g. corresponding to a Postgres LSN). Versions are represented as\n // lexicographically sortable watermarks (e.g. LexiVersions).\n // writeTimeMs : The millisecond epoch at which this version was written to the replica.\n //\n /*sql*/ `\n CREATE TABLE \"_zero.replicationState\" (\n stateVersion TEXT NOT NULL,\n writeTimeMs INTEGER,\n lock INTEGER PRIMARY KEY DEFAULT 1 CHECK (lock=1)\n );\n ` +\n CREATE_CHANGELOG_SCHEMA +\n CREATE_RUNTIME_EVENTS_TABLE +\n CREATE_COLUMN_METADATA_TABLE +\n CREATE_TABLE_METADATA_TABLE;\n\nconst stringArray = v.array(v.string());\n\nconst subscriptionStateSchema = v\n .object({\n replicaVersion: v.string(),\n publications: v.string(),\n watermark: v.string(),\n })\n .map(s => ({\n ...s,\n publications: v.parse(JSON.parse(s.publications), stringArray),\n }));\n\nexport type SubscriptionState = v.Infer<typeof subscriptionStateSchema>;\n\nconst subscriptionStateAndContextSchema = v\n .object({\n replicaVersion: v.string(),\n publications: v.string(),\n initialSyncContext: v.string(),\n watermark: v.string(),\n })\n .map(s => ({\n ...s,\n publications: v.parse(JSON.parse(s.publications), stringArray),\n initialSyncContext: v.parse(\n JSON.parse(s.initialSyncContext),\n jsonObjectSchema,\n ),\n }));\n\nexport type SubscriptionStateAndContext = v.Infer<\n typeof subscriptionStateAndContextSchema\n>;\n\nconst replicationStateSchema = v.object({\n stateVersion: v.string(),\n});\n\nexport type ReplicationState = v.Infer<typeof replicationStateSchema>;\n\nexport function initReplicationState(\n db: Database,\n publications: string[],\n watermark: string,\n initialSyncContext: JSONObject = {},\n createTables = true,\n) {\n if (createTables) {\n createReplicationStateTables(db);\n }\n db.prepare(\n `\n INSERT INTO \"_zero.replicationConfig\" \n (replicaVersion, publications, initialSyncContext) VALUES (?, ?, ?)\n `,\n ).run(\n watermark,\n JSON.stringify(publications.sort()),\n stringify(initialSyncContext),\n );\n db.prepare(/*sql*/ `\n INSERT INTO \"_zero.replicationState\" (stateVersion, writeTimeMs) \n VALUES (?, unixepoch('subsec') * 1000)\n `).run(watermark);\n recordEvent(db, 'sync');\n}\n\n/**\n * Exposed as a separate function for the custom change source,\n * which needs the tables to be created in order to construct\n * ChangeProcessor before it knows the initial watermark.\n */\nexport function createReplicationStateTables(db: Database) {\n db.exec(CREATE_REPLICATION_STATE_SCHEMA);\n}\n\nexport function recordEvent(db: Database, event: RuntimeEvent) {\n db.prepare(\n `\n INSERT INTO \"_zero.runtimeEvents\" (event) VALUES (?) \n `,\n ).run(event);\n}\n\nexport function getAscendingEvents(db: Database) {\n const result = db\n .prepare(\n `SELECT event, timestamp FROM \"_zero.runtimeEvents\" \n ORDER BY timestamp ASC\n `,\n )\n .all<{event: string; timestamp: string}>();\n return result.map(({event, timestamp}) => ({\n event,\n timestamp: new Date(timestamp + 'Z'),\n }));\n}\n\nexport function getSubscriptionState(db: StatementRunner): SubscriptionState {\n const result = db.get(/*sql*/ `\n SELECT c.replicaVersion, c.publications, s.stateVersion as watermark\n FROM \"_zero.replicationConfig\" as c\n JOIN \"_zero.replicationState\" as s\n ON c.lock = s.lock\n `);\n return v.parse(result, subscriptionStateSchema);\n}\n\nexport function getSubscriptionStateAndContext(\n db: StatementRunner,\n): SubscriptionStateAndContext {\n const result = db.get(/*sql*/ `\n SELECT c.replicaVersion, c.publications, c.initialSyncContext,\n s.stateVersion as watermark\n FROM \"_zero.replicationConfig\" as c\n JOIN \"_zero.replicationState\" as s\n ON c.lock = s.lock\n `);\n return v.parse(result, subscriptionStateAndContextSchema);\n}\n\nexport function updateReplicationWatermark(\n db: StatementRunner,\n watermark: string,\n) {\n db.run(\n /*sql*/ `\n UPDATE \"_zero.replicationState\" \n SET stateVersion=?, writeTimeMs=unixepoch('subsec') * 1000`,\n watermark,\n );\n}\n\nexport function getReplicationState(db: StatementRunner): ReplicationState {\n const result = db.get(`SELECT stateVersion FROM \"_zero.replicationState\"`);\n return v.parse(result, replicationStateSchema);\n}\n"],"mappings":";;;;;;;;;;;;;AA4BA,IAAa,8BAA8B;;;;;;AAO3C,IAAM,kCAYI,4YAqBR,0BACA,8BACA,+BACA;AAEF,IAAM,cAAc,eAAE,MAAM,eAAE,QAAQ,CAAC;AAEvC,IAAM,0BAA0B,eAC7B,OAAO;CACN,gBAAgB,eAAE,QAAQ;CAC1B,cAAc,eAAE,QAAQ;CACxB,WAAW,eAAE,QAAQ;CACtB,CAAC,CACD,KAAI,OAAM;CACT,GAAG;CACH,cAAc,MAAQ,KAAK,MAAM,EAAE,aAAa,EAAE,YAAY;CAC/D,EAAE;AAIL,IAAM,oCAAoC,eACvC,OAAO;CACN,gBAAgB,eAAE,QAAQ;CAC1B,cAAc,eAAE,QAAQ;CACxB,oBAAoB,eAAE,QAAQ;CAC9B,WAAW,eAAE,QAAQ;CACtB,CAAC,CACD,KAAI,OAAM;CACT,GAAG;CACH,cAAc,MAAQ,KAAK,MAAM,EAAE,aAAa,EAAE,YAAY;CAC9D,oBAAoB,MAClB,KAAK,MAAM,EAAE,mBAAmB,EAChC,iBACD;CACF,EAAE;AAML,IAAM,yBAAyB,eAAE,OAAO,EACtC,cAAc,eAAE,QAAQ,EACzB,CAAC;AAIF,SAAgB,qBACd,IACA,cACA,WACA,qBAAiC,EAAE,EACnC,eAAe,MACf;AACA,KAAI,aACF,8BAA6B,GAAG;AAElC,IAAG,QACD;;;MAID,CAAC,IACA,WACA,KAAK,UAAU,aAAa,MAAM,CAAC,EACnC,UAAU,mBAAmB,CAC9B;AACD,IAAG,QAAgB;;;MAGf,CAAC,IAAI,UAAU;AACnB,aAAY,IAAI,OAAO;;;;;;;AAQzB,SAAgB,6BAA6B,IAAc;AACzD,IAAG,KAAK,gCAAgC;;AAG1C,SAAgB,YAAY,IAAc,OAAqB;AAC7D,IAAG,QACD;;MAGD,CAAC,IAAI,MAAM;;AAGd,SAAgB,mBAAmB,IAAc;AAQ/C,QAPe,GACZ,QACC;;MAGD,CACA,KAAyC,CAC9B,KAAK,EAAC,OAAO,iBAAgB;EACzC;EACA,2BAAW,IAAI,KAAK,YAAY,IAAI;EACrC,EAAE;;AAGL,SAAgB,qBAAqB,IAAwC;AAO3E,QAAO,MANQ,GAAG,IAAY;;;;;MAK1B,EACmB,wBAAwB;;AAGjD,SAAgB,+BACd,IAC6B;AAQ7B,QAAO,MAPQ,GAAG,IAAY;;;;;;MAM1B,EACmB,kCAAkC;;AAG3D,SAAgB,2BACd,IACA,WACA;AACA,IAAG,IACO;;mEAGR,UACD;;AAGH,SAAgB,oBAAoB,IAAuC;AAEzE,QAAO,MADQ,GAAG,IAAI,oDAAoD,EACnD,uBAAuB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cvr.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/cvr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAGjD,OAAO,EAAC,KAAK,UAAU,EAAC,MAAM,uCAAuC,CAAC;AAEtE,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,gCAAgC,CAAC;AAQxC,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,sCAAsC,CAAC;AAC9D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gDAAgD,CAAC;AASjF,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,6BAA6B,CAAC;AAE7D,OAAO,EAAiB,KAAK,OAAO,EAAC,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAQ,cAAc,EAAC,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAC,KAAK,aAAa,EAAE,KAAK,QAAQ,EAAC,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAKL,KAAK,YAAY,EAEjB,KAAK,UAAU,EACf,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,KAAK,
|
|
1
|
+
{"version":3,"file":"cvr.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/cvr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAGjD,OAAO,EAAC,KAAK,UAAU,EAAC,MAAM,uCAAuC,CAAC;AAEtE,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,gCAAgC,CAAC;AAQxC,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,sCAAsC,CAAC;AAC9D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gDAAgD,CAAC;AASjF,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,6BAA6B,CAAC;AAE7D,OAAO,EAAiB,KAAK,OAAO,EAAC,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAQ,cAAc,EAAC,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAC,KAAK,aAAa,EAAE,KAAK,QAAQ,EAAC,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAKL,KAAK,YAAY,EAEjB,KAAK,UAAU,EACf,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,KAAK,EAGX,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAmB,KAAK,QAAQ,EAAC,MAAM,gBAAgB,CAAC;AAE/D,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,SAAS,EAAE;QAAC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;KAAC,CAAC;CACrC,CAAC;AAEF,wCAAwC;AACxC,MAAM,MAAM,GAAG,GAAG;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,UAAU,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,QAAQ,CAAC;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrC,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAClC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEF,mCAAmC;AAEnC,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC;IACzD,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IACxD,QAAQ,CAAC,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAC3C,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC,CAAC;AAcF,wBAAgB,uBAAuB,CACrC,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,MAAM,GACpB,mBAAmB,CA+BrB;AAED;;;;;;;;GAQG;AACH,qBAAa,UAAU;IACrB,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IACtC,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC;IAE7B,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC;IAEvC;;;OAGG;gBAED,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,WAAW,EAChB,cAAc,EAAE,MAAM,GAAG,IAAI;IAQ/B,SAAS,CAAC,WAAW,CAAC,OAAO,EAAE,UAAU;;;;IASzC;;;;OAIG;IACH,SAAS,CAAC,iBAAiB,IAAI,UAAU;IAOnC,KAAK,CACT,EAAE,EAAE,UAAU,EACd,eAAe,EAAE,MAAM,EACvB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC;QACT,GAAG,EAAE,WAAW,CAAC;QACjB,OAAO,EAAE,aAAa,GAAG,KAAK,CAAC;KAChC,CAAC;CAcH;AAED;;;;GAIG;AACH,qBAAa,sBAAuB,SAAQ,UAAU;;gBAGxC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO;IAKhE,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY;IAqDtC,eAAe,CAAC,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY;IA0B1D,YAAY,CAAC,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM;IAkB9C,iBAAiB,CACf,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,QAAQ,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,CAAC,EAAE,GAAG,GAAG,SAAS,CAAC;QACtB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,IAAI,CAAC,EAAE,SAAS,iBAAiB,EAAE,GAAG,SAAS,CAAC;QAChD,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAC1B,CAAC,EAAE,GACH,cAAc,EAAE;IAwFnB,4BAA4B,CAC1B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EAAE,EACrB,QAAQ,EAAE,QAAQ,GACjB,cAAc,EAAE;IAInB,oBAAoB,CAClB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EAAE,GACpB,cAAc,EAAE;IAuEnB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,EAAE;IAKvD,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,cAAc,EAAE;CAyBrE;AAED,KAAK,IAAI,GAAG,MAAM,CAAC;AACnB,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC;AAC5B,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAO7C;;;;;;;;;;;;;GAaG;AACH,qBAAa,qBAAsB,SAAQ,UAAU;;IASnD;;OAEG;gBAED,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,WAAW,EAChB,YAAY,EAAE,WAAW,EACzB,cAAc,EAAE,MAAM;IAqBxB;;;;;;;;;;;;OAYG;IACH,YAAY,CACV,EAAE,EAAE,UAAU,EACd,QAAQ,EAAE;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAA;KAAC,EAAE,EACpD,OAAO,EAAE;QAAC,EAAE,EAAE,MAAM,CAAA;KAAC,EAAE,GACtB;QAAC,UAAU,EAAE,UAAU,CAAC;QAAC,YAAY,EAAE,cAAc,EAAE,CAAA;KAAC;IAwK3D,cAAc,IAAI,UAAU;IAI5B;;;;;OAKG;IACH,QAAQ,CACN,GAAG,EAAE,UAAU,EACf,IAAI,EAAE,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,GAC1B,OAAO,CAAC,cAAc,EAAE,CAAC;IA4G5B;;;;;;;;;;;OAWG;IACH,sBAAsB,CAAC,EAAE,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;CA8EnE;AAgCD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,GAAG,GAAG;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,QAAQ,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;CACb,EAAE,CA+DF;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,GAAG,GAAG,QAAQ,GAAG,SAAS,CAS/D"}
|
|
@@ -13,9 +13,9 @@ import { rowIDString } from "../../types/row-key.js";
|
|
|
13
13
|
import { CustomKeyMap } from "../../../../shared/src/custom-key-map.js";
|
|
14
14
|
import { recordQuery } from "../../server/anonymous-otel-start.js";
|
|
15
15
|
import { ttlClockAsNumber } from "./ttl-clock.js";
|
|
16
|
-
import { cmpVersions, maxVersion, oneAfter } from "./schema/types.js";
|
|
17
|
-
import "./cvr-store.js";
|
|
16
|
+
import { cmpVersions, maxVersion, oneAfter, versionString } from "./schema/types.js";
|
|
18
17
|
import { tracer } from "./tracer.js";
|
|
18
|
+
import "./cvr-store.js";
|
|
19
19
|
//#region ../zero-cache/src/services/view-syncer/cvr.ts
|
|
20
20
|
var CLIENT_LMID_QUERY_ID = "lmids";
|
|
21
21
|
var CLIENT_MUTATION_RESULTS_QUERY_ID = "mutationResults";
|
|
@@ -349,6 +349,8 @@ var CVRQueryDrivenUpdater = class extends CVRUpdater {
|
|
|
349
349
|
const queryPatches = [executed.map((q) => this.#trackExecuted(q.id, q.transformationHash)), removed.map((q) => this.#trackRemoved(q.id))].flat(2);
|
|
350
350
|
this.#existingRows = this.#lookupRowsForExecutedAndRemovedQueries(lc);
|
|
351
351
|
this.#existingRows.then(() => {});
|
|
352
|
+
const versionBumped = cmpVersions(this._orig.version, this._cvr.version) < 0;
|
|
353
|
+
lc.info?.(`trackQueries: ${executed.length} executed, ${removed.length} removed, version ${versionBumped ? "bumped" : "unchanged"}`);
|
|
352
354
|
return {
|
|
353
355
|
newVersion: this._cvr.version,
|
|
354
356
|
queryPatches: queryPatches.map((patch) => ({
|
|
@@ -440,8 +442,8 @@ var CVRQueryDrivenUpdater = class extends CVRUpdater {
|
|
|
440
442
|
* final cookie (i.e. version), and that must be sent before any poke parts
|
|
441
443
|
* generated from {@link received} are sent.
|
|
442
444
|
*/
|
|
443
|
-
#assertNewVersion() {
|
|
444
|
-
assert(cmpVersions(this._orig.version, this._cvr.version) < 0,
|
|
445
|
+
#assertNewVersion(rowID, existingVersion, newVersion, refCounts) {
|
|
446
|
+
assert(cmpVersions(this._orig.version, this._cvr.version) < 0, () => `Expected CVR version to have been bumped above original (orig=${versionString(this._orig.version)}, curr=${versionString(this._cvr.version)}). Row ${JSON.stringify(rowID)}: existing=${existingVersion}, new=${newVersion}, queries=[${Object.keys(refCounts).join(",")}]`);
|
|
445
447
|
return this._cvr.version;
|
|
446
448
|
}
|
|
447
449
|
updatedVersion() {
|
|
@@ -464,7 +466,7 @@ var CVRQueryDrivenUpdater = class extends CVRUpdater {
|
|
|
464
466
|
const merged = previouslyReceived !== void 0 ? mergeRefCounts(previouslyReceived, refCounts) : mergeRefCounts(existing?.refCounts, refCounts, this.#removedOrExecutedQueryIDs);
|
|
465
467
|
this.#receivedRows.set(id, merged);
|
|
466
468
|
const newRowVersion = merged === null ? void 0 : version;
|
|
467
|
-
const patchVersion = existing && existing.rowVersion === newRowVersion ? existing.patchVersion : this.#assertNewVersion();
|
|
469
|
+
const patchVersion = existing && existing.rowVersion === newRowVersion ? existing.patchVersion : this.#assertNewVersion(id, existing?.rowVersion, newRowVersion, refCounts);
|
|
468
470
|
const rowVersion = version ?? existing?.rowVersion;
|
|
469
471
|
if (rowVersion) this._cvrStore.putRowRecord({
|
|
470
472
|
id,
|
|
@@ -555,7 +557,7 @@ var CVRQueryDrivenUpdater = class extends CVRUpdater {
|
|
|
555
557
|
return startSpan(tracer, "CVRQueryDrivenUpdater.#deleteUnreferencedRow", () => {
|
|
556
558
|
if (this.#receivedRows.get(existing.id)) return null;
|
|
557
559
|
const newRefCounts = mergeRefCounts(existing.refCounts, void 0, this.#removedOrExecutedQueryIDs);
|
|
558
|
-
const patchVersion = newRefCounts ? existing.patchVersion : this.#assertNewVersion();
|
|
560
|
+
const patchVersion = newRefCounts ? existing.patchVersion : this.#assertNewVersion(existing.id, existing.rowVersion, void 0, existing.refCounts ?? {});
|
|
559
561
|
const rowRecord = {
|
|
560
562
|
...existing,
|
|
561
563
|
patchVersion,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cvr.js","names":["#shard","#deleteQueries","#removedOrExecutedQueryIDs","#receivedRows","#lastPatches","#existingRows","#trackExecuted","#trackRemoved","#lookupRowsForExecutedAndRemovedQueries","#assertNewVersion","#deleteUnreferencedRow"],"sources":["../../../../../../zero-cache/src/services/view-syncer/cvr.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {startAsyncSpan, startSpan} from '../../../../otel/src/span.ts';\nimport {assert} from '../../../../shared/src/asserts.ts';\nimport {type JSONObject} from '../../../../shared/src/bigint-json.ts';\nimport {CustomKeyMap} from '../../../../shared/src/custom-key-map.ts';\nimport {\n deepEqual,\n type ReadonlyJSONValue,\n} from '../../../../shared/src/json.ts';\nimport {must} from '../../../../shared/src/must.ts';\nimport {\n difference,\n intersection,\n union,\n} from '../../../../shared/src/set-utils.ts';\nimport {stringCompare} from '../../../../shared/src/string-compare.ts';\nimport type {AST} from '../../../../zero-protocol/src/ast.ts';\nimport type {ClientSchema} from '../../../../zero-protocol/src/client-schema.ts';\nimport {ErrorOrigin} from '../../../../zero-protocol/src/error-origin.ts';\nimport {ProtocolError} from '../../../../zero-protocol/src/error.ts';\nimport {\n clampTTL,\n compareTTL,\n DEFAULT_TTL_MS,\n} from '../../../../zql/src/query/ttl.ts';\nimport {recordQuery} from '../../server/anonymous-otel-start.ts';\nimport type {LexiVersion} from '../../types/lexi-version.ts';\nimport {rowIDString} from '../../types/row-key.ts';\nimport {upstreamSchema, type ShardID} from '../../types/shards.ts';\nimport type {Patch, PatchToVersion} from './client-handler.ts';\nimport {type CVRFlushStats, type CVRStore} from './cvr-store.ts';\nimport {\n cmpVersions,\n maxVersion,\n oneAfter,\n type ClientQueryRecord,\n type ClientRecord,\n type CustomQueryRecord,\n type CVRVersion,\n type InternalQueryRecord,\n type QueryRecord,\n type RowID,\n type RowRecord,\n} from './schema/types.ts';\nimport {tracer} from './tracer.ts';\nimport {ttlClockAsNumber, type TTLClock} from './ttl-clock.ts';\n\nexport type RowUpdate = {\n version?: string; // Undefined for an unref.\n contents?: JSONObject; // Undefined for an unref.\n refCounts: {[hash: string]: number}; // Counts are negative when a row is unrefed.\n};\n\n/** Internally used mutable CVR type. */\nexport type CVR = {\n id: string;\n version: CVRVersion;\n lastActive: number;\n ttlClock: TTLClock;\n replicaVersion: string | null;\n clients: Record<string, ClientRecord>;\n queries: Record<string, QueryRecord>;\n clientSchema: ClientSchema | null;\n profileID: string | null;\n};\n\n/** Exported immutable CVR type. */\n// TODO: Use Immutable<CVR> when the AST is immutable.\nexport type CVRSnapshot = {\n readonly id: string;\n readonly version: CVRVersion;\n readonly lastActive: number;\n readonly ttlClock: TTLClock;\n readonly replicaVersion: string | null;\n readonly clients: Readonly<Record<string, ClientRecord>>;\n readonly queries: Readonly<Record<string, QueryRecord>>;\n readonly clientSchema: ClientSchema | null;\n readonly profileID: string | null;\n};\n\nconst CLIENT_LMID_QUERY_ID = 'lmids';\nconst CLIENT_MUTATION_RESULTS_QUERY_ID = 'mutationResults';\n\nfunction assertNotInternal(\n query: QueryRecord,\n): asserts query is ClientQueryRecord {\n if (query.type === 'internal') {\n // This should never happen for behaving clients, as query ids should be hashes.\n throw new Error(`Query ID ${query.id} is reserved for internal use`);\n }\n}\n\nexport function getMutationResultsQuery(\n upstreamSchema: string,\n clientGroupID: string,\n): InternalQueryRecord {\n return {\n id: CLIENT_MUTATION_RESULTS_QUERY_ID,\n type: 'internal',\n ast: {\n schema: '',\n table: `${upstreamSchema}.mutations`,\n where: {\n type: 'and',\n conditions: [\n {\n type: 'simple',\n left: {\n type: 'column',\n name: 'clientGroupID',\n },\n op: '=',\n right: {\n type: 'literal',\n value: clientGroupID,\n },\n },\n ],\n },\n orderBy: [\n ['clientGroupID', 'asc'],\n ['clientID', 'asc'],\n ['mutationID', 'asc'],\n ],\n },\n };\n}\n\n/**\n * The base CVRUpdater contains logic common to the {@link CVRConfigDrivenUpdater} and\n * {@link CVRQueryDrivenUpdater}. The CVRUpdater class itself is exported for updating\n * the `lastActive` time of the CVR in the absence of any changes to the CVR contents.\n * Although activity is automatically tracked when the CVR contents change, there may be\n * edge cases in which a client actively connects to a CVR that doesn't itself change.\n * Calling `new CVRUpdater(...).flush()` will explicitly update the active index and\n * prevent the CVR from being garbage collected.\n */\nexport class CVRUpdater {\n protected readonly _orig: CVRSnapshot;\n protected readonly _cvr: CVR;\n\n protected readonly _cvrStore: CVRStore;\n\n /**\n * @param cvrStore The CVRStore to use for storage\n * @param cvr The current CVR\n */\n constructor(\n cvrStore: CVRStore,\n cvr: CVRSnapshot,\n replicaVersion: string | null,\n ) {\n this._cvrStore = cvrStore;\n this._orig = cvr;\n this._cvr = structuredClone(cvr) as CVR; // mutable deep copy\n this._cvr.replicaVersion = replicaVersion;\n }\n\n protected _setVersion(version: CVRVersion) {\n assert(\n cmpVersions(this._cvr.version, version) < 0,\n 'Expected new version to be greater than current version',\n );\n this._cvr.version = version;\n return version;\n }\n\n /**\n * Ensures that the new CVR has a higher version than the original.\n * This method is idempotent in that it will always return the same\n * (possibly bumped) version.\n */\n protected _ensureNewVersion(): CVRVersion {\n if (cmpVersions(this._orig.version, this._cvr.version) === 0) {\n this._setVersion(oneAfter(this._cvr.version));\n }\n return this._cvr.version;\n }\n\n async flush(\n lc: LogContext,\n lastConnectTime: number,\n lastActive: number,\n ttlClock: TTLClock,\n ): Promise<{\n cvr: CVRSnapshot;\n flushed: CVRFlushStats | false;\n }> {\n this._cvr.ttlClock = ttlClock;\n this._cvr.lastActive = lastActive;\n const flushed = await this._cvrStore.flush(\n lc,\n this._orig.version,\n this._cvr,\n lastConnectTime,\n );\n if (!flushed) {\n return {cvr: this._orig, flushed: false};\n }\n return {cvr: this._cvr, flushed};\n }\n}\n\n/**\n * A {@link CVRConfigDrivenUpdater} is used for updating a CVR with config-driven\n * changes. Note that this may result in row deletion (e.g. if queries get dropped),\n * but the `stateVersion` of the CVR does not change.\n */\nexport class CVRConfigDrivenUpdater extends CVRUpdater {\n readonly #shard: ShardID;\n\n constructor(cvrStore: CVRStore, cvr: CVRSnapshot, shard: ShardID) {\n super(cvrStore, cvr, cvr.replicaVersion);\n this.#shard = shard;\n }\n\n ensureClient(id: string): ClientRecord {\n return startSpan(tracer, 'CVRConfigDrivenUpdater.ensureClient', () => {\n let client = this._cvr.clients[id];\n if (client) {\n return client;\n }\n // Add the ClientRecord and PutPatch\n client = {id, desiredQueryIDs: []};\n this._cvr.clients[id] = client;\n\n this._ensureNewVersion();\n this._cvrStore.insertClient(client);\n\n if (!this._cvr.queries[CLIENT_LMID_QUERY_ID]) {\n const lmidsQuery: InternalQueryRecord = {\n id: CLIENT_LMID_QUERY_ID,\n ast: {\n schema: '',\n table: `${upstreamSchema(this.#shard)}.clients`,\n where: {\n type: 'simple',\n left: {\n type: 'column',\n name: 'clientGroupID',\n },\n op: '=',\n right: {\n type: 'literal',\n value: this._cvr.id,\n },\n },\n orderBy: [\n ['clientGroupID', 'asc'],\n ['clientID', 'asc'],\n ],\n },\n type: 'internal',\n };\n this._cvr.queries[CLIENT_LMID_QUERY_ID] = lmidsQuery;\n this._cvrStore.putQuery(lmidsQuery);\n }\n if (!this._cvr.queries[CLIENT_MUTATION_RESULTS_QUERY_ID]) {\n const mutationResultsQuery: InternalQueryRecord =\n getMutationResultsQuery(upstreamSchema(this.#shard), this._cvr.id);\n this._cvr.queries[CLIENT_MUTATION_RESULTS_QUERY_ID] =\n mutationResultsQuery;\n this._cvrStore.putQuery(mutationResultsQuery);\n }\n\n return client;\n });\n }\n\n setClientSchema(lc: LogContext, clientSchema: ClientSchema) {\n startSpan(tracer, 'CVRConfigDrivenUpdater.setClientSchema', () => {\n if (this._cvr.clientSchema === null) {\n this._cvr.clientSchema = clientSchema;\n this._cvrStore.putInstance(this._cvr);\n } else if (!deepEqual(this._cvr.clientSchema, clientSchema)) {\n // This should not be possible with a correct Zero client, as clients\n // of a CVR should all have the same schema (given that the schema hash\n // is part of the idb key). In fact, clients joining an existing group\n // (i.e. non-empty baseCookie) do not send the clientSchema message.\n lc.warn?.(\n `New schema ${JSON.stringify(\n clientSchema,\n )} does not match existing schema ${JSON.stringify(\n this._cvr.clientSchema,\n )}`,\n );\n throw new ProtocolError({\n kind: 'InvalidConnectionRequest',\n message: `Provided schema does not match previous schema`,\n origin: ErrorOrigin.ZeroCache,\n });\n }\n });\n }\n\n setProfileID(lc: LogContext, profileID: string) {\n if (this._cvr.profileID !== profileID) {\n if (\n this._cvr.profileID !== null &&\n !this._cvr.profileID.startsWith('cg')\n ) {\n // We expect profile ID's to change from null or from the back-filled\n // \"cg...\" value. Log a warning otherwise to surface unexpected or\n // pathological conditions.\n lc.warn?.(\n `changing profile ID from ${this._cvr.profileID} to ${profileID}`,\n );\n }\n this._cvr.profileID = profileID;\n this._cvrStore.putInstance(this._cvr);\n }\n }\n\n putDesiredQueries(\n clientID: string,\n queries: Readonly<{\n hash: string;\n ast?: AST | undefined;\n name?: string | undefined;\n args?: readonly ReadonlyJSONValue[] | undefined;\n ttl?: number | undefined;\n }>[],\n ): PatchToVersion[] {\n return startSpan(tracer, 'CVRConfigDrivenUpdater.putDesiredQueries', () => {\n const patches: PatchToVersion[] = [];\n const client = this.ensureClient(clientID);\n const current = new Set(client.desiredQueryIDs);\n\n // Find the new/changed desired queries.\n const needed: Set<string> = new Set();\n\n const recordQueryForTelemetry = (q: (typeof queries)[0]) => {\n const {ast, name, args} = q;\n if (ast) {\n recordQuery('crud');\n } else if (name && args) {\n recordQuery('custom');\n }\n };\n\n for (const q of queries) {\n const {hash, ttl = DEFAULT_TTL_MS} = q;\n const query = this._cvr.queries[hash];\n if (!query) {\n // New query - record for telemetry\n recordQueryForTelemetry(q);\n needed.add(hash);\n continue;\n }\n if (query.type === 'internal') {\n continue;\n }\n\n const oldClientState = query.clientState[clientID];\n // Old query was inactivated or never desired by this client.\n if (!oldClientState || oldClientState.inactivatedAt !== undefined) {\n // Reactivated query - record for telemetry\n recordQueryForTelemetry(q);\n needed.add(hash);\n continue;\n }\n\n if (compareTTL(ttl, oldClientState.ttl) > 0) {\n // TTL update only - don't record for telemetry\n needed.add(hash);\n }\n }\n\n if (needed.size === 0) {\n return patches;\n }\n const newVersion = this._ensureNewVersion();\n client.desiredQueryIDs = [...union(current, needed)].sort(stringCompare);\n\n for (const id of needed) {\n const q = must(queries.find(({hash}) => hash === id));\n const {ast, name, args} = q;\n\n const ttl = clampTTL(q.ttl ?? DEFAULT_TTL_MS);\n const query =\n this._cvr.queries[id] ?? newQueryRecord(id, ast, name, args);\n assertNotInternal(query);\n\n const inactivatedAt = undefined;\n\n query.clientState[clientID] = {\n inactivatedAt,\n ttl,\n version: newVersion,\n };\n this._cvr.queries[id] = query;\n patches.push({\n toVersion: newVersion,\n patch: {type: 'query', op: 'put', id, clientID},\n });\n\n this._cvrStore.putQuery(query);\n this._cvrStore.putDesiredQuery(\n newVersion,\n query,\n client,\n false,\n inactivatedAt,\n ttl,\n );\n }\n return patches;\n });\n }\n\n markDesiredQueriesAsInactive(\n clientID: string,\n queryHashes: string[],\n ttlClock: TTLClock,\n ): PatchToVersion[] {\n return this.#deleteQueries(clientID, queryHashes, ttlClock);\n }\n\n deleteDesiredQueries(\n clientID: string,\n queryHashes: string[],\n ): PatchToVersion[] {\n return this.#deleteQueries(clientID, queryHashes, undefined);\n }\n\n #deleteQueries(\n clientID: string,\n queryHashes: string[],\n inactivatedAt: TTLClock | undefined,\n ): PatchToVersion[] {\n return startSpan(tracer, 'CVRConfigDrivenUpdater.#deleteQueries', () => {\n const patches: PatchToVersion[] = [];\n const client = this.ensureClient(clientID);\n const current = new Set(client.desiredQueryIDs);\n const unwanted = new Set(queryHashes);\n const remove = intersection(unwanted, current);\n if (remove.size === 0) {\n return patches;\n }\n\n const newVersion = this._ensureNewVersion();\n client.desiredQueryIDs = [...difference(current, remove)].sort(\n stringCompare,\n );\n\n for (const id of remove) {\n const query = this._cvr.queries[id];\n if (!query) {\n continue; // Query itself has already been removed. Should not happen?\n }\n assertNotInternal(query);\n\n let ttl = DEFAULT_TTL_MS;\n if (inactivatedAt === undefined) {\n delete query.clientState[clientID];\n } else {\n // client state can be missing if the query never transformed so we never\n // recorded it.\n const clientState = query.clientState[clientID];\n if (clientState !== undefined) {\n assert(\n clientState.inactivatedAt === undefined,\n `Query ${id} is already inactivated`,\n );\n // Clamp TTL to ensure we don't propagate historical unclamped values.\n ttl = clampTTL(clientState.ttl);\n query.clientState[clientID] = {\n inactivatedAt,\n ttl,\n version: newVersion,\n };\n }\n }\n\n this._cvrStore.putQuery(query);\n this._cvrStore.putDesiredQuery(\n newVersion,\n query,\n client,\n true,\n inactivatedAt,\n ttl,\n );\n patches.push({\n toVersion: newVersion,\n patch: {type: 'query', op: 'del', id, clientID},\n });\n }\n return patches;\n });\n }\n\n clearDesiredQueries(clientID: string): PatchToVersion[] {\n const client = this.ensureClient(clientID);\n return this.#deleteQueries(clientID, client.desiredQueryIDs, undefined);\n }\n\n deleteClient(clientID: string, ttlClock: TTLClock): PatchToVersion[] {\n return startSpan(tracer, 'CVRConfigDrivenUpdater.deleteClient', () => {\n // clientID might not be part of this client group but if it is, this delete\n // may generate changes to the desired queries.\n\n const client = this._cvr.clients[clientID];\n if (!client) {\n // Clients in different client groups are no longer deleted, leaving\n // cleanup to inactive CVR purging logic.\n return [];\n }\n\n // When a client is deleted we mark all of its desired queries as inactive.\n // They will then be removed when the queries expire.\n const patches = this.markDesiredQueriesAsInactive(\n clientID,\n client.desiredQueryIDs,\n ttlClock,\n );\n delete this._cvr.clients[clientID];\n this._cvrStore.deleteClient(clientID);\n\n return patches;\n });\n }\n}\n\ntype Hash = string;\nexport type Column = string;\nexport type RefCounts = Record<Hash, number>;\n\ntype RowPatchInfo = {\n rowVersion: string | null; // null for a row-del\n toVersion: CVRVersion; // patchVersion\n};\n\n/**\n * A {@link CVRQueryDrivenUpdater} is used for updating a CVR after making queries.\n * The caller should invoke:\n *\n * * {@link trackQueries} for queries that are being executed or removed.\n * * {@link received} for all rows received from the executed queries\n * * {@link deleteUnreferencedRows} to remove any rows that have\n * fallen out of the query result view.\n * * {@link flush}\n *\n * After flushing, the caller should perform any necessary catchup of\n * config and row patches for clients that are behind. See\n * {@link CVRStore.catchupConfigPatches} and {@link CVRStore.catchupRowPatches}.\n */\nexport class CVRQueryDrivenUpdater extends CVRUpdater {\n readonly #removedOrExecutedQueryIDs = new Set<string>();\n readonly #receivedRows = new CustomKeyMap<RowID, RefCounts | null>(\n rowIDString,\n );\n readonly #lastPatches = new CustomKeyMap<RowID, RowPatchInfo>(rowIDString);\n\n #existingRows: Promise<Iterable<RowRecord>> | undefined = undefined;\n\n /**\n * @param stateVersion The `stateVersion` at which the queries were executed.\n */\n constructor(\n cvrStore: CVRStore,\n cvr: CVRSnapshot,\n stateVersion: LexiVersion,\n replicaVersion: string,\n ) {\n super(cvrStore, cvr, replicaVersion);\n\n assert(\n // We should either be setting the cvr.replicaVersion for the first time, or it should\n // be something newer than the current cvr.replicaVersion. Otherwise, the CVR should\n // have been rejected by the ViewSyncer.\n (cvr.replicaVersion ?? replicaVersion) <= replicaVersion,\n `Cannot sync from an older replicaVersion: CVR=${cvr.replicaVersion}, DB=${replicaVersion}`,\n );\n assert(\n stateVersion >= cvr.version.stateVersion,\n () =>\n `stateVersion (${stateVersion}) must be >= cvr.version.stateVersion (${cvr.version.stateVersion})`,\n );\n if (stateVersion > cvr.version.stateVersion) {\n this._setVersion({stateVersion});\n }\n }\n\n /**\n * Initiates the tracking of the specified `executed` and `removed` queries.\n * This kicks of a lookup of existing {@link RowRecord}s currently associated\n * with those queries, which will be used to reconcile the rows to keep\n * after all rows have been {@link received()}.\n *\n * \"transformed\" queries are queries that are currently\n * gotten and running in the pipeline driver but\n * received a new transformation hash due to an auth token\n * update.\n *\n * @returns The new CVRVersion to be used when all changes are committed.\n */\n trackQueries(\n lc: LogContext,\n executed: {id: string; transformationHash: string}[],\n removed: {id: string}[],\n ): {newVersion: CVRVersion; queryPatches: PatchToVersion[]} {\n return startSpan(tracer, 'CVRQueryDrivenUpdater.trackQueries', () => {\n assert(this.#existingRows === undefined, `trackQueries already called`);\n\n const queryPatches: Patch[] = [\n executed.map(q => this.#trackExecuted(q.id, q.transformationHash)),\n removed.map(q => this.#trackRemoved(q.id)),\n ].flat(2);\n\n this.#existingRows = this.#lookupRowsForExecutedAndRemovedQueries(lc);\n // Immediately attach a rejection handler to avoid unhandled rejections.\n // The error will surface when this.#existingRows is awaited.\n void this.#existingRows.then(() => {});\n\n return {\n newVersion: this._cvr.version,\n queryPatches: queryPatches.map(patch => ({\n patch,\n toVersion: this._cvr.version,\n })),\n };\n });\n }\n\n #lookupRowsForExecutedAndRemovedQueries(\n lc: LogContext,\n ): Promise<Iterable<RowRecord>> {\n return startAsyncSpan(\n tracer,\n 'CVRQueryDrivenUpdater.#lookupRowsForExecutedAndRemovedQueries',\n async () => {\n const results = new CustomKeyMap<RowID, RowRecord>(rowIDString);\n\n if (this.#removedOrExecutedQueryIDs.size === 0) {\n // Query-less update. This can happen for config only changes.\n return [];\n }\n\n // Utilizes the in-memory RowCache.\n const allRowRecords = (await this._cvrStore.getRowRecords()).values();\n let total = 0;\n for (const existing of allRowRecords) {\n total++;\n assert(\n existing.refCounts !== null,\n 'allRowRecords should not include null refCounts',\n );\n for (const id of Object.keys(existing.refCounts)) {\n if (this.#removedOrExecutedQueryIDs.has(id)) {\n results.set(existing.id, existing);\n break;\n }\n }\n }\n\n lc.debug?.(\n `found ${\n results.size\n } (of ${total}) rows for executed / removed queries ${[\n ...this.#removedOrExecutedQueryIDs,\n ]}`,\n );\n return results.values();\n },\n );\n }\n\n /**\n * Tracks an executed query, ensures that it is marked as \"gotten\",\n * updating the CVR and creating put patches if necessary.\n *\n * This must be called for all executed queries.\n */\n #trackExecuted(queryID: string, transformationHash: string): Patch[] {\n return startSpan(tracer, 'CVRQueryDrivenUpdater.#trackExecuted', () => {\n assert(\n !this.#removedOrExecutedQueryIDs.has(queryID),\n () => `Query ${queryID} already tracked as executed or removed`,\n );\n this.#removedOrExecutedQueryIDs.add(queryID);\n\n let gotQueryPatch: Patch | undefined;\n const query = this._cvr.queries[queryID];\n if (query.transformationHash !== transformationHash) {\n const transformationVersion = this._ensureNewVersion();\n\n if (query.type !== 'internal' && query.patchVersion === undefined) {\n // client query: desired -> gotten\n query.patchVersion = transformationVersion;\n gotQueryPatch = {\n type: 'query',\n op: 'put',\n id: query.id,\n };\n }\n\n query.transformationHash = transformationHash;\n query.transformationVersion = transformationVersion;\n this._cvrStore.updateQuery(query);\n }\n return gotQueryPatch ? [gotQueryPatch] : [];\n });\n }\n\n /**\n * Tracks a query removed from the \"gotten\" set. In addition to producing the\n * appropriate patches for deleting the query, the removed query is taken into\n * account when computing the final row records in\n * {@link deleteUnreferencedRows}.\n * Namely, any rows with columns that are no longer referenced by a\n * query are deleted.\n *\n * This must only be called on queries that are not \"desired\" by any client.\n */\n #trackRemoved(queryID: string): Patch[] {\n return startSpan(tracer, 'CVRQueryDrivenUpdater.#trackRemoved', () => {\n const query = this._cvr.queries[queryID];\n assertNotInternal(query);\n\n assert(\n !this.#removedOrExecutedQueryIDs.has(queryID),\n () => `Query ${queryID} already tracked as executed or removed`,\n );\n this.#removedOrExecutedQueryIDs.add(queryID);\n delete this._cvr.queries[queryID];\n\n const newVersion = this._ensureNewVersion();\n const queryPatch = {type: 'query', op: 'del', id: queryID} as const;\n this._cvrStore.markQueryAsDeleted(newVersion, queryPatch);\n return [queryPatch];\n });\n }\n\n /**\n * Asserts that a new version has already been set.\n *\n * After {@link #executed} and {@link #removed} are called, we must have properly\n * decided on the final CVR version because the poke-start message declares the\n * final cookie (i.e. version), and that must be sent before any poke parts\n * generated from {@link received} are sent.\n */\n #assertNewVersion(): CVRVersion {\n assert(\n cmpVersions(this._orig.version, this._cvr.version) < 0,\n 'Expected CVR version to have been bumped above original',\n );\n return this._cvr.version;\n }\n\n updatedVersion(): CVRVersion {\n return this._cvr.version;\n }\n\n /**\n * Tracks rows received from executing queries. This will update row records\n * and row patches if the received rows have a new version. The method also\n * returns (put) patches to be returned to update their state, versioned by\n * patchVersion so that only the patches new to the clients are sent.\n */\n received(\n _lc: LogContext,\n rows: Map<RowID, RowUpdate>,\n ): Promise<PatchToVersion[]> {\n return startAsyncSpan(\n tracer,\n 'CVRQueryDrivenUpdater.received',\n async () => {\n const patches: PatchToVersion[] = [];\n const existingRows = await this._cvrStore.getRowRecords();\n\n for (const [id, update] of rows.entries()) {\n const {contents, version, refCounts} = update;\n\n let existing = existingRows.get(id);\n // Accumulate all received refCounts to determine which rows to prune.\n const previouslyReceived = this.#receivedRows.get(id);\n\n const merged =\n previouslyReceived !== undefined\n ? mergeRefCounts(previouslyReceived, refCounts)\n : mergeRefCounts(\n existing?.refCounts,\n refCounts,\n this.#removedOrExecutedQueryIDs,\n );\n\n this.#receivedRows.set(id, merged);\n\n const newRowVersion = merged === null ? undefined : version;\n const patchVersion =\n existing && existing.rowVersion === newRowVersion\n ? existing.patchVersion // existing row is unchanged\n : this.#assertNewVersion();\n\n // Note: for determining what to commit to the CVR store, use the\n // `version` of the update even if `merged` is null (i.e. don't\n // use `newRowVersion`). This will be deduped by the cvr-store flush\n // if it is redundant. In rare cases--namely, if the row key has\n // changed--we _do_ want to add row-put for the new row key with\n // `refCounts: null` in order to correctly record a delete patch\n // for that row, as the row with the old key will be removed.\n const rowVersion = version ?? existing?.rowVersion;\n if (rowVersion) {\n this._cvrStore.putRowRecord({\n id,\n rowVersion,\n patchVersion,\n refCounts: merged,\n });\n } else {\n // This means that a row that was not in the CVR was added during\n // this update, and then subsequently removed. Since there's no\n // corresponding row in the CVR itself, cancel the previous put.\n // Note that we still send a 'del' patch to the client in order to\n // cancel the previous 'put' patch.\n this._cvrStore.delRowRecord(id);\n }\n\n // Dedupe against the lastPatch sent for the row, and ensure that\n // toVersion never backtracks (lest it be undesirably filtered).\n const lastPatch = this.#lastPatches.get(id);\n const toVersion = maxVersion(patchVersion, lastPatch?.toVersion);\n\n if (merged === null) {\n // All refCounts have gone to zero, if row was previously synced\n // delete it.\n if (existing || previouslyReceived) {\n // dedupe\n if (lastPatch?.rowVersion !== null) {\n patches.push({\n patch: {\n type: 'row',\n op: 'del',\n id,\n },\n toVersion,\n });\n this.#lastPatches.set(id, {rowVersion: null, toVersion});\n }\n }\n } else if (contents) {\n assert(\n rowVersion,\n 'rowVersion is required when contents is present',\n );\n // dedupe\n if (!lastPatch?.rowVersion || lastPatch.rowVersion < rowVersion) {\n patches.push({\n patch: {\n type: 'row',\n op: 'put',\n id,\n contents,\n },\n toVersion,\n });\n this.#lastPatches.set(id, {rowVersion, toVersion});\n }\n }\n }\n return patches;\n },\n );\n }\n\n /**\n * Computes and updates the row records based on:\n * * The {@link #executed} queries\n * * The {@link #removed} queries\n * * The {@link received} rows\n *\n * Returns the final delete and patch ops that must be sent to the client\n * to delete rows that are no longer referenced by any query.\n *\n * This is Step [5] of the\n * [CVR Sync Algorithm](https://www.notion.so/replicache/Sync-and-Client-View-Records-CVR-a18e02ec3ec543449ea22070855ff33d?pvs=4#7874f9b80a514be2b8cd5cf538b88d37).\n */\n deleteUnreferencedRows(lc?: LogContext): Promise<PatchToVersion[]> {\n return startAsyncSpan(\n tracer,\n 'CVRQueryDrivenUpdater.deleteUnreferencedRows',\n async () => {\n if (this.#removedOrExecutedQueryIDs.size === 0) {\n // Query-less update. This can happen for config-only changes.\n assert(\n this.#receivedRows.size === 0,\n () =>\n `Expected no received rows for query-less update, got ${this.#receivedRows.size}`,\n );\n return [];\n }\n\n // patches to send to the client.\n const patches: PatchToVersion[] = [];\n\n const start = Date.now();\n assert(this.#existingRows, `trackQueries() was not called`);\n for (const existing of await this.#existingRows) {\n const deletedID = this.#deleteUnreferencedRow(existing);\n if (deletedID === null) {\n continue;\n }\n patches.push({\n toVersion: this._cvr.version,\n patch: {type: 'row', op: 'del', id: deletedID},\n });\n }\n lc?.debug?.(\n `computed ${patches.length} delete patches (${Date.now() - start} ms)`,\n );\n\n return patches;\n },\n );\n }\n\n #deleteUnreferencedRow(existing: RowRecord): RowID | null {\n return startSpan(\n tracer,\n 'CVRQueryDrivenUpdater.#deleteUnreferencedRow',\n () => {\n if (this.#receivedRows.get(existing.id)) {\n return null;\n }\n\n const newRefCounts = mergeRefCounts(\n existing.refCounts,\n undefined,\n this.#removedOrExecutedQueryIDs,\n );\n // If a row is still referenced, we update the refCounts but not the\n // patchVersion (as the existence and contents of the row have not\n // changed from the clients' perspective). If the row is deleted, it\n // gets a new patchVersion (and corresponding poke).\n const patchVersion = newRefCounts\n ? existing.patchVersion\n : this.#assertNewVersion();\n const rowRecord: RowRecord = {\n ...existing,\n patchVersion,\n refCounts: newRefCounts,\n };\n\n this._cvrStore.putRowRecord(rowRecord);\n\n // Return the id to delete if no longer referenced.\n return newRefCounts ? null : existing.id;\n },\n );\n }\n}\n\nfunction mergeRefCounts(\n existing: RefCounts | null | undefined,\n received: RefCounts | null | undefined,\n removeHashes?: Set<string>,\n): RefCounts | null {\n let merged: RefCounts = {};\n if (!existing) {\n merged = received ?? {};\n } else {\n [existing, received].forEach((refCounts, i) => {\n if (!refCounts) {\n return;\n }\n for (const [hash, count] of Object.entries(refCounts)) {\n if (i === 0 /* existing */ && removeHashes?.has(hash)) {\n continue; // removeHashes from existing row.\n }\n merged[hash] = (merged[hash] ?? 0) + count;\n if (merged[hash] === 0) {\n delete merged[hash];\n }\n }\n\n return merged;\n });\n }\n\n return Object.values(merged).some(v => v > 0) ? merged : null;\n}\n\n/**\n * The query must be inactive for all clients to be considered inactive.\n * This is because expiration is defined that way: a query is expired for a client group\n * only if it is expired for all clients in the group.\n *\n * If all clients have inactivated the query, we return\n * the one with the expiration furthest in the future.\n */\nexport function getInactiveQueries(cvr: CVR): {\n hash: string;\n inactivatedAt: TTLClock;\n ttl: number;\n}[] {\n // We no longer support a TTL larger than 10 minutes.\n const inactive: Map<\n string,\n {\n hash: string;\n inactivatedAt: TTLClock;\n ttl: number;\n }\n > = new Map();\n for (const [queryID, query] of Object.entries(cvr.queries)) {\n if (query.type === 'internal') {\n continue;\n }\n for (const clientState of Object.values(query.clientState)) {\n // 1. Take the longest TTL\n // 2. If the query is not inactivated (for any client), do not return it\n const {inactivatedAt, ttl} = clientState;\n const existing = inactive.get(queryID);\n if (inactivatedAt === undefined) {\n if (existing) {\n inactive.delete(queryID);\n }\n break;\n }\n\n const clampedTTL = clampTTL(ttl);\n if (existing) {\n // The stored one might be too large because from a previous version of\n // zero\n const existingTTL = clampTTL(existing.ttl);\n // Use the last eviction time.\n if (\n existingTTL + ttlClockAsNumber(existing.inactivatedAt) <\n ttlClockAsNumber(inactivatedAt) + clampedTTL\n ) {\n existing.ttl = clampedTTL;\n existing.inactivatedAt = inactivatedAt;\n }\n } else {\n inactive.set(queryID, {\n hash: queryID,\n inactivatedAt,\n ttl: clampedTTL,\n });\n }\n }\n }\n\n // First sort all the queries that have TTL. Oldest first.\n return [...inactive.values()].sort((a, b) => {\n if (a.ttl === b.ttl) {\n return (\n ttlClockAsNumber(a.inactivatedAt) - ttlClockAsNumber(b.inactivatedAt)\n );\n }\n return (\n ttlClockAsNumber(a.inactivatedAt) +\n a.ttl -\n ttlClockAsNumber(b.inactivatedAt) -\n b.ttl\n );\n });\n}\n\nexport function nextEvictionTime(cvr: CVR): TTLClock | undefined {\n let next: number | undefined;\n for (const {inactivatedAt, ttl} of getInactiveQueries(cvr)) {\n const expire = ttlClockAsNumber(inactivatedAt) + ttl;\n if (next === undefined || expire < next) {\n next = expire;\n }\n }\n return next as TTLClock | undefined;\n}\n\nfunction newQueryRecord(\n id: string,\n ast: AST | undefined,\n name: string | undefined,\n args: readonly ReadonlyJSONValue[] | undefined,\n): ClientQueryRecord | CustomQueryRecord {\n if (ast !== undefined) {\n assert(\n name === undefined && args === undefined,\n 'Cannot provide name or args with ast',\n );\n return {\n id,\n type: 'client',\n ast,\n clientState: {},\n } satisfies ClientQueryRecord;\n }\n\n assert(\n name !== undefined && args !== undefined,\n 'Must provide name and args',\n );\n return {\n id,\n type: 'custom',\n name,\n args,\n clientState: {},\n } satisfies CustomQueryRecord;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cvr.js","names":["#shard","#deleteQueries","#removedOrExecutedQueryIDs","#receivedRows","#lastPatches","#existingRows","#trackExecuted","#trackRemoved","#lookupRowsForExecutedAndRemovedQueries","#assertNewVersion","#deleteUnreferencedRow"],"sources":["../../../../../../zero-cache/src/services/view-syncer/cvr.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {startAsyncSpan, startSpan} from '../../../../otel/src/span.ts';\nimport {assert} from '../../../../shared/src/asserts.ts';\nimport {type JSONObject} from '../../../../shared/src/bigint-json.ts';\nimport {CustomKeyMap} from '../../../../shared/src/custom-key-map.ts';\nimport {\n deepEqual,\n type ReadonlyJSONValue,\n} from '../../../../shared/src/json.ts';\nimport {must} from '../../../../shared/src/must.ts';\nimport {\n difference,\n intersection,\n union,\n} from '../../../../shared/src/set-utils.ts';\nimport {stringCompare} from '../../../../shared/src/string-compare.ts';\nimport type {AST} from '../../../../zero-protocol/src/ast.ts';\nimport type {ClientSchema} from '../../../../zero-protocol/src/client-schema.ts';\nimport {ErrorOrigin} from '../../../../zero-protocol/src/error-origin.ts';\nimport {ProtocolError} from '../../../../zero-protocol/src/error.ts';\nimport {\n clampTTL,\n compareTTL,\n DEFAULT_TTL_MS,\n} from '../../../../zql/src/query/ttl.ts';\nimport {recordQuery} from '../../server/anonymous-otel-start.ts';\nimport type {LexiVersion} from '../../types/lexi-version.ts';\nimport {rowIDString} from '../../types/row-key.ts';\nimport {upstreamSchema, type ShardID} from '../../types/shards.ts';\nimport type {Patch, PatchToVersion} from './client-handler.ts';\nimport {type CVRFlushStats, type CVRStore} from './cvr-store.ts';\nimport {\n cmpVersions,\n maxVersion,\n oneAfter,\n type ClientQueryRecord,\n type ClientRecord,\n type CustomQueryRecord,\n type CVRVersion,\n type InternalQueryRecord,\n type QueryRecord,\n type RowID,\n type RowRecord,\n versionString,\n} from './schema/types.ts';\nimport {tracer} from './tracer.ts';\nimport {ttlClockAsNumber, type TTLClock} from './ttl-clock.ts';\n\nexport type RowUpdate = {\n version?: string; // Undefined for an unref.\n contents?: JSONObject; // Undefined for an unref.\n refCounts: {[hash: string]: number}; // Counts are negative when a row is unrefed.\n};\n\n/** Internally used mutable CVR type. */\nexport type CVR = {\n id: string;\n version: CVRVersion;\n lastActive: number;\n ttlClock: TTLClock;\n replicaVersion: string | null;\n clients: Record<string, ClientRecord>;\n queries: Record<string, QueryRecord>;\n clientSchema: ClientSchema | null;\n profileID: string | null;\n};\n\n/** Exported immutable CVR type. */\n// TODO: Use Immutable<CVR> when the AST is immutable.\nexport type CVRSnapshot = {\n readonly id: string;\n readonly version: CVRVersion;\n readonly lastActive: number;\n readonly ttlClock: TTLClock;\n readonly replicaVersion: string | null;\n readonly clients: Readonly<Record<string, ClientRecord>>;\n readonly queries: Readonly<Record<string, QueryRecord>>;\n readonly clientSchema: ClientSchema | null;\n readonly profileID: string | null;\n};\n\nconst CLIENT_LMID_QUERY_ID = 'lmids';\nconst CLIENT_MUTATION_RESULTS_QUERY_ID = 'mutationResults';\n\nfunction assertNotInternal(\n query: QueryRecord,\n): asserts query is ClientQueryRecord {\n if (query.type === 'internal') {\n // This should never happen for behaving clients, as query ids should be hashes.\n throw new Error(`Query ID ${query.id} is reserved for internal use`);\n }\n}\n\nexport function getMutationResultsQuery(\n upstreamSchema: string,\n clientGroupID: string,\n): InternalQueryRecord {\n return {\n id: CLIENT_MUTATION_RESULTS_QUERY_ID,\n type: 'internal',\n ast: {\n schema: '',\n table: `${upstreamSchema}.mutations`,\n where: {\n type: 'and',\n conditions: [\n {\n type: 'simple',\n left: {\n type: 'column',\n name: 'clientGroupID',\n },\n op: '=',\n right: {\n type: 'literal',\n value: clientGroupID,\n },\n },\n ],\n },\n orderBy: [\n ['clientGroupID', 'asc'],\n ['clientID', 'asc'],\n ['mutationID', 'asc'],\n ],\n },\n };\n}\n\n/**\n * The base CVRUpdater contains logic common to the {@link CVRConfigDrivenUpdater} and\n * {@link CVRQueryDrivenUpdater}. The CVRUpdater class itself is exported for updating\n * the `lastActive` time of the CVR in the absence of any changes to the CVR contents.\n * Although activity is automatically tracked when the CVR contents change, there may be\n * edge cases in which a client actively connects to a CVR that doesn't itself change.\n * Calling `new CVRUpdater(...).flush()` will explicitly update the active index and\n * prevent the CVR from being garbage collected.\n */\nexport class CVRUpdater {\n protected readonly _orig: CVRSnapshot;\n protected readonly _cvr: CVR;\n\n protected readonly _cvrStore: CVRStore;\n\n /**\n * @param cvrStore The CVRStore to use for storage\n * @param cvr The current CVR\n */\n constructor(\n cvrStore: CVRStore,\n cvr: CVRSnapshot,\n replicaVersion: string | null,\n ) {\n this._cvrStore = cvrStore;\n this._orig = cvr;\n this._cvr = structuredClone(cvr) as CVR; // mutable deep copy\n this._cvr.replicaVersion = replicaVersion;\n }\n\n protected _setVersion(version: CVRVersion) {\n assert(\n cmpVersions(this._cvr.version, version) < 0,\n 'Expected new version to be greater than current version',\n );\n this._cvr.version = version;\n return version;\n }\n\n /**\n * Ensures that the new CVR has a higher version than the original.\n * This method is idempotent in that it will always return the same\n * (possibly bumped) version.\n */\n protected _ensureNewVersion(): CVRVersion {\n if (cmpVersions(this._orig.version, this._cvr.version) === 0) {\n this._setVersion(oneAfter(this._cvr.version));\n }\n return this._cvr.version;\n }\n\n async flush(\n lc: LogContext,\n lastConnectTime: number,\n lastActive: number,\n ttlClock: TTLClock,\n ): Promise<{\n cvr: CVRSnapshot;\n flushed: CVRFlushStats | false;\n }> {\n this._cvr.ttlClock = ttlClock;\n this._cvr.lastActive = lastActive;\n const flushed = await this._cvrStore.flush(\n lc,\n this._orig.version,\n this._cvr,\n lastConnectTime,\n );\n if (!flushed) {\n return {cvr: this._orig, flushed: false};\n }\n return {cvr: this._cvr, flushed};\n }\n}\n\n/**\n * A {@link CVRConfigDrivenUpdater} is used for updating a CVR with config-driven\n * changes. Note that this may result in row deletion (e.g. if queries get dropped),\n * but the `stateVersion` of the CVR does not change.\n */\nexport class CVRConfigDrivenUpdater extends CVRUpdater {\n readonly #shard: ShardID;\n\n constructor(cvrStore: CVRStore, cvr: CVRSnapshot, shard: ShardID) {\n super(cvrStore, cvr, cvr.replicaVersion);\n this.#shard = shard;\n }\n\n ensureClient(id: string): ClientRecord {\n return startSpan(tracer, 'CVRConfigDrivenUpdater.ensureClient', () => {\n let client = this._cvr.clients[id];\n if (client) {\n return client;\n }\n // Add the ClientRecord and PutPatch\n client = {id, desiredQueryIDs: []};\n this._cvr.clients[id] = client;\n\n this._ensureNewVersion();\n this._cvrStore.insertClient(client);\n\n if (!this._cvr.queries[CLIENT_LMID_QUERY_ID]) {\n const lmidsQuery: InternalQueryRecord = {\n id: CLIENT_LMID_QUERY_ID,\n ast: {\n schema: '',\n table: `${upstreamSchema(this.#shard)}.clients`,\n where: {\n type: 'simple',\n left: {\n type: 'column',\n name: 'clientGroupID',\n },\n op: '=',\n right: {\n type: 'literal',\n value: this._cvr.id,\n },\n },\n orderBy: [\n ['clientGroupID', 'asc'],\n ['clientID', 'asc'],\n ],\n },\n type: 'internal',\n };\n this._cvr.queries[CLIENT_LMID_QUERY_ID] = lmidsQuery;\n this._cvrStore.putQuery(lmidsQuery);\n }\n if (!this._cvr.queries[CLIENT_MUTATION_RESULTS_QUERY_ID]) {\n const mutationResultsQuery: InternalQueryRecord =\n getMutationResultsQuery(upstreamSchema(this.#shard), this._cvr.id);\n this._cvr.queries[CLIENT_MUTATION_RESULTS_QUERY_ID] =\n mutationResultsQuery;\n this._cvrStore.putQuery(mutationResultsQuery);\n }\n\n return client;\n });\n }\n\n setClientSchema(lc: LogContext, clientSchema: ClientSchema) {\n startSpan(tracer, 'CVRConfigDrivenUpdater.setClientSchema', () => {\n if (this._cvr.clientSchema === null) {\n this._cvr.clientSchema = clientSchema;\n this._cvrStore.putInstance(this._cvr);\n } else if (!deepEqual(this._cvr.clientSchema, clientSchema)) {\n // This should not be possible with a correct Zero client, as clients\n // of a CVR should all have the same schema (given that the schema hash\n // is part of the idb key). In fact, clients joining an existing group\n // (i.e. non-empty baseCookie) do not send the clientSchema message.\n lc.warn?.(\n `New schema ${JSON.stringify(\n clientSchema,\n )} does not match existing schema ${JSON.stringify(\n this._cvr.clientSchema,\n )}`,\n );\n throw new ProtocolError({\n kind: 'InvalidConnectionRequest',\n message: `Provided schema does not match previous schema`,\n origin: ErrorOrigin.ZeroCache,\n });\n }\n });\n }\n\n setProfileID(lc: LogContext, profileID: string) {\n if (this._cvr.profileID !== profileID) {\n if (\n this._cvr.profileID !== null &&\n !this._cvr.profileID.startsWith('cg')\n ) {\n // We expect profile ID's to change from null or from the back-filled\n // \"cg...\" value. Log a warning otherwise to surface unexpected or\n // pathological conditions.\n lc.warn?.(\n `changing profile ID from ${this._cvr.profileID} to ${profileID}`,\n );\n }\n this._cvr.profileID = profileID;\n this._cvrStore.putInstance(this._cvr);\n }\n }\n\n putDesiredQueries(\n clientID: string,\n queries: Readonly<{\n hash: string;\n ast?: AST | undefined;\n name?: string | undefined;\n args?: readonly ReadonlyJSONValue[] | undefined;\n ttl?: number | undefined;\n }>[],\n ): PatchToVersion[] {\n return startSpan(tracer, 'CVRConfigDrivenUpdater.putDesiredQueries', () => {\n const patches: PatchToVersion[] = [];\n const client = this.ensureClient(clientID);\n const current = new Set(client.desiredQueryIDs);\n\n // Find the new/changed desired queries.\n const needed: Set<string> = new Set();\n\n const recordQueryForTelemetry = (q: (typeof queries)[0]) => {\n const {ast, name, args} = q;\n if (ast) {\n recordQuery('crud');\n } else if (name && args) {\n recordQuery('custom');\n }\n };\n\n for (const q of queries) {\n const {hash, ttl = DEFAULT_TTL_MS} = q;\n const query = this._cvr.queries[hash];\n if (!query) {\n // New query - record for telemetry\n recordQueryForTelemetry(q);\n needed.add(hash);\n continue;\n }\n if (query.type === 'internal') {\n continue;\n }\n\n const oldClientState = query.clientState[clientID];\n // Old query was inactivated or never desired by this client.\n if (!oldClientState || oldClientState.inactivatedAt !== undefined) {\n // Reactivated query - record for telemetry\n recordQueryForTelemetry(q);\n needed.add(hash);\n continue;\n }\n\n if (compareTTL(ttl, oldClientState.ttl) > 0) {\n // TTL update only - don't record for telemetry\n needed.add(hash);\n }\n }\n\n if (needed.size === 0) {\n return patches;\n }\n const newVersion = this._ensureNewVersion();\n client.desiredQueryIDs = [...union(current, needed)].sort(stringCompare);\n\n for (const id of needed) {\n const q = must(queries.find(({hash}) => hash === id));\n const {ast, name, args} = q;\n\n const ttl = clampTTL(q.ttl ?? DEFAULT_TTL_MS);\n const query =\n this._cvr.queries[id] ?? newQueryRecord(id, ast, name, args);\n assertNotInternal(query);\n\n const inactivatedAt = undefined;\n\n query.clientState[clientID] = {\n inactivatedAt,\n ttl,\n version: newVersion,\n };\n this._cvr.queries[id] = query;\n patches.push({\n toVersion: newVersion,\n patch: {type: 'query', op: 'put', id, clientID},\n });\n\n this._cvrStore.putQuery(query);\n this._cvrStore.putDesiredQuery(\n newVersion,\n query,\n client,\n false,\n inactivatedAt,\n ttl,\n );\n }\n return patches;\n });\n }\n\n markDesiredQueriesAsInactive(\n clientID: string,\n queryHashes: string[],\n ttlClock: TTLClock,\n ): PatchToVersion[] {\n return this.#deleteQueries(clientID, queryHashes, ttlClock);\n }\n\n deleteDesiredQueries(\n clientID: string,\n queryHashes: string[],\n ): PatchToVersion[] {\n return this.#deleteQueries(clientID, queryHashes, undefined);\n }\n\n #deleteQueries(\n clientID: string,\n queryHashes: string[],\n inactivatedAt: TTLClock | undefined,\n ): PatchToVersion[] {\n return startSpan(tracer, 'CVRConfigDrivenUpdater.#deleteQueries', () => {\n const patches: PatchToVersion[] = [];\n const client = this.ensureClient(clientID);\n const current = new Set(client.desiredQueryIDs);\n const unwanted = new Set(queryHashes);\n const remove = intersection(unwanted, current);\n if (remove.size === 0) {\n return patches;\n }\n\n const newVersion = this._ensureNewVersion();\n client.desiredQueryIDs = [...difference(current, remove)].sort(\n stringCompare,\n );\n\n for (const id of remove) {\n const query = this._cvr.queries[id];\n if (!query) {\n continue; // Query itself has already been removed. Should not happen?\n }\n assertNotInternal(query);\n\n let ttl = DEFAULT_TTL_MS;\n if (inactivatedAt === undefined) {\n delete query.clientState[clientID];\n } else {\n // client state can be missing if the query never transformed so we never\n // recorded it.\n const clientState = query.clientState[clientID];\n if (clientState !== undefined) {\n assert(\n clientState.inactivatedAt === undefined,\n `Query ${id} is already inactivated`,\n );\n // Clamp TTL to ensure we don't propagate historical unclamped values.\n ttl = clampTTL(clientState.ttl);\n query.clientState[clientID] = {\n inactivatedAt,\n ttl,\n version: newVersion,\n };\n }\n }\n\n this._cvrStore.putQuery(query);\n this._cvrStore.putDesiredQuery(\n newVersion,\n query,\n client,\n true,\n inactivatedAt,\n ttl,\n );\n patches.push({\n toVersion: newVersion,\n patch: {type: 'query', op: 'del', id, clientID},\n });\n }\n return patches;\n });\n }\n\n clearDesiredQueries(clientID: string): PatchToVersion[] {\n const client = this.ensureClient(clientID);\n return this.#deleteQueries(clientID, client.desiredQueryIDs, undefined);\n }\n\n deleteClient(clientID: string, ttlClock: TTLClock): PatchToVersion[] {\n return startSpan(tracer, 'CVRConfigDrivenUpdater.deleteClient', () => {\n // clientID might not be part of this client group but if it is, this delete\n // may generate changes to the desired queries.\n\n const client = this._cvr.clients[clientID];\n if (!client) {\n // Clients in different client groups are no longer deleted, leaving\n // cleanup to inactive CVR purging logic.\n return [];\n }\n\n // When a client is deleted we mark all of its desired queries as inactive.\n // They will then be removed when the queries expire.\n const patches = this.markDesiredQueriesAsInactive(\n clientID,\n client.desiredQueryIDs,\n ttlClock,\n );\n delete this._cvr.clients[clientID];\n this._cvrStore.deleteClient(clientID);\n\n return patches;\n });\n }\n}\n\ntype Hash = string;\nexport type Column = string;\nexport type RefCounts = Record<Hash, number>;\n\ntype RowPatchInfo = {\n rowVersion: string | null; // null for a row-del\n toVersion: CVRVersion; // patchVersion\n};\n\n/**\n * A {@link CVRQueryDrivenUpdater} is used for updating a CVR after making queries.\n * The caller should invoke:\n *\n * * {@link trackQueries} for queries that are being executed or removed.\n * * {@link received} for all rows received from the executed queries\n * * {@link deleteUnreferencedRows} to remove any rows that have\n * fallen out of the query result view.\n * * {@link flush}\n *\n * After flushing, the caller should perform any necessary catchup of\n * config and row patches for clients that are behind. See\n * {@link CVRStore.catchupConfigPatches} and {@link CVRStore.catchupRowPatches}.\n */\nexport class CVRQueryDrivenUpdater extends CVRUpdater {\n readonly #removedOrExecutedQueryIDs = new Set<string>();\n readonly #receivedRows = new CustomKeyMap<RowID, RefCounts | null>(\n rowIDString,\n );\n readonly #lastPatches = new CustomKeyMap<RowID, RowPatchInfo>(rowIDString);\n\n #existingRows: Promise<Iterable<RowRecord>> | undefined = undefined;\n\n /**\n * @param stateVersion The `stateVersion` at which the queries were executed.\n */\n constructor(\n cvrStore: CVRStore,\n cvr: CVRSnapshot,\n stateVersion: LexiVersion,\n replicaVersion: string,\n ) {\n super(cvrStore, cvr, replicaVersion);\n\n assert(\n // We should either be setting the cvr.replicaVersion for the first time, or it should\n // be something newer than the current cvr.replicaVersion. Otherwise, the CVR should\n // have been rejected by the ViewSyncer.\n (cvr.replicaVersion ?? replicaVersion) <= replicaVersion,\n `Cannot sync from an older replicaVersion: CVR=${cvr.replicaVersion}, DB=${replicaVersion}`,\n );\n assert(\n stateVersion >= cvr.version.stateVersion,\n () =>\n `stateVersion (${stateVersion}) must be >= cvr.version.stateVersion (${cvr.version.stateVersion})`,\n );\n if (stateVersion > cvr.version.stateVersion) {\n this._setVersion({stateVersion});\n }\n }\n\n /**\n * Initiates the tracking of the specified `executed` and `removed` queries.\n * This kicks of a lookup of existing {@link RowRecord}s currently associated\n * with those queries, which will be used to reconcile the rows to keep\n * after all rows have been {@link received()}.\n *\n * \"transformed\" queries are queries that are currently\n * gotten and running in the pipeline driver but\n * received a new transformation hash due to an auth token\n * update.\n *\n * @returns The new CVRVersion to be used when all changes are committed.\n */\n trackQueries(\n lc: LogContext,\n executed: {id: string; transformationHash: string}[],\n removed: {id: string}[],\n ): {newVersion: CVRVersion; queryPatches: PatchToVersion[]} {\n return startSpan(tracer, 'CVRQueryDrivenUpdater.trackQueries', () => {\n assert(this.#existingRows === undefined, `trackQueries already called`);\n\n const queryPatches: Patch[] = [\n executed.map(q => this.#trackExecuted(q.id, q.transformationHash)),\n removed.map(q => this.#trackRemoved(q.id)),\n ].flat(2);\n\n this.#existingRows = this.#lookupRowsForExecutedAndRemovedQueries(lc);\n // Immediately attach a rejection handler to avoid unhandled rejections.\n // The error will surface when this.#existingRows is awaited.\n void this.#existingRows.then(() => {});\n\n const versionBumped =\n cmpVersions(this._orig.version, this._cvr.version) < 0;\n lc.info?.(\n `trackQueries: ${executed.length} executed, ${removed.length} removed, ` +\n `version ${versionBumped ? 'bumped' : 'unchanged'}`,\n );\n\n return {\n newVersion: this._cvr.version,\n queryPatches: queryPatches.map(patch => ({\n patch,\n toVersion: this._cvr.version,\n })),\n };\n });\n }\n\n #lookupRowsForExecutedAndRemovedQueries(\n lc: LogContext,\n ): Promise<Iterable<RowRecord>> {\n return startAsyncSpan(\n tracer,\n 'CVRQueryDrivenUpdater.#lookupRowsForExecutedAndRemovedQueries',\n async () => {\n const results = new CustomKeyMap<RowID, RowRecord>(rowIDString);\n\n if (this.#removedOrExecutedQueryIDs.size === 0) {\n // Query-less update. This can happen for config only changes.\n return [];\n }\n\n // Utilizes the in-memory RowCache.\n const allRowRecords = (await this._cvrStore.getRowRecords()).values();\n let total = 0;\n for (const existing of allRowRecords) {\n total++;\n assert(\n existing.refCounts !== null,\n 'allRowRecords should not include null refCounts',\n );\n for (const id of Object.keys(existing.refCounts)) {\n if (this.#removedOrExecutedQueryIDs.has(id)) {\n results.set(existing.id, existing);\n break;\n }\n }\n }\n\n lc.debug?.(\n `found ${\n results.size\n } (of ${total}) rows for executed / removed queries ${[\n ...this.#removedOrExecutedQueryIDs,\n ]}`,\n );\n return results.values();\n },\n );\n }\n\n /**\n * Tracks an executed query, ensures that it is marked as \"gotten\",\n * updating the CVR and creating put patches if necessary.\n *\n * This must be called for all executed queries.\n */\n #trackExecuted(queryID: string, transformationHash: string): Patch[] {\n return startSpan(tracer, 'CVRQueryDrivenUpdater.#trackExecuted', () => {\n assert(\n !this.#removedOrExecutedQueryIDs.has(queryID),\n () => `Query ${queryID} already tracked as executed or removed`,\n );\n this.#removedOrExecutedQueryIDs.add(queryID);\n\n let gotQueryPatch: Patch | undefined;\n const query = this._cvr.queries[queryID];\n if (query.transformationHash !== transformationHash) {\n const transformationVersion = this._ensureNewVersion();\n\n if (query.type !== 'internal' && query.patchVersion === undefined) {\n // client query: desired -> gotten\n query.patchVersion = transformationVersion;\n gotQueryPatch = {\n type: 'query',\n op: 'put',\n id: query.id,\n };\n }\n\n query.transformationHash = transformationHash;\n query.transformationVersion = transformationVersion;\n this._cvrStore.updateQuery(query);\n }\n return gotQueryPatch ? [gotQueryPatch] : [];\n });\n }\n\n /**\n * Tracks a query removed from the \"gotten\" set. In addition to producing the\n * appropriate patches for deleting the query, the removed query is taken into\n * account when computing the final row records in\n * {@link deleteUnreferencedRows}.\n * Namely, any rows with columns that are no longer referenced by a\n * query are deleted.\n *\n * This must only be called on queries that are not \"desired\" by any client.\n */\n #trackRemoved(queryID: string): Patch[] {\n return startSpan(tracer, 'CVRQueryDrivenUpdater.#trackRemoved', () => {\n const query = this._cvr.queries[queryID];\n assertNotInternal(query);\n\n assert(\n !this.#removedOrExecutedQueryIDs.has(queryID),\n () => `Query ${queryID} already tracked as executed or removed`,\n );\n this.#removedOrExecutedQueryIDs.add(queryID);\n delete this._cvr.queries[queryID];\n\n const newVersion = this._ensureNewVersion();\n const queryPatch = {type: 'query', op: 'del', id: queryID} as const;\n this._cvrStore.markQueryAsDeleted(newVersion, queryPatch);\n return [queryPatch];\n });\n }\n\n /**\n * Asserts that a new version has already been set.\n *\n * After {@link #executed} and {@link #removed} are called, we must have properly\n * decided on the final CVR version because the poke-start message declares the\n * final cookie (i.e. version), and that must be sent before any poke parts\n * generated from {@link received} are sent.\n */\n #assertNewVersion(\n rowID: RowID,\n existingVersion: string | undefined,\n newVersion: string | undefined,\n refCounts: RefCounts,\n ): CVRVersion {\n assert(\n cmpVersions(this._orig.version, this._cvr.version) < 0,\n () =>\n `Expected CVR version to have been bumped above original` +\n ` (orig=${versionString(this._orig.version)},` +\n ` curr=${versionString(this._cvr.version)}).` +\n ` Row ${JSON.stringify(rowID)}:` +\n ` existing=${existingVersion},` +\n ` new=${newVersion},` +\n ` queries=[${Object.keys(refCounts).join(',')}]`,\n );\n return this._cvr.version;\n }\n\n updatedVersion(): CVRVersion {\n return this._cvr.version;\n }\n\n /**\n * Tracks rows received from executing queries. This will update row records\n * and row patches if the received rows have a new version. The method also\n * returns (put) patches to be returned to update their state, versioned by\n * patchVersion so that only the patches new to the clients are sent.\n */\n received(\n _lc: LogContext,\n rows: Map<RowID, RowUpdate>,\n ): Promise<PatchToVersion[]> {\n return startAsyncSpan(\n tracer,\n 'CVRQueryDrivenUpdater.received',\n async () => {\n const patches: PatchToVersion[] = [];\n const existingRows = await this._cvrStore.getRowRecords();\n\n for (const [id, update] of rows.entries()) {\n const {contents, version, refCounts} = update;\n\n let existing = existingRows.get(id);\n // Accumulate all received refCounts to determine which rows to prune.\n const previouslyReceived = this.#receivedRows.get(id);\n\n const merged =\n previouslyReceived !== undefined\n ? mergeRefCounts(previouslyReceived, refCounts)\n : mergeRefCounts(\n existing?.refCounts,\n refCounts,\n this.#removedOrExecutedQueryIDs,\n );\n\n this.#receivedRows.set(id, merged);\n\n const newRowVersion = merged === null ? undefined : version;\n const patchVersion =\n existing && existing.rowVersion === newRowVersion\n ? existing.patchVersion // existing row is unchanged\n : this.#assertNewVersion(\n id,\n existing?.rowVersion,\n newRowVersion,\n refCounts,\n );\n\n // Note: for determining what to commit to the CVR store, use the\n // `version` of the update even if `merged` is null (i.e. don't\n // use `newRowVersion`). This will be deduped by the cvr-store flush\n // if it is redundant. In rare cases--namely, if the row key has\n // changed--we _do_ want to add row-put for the new row key with\n // `refCounts: null` in order to correctly record a delete patch\n // for that row, as the row with the old key will be removed.\n const rowVersion = version ?? existing?.rowVersion;\n if (rowVersion) {\n this._cvrStore.putRowRecord({\n id,\n rowVersion,\n patchVersion,\n refCounts: merged,\n });\n } else {\n // This means that a row that was not in the CVR was added during\n // this update, and then subsequently removed. Since there's no\n // corresponding row in the CVR itself, cancel the previous put.\n // Note that we still send a 'del' patch to the client in order to\n // cancel the previous 'put' patch.\n this._cvrStore.delRowRecord(id);\n }\n\n // Dedupe against the lastPatch sent for the row, and ensure that\n // toVersion never backtracks (lest it be undesirably filtered).\n const lastPatch = this.#lastPatches.get(id);\n const toVersion = maxVersion(patchVersion, lastPatch?.toVersion);\n\n if (merged === null) {\n // All refCounts have gone to zero, if row was previously synced\n // delete it.\n if (existing || previouslyReceived) {\n // dedupe\n if (lastPatch?.rowVersion !== null) {\n patches.push({\n patch: {\n type: 'row',\n op: 'del',\n id,\n },\n toVersion,\n });\n this.#lastPatches.set(id, {rowVersion: null, toVersion});\n }\n }\n } else if (contents) {\n assert(\n rowVersion,\n 'rowVersion is required when contents is present',\n );\n // dedupe\n if (!lastPatch?.rowVersion || lastPatch.rowVersion < rowVersion) {\n patches.push({\n patch: {\n type: 'row',\n op: 'put',\n id,\n contents,\n },\n toVersion,\n });\n this.#lastPatches.set(id, {rowVersion, toVersion});\n }\n }\n }\n return patches;\n },\n );\n }\n\n /**\n * Computes and updates the row records based on:\n * * The {@link #executed} queries\n * * The {@link #removed} queries\n * * The {@link received} rows\n *\n * Returns the final delete and patch ops that must be sent to the client\n * to delete rows that are no longer referenced by any query.\n *\n * This is Step [5] of the\n * [CVR Sync Algorithm](https://www.notion.so/replicache/Sync-and-Client-View-Records-CVR-a18e02ec3ec543449ea22070855ff33d?pvs=4#7874f9b80a514be2b8cd5cf538b88d37).\n */\n deleteUnreferencedRows(lc?: LogContext): Promise<PatchToVersion[]> {\n return startAsyncSpan(\n tracer,\n 'CVRQueryDrivenUpdater.deleteUnreferencedRows',\n async () => {\n if (this.#removedOrExecutedQueryIDs.size === 0) {\n // Query-less update. This can happen for config-only changes.\n assert(\n this.#receivedRows.size === 0,\n () =>\n `Expected no received rows for query-less update, got ${this.#receivedRows.size}`,\n );\n return [];\n }\n\n // patches to send to the client.\n const patches: PatchToVersion[] = [];\n\n const start = Date.now();\n assert(this.#existingRows, `trackQueries() was not called`);\n for (const existing of await this.#existingRows) {\n const deletedID = this.#deleteUnreferencedRow(existing);\n if (deletedID === null) {\n continue;\n }\n patches.push({\n toVersion: this._cvr.version,\n patch: {type: 'row', op: 'del', id: deletedID},\n });\n }\n lc?.debug?.(\n `computed ${patches.length} delete patches (${Date.now() - start} ms)`,\n );\n\n return patches;\n },\n );\n }\n\n #deleteUnreferencedRow(existing: RowRecord): RowID | null {\n return startSpan(\n tracer,\n 'CVRQueryDrivenUpdater.#deleteUnreferencedRow',\n () => {\n if (this.#receivedRows.get(existing.id)) {\n return null;\n }\n\n const newRefCounts = mergeRefCounts(\n existing.refCounts,\n undefined,\n this.#removedOrExecutedQueryIDs,\n );\n // If a row is still referenced, we update the refCounts but not the\n // patchVersion (as the existence and contents of the row have not\n // changed from the clients' perspective). If the row is deleted, it\n // gets a new patchVersion (and corresponding poke).\n const patchVersion = newRefCounts\n ? existing.patchVersion\n : this.#assertNewVersion(\n existing.id,\n existing.rowVersion,\n undefined,\n existing.refCounts ?? {},\n );\n const rowRecord: RowRecord = {\n ...existing,\n patchVersion,\n refCounts: newRefCounts,\n };\n\n this._cvrStore.putRowRecord(rowRecord);\n\n // Return the id to delete if no longer referenced.\n return newRefCounts ? null : existing.id;\n },\n );\n }\n}\n\nfunction mergeRefCounts(\n existing: RefCounts | null | undefined,\n received: RefCounts | null | undefined,\n removeHashes?: Set<string>,\n): RefCounts | null {\n let merged: RefCounts = {};\n if (!existing) {\n merged = received ?? {};\n } else {\n [existing, received].forEach((refCounts, i) => {\n if (!refCounts) {\n return;\n }\n for (const [hash, count] of Object.entries(refCounts)) {\n if (i === 0 /* existing */ && removeHashes?.has(hash)) {\n continue; // removeHashes from existing row.\n }\n merged[hash] = (merged[hash] ?? 0) + count;\n if (merged[hash] === 0) {\n delete merged[hash];\n }\n }\n\n return merged;\n });\n }\n\n return Object.values(merged).some(v => v > 0) ? merged : null;\n}\n\n/**\n * The query must be inactive for all clients to be considered inactive.\n * This is because expiration is defined that way: a query is expired for a client group\n * only if it is expired for all clients in the group.\n *\n * If all clients have inactivated the query, we return\n * the one with the expiration furthest in the future.\n */\nexport function getInactiveQueries(cvr: CVR): {\n hash: string;\n inactivatedAt: TTLClock;\n ttl: number;\n}[] {\n // We no longer support a TTL larger than 10 minutes.\n const inactive: Map<\n string,\n {\n hash: string;\n inactivatedAt: TTLClock;\n ttl: number;\n }\n > = new Map();\n for (const [queryID, query] of Object.entries(cvr.queries)) {\n if (query.type === 'internal') {\n continue;\n }\n for (const clientState of Object.values(query.clientState)) {\n // 1. Take the longest TTL\n // 2. If the query is not inactivated (for any client), do not return it\n const {inactivatedAt, ttl} = clientState;\n const existing = inactive.get(queryID);\n if (inactivatedAt === undefined) {\n if (existing) {\n inactive.delete(queryID);\n }\n break;\n }\n\n const clampedTTL = clampTTL(ttl);\n if (existing) {\n // The stored one might be too large because from a previous version of\n // zero\n const existingTTL = clampTTL(existing.ttl);\n // Use the last eviction time.\n if (\n existingTTL + ttlClockAsNumber(existing.inactivatedAt) <\n ttlClockAsNumber(inactivatedAt) + clampedTTL\n ) {\n existing.ttl = clampedTTL;\n existing.inactivatedAt = inactivatedAt;\n }\n } else {\n inactive.set(queryID, {\n hash: queryID,\n inactivatedAt,\n ttl: clampedTTL,\n });\n }\n }\n }\n\n // First sort all the queries that have TTL. Oldest first.\n return [...inactive.values()].sort((a, b) => {\n if (a.ttl === b.ttl) {\n return (\n ttlClockAsNumber(a.inactivatedAt) - ttlClockAsNumber(b.inactivatedAt)\n );\n }\n return (\n ttlClockAsNumber(a.inactivatedAt) +\n a.ttl -\n ttlClockAsNumber(b.inactivatedAt) -\n b.ttl\n );\n });\n}\n\nexport function nextEvictionTime(cvr: CVR): TTLClock | undefined {\n let next: number | undefined;\n for (const {inactivatedAt, ttl} of getInactiveQueries(cvr)) {\n const expire = ttlClockAsNumber(inactivatedAt) + ttl;\n if (next === undefined || expire < next) {\n next = expire;\n }\n }\n return next as TTLClock | undefined;\n}\n\nfunction newQueryRecord(\n id: string,\n ast: AST | undefined,\n name: string | undefined,\n args: readonly ReadonlyJSONValue[] | undefined,\n): ClientQueryRecord | CustomQueryRecord {\n if (ast !== undefined) {\n assert(\n name === undefined && args === undefined,\n 'Cannot provide name or args with ast',\n );\n return {\n id,\n type: 'client',\n ast,\n clientState: {},\n } satisfies ClientQueryRecord;\n }\n\n assert(\n name !== undefined && args !== undefined,\n 'Must provide name and args',\n );\n return {\n id,\n type: 'custom',\n name,\n args,\n clientState: {},\n } satisfies CustomQueryRecord;\n}\n"],"mappings":"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"}
|
|
@@ -47,7 +47,7 @@ export type Timer = {
|
|
|
47
47
|
*/
|
|
48
48
|
export declare class PipelineDriver {
|
|
49
49
|
#private;
|
|
50
|
-
constructor(lc: LogContext, logConfig: LogConfig, snapshotter: Snapshotter, shardID: ShardID, storage: ClientGroupStorage, clientGroupID: string, inspectorDelegate: InspectorDelegate, yieldThresholdMs: () => number, enablePlanner?: boolean
|
|
50
|
+
constructor(lc: LogContext, logConfig: LogConfig, snapshotter: Snapshotter, shardID: ShardID, storage: ClientGroupStorage, clientGroupID: string, inspectorDelegate: InspectorDelegate, yieldThresholdMs: () => number, enablePlanner?: boolean, config?: ZeroConfig);
|
|
51
51
|
/**
|
|
52
52
|
* Initializes the PipelineDriver to the current head of the database.
|
|
53
53
|
* Queries can then be added (i.e. hydrated) with {@link addQuery()}.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline-driver.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/pipeline-driver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAIjD,OAAO,KAAK,EAAC,GAAG,EAAe,MAAM,sCAAsC,CAAC;AAC5E,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gDAAgD,CAAC;AACjF,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,uCAAuC,CAAC;AAC/D,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,8CAA8C,CAAC;AAQ7E,OAAO,EAEL,KAAK,KAAK,EAEX,MAAM,qCAAqC,CAAC;AAS7C,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,4CAA4C,CAAC;AAQnF,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAC,SAAS,EAAE,UAAU,EAAC,MAAM,6BAA6B,CAAC;AAEvE,OAAO,KAAK,EAAC,cAAc,EAAgB,MAAM,mBAAmB,CAAC;AAKrE,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAC,KAAK,OAAO,EAAC,MAAM,uBAAuB,CAAC;AAMnD,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,kBAAkB,CAAC;AAGlD,MAAM,MAAM,MAAM,GAAG;IACnB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAgBrD,KAAK,SAAS,GAAG;IACf,QAAQ,CAAC,cAAc,EAAE,GAAG,CAAC;IAC7B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;CACrC,CAAC;AAaF,MAAM,MAAM,KAAK,GAAG;IAClB,UAAU,EAAE,MAAM,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,MAAM,CAAC;CAC5B,CAAC;AAQF;;GAEG;AACH,qBAAa,cAAc;;gBAqCvB,EAAE,EAAE,UAAU,EACd,SAAS,EAAE,SAAS,EACpB,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,kBAAkB,EAC3B,aAAa,EAAE,MAAM,EACrB,iBAAiB,EAAE,iBAAiB,EACpC,gBAAgB,EAAE,MAAM,MAAM,EAC9B,aAAa,CAAC,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"pipeline-driver.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/pipeline-driver.ts"],"names":[],"mappings":"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"}
|