@rocicorp/zero 0.25.10-canary.16 → 0.25.10-canary.18

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.
@@ -1,4 +1,4 @@
1
- const version = "0.25.10-canary.16";
1
+ const version = "0.25.10-canary.18";
2
2
  const packageJson = {
3
3
  version
4
4
  };
@@ -1 +1 @@
1
- {"version":3,"file":"replica-schema.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-source/replica-schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAGjD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAEL,KAAK,uBAAuB,EAE7B,MAAM,4BAA4B,CAAC;AAYpC,wBAAsB,WAAW,CAC/B,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,GAC3D,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,iBAgBf;AAED,eAAO,MAAM,yBAAyB,EAAE,uBA4BvC,CAAC"}
1
+ {"version":3,"file":"replica-schema.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-source/replica-schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAGjD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,8BAA8B,CAAC;AAE3D,OAAO,EAEL,KAAK,uBAAuB,EAE7B,MAAM,4BAA4B,CAAC;AAWpC,wBAAsB,WAAW,CAC/B,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,GAC3D,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,iBAgBf;AAED,eAAO,MAAM,yBAAyB,EAAE,uBAgCvC,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { SqliteError } from "@rocicorp/zero-sqlite3";
2
2
  import { must } from "../../../../shared/src/must.js";
3
- import { runSchemaMigrations } from "../../db/migration-lite.js";
4
3
  import { listTables } from "../../db/lite-tables.js";
4
+ import { runSchemaMigrations } from "../../db/migration-lite.js";
5
5
  import { AutoResetSignal } from "../change-streamer/schema/tables.js";
6
6
  import { recordEvent, CREATE_RUNTIME_EVENTS_TABLE } from "../replicator/schema/replication-state.js";
7
7
  import { ColumnMetadataStore, CREATE_COLUMN_METADATA_TABLE } from "./column-metadata.js";
@@ -62,6 +62,10 @@ const schemaVersionMigrationMap = {
62
62
  db.exec(CREATE_COLUMN_METADATA_TABLE);
63
63
  },
64
64
  migrateData: (_, db) => {
65
+ db.exec(
66
+ /*sql*/
67
+ `DELETE FROM "_zero.column_metadata"`
68
+ );
65
69
  const store = ColumnMetadataStore.getInstance(db);
66
70
  const tables = listTables(db);
67
71
  must(store).populateFromExistingTables(tables);
@@ -1 +1 @@
1
- {"version":3,"file":"replica-schema.js","sources":["../../../../../../zero-cache/src/services/change-source/replica-schema.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {SqliteError} from '@rocicorp/zero-sqlite3';\nimport {must} from '../../../../shared/src/must.ts';\nimport type {Database} from '../../../../zqlite/src/db.ts';\nimport {\n runSchemaMigrations,\n type IncrementalMigrationMap,\n type Migration,\n} from '../../db/migration-lite.ts';\nimport {listTables} from '../../db/lite-tables.ts';\nimport {AutoResetSignal} from '../change-streamer/schema/tables.ts';\nimport {\n CREATE_RUNTIME_EVENTS_TABLE,\n recordEvent,\n} from '../replicator/schema/replication-state.ts';\nimport {\n ColumnMetadataStore,\n CREATE_COLUMN_METADATA_TABLE,\n} from './column-metadata.ts';\n\nexport async function initReplica(\n log: LogContext,\n debugName: string,\n dbPath: string,\n initialSync: (lc: LogContext, tx: Database) => Promise<void>,\n): Promise<void> {\n const setupMigration: Migration = {\n migrateSchema: (log, tx) => initialSync(log, tx),\n minSafeVersion: 1,\n };\n\n try {\n await runSchemaMigrations(\n log,\n debugName,\n dbPath,\n setupMigration,\n schemaVersionMigrationMap,\n );\n } catch (e) {\n if (e instanceof SqliteError && e.code === 'SQLITE_CORRUPT') {\n throw new AutoResetSignal(e.message);\n }\n throw e;\n }\n}\n\nexport async function upgradeReplica(\n log: LogContext,\n debugName: string,\n dbPath: string,\n) {\n await runSchemaMigrations(\n log,\n debugName,\n dbPath,\n // setupMigration should never be invoked\n {\n migrateSchema: () => {\n throw new Error(\n 'This should only be called for already synced replicas',\n );\n },\n },\n schemaVersionMigrationMap,\n );\n}\n\nexport const schemaVersionMigrationMap: IncrementalMigrationMap = {\n // There's no incremental migration from v1. Just reset the replica.\n 4: {\n migrateSchema: () => {\n throw new AutoResetSignal('upgrading replica to new schema');\n },\n minSafeVersion: 3,\n },\n\n 5: {\n migrateSchema: (_, db) => {\n db.exec(CREATE_RUNTIME_EVENTS_TABLE);\n },\n migrateData: (_, db) => {\n recordEvent(db, 'upgrade');\n },\n },\n\n 6: {\n migrateSchema: (_, db) => {\n db.exec(CREATE_COLUMN_METADATA_TABLE);\n },\n migrateData: (_, db) => {\n const store = ColumnMetadataStore.getInstance(db);\n const tables = listTables(db);\n must(store).populateFromExistingTables(tables);\n },\n },\n};\n"],"names":["log"],"mappings":";;;;;;;AAoBA,eAAsB,YACpB,KACA,WACA,QACA,aACe;AACf,QAAM,iBAA4B;AAAA,IAChC,eAAe,CAACA,MAAK,OAAO,YAAYA,MAAK,EAAE;AAAA,IAC/C,gBAAgB;AAAA,EAAA;AAGlB,MAAI;AACF,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ,SAAS,GAAG;AACV,QAAI,aAAa,eAAe,EAAE,SAAS,kBAAkB;AAC3D,YAAM,IAAI,gBAAgB,EAAE,OAAO;AAAA,IACrC;AACA,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,eACpB,KACA,WACA,QACA;AACA,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,MACE,eAAe,MAAM;AACnB,cAAM,IAAI;AAAA,UACR;AAAA,QAAA;AAAA,MAEJ;AAAA,IAAA;AAAA,IAEF;AAAA,EAAA;AAEJ;AAEO,MAAM,4BAAqD;AAAA;AAAA,EAEhE,GAAG;AAAA,IACD,eAAe,MAAM;AACnB,YAAM,IAAI,gBAAgB,iCAAiC;AAAA,IAC7D;AAAA,IACA,gBAAgB;AAAA,EAAA;AAAA,EAGlB,GAAG;AAAA,IACD,eAAe,CAAC,GAAG,OAAO;AACxB,SAAG,KAAK,2BAA2B;AAAA,IACrC;AAAA,IACA,aAAa,CAAC,GAAG,OAAO;AACtB,kBAAY,IAAI,SAAS;AAAA,IAC3B;AAAA,EAAA;AAAA,EAGF,GAAG;AAAA,IACD,eAAe,CAAC,GAAG,OAAO;AACxB,SAAG,KAAK,4BAA4B;AAAA,IACtC;AAAA,IACA,aAAa,CAAC,GAAG,OAAO;AACtB,YAAM,QAAQ,oBAAoB,YAAY,EAAE;AAChD,YAAM,SAAS,WAAW,EAAE;AAC5B,WAAK,KAAK,EAAE,2BAA2B,MAAM;AAAA,IAC/C;AAAA,EAAA;AAEJ;"}
1
+ {"version":3,"file":"replica-schema.js","sources":["../../../../../../zero-cache/src/services/change-source/replica-schema.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {SqliteError} from '@rocicorp/zero-sqlite3';\nimport {must} from '../../../../shared/src/must.ts';\nimport type {Database} from '../../../../zqlite/src/db.ts';\nimport {listTables} from '../../db/lite-tables.ts';\nimport {\n runSchemaMigrations,\n type IncrementalMigrationMap,\n type Migration,\n} from '../../db/migration-lite.ts';\nimport {AutoResetSignal} from '../change-streamer/schema/tables.ts';\nimport {\n CREATE_RUNTIME_EVENTS_TABLE,\n recordEvent,\n} from '../replicator/schema/replication-state.ts';\nimport {\n ColumnMetadataStore,\n CREATE_COLUMN_METADATA_TABLE,\n} from './column-metadata.ts';\n\nexport async function initReplica(\n log: LogContext,\n debugName: string,\n dbPath: string,\n initialSync: (lc: LogContext, tx: Database) => Promise<void>,\n): Promise<void> {\n const setupMigration: Migration = {\n migrateSchema: (log, tx) => initialSync(log, tx),\n minSafeVersion: 1,\n };\n\n try {\n await runSchemaMigrations(\n log,\n debugName,\n dbPath,\n setupMigration,\n schemaVersionMigrationMap,\n );\n } catch (e) {\n if (e instanceof SqliteError && e.code === 'SQLITE_CORRUPT') {\n throw new AutoResetSignal(e.message);\n }\n throw e;\n }\n}\n\nexport async function upgradeReplica(\n log: LogContext,\n debugName: string,\n dbPath: string,\n) {\n await runSchemaMigrations(\n log,\n debugName,\n dbPath,\n // setupMigration should never be invoked\n {\n migrateSchema: () => {\n throw new Error(\n 'This should only be called for already synced replicas',\n );\n },\n },\n schemaVersionMigrationMap,\n );\n}\n\nexport const schemaVersionMigrationMap: IncrementalMigrationMap = {\n // There's no incremental migration from v1. Just reset the replica.\n 4: {\n migrateSchema: () => {\n throw new AutoResetSignal('upgrading replica to new schema');\n },\n minSafeVersion: 3,\n },\n\n 5: {\n migrateSchema: (_, db) => {\n db.exec(CREATE_RUNTIME_EVENTS_TABLE);\n },\n migrateData: (_, db) => {\n recordEvent(db, 'upgrade');\n },\n },\n\n 6: {\n migrateSchema: (_, db) => {\n db.exec(CREATE_COLUMN_METADATA_TABLE);\n },\n migrateData: (_, db) => {\n // Clear the table before (re-)populating it to be resilient to\n // roll-back and roll-forward.\n db.exec(/*sql*/ `DELETE FROM \"_zero.column_metadata\"`);\n\n const store = ColumnMetadataStore.getInstance(db);\n const tables = listTables(db);\n must(store).populateFromExistingTables(tables);\n },\n },\n};\n"],"names":["log"],"mappings":";;;;;;;AAoBA,eAAsB,YACpB,KACA,WACA,QACA,aACe;AACf,QAAM,iBAA4B;AAAA,IAChC,eAAe,CAACA,MAAK,OAAO,YAAYA,MAAK,EAAE;AAAA,IAC/C,gBAAgB;AAAA,EAAA;AAGlB,MAAI;AACF,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ,SAAS,GAAG;AACV,QAAI,aAAa,eAAe,EAAE,SAAS,kBAAkB;AAC3D,YAAM,IAAI,gBAAgB,EAAE,OAAO;AAAA,IACrC;AACA,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,eACpB,KACA,WACA,QACA;AACA,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,MACE,eAAe,MAAM;AACnB,cAAM,IAAI;AAAA,UACR;AAAA,QAAA;AAAA,MAEJ;AAAA,IAAA;AAAA,IAEF;AAAA,EAAA;AAEJ;AAEO,MAAM,4BAAqD;AAAA;AAAA,EAEhE,GAAG;AAAA,IACD,eAAe,MAAM;AACnB,YAAM,IAAI,gBAAgB,iCAAiC;AAAA,IAC7D;AAAA,IACA,gBAAgB;AAAA,EAAA;AAAA,EAGlB,GAAG;AAAA,IACD,eAAe,CAAC,GAAG,OAAO;AACxB,SAAG,KAAK,2BAA2B;AAAA,IACrC;AAAA,IACA,aAAa,CAAC,GAAG,OAAO;AACtB,kBAAY,IAAI,SAAS;AAAA,IAC3B;AAAA,EAAA;AAAA,EAGF,GAAG;AAAA,IACD,eAAe,CAAC,GAAG,OAAO;AACxB,SAAG,KAAK,4BAA4B;AAAA,IACtC;AAAA,IACA,aAAa,CAAC,GAAG,OAAO;AAGtB,SAAG;AAAA;AAAA,QAAa;AAAA,MAAA;AAEhB,YAAM,QAAQ,oBAAoB,YAAY,EAAE;AAChD,YAAM,SAAS,WAAW,EAAE;AAC5B,WAAK,KAAK,EAAE,2BAA2B,MAAM;AAAA,IAC/C;AAAA,EAAA;AAEJ;"}
@@ -1 +1 @@
1
- {"version":3,"file":"cvr.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/cvr.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cvr.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/cvr.ts"],"names":[],"mappings":"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"}
@@ -371,6 +371,8 @@ class CVRQueryDrivenUpdater extends CVRUpdater {
371
371
  removed.map((q) => this.#trackRemoved(q.id))
372
372
  ].flat(2);
373
373
  this.#existingRows = this.#lookupRowsForExecutedAndRemovedQueries(lc);
374
+ void this.#existingRows.then(() => {
375
+ });
374
376
  return {
375
377
  newVersion: this._cvr.version,
376
378
  queryPatches: queryPatches.map((patch) => ({
@@ -1 +1 @@
1
- {"version":3,"file":"cvr.js","sources":["../../../../../../zero-cache/src/services/view-syncer/cvr.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\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 {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(cmpVersions(this._cvr.version, version) < 0);\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 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 = getMutationResultsQuery(\n upstreamSchema(this.#shard),\n this._cvr.id,\n );\n this._cvr.queries[CLIENT_MUTATION_RESULTS_QUERY_ID] =\n mutationResultsQuery;\n this._cvrStore.putQuery(mutationResultsQuery);\n }\n\n return client;\n }\n\n setClientSchema(lc: LogContext, clientSchema: ClientSchema) {\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 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 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 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 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 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 // 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\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(stateVersion >= cvr.version.stateVersion);\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; transformationHash: string | undefined}[],\n ): {newVersion: CVRVersion; queryPatches: PatchToVersion[]} {\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\n return {\n newVersion: this._cvr.version,\n queryPatches: queryPatches.map(patch => ({\n patch,\n toVersion: this._cvr.version,\n })),\n };\n }\n\n async #lookupRowsForExecutedAndRemovedQueries(\n lc: LogContext,\n ): Promise<Iterable<RowRecord>> {\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(existing.refCounts !== null); // allRowRecords does not include null.\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 * 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 assert(!this.#removedOrExecutedQueryIDs.has(queryID));\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 * 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 const query = this._cvr.queries[queryID];\n assertNotInternal(query);\n\n assert(!this.#removedOrExecutedQueryIDs.has(queryID));\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 * 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(cmpVersions(this._orig.version, this._cvr.version) < 0);\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 async received(\n _lc: LogContext,\n rows: Map<RowID, RowUpdate>,\n ): Promise<PatchToVersion[]> {\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(rowVersion);\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 * 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 async deleteUnreferencedRows(lc?: LogContext): Promise<PatchToVersion[]> {\n if (this.#removedOrExecutedQueryIDs.size === 0) {\n // Query-less update. This can happen for config-only changes.\n assert(this.#receivedRows.size === 0);\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 #deleteUnreferencedRow(existing: RowRecord): RowID | null {\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\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"],"names":["upstreamSchema","ErrorOrigin.ZeroCache"],"mappings":"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"}
1
+ {"version":3,"file":"cvr.js","sources":["../../../../../../zero-cache/src/services/view-syncer/cvr.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\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 {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(cmpVersions(this._cvr.version, version) < 0);\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 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 = getMutationResultsQuery(\n upstreamSchema(this.#shard),\n this._cvr.id,\n );\n this._cvr.queries[CLIENT_MUTATION_RESULTS_QUERY_ID] =\n mutationResultsQuery;\n this._cvrStore.putQuery(mutationResultsQuery);\n }\n\n return client;\n }\n\n setClientSchema(lc: LogContext, clientSchema: ClientSchema) {\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 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 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 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 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 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 // 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\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(stateVersion >= cvr.version.stateVersion);\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; transformationHash: string | undefined}[],\n ): {newVersion: CVRVersion; queryPatches: PatchToVersion[]} {\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 async #lookupRowsForExecutedAndRemovedQueries(\n lc: LogContext,\n ): Promise<Iterable<RowRecord>> {\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(existing.refCounts !== null); // allRowRecords does not include null.\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 * 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 assert(!this.#removedOrExecutedQueryIDs.has(queryID));\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 * 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 const query = this._cvr.queries[queryID];\n assertNotInternal(query);\n\n assert(!this.#removedOrExecutedQueryIDs.has(queryID));\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 * 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(cmpVersions(this._orig.version, this._cvr.version) < 0);\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 async received(\n _lc: LogContext,\n rows: Map<RowID, RowUpdate>,\n ): Promise<PatchToVersion[]> {\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(rowVersion);\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 * 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 async deleteUnreferencedRows(lc?: LogContext): Promise<PatchToVersion[]> {\n if (this.#removedOrExecutedQueryIDs.size === 0) {\n // Query-less update. This can happen for config-only changes.\n assert(this.#receivedRows.size === 0);\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 #deleteUnreferencedRow(existing: RowRecord): RowID | null {\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\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"],"names":["upstreamSchema","ErrorOrigin.ZeroCache"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"row-record-cache.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/row-record-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,YAAY,EAAE,GAAG,EAAC,MAAM,UAAU,CAAC;AAUhD,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,mBAAmB,EACzB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAY,KAAK,OAAO,EAAC,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAe,KAAK,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAChE,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,UAAU,CAAC;AAC1C,OAAO,EAEL,KAAK,OAAO,EAEb,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,KAAK,EACV,KAAK,SAAS,EAGf,MAAM,mBAAmB,CAAC;AAI3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,qBAAa,cAAc;;gBAgCvB,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,EACjC,yBAAyB,SAAM,EAC/B,YAAY,oBAAa;IAW3B,oBAAoB,CAAC,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM;IAgD5D,aAAa,IAAI,OAAO,CAAC,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAIvD;;;;;;;;;;;;;;OAcG;IACG,KAAK,CACT,UAAU,EAAE,GAAG,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC,EACxC,WAAW,EAAE,UAAU,EACvB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,MAAM,CAAC;IAmElB,iBAAiB;IAIjB;;;OAGG;IACH,OAAO,CAAC,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAQtC,KAAK;IAOE,iBAAiB,CACtB,EAAE,EAAE,UAAU,EACd,YAAY,EAAE,kBAAkB,EAChC,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,UAAU,EACnB,kBAAkB,GAAE,MAAM,EAAO,GAChC,cAAc,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,CAAC;IAmD7C,iBAAiB,CACf,EAAE,EAAE,mBAAmB,EACvB,OAAO,EAAE,UAAU,EACnB,UAAU,EAAE,GAAG,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC,EACxC,IAAI,EAAE,aAAa,GAAG,OAAO,EAC7B,EAAE,yDAAW,GACZ,YAAY,CAAC,GAAG,EAAE,CAAC,EAAE;CAiEzB"}
1
+ {"version":3,"file":"row-record-cache.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/row-record-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,YAAY,EAAE,GAAG,EAAC,MAAM,UAAU,CAAC;AAUhD,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,mBAAmB,EACzB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAY,KAAK,OAAO,EAAC,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAe,KAAK,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAChE,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,UAAU,CAAC;AAC1C,OAAO,EAEL,KAAK,OAAO,EAEb,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,KAAK,EACV,KAAK,SAAS,EAGf,MAAM,mBAAmB,CAAC;AAI3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,qBAAa,cAAc;;gBAgCvB,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,EACjC,yBAAyB,SAAM,EAC/B,YAAY,oBAAa;IAW3B,oBAAoB,CAAC,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM;IAsD5D,aAAa,IAAI,OAAO,CAAC,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAIvD;;;;;;;;;;;;;;OAcG;IACG,KAAK,CACT,UAAU,EAAE,GAAG,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC,EACxC,WAAW,EAAE,UAAU,EACvB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,MAAM,CAAC;IAmElB,iBAAiB;IAIjB;;;OAGG;IACH,OAAO,CAAC,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAQtC,KAAK;IAOE,iBAAiB,CACtB,EAAE,EAAE,UAAU,EACd,YAAY,EAAE,kBAAkB,EAChC,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,UAAU,EACnB,kBAAkB,GAAE,MAAM,EAAO,GAChC,cAAc,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,CAAC;IAmD7C,iBAAiB,CACf,EAAE,EAAE,mBAAmB,EACvB,OAAO,EAAE,UAAU,EACnB,UAAU,EAAE,GAAG,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC,EACxC,IAAI,EAAE,aAAa,GAAG,OAAO,EAC7B,EAAE,yDAAW,GACZ,YAAY,CAAC,GAAG,EAAE,CAAC,EAAE;CAiEzB"}
@@ -71,20 +71,27 @@ class RowRecordCache {
71
71
  const start = Date.now();
72
72
  const r = resolver();
73
73
  this.#cache = r.promise;
74
- const cache = new CustomKeyMap(rowIDString);
75
- for await (const rows of this.#db`
76
- SELECT * FROM ${this.#cvr(`rows`)}
77
- WHERE "clientGroupID" = ${this.#cvrID} AND "refCounts" IS NOT NULL`.cursor(5e3)) {
78
- for (const row of rows) {
79
- const rowRecord = rowsRowToRowRecord(row);
80
- cache.set(rowRecord.id, rowRecord);
74
+ try {
75
+ const cache = new CustomKeyMap(
76
+ rowIDString
77
+ );
78
+ for await (const rows of this.#db`
79
+ SELECT * FROM ${this.#cvr(`rows`)}
80
+ WHERE "clientGroupID" = ${this.#cvrID} AND "refCounts" IS NOT NULL`.cursor(5e3)) {
81
+ for (const row of rows) {
82
+ const rowRecord = rowsRowToRowRecord(row);
83
+ cache.set(rowRecord.id, rowRecord);
84
+ }
81
85
  }
86
+ this.#lc.debug?.(
87
+ `Loaded ${cache.size} row records in ${Date.now() - start} ms`
88
+ );
89
+ r.resolve(cache);
90
+ return this.#cache;
91
+ } catch (e) {
92
+ r.reject(e);
93
+ throw e;
82
94
  }
83
- this.#lc.debug?.(
84
- `Loaded ${cache.size} row records in ${Date.now() - start} ms`
85
- );
86
- r.resolve(cache);
87
- return this.#cache;
88
95
  }
89
96
  getRowRecords() {
90
97
  return this.#ensureLoaded();
@@ -1 +1 @@
1
- {"version":3,"file":"row-record-cache.js","sources":["../../../../../../zero-cache/src/services/view-syncer/row-record-cache.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {type Resolver, resolver} from '@rocicorp/resolver';\nimport type {PendingQuery, Row} from 'postgres';\nimport {CustomKeyMap} from '../../../../shared/src/custom-key-map.ts';\nimport {must} from '../../../../shared/src/must.ts';\nimport {promiseVoid} from '../../../../shared/src/resolved-promises.ts';\nimport * as Mode from '../../db/mode-enum.ts';\nimport {TransactionPool} from '../../db/transaction-pool.ts';\nimport {\n getOrCreateCounter,\n getOrCreateHistogram,\n} from '../../observability/metrics.ts';\nimport {\n disableStatementTimeout,\n type PostgresDB,\n type PostgresTransaction,\n} from '../../types/pg.ts';\nimport {rowIDString} from '../../types/row-key.ts';\nimport {cvrSchema, type ShardID} from '../../types/shards.ts';\nimport {checkVersion, type CVRFlushStats} from './cvr-store.ts';\nimport type {CVRSnapshot} from './cvr.ts';\nimport {\n rowRecordToRowsRow,\n type RowsRow,\n rowsRowToRowRecord,\n} from './schema/cvr.ts';\nimport {\n cmpVersions,\n type CVRVersion,\n type NullableCVRVersion,\n type RowID,\n type RowRecord,\n versionString,\n versionToNullableCookie,\n} from './schema/types.ts';\n\nconst FLUSH_TYPE_ATTRIBUTE = 'flush.type';\n\n/**\n * The RowRecordCache is an in-memory cache of the `cvr.rows` tables that\n * operates as both a write-through and write-back cache.\n *\n * For \"small\" CVR updates (i.e. zero or small numbers of rows) the\n * RowRecordCache operates as write-through, executing commits in\n * {@link executeRowUpdates()} before they are {@link apply}-ed to the\n * in-memory state.\n *\n * For \"large\" CVR updates (i.e. with many rows), the cache switches to a\n * write-back mode of operation, in which {@link executeRowUpdates()} is a\n * no-op, and {@link apply()} initiates a background task to flush the pending\n * row changes to the store. This allows the client poke to be completed and\n * committed on the client without waiting for the heavyweight operation of\n * committing the row records to the CVR store.\n *\n * Note that when the cache is in write-back mode, all updates become\n * write-back (i.e. asynchronously flushed) until the pending update queue is\n * fully flushed. This is required because updates must be applied in version\n * order. As with all pending work systems in zero-cache, multiple pending\n * updates are coalesced to reduce buildup of work.\n *\n * ### High level consistency\n *\n * Note that the above caching scheme only applies to the row data in `cvr.rows`\n * and corresponding `cvr.rowsVersion` tables. CVR metadata and query\n * information, on the other hand, are always committed before completing the\n * client poke. In this manner, the difference between the `version` column in\n * `cvr.instances` and the analogous column in `cvr.rowsVersion` determines\n * whether the data in the store is consistent, or whether it is awaiting a\n * pending update.\n *\n * The logic in {@link CVRStore#load()} takes this into account by loading both\n * the `cvr.instances` version and the `cvr.rowsVersion` version and checking\n * if they are in sync, waiting for a configurable delay until they are.\n *\n * ### Eventual conversion\n *\n * In the event of a continual stream of mutations (e.g. an animation-style\n * app), it is conceivable that the row record data be continually behind\n * the CVR metadata. In order to effect eventual convergence, a new view-syncer\n * signals the current view-syncer to stop updating by writing new `owner`\n * information to the `cvr.instances` row. This effectively stops the mutation\n * processing (in {@link CVRStore.#checkVersionAndOwnership}) so that the row\n * data can eventually catch up, allowing the new view-syncer to take over.\n *\n * Of course, there is the pathological situation in which a view-syncer\n * process crashes before the pending row updates are flushed. In this case,\n * the wait timeout will elapse and the CVR considered invalid.\n */\nexport class RowRecordCache {\n // The state in the #cache is always in sync with the CVR metadata\n // (i.e. cvr.instances). It may contain information that has not yet\n // been flushed to cvr.rows.\n #cache: Promise<CustomKeyMap<RowID, RowRecord>> | undefined;\n readonly #lc: LogContext;\n readonly #db: PostgresDB;\n readonly #schema: string;\n readonly #cvrID: string;\n readonly #failService: (e: unknown) => void;\n readonly #deferredRowFlushThreshold: number;\n readonly #setTimeout: typeof setTimeout;\n\n // Write-back cache state.\n readonly #pending = new CustomKeyMap<RowID, RowRecord | null>(rowIDString);\n #pendingRowsVersion: CVRVersion | null = null;\n #flushedRowsVersion: CVRVersion | null = null;\n #flushing: Resolver<void> | null = null;\n\n readonly #cvrFlushTime = getOrCreateHistogram('sync', 'cvr.flush-time', {\n description:\n 'Time to flush a CVR transaction. This includes both synchronous ' +\n 'and asynchronous flushes, distinguished by the flush.type attribute',\n unit: 's',\n });\n readonly #cvrRowsFlushed = getOrCreateCounter(\n 'sync',\n 'cvr.rows-flushed',\n 'Number of (changed) rows flushed to a CVR',\n );\n\n constructor(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardID,\n cvrID: string,\n failService: (e: unknown) => void,\n deferredRowFlushThreshold = 100,\n setTimeoutFn = setTimeout,\n ) {\n this.#lc = lc;\n this.#db = db;\n this.#schema = cvrSchema(shard);\n this.#cvrID = cvrID;\n this.#failService = failService;\n this.#deferredRowFlushThreshold = deferredRowFlushThreshold;\n this.#setTimeout = setTimeoutFn;\n }\n\n recordSyncFlushStats(stats: CVRFlushStats, elapsedMs: number) {\n this.#cvrFlushTime.record(elapsedMs / 1000, {\n [FLUSH_TYPE_ATTRIBUTE]: 'sync',\n });\n if (stats.rowsDeferred === 0) {\n this.#cvrRowsFlushed.add(stats.rows);\n }\n }\n\n #recordAsyncFlushStats(rows: number, elapsedMs: number) {\n this.#cvrFlushTime.record(elapsedMs / 1000, {\n [FLUSH_TYPE_ATTRIBUTE]: 'async',\n });\n this.#cvrRowsFlushed.add(rows);\n }\n\n #cvr(table: string) {\n return this.#db(`${this.#schema}.${table}`);\n }\n\n async #ensureLoaded(): Promise<CustomKeyMap<RowID, RowRecord>> {\n if (this.#cache) {\n return this.#cache;\n }\n const start = Date.now();\n const r = resolver<CustomKeyMap<RowID, RowRecord>>();\n // Set this.#cache immediately (before await) so that only one db\n // query is made even if there are multiple callers.\n this.#cache = r.promise;\n\n const cache: CustomKeyMap<RowID, RowRecord> = new CustomKeyMap(rowIDString);\n for await (const rows of this.#db<RowsRow[]>`\n SELECT * FROM ${this.#cvr(`rows`)}\n WHERE \"clientGroupID\" = ${this.#cvrID} AND \"refCounts\" IS NOT NULL`\n // TODO(arv): Arbitrary page size\n .cursor(5000)) {\n for (const row of rows) {\n const rowRecord = rowsRowToRowRecord(row);\n cache.set(rowRecord.id, rowRecord);\n }\n }\n this.#lc.debug?.(\n `Loaded ${cache.size} row records in ${Date.now() - start} ms`,\n );\n r.resolve(cache);\n return this.#cache;\n }\n\n getRowRecords(): Promise<ReadonlyMap<RowID, RowRecord>> {\n return this.#ensureLoaded();\n }\n\n /**\n * Applies the `rowRecords` corresponding to the `rowsVersion`\n * to the cache, indicating whether the corresponding updates\n * (generated by {@link executeRowUpdates}) were `flushed`.\n *\n * If `flushed` is false, the RowRecordCache will flush the records\n * asynchronously.\n *\n * Note that `apply()` indicates that the CVR metadata associated with\n * the `rowRecords` was successfully committed, which essentially means\n * that this process has the unconditional right (and responsibility) of\n * following up with a flush of the `rowRecords`. In particular, the\n * commit of row records are not conditioned on the version or ownership\n * columns of the `cvr.instances` row.\n */\n async apply(\n rowRecords: Map<RowID, RowRecord | null>,\n rowsVersion: CVRVersion,\n flushed: boolean,\n ): Promise<number> {\n const cache = await this.#ensureLoaded();\n for (const [id, row] of rowRecords.entries()) {\n if (row === null || row.refCounts === null) {\n cache.delete(id);\n } else {\n cache.set(id, row);\n }\n if (!flushed) {\n this.#pending.set(id, row);\n }\n }\n this.#pendingRowsVersion = rowsVersion;\n // Initiate a flush if not already flushing.\n if (!flushed && this.#flushing === null) {\n this.#flushing = resolver();\n this.#setTimeout(() => this.#flush(), 0);\n }\n return cache.size;\n }\n\n async #flush() {\n const flushing = must(this.#flushing);\n try {\n while (this.#pendingRowsVersion !== this.#flushedRowsVersion) {\n const start = performance.now();\n\n const {rows, rowsVersion} = await this.#db.begin(\n Mode.READ_COMMITTED,\n tx => {\n disableStatementTimeout(tx);\n\n // Note: This code block is synchronous, guaranteeing that the\n // #pendingRowsVersion is consistent with the #pending rows.\n const rows = this.#pending.size;\n const rowsVersion = must(this.#pendingRowsVersion);\n // Awaiting all of the individual statements incurs too much\n // overhead. Instead, just catch and log exception(s); the outer\n // transaction will properly fail.\n void Promise.all(\n this.executeRowUpdates(tx, rowsVersion, this.#pending, 'force'),\n ).catch(e => this.#lc.error?.(`error flushing cvr rows`, e));\n\n this.#pending.clear();\n return {rows, rowsVersion};\n },\n );\n const elapsed = performance.now() - start;\n this.#lc.debug?.(\n `flushed ${rows} rows@${versionString(rowsVersion)} (${elapsed} ms)`,\n );\n this.#recordAsyncFlushStats(rows, elapsed);\n this.#flushedRowsVersion = rowsVersion;\n // Note: apply() may have called while the transaction was committing,\n // which will result in looping to commit the next #pendingRowsVersion.\n }\n this.#lc.debug?.(\n `up to date rows@${versionToNullableCookie(this.#flushedRowsVersion)}`,\n );\n flushing.resolve();\n this.#flushing = null;\n } catch (e) {\n flushing.reject(e);\n this.#failService(e);\n }\n }\n\n hasPendingUpdates() {\n return this.#flushing !== null;\n }\n\n /**\n * Returns a promise that resolves when all outstanding row-records\n * have been committed.\n */\n flushed(lc: LogContext): Promise<void> {\n if (this.#flushing) {\n lc.debug?.('awaiting pending row flush');\n return this.#flushing.promise;\n }\n return promiseVoid;\n }\n\n clear() {\n // Note: Only the #cache is cleared. #pending updates, on the other hand,\n // comprise canonical (i.e. already flushed) data and must be flushed\n // even if the snapshot of the present state (the #cache) is cleared.\n this.#cache = undefined;\n }\n\n async *catchupRowPatches(\n lc: LogContext,\n afterVersion: NullableCVRVersion,\n upToCVR: CVRSnapshot,\n current: CVRVersion,\n excludeQueryHashes: string[] = [],\n ): AsyncGenerator<RowsRow[], void, undefined> {\n if (cmpVersions(afterVersion, upToCVR.version) >= 0) {\n return;\n }\n\n const startMs = Date.now();\n const start = afterVersion ? versionString(afterVersion) : '';\n const end = versionString(upToCVR.version);\n lc.debug?.(`scanning row patches for clients from ${start}`);\n\n // Before accessing the CVR db, pending row records must be flushed.\n // Note that because catchupRowPatches() is called from within the\n // view syncer lock, this flush is guaranteed to complete since no\n // new CVR updates can happen while the lock is held.\n await this.flushed(lc);\n const flushMs = Date.now() - startMs;\n\n const reader = new TransactionPool(lc, Mode.READONLY).run(this.#db);\n try {\n // Verify that we are reading the right version of the CVR.\n await reader.processReadTask(tx =>\n checkVersion(tx, this.#schema, this.#cvrID, current),\n );\n\n const {query} = await reader.processReadTask(tx => {\n const query =\n excludeQueryHashes.length === 0\n ? tx<RowsRow[]>`SELECT * FROM ${this.#cvr('rows')}\n WHERE \"clientGroupID\" = ${this.#cvrID}\n AND \"patchVersion\" > ${start}\n AND \"patchVersion\" <= ${end}`\n : // Exclude rows that were already sent as part of query hydration.\n tx<RowsRow[]>`SELECT * FROM ${this.#cvr('rows')}\n WHERE \"clientGroupID\" = ${this.#cvrID}\n AND \"patchVersion\" > ${start}\n AND \"patchVersion\" <= ${end}\n AND (\"refCounts\" IS NULL OR NOT \"refCounts\" ?| ${excludeQueryHashes})`;\n return {query};\n });\n\n yield* query.cursor(10000);\n } finally {\n reader.setDone();\n }\n\n const totalMs = Date.now() - startMs;\n lc.debug?.(\n `finished row catchup (flush: ${flushMs} ms, total: ${totalMs} ms)`,\n );\n }\n\n executeRowUpdates(\n tx: PostgresTransaction,\n version: CVRVersion,\n rowUpdates: Map<RowID, RowRecord | null>,\n mode: 'allow-defer' | 'force',\n lc = this.#lc,\n ): PendingQuery<Row[]>[] {\n if (\n mode === 'allow-defer' &&\n // defer if pending rows are being flushed\n (this.#flushing !== null ||\n // or if the new batch is above the limit.\n rowUpdates.size > this.#deferredRowFlushThreshold)\n ) {\n return [];\n }\n const rowsVersion = {\n clientGroupID: this.#cvrID,\n version: versionString(version),\n };\n const pending: PendingQuery<Row[]>[] = [\n tx`INSERT INTO ${this.#cvr('rowsVersion')} ${tx(rowsVersion)}\n ON CONFLICT (\"clientGroupID\") \n DO UPDATE SET ${tx(rowsVersion)}`.execute(),\n ];\n\n const rowRecordRows: RowsRow[] = [];\n for (const [id, row] of rowUpdates.entries()) {\n if (row === null) {\n pending.push(\n tx`\n DELETE FROM ${this.#cvr('rows')}\n WHERE \"clientGroupID\" = ${this.#cvrID}\n AND \"schema\" = ${id.schema}\n AND \"table\" = ${id.table}\n AND \"rowKey\" = ${id.rowKey}\n `.execute(),\n );\n } else {\n rowRecordRows.push(rowRecordToRowsRow(this.#cvrID, row));\n }\n }\n if (rowRecordRows.length) {\n pending.push(\n tx`\n INSERT INTO ${this.#cvr('rows')}(\n \"clientGroupID\", \"schema\", \"table\", \"rowKey\", \"rowVersion\", \"patchVersion\", \"refCounts\"\n ) SELECT\n \"clientGroupID\", \"schema\", \"table\", \"rowKey\", \"rowVersion\", \"patchVersion\", \"refCounts\"\n FROM json_to_recordset(${rowRecordRows}) AS x(\n \"clientGroupID\" TEXT,\n \"schema\" TEXT,\n \"table\" TEXT,\n \"rowKey\" JSONB,\n \"rowVersion\" TEXT,\n \"patchVersion\" TEXT,\n \"refCounts\" JSONB\n ) ON CONFLICT (\"clientGroupID\", \"schema\", \"table\", \"rowKey\")\n DO UPDATE SET \"rowVersion\" = excluded.\"rowVersion\",\n \"patchVersion\" = excluded.\"patchVersion\",\n \"refCounts\" = excluded.\"refCounts\"\n `.execute(),\n );\n lc.debug?.(\n `flushing ${rowUpdates.size} rows (${rowRecordRows.length} inserts, ${\n rowUpdates.size - rowRecordRows.length\n } deletes)`,\n );\n }\n return pending;\n }\n}\n"],"names":["Mode.READ_COMMITTED","rows","rowsVersion","Mode.READONLY","query"],"mappings":"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"}
1
+ {"version":3,"file":"row-record-cache.js","sources":["../../../../../../zero-cache/src/services/view-syncer/row-record-cache.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {type Resolver, resolver} from '@rocicorp/resolver';\nimport type {PendingQuery, Row} from 'postgres';\nimport {CustomKeyMap} from '../../../../shared/src/custom-key-map.ts';\nimport {must} from '../../../../shared/src/must.ts';\nimport {promiseVoid} from '../../../../shared/src/resolved-promises.ts';\nimport * as Mode from '../../db/mode-enum.ts';\nimport {TransactionPool} from '../../db/transaction-pool.ts';\nimport {\n getOrCreateCounter,\n getOrCreateHistogram,\n} from '../../observability/metrics.ts';\nimport {\n disableStatementTimeout,\n type PostgresDB,\n type PostgresTransaction,\n} from '../../types/pg.ts';\nimport {rowIDString} from '../../types/row-key.ts';\nimport {cvrSchema, type ShardID} from '../../types/shards.ts';\nimport {checkVersion, type CVRFlushStats} from './cvr-store.ts';\nimport type {CVRSnapshot} from './cvr.ts';\nimport {\n rowRecordToRowsRow,\n type RowsRow,\n rowsRowToRowRecord,\n} from './schema/cvr.ts';\nimport {\n cmpVersions,\n type CVRVersion,\n type NullableCVRVersion,\n type RowID,\n type RowRecord,\n versionString,\n versionToNullableCookie,\n} from './schema/types.ts';\n\nconst FLUSH_TYPE_ATTRIBUTE = 'flush.type';\n\n/**\n * The RowRecordCache is an in-memory cache of the `cvr.rows` tables that\n * operates as both a write-through and write-back cache.\n *\n * For \"small\" CVR updates (i.e. zero or small numbers of rows) the\n * RowRecordCache operates as write-through, executing commits in\n * {@link executeRowUpdates()} before they are {@link apply}-ed to the\n * in-memory state.\n *\n * For \"large\" CVR updates (i.e. with many rows), the cache switches to a\n * write-back mode of operation, in which {@link executeRowUpdates()} is a\n * no-op, and {@link apply()} initiates a background task to flush the pending\n * row changes to the store. This allows the client poke to be completed and\n * committed on the client without waiting for the heavyweight operation of\n * committing the row records to the CVR store.\n *\n * Note that when the cache is in write-back mode, all updates become\n * write-back (i.e. asynchronously flushed) until the pending update queue is\n * fully flushed. This is required because updates must be applied in version\n * order. As with all pending work systems in zero-cache, multiple pending\n * updates are coalesced to reduce buildup of work.\n *\n * ### High level consistency\n *\n * Note that the above caching scheme only applies to the row data in `cvr.rows`\n * and corresponding `cvr.rowsVersion` tables. CVR metadata and query\n * information, on the other hand, are always committed before completing the\n * client poke. In this manner, the difference between the `version` column in\n * `cvr.instances` and the analogous column in `cvr.rowsVersion` determines\n * whether the data in the store is consistent, or whether it is awaiting a\n * pending update.\n *\n * The logic in {@link CVRStore#load()} takes this into account by loading both\n * the `cvr.instances` version and the `cvr.rowsVersion` version and checking\n * if they are in sync, waiting for a configurable delay until they are.\n *\n * ### Eventual conversion\n *\n * In the event of a continual stream of mutations (e.g. an animation-style\n * app), it is conceivable that the row record data be continually behind\n * the CVR metadata. In order to effect eventual convergence, a new view-syncer\n * signals the current view-syncer to stop updating by writing new `owner`\n * information to the `cvr.instances` row. This effectively stops the mutation\n * processing (in {@link CVRStore.#checkVersionAndOwnership}) so that the row\n * data can eventually catch up, allowing the new view-syncer to take over.\n *\n * Of course, there is the pathological situation in which a view-syncer\n * process crashes before the pending row updates are flushed. In this case,\n * the wait timeout will elapse and the CVR considered invalid.\n */\nexport class RowRecordCache {\n // The state in the #cache is always in sync with the CVR metadata\n // (i.e. cvr.instances). It may contain information that has not yet\n // been flushed to cvr.rows.\n #cache: Promise<CustomKeyMap<RowID, RowRecord>> | undefined;\n readonly #lc: LogContext;\n readonly #db: PostgresDB;\n readonly #schema: string;\n readonly #cvrID: string;\n readonly #failService: (e: unknown) => void;\n readonly #deferredRowFlushThreshold: number;\n readonly #setTimeout: typeof setTimeout;\n\n // Write-back cache state.\n readonly #pending = new CustomKeyMap<RowID, RowRecord | null>(rowIDString);\n #pendingRowsVersion: CVRVersion | null = null;\n #flushedRowsVersion: CVRVersion | null = null;\n #flushing: Resolver<void> | null = null;\n\n readonly #cvrFlushTime = getOrCreateHistogram('sync', 'cvr.flush-time', {\n description:\n 'Time to flush a CVR transaction. This includes both synchronous ' +\n 'and asynchronous flushes, distinguished by the flush.type attribute',\n unit: 's',\n });\n readonly #cvrRowsFlushed = getOrCreateCounter(\n 'sync',\n 'cvr.rows-flushed',\n 'Number of (changed) rows flushed to a CVR',\n );\n\n constructor(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardID,\n cvrID: string,\n failService: (e: unknown) => void,\n deferredRowFlushThreshold = 100,\n setTimeoutFn = setTimeout,\n ) {\n this.#lc = lc;\n this.#db = db;\n this.#schema = cvrSchema(shard);\n this.#cvrID = cvrID;\n this.#failService = failService;\n this.#deferredRowFlushThreshold = deferredRowFlushThreshold;\n this.#setTimeout = setTimeoutFn;\n }\n\n recordSyncFlushStats(stats: CVRFlushStats, elapsedMs: number) {\n this.#cvrFlushTime.record(elapsedMs / 1000, {\n [FLUSH_TYPE_ATTRIBUTE]: 'sync',\n });\n if (stats.rowsDeferred === 0) {\n this.#cvrRowsFlushed.add(stats.rows);\n }\n }\n\n #recordAsyncFlushStats(rows: number, elapsedMs: number) {\n this.#cvrFlushTime.record(elapsedMs / 1000, {\n [FLUSH_TYPE_ATTRIBUTE]: 'async',\n });\n this.#cvrRowsFlushed.add(rows);\n }\n\n #cvr(table: string) {\n return this.#db(`${this.#schema}.${table}`);\n }\n\n async #ensureLoaded(): Promise<CustomKeyMap<RowID, RowRecord>> {\n if (this.#cache) {\n return this.#cache;\n }\n const start = Date.now();\n const r = resolver<CustomKeyMap<RowID, RowRecord>>();\n // Set this.#cache immediately (before await) so that only one db\n // query is made even if there are multiple callers.\n this.#cache = r.promise;\n try {\n const cache: CustomKeyMap<RowID, RowRecord> = new CustomKeyMap(\n rowIDString,\n );\n for await (const rows of this.#db<RowsRow[]>`\n SELECT * FROM ${this.#cvr(`rows`)}\n WHERE \"clientGroupID\" = ${this.#cvrID} AND \"refCounts\" IS NOT NULL`\n // TODO(arv): Arbitrary page size\n .cursor(5000)) {\n for (const row of rows) {\n const rowRecord = rowsRowToRowRecord(row);\n cache.set(rowRecord.id, rowRecord);\n }\n }\n this.#lc.debug?.(\n `Loaded ${cache.size} row records in ${Date.now() - start} ms`,\n );\n r.resolve(cache);\n return this.#cache;\n } catch (e) {\n r.reject(e); // Make sure the error is reflected in the cached promise\n throw e;\n }\n }\n\n getRowRecords(): Promise<ReadonlyMap<RowID, RowRecord>> {\n return this.#ensureLoaded();\n }\n\n /**\n * Applies the `rowRecords` corresponding to the `rowsVersion`\n * to the cache, indicating whether the corresponding updates\n * (generated by {@link executeRowUpdates}) were `flushed`.\n *\n * If `flushed` is false, the RowRecordCache will flush the records\n * asynchronously.\n *\n * Note that `apply()` indicates that the CVR metadata associated with\n * the `rowRecords` was successfully committed, which essentially means\n * that this process has the unconditional right (and responsibility) of\n * following up with a flush of the `rowRecords`. In particular, the\n * commit of row records are not conditioned on the version or ownership\n * columns of the `cvr.instances` row.\n */\n async apply(\n rowRecords: Map<RowID, RowRecord | null>,\n rowsVersion: CVRVersion,\n flushed: boolean,\n ): Promise<number> {\n const cache = await this.#ensureLoaded();\n for (const [id, row] of rowRecords.entries()) {\n if (row === null || row.refCounts === null) {\n cache.delete(id);\n } else {\n cache.set(id, row);\n }\n if (!flushed) {\n this.#pending.set(id, row);\n }\n }\n this.#pendingRowsVersion = rowsVersion;\n // Initiate a flush if not already flushing.\n if (!flushed && this.#flushing === null) {\n this.#flushing = resolver();\n this.#setTimeout(() => this.#flush(), 0);\n }\n return cache.size;\n }\n\n async #flush() {\n const flushing = must(this.#flushing);\n try {\n while (this.#pendingRowsVersion !== this.#flushedRowsVersion) {\n const start = performance.now();\n\n const {rows, rowsVersion} = await this.#db.begin(\n Mode.READ_COMMITTED,\n tx => {\n disableStatementTimeout(tx);\n\n // Note: This code block is synchronous, guaranteeing that the\n // #pendingRowsVersion is consistent with the #pending rows.\n const rows = this.#pending.size;\n const rowsVersion = must(this.#pendingRowsVersion);\n // Awaiting all of the individual statements incurs too much\n // overhead. Instead, just catch and log exception(s); the outer\n // transaction will properly fail.\n void Promise.all(\n this.executeRowUpdates(tx, rowsVersion, this.#pending, 'force'),\n ).catch(e => this.#lc.error?.(`error flushing cvr rows`, e));\n\n this.#pending.clear();\n return {rows, rowsVersion};\n },\n );\n const elapsed = performance.now() - start;\n this.#lc.debug?.(\n `flushed ${rows} rows@${versionString(rowsVersion)} (${elapsed} ms)`,\n );\n this.#recordAsyncFlushStats(rows, elapsed);\n this.#flushedRowsVersion = rowsVersion;\n // Note: apply() may have called while the transaction was committing,\n // which will result in looping to commit the next #pendingRowsVersion.\n }\n this.#lc.debug?.(\n `up to date rows@${versionToNullableCookie(this.#flushedRowsVersion)}`,\n );\n flushing.resolve();\n this.#flushing = null;\n } catch (e) {\n flushing.reject(e);\n this.#failService(e);\n }\n }\n\n hasPendingUpdates() {\n return this.#flushing !== null;\n }\n\n /**\n * Returns a promise that resolves when all outstanding row-records\n * have been committed.\n */\n flushed(lc: LogContext): Promise<void> {\n if (this.#flushing) {\n lc.debug?.('awaiting pending row flush');\n return this.#flushing.promise;\n }\n return promiseVoid;\n }\n\n clear() {\n // Note: Only the #cache is cleared. #pending updates, on the other hand,\n // comprise canonical (i.e. already flushed) data and must be flushed\n // even if the snapshot of the present state (the #cache) is cleared.\n this.#cache = undefined;\n }\n\n async *catchupRowPatches(\n lc: LogContext,\n afterVersion: NullableCVRVersion,\n upToCVR: CVRSnapshot,\n current: CVRVersion,\n excludeQueryHashes: string[] = [],\n ): AsyncGenerator<RowsRow[], void, undefined> {\n if (cmpVersions(afterVersion, upToCVR.version) >= 0) {\n return;\n }\n\n const startMs = Date.now();\n const start = afterVersion ? versionString(afterVersion) : '';\n const end = versionString(upToCVR.version);\n lc.debug?.(`scanning row patches for clients from ${start}`);\n\n // Before accessing the CVR db, pending row records must be flushed.\n // Note that because catchupRowPatches() is called from within the\n // view syncer lock, this flush is guaranteed to complete since no\n // new CVR updates can happen while the lock is held.\n await this.flushed(lc);\n const flushMs = Date.now() - startMs;\n\n const reader = new TransactionPool(lc, Mode.READONLY).run(this.#db);\n try {\n // Verify that we are reading the right version of the CVR.\n await reader.processReadTask(tx =>\n checkVersion(tx, this.#schema, this.#cvrID, current),\n );\n\n const {query} = await reader.processReadTask(tx => {\n const query =\n excludeQueryHashes.length === 0\n ? tx<RowsRow[]>`SELECT * FROM ${this.#cvr('rows')}\n WHERE \"clientGroupID\" = ${this.#cvrID}\n AND \"patchVersion\" > ${start}\n AND \"patchVersion\" <= ${end}`\n : // Exclude rows that were already sent as part of query hydration.\n tx<RowsRow[]>`SELECT * FROM ${this.#cvr('rows')}\n WHERE \"clientGroupID\" = ${this.#cvrID}\n AND \"patchVersion\" > ${start}\n AND \"patchVersion\" <= ${end}\n AND (\"refCounts\" IS NULL OR NOT \"refCounts\" ?| ${excludeQueryHashes})`;\n return {query};\n });\n\n yield* query.cursor(10000);\n } finally {\n reader.setDone();\n }\n\n const totalMs = Date.now() - startMs;\n lc.debug?.(\n `finished row catchup (flush: ${flushMs} ms, total: ${totalMs} ms)`,\n );\n }\n\n executeRowUpdates(\n tx: PostgresTransaction,\n version: CVRVersion,\n rowUpdates: Map<RowID, RowRecord | null>,\n mode: 'allow-defer' | 'force',\n lc = this.#lc,\n ): PendingQuery<Row[]>[] {\n if (\n mode === 'allow-defer' &&\n // defer if pending rows are being flushed\n (this.#flushing !== null ||\n // or if the new batch is above the limit.\n rowUpdates.size > this.#deferredRowFlushThreshold)\n ) {\n return [];\n }\n const rowsVersion = {\n clientGroupID: this.#cvrID,\n version: versionString(version),\n };\n const pending: PendingQuery<Row[]>[] = [\n tx`INSERT INTO ${this.#cvr('rowsVersion')} ${tx(rowsVersion)}\n ON CONFLICT (\"clientGroupID\") \n DO UPDATE SET ${tx(rowsVersion)}`.execute(),\n ];\n\n const rowRecordRows: RowsRow[] = [];\n for (const [id, row] of rowUpdates.entries()) {\n if (row === null) {\n pending.push(\n tx`\n DELETE FROM ${this.#cvr('rows')}\n WHERE \"clientGroupID\" = ${this.#cvrID}\n AND \"schema\" = ${id.schema}\n AND \"table\" = ${id.table}\n AND \"rowKey\" = ${id.rowKey}\n `.execute(),\n );\n } else {\n rowRecordRows.push(rowRecordToRowsRow(this.#cvrID, row));\n }\n }\n if (rowRecordRows.length) {\n pending.push(\n tx`\n INSERT INTO ${this.#cvr('rows')}(\n \"clientGroupID\", \"schema\", \"table\", \"rowKey\", \"rowVersion\", \"patchVersion\", \"refCounts\"\n ) SELECT\n \"clientGroupID\", \"schema\", \"table\", \"rowKey\", \"rowVersion\", \"patchVersion\", \"refCounts\"\n FROM json_to_recordset(${rowRecordRows}) AS x(\n \"clientGroupID\" TEXT,\n \"schema\" TEXT,\n \"table\" TEXT,\n \"rowKey\" JSONB,\n \"rowVersion\" TEXT,\n \"patchVersion\" TEXT,\n \"refCounts\" JSONB\n ) ON CONFLICT (\"clientGroupID\", \"schema\", \"table\", \"rowKey\")\n DO UPDATE SET \"rowVersion\" = excluded.\"rowVersion\",\n \"patchVersion\" = excluded.\"patchVersion\",\n \"refCounts\" = excluded.\"refCounts\"\n `.execute(),\n );\n lc.debug?.(\n `flushing ${rowUpdates.size} rows (${rowRecordRows.length} inserts, ${\n rowUpdates.size - rowRecordRows.length\n } deletes)`,\n );\n }\n return pending;\n }\n}\n"],"names":["Mode.READ_COMMITTED","rows","rowsVersion","Mode.READONLY","query"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- const version = "0.25.10-canary.16";
1
+ const version = "0.25.10-canary.18";
2
2
  export {
3
3
  version
4
4
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rocicorp/zero",
3
- "version": "0.25.10-canary.16",
3
+ "version": "0.25.10-canary.18",
4
4
  "description": "Zero is a web framework for serverless web development.",
5
5
  "author": "Rocicorp, Inc.",
6
6
  "repository": {