@rocicorp/zero 1.11.0-canary.34 → 1.11.0-canary.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/analyze-query/src/analyze-cli.js +2 -2
- package/out/analyze-query/src/analyze-cli.js.map +1 -1
- package/out/zero/package.js +1 -1
- package/out/zero/package.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +20 -0
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +52 -0
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/server/syncer.js +17 -1
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +2 -2
- package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +27 -7
- package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/replication-slots.js +124 -62
- package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +2 -2
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/batch-pacer.d.ts +79 -0
- package/out/zero-cache/src/services/view-syncer/batch-pacer.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/batch-pacer.js +249 -0
- package/out/zero-cache/src/services/view-syncer/batch-pacer.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.js +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts +27 -1
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js +107 -17
- package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +85 -3
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +301 -20
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/push-cost.d.ts +132 -0
- package/out/zero-cache/src/services/view-syncer/push-cost.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/push-cost.js +164 -0
- package/out/zero-cache/src/services/view-syncer/push-cost.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/push-cutoff.d.ts +55 -0
- package/out/zero-cache/src/services/view-syncer/push-cutoff.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/push-cutoff.js +56 -0
- package/out/zero-cache/src/services/view-syncer/push-cutoff.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/query-stats.d.ts +7 -0
- package/out/zero-cache/src/services/view-syncer/query-stats.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/query-stats.js +20 -1
- package/out/zero-cache/src/services/view-syncer/query-stats.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +136 -5
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js +246 -18
- package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +8 -2
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +381 -16
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/workers/connection.js +3 -3
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.js +2 -2
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-react/src/use-query.js +1 -1
- package/out/zero-react/src/use-query.js.map +1 -1
- package/out/zero-server/src/queries/process-queries.d.ts.map +1 -1
- package/out/zero-server/src/queries/process-queries.js +1 -1
- package/out/zero-server/src/queries/process-queries.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cvr.js","names":["#shard","#deleteQueries","#removedOrExecutedQueryIDs","#executedQueryIDs","#receivedRows","#lastPatches","#rowSetSignature","#existingRows","#trackExecuted","#trackRemoved","#lookupRowsForExecutedAndRemovedQueries","#assertNewVersion","#deleteUnreferencedRow"],"sources":["../../../../../../zero-cache/src/services/view-syncer/cvr.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {startAsyncSpan, startSpan} from '../../../../otel/src/span.ts';\nimport {assert} from '../../../../shared/src/asserts.ts';\nimport {type JSONObject} from '../../../../shared/src/bigint-json.ts';\nimport {CustomKeyMap} from '../../../../shared/src/custom-key-map.ts';\nimport {toSorted} from '../../../../shared/src/iterables.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 versionString,\n type ClientQueryRecord,\n type ClientRecord,\n type CustomQueryRecord,\n type CVRVersion,\n type InternalQueryRecord,\n type QueryRecord,\n type RowID,\n type RowRecord,\n} from './schema/types.ts';\nimport {tracer} from './tracer.ts';\nimport {ttlClockAsNumber, type TTLClock} from './ttl-clock.ts';\n\nexport type RowUpdate = {\n version?: string; // Undefined for an unref.\n contents?: JSONObject; // Undefined for an unref.\n refCounts: {[hash: string]: number}; // Counts are negative when a row is unrefed.\n};\n\n/** Internally used mutable CVR type. */\nexport type CVR = {\n id: string;\n version: CVRVersion;\n lastActive: number;\n ttlClock: TTLClock;\n replicaVersion: string | null;\n clients: Record<string, ClientRecord>;\n queries: Record<string, QueryRecord>;\n clientSchema: ClientSchema | null;\n profileID: string | null;\n};\n\n/** Exported immutable CVR type. */\n// TODO: Use Immutable<CVR> when the AST is immutable.\nexport type CVRSnapshot = {\n readonly id: string;\n readonly version: CVRVersion;\n readonly lastActive: number;\n readonly ttlClock: TTLClock;\n readonly replicaVersion: string | null;\n readonly clients: Readonly<Record<string, ClientRecord>>;\n readonly queries: Readonly<Record<string, QueryRecord>>;\n readonly clientSchema: ClientSchema | null;\n readonly profileID: string | null;\n};\n\nconst CLIENT_LMID_QUERY_ID = 'lmids';\nconst CLIENT_MUTATION_RESULTS_QUERY_ID = 'mutationResults';\n\nfunction assertNotInternal(\n query: QueryRecord,\n): asserts query is ClientQueryRecord {\n if (query.type === 'internal') {\n // This should never happen for behaving clients, as query ids should be hashes.\n throw new Error(`Query ID ${query.id} is reserved for internal use`);\n }\n}\n\nexport function getMutationResultsQuery(\n upstreamSchema: string,\n clientGroupID: string,\n): InternalQueryRecord {\n return {\n id: CLIENT_MUTATION_RESULTS_QUERY_ID,\n type: 'internal',\n ast: {\n schema: '',\n table: `${upstreamSchema}.mutations`,\n where: {\n type: 'and',\n conditions: [\n {\n type: 'simple',\n left: {\n type: 'column',\n name: 'clientGroupID',\n },\n op: '=',\n right: {\n type: 'literal',\n value: clientGroupID,\n },\n },\n ],\n },\n orderBy: [\n ['clientGroupID', 'asc'],\n ['clientID', 'asc'],\n ['mutationID', 'asc'],\n ],\n },\n };\n}\n\n/**\n * The base CVRUpdater contains logic common to the {@link CVRConfigDrivenUpdater} and\n * {@link CVRQueryDrivenUpdater}. The CVRUpdater class itself is exported for updating\n * the `lastActive` time of the CVR in the absence of any changes to the CVR contents.\n * Although activity is automatically tracked when the CVR contents change, there may be\n * edge cases in which a client actively connects to a CVR that doesn't itself change.\n * Calling `new CVRUpdater(...).flush()` will explicitly update the active index and\n * prevent the CVR from being garbage collected.\n */\nexport class CVRUpdater {\n protected readonly _orig: CVRSnapshot;\n protected readonly _cvr: CVR;\n\n protected readonly _cvrStore: CVRStore;\n\n /**\n * @param cvrStore The CVRStore to use for storage\n * @param cvr The current CVR\n */\n constructor(\n cvrStore: CVRStore,\n cvr: CVRSnapshot,\n replicaVersion: string | null,\n ) {\n this._cvrStore = cvrStore;\n this._orig = cvr;\n this._cvr = {\n ...cvr,\n replicaVersion,\n };\n }\n\n protected _mutableClients(): Record<string, ClientRecord> {\n if (this._cvr.clients === this._orig.clients) {\n this._cvr.clients = {...this._orig.clients};\n }\n return this._cvr.clients;\n }\n\n protected _mutableClient(id: string): ClientRecord {\n const clients = this._mutableClients();\n let client = clients[id];\n assert(client !== undefined, () => `Client ${id} not found in CVR`);\n if (client === this._orig.clients[id]) {\n client = {\n ...client,\n desiredQueryIDs: [...client.desiredQueryIDs],\n };\n clients[id] = client;\n }\n return client;\n }\n\n protected _mutableQueries(): Record<string, QueryRecord> {\n if (this._cvr.queries === this._orig.queries) {\n this._cvr.queries = {...this._orig.queries};\n }\n return this._cvr.queries;\n }\n\n protected _mutableQuery(id: string): QueryRecord {\n const queries = this._mutableQueries();\n let query = queries[id];\n assert(query !== undefined, () => `Query ${id} not found in CVR`);\n if (query === this._orig.queries[id]) {\n query = {\n ...query,\n ...(query.type !== 'internal'\n ? {clientState: {...query.clientState}}\n : {}),\n };\n queries[id] = query;\n }\n return query;\n }\n\n protected _setVersion(version: CVRVersion) {\n assert(\n cmpVersions(this._cvr.version, version) < 0,\n 'Expected new version to be greater than current version',\n );\n this._cvr.version = version;\n return version;\n }\n\n /**\n * Ensures that the new CVR has a higher version than the original.\n * This method is idempotent in that it will always return the same\n * (possibly bumped) version.\n */\n protected _ensureNewVersion(): CVRVersion {\n if (cmpVersions(this._orig.version, this._cvr.version) === 0) {\n this._setVersion(oneAfter(this._cvr.version));\n }\n return this._cvr.version;\n }\n\n /**\n * @param verifyNoop Check that the original CVR is still current even if\n * there is nothing to write. See {@link CVRStore.flush}.\n */\n async flush(\n lc: LogContext,\n lastConnectTime: number,\n lastActive: number,\n ttlClock: TTLClock,\n verifyNoop = false,\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 verifyNoop,\n );\n if (!flushed) {\n return {cvr: this._orig, flushed: false};\n }\n return {cvr: this._cvr, flushed};\n }\n}\n\n/**\n * A {@link CVRConfigDrivenUpdater} is used for updating a CVR with config-driven\n * changes. Note that this may result in row deletion (e.g. if queries get dropped),\n * but the `stateVersion` of the CVR does not change.\n */\nexport class CVRConfigDrivenUpdater extends CVRUpdater {\n readonly #shard: ShardID;\n\n constructor(cvrStore: CVRStore, cvr: CVRSnapshot, shard: ShardID) {\n super(cvrStore, cvr, cvr.replicaVersion);\n this.#shard = shard;\n }\n\n ensureClient(id: string): ClientRecord {\n return startSpan(tracer, 'CVRConfigDrivenUpdater.ensureClient', () => {\n let client = this._cvr.clients[id];\n if (client) {\n return client;\n }\n // Add the ClientRecord and PutPatch\n client = {id, desiredQueryIDs: []};\n this._mutableClients()[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._mutableQueries()[CLIENT_LMID_QUERY_ID] = lmidsQuery;\n this._cvrStore.putQuery(lmidsQuery);\n }\n if (!this._cvr.queries[CLIENT_MUTATION_RESULTS_QUERY_ID]) {\n const mutationResultsQuery: InternalQueryRecord =\n getMutationResultsQuery(upstreamSchema(this.#shard), this._cvr.id);\n this._mutableQueries()[CLIENT_MUTATION_RESULTS_QUERY_ID] =\n mutationResultsQuery;\n this._cvrStore.putQuery(mutationResultsQuery);\n }\n\n return client;\n });\n }\n\n setClientSchema(lc: LogContext, clientSchema: ClientSchema) {\n startSpan(tracer, 'CVRConfigDrivenUpdater.setClientSchema', () => {\n if (this._cvr.clientSchema === null) {\n this._cvr.clientSchema = clientSchema;\n this._cvrStore.putInstance(this._cvr);\n } else if (!deepEqual(this._cvr.clientSchema, clientSchema)) {\n // This should not be possible with a correct Zero client, as clients\n // of a CVR should all have the same schema (given that the schema hash\n // is part of the idb key). In fact, clients joining an existing group\n // (i.e. non-empty baseCookie) do not send the clientSchema message.\n lc.warn?.(\n `New schema ${JSON.stringify(\n clientSchema,\n )} does not match existing schema ${JSON.stringify(\n this._cvr.clientSchema,\n )}`,\n );\n throw new ProtocolError({\n kind: 'InvalidConnectionRequest',\n message: `Provided schema does not match previous schema`,\n origin: ErrorOrigin.ZeroCache,\n });\n }\n });\n }\n\n setProfileID(lc: LogContext, profileID: string) {\n if (this._cvr.profileID !== profileID) {\n if (\n this._cvr.profileID !== null &&\n !this._cvr.profileID.startsWith('cg')\n ) {\n // We expect profile ID's to change from null or from the back-filled\n // \"cg...\" value. Log a warning otherwise to surface unexpected or\n // pathological conditions.\n lc.warn?.(\n `changing profile ID from ${this._cvr.profileID} to ${profileID}`,\n );\n }\n this._cvr.profileID = profileID;\n this._cvrStore.putInstance(this._cvr);\n }\n }\n\n putDesiredQueries(\n clientID: string,\n queries: Readonly<{\n hash: string;\n ast?: AST | undefined;\n name?: string | undefined;\n args?: readonly ReadonlyJSONValue[] | undefined;\n ttl?: number | undefined;\n }>[],\n ): PatchToVersion[] {\n return startSpan(tracer, 'CVRConfigDrivenUpdater.putDesiredQueries', () => {\n const patches: PatchToVersion[] = [];\n const client = this.ensureClient(clientID);\n const current = new Set(client.desiredQueryIDs);\n\n // Find the new/changed desired queries.\n const needed: Set<string> = new Set();\n\n const recordQueryForTelemetry = (q: (typeof queries)[0]) => {\n const {ast, name, args} = q;\n if (ast) {\n recordQuery('crud');\n } else if (name && args) {\n recordQuery('custom');\n }\n };\n\n for (const q of queries) {\n const {hash, ttl = DEFAULT_TTL_MS} = q;\n const query = this._cvr.queries[hash];\n if (!query) {\n // New query - record for telemetry\n recordQueryForTelemetry(q);\n needed.add(hash);\n continue;\n }\n if (query.type === 'internal') {\n continue;\n }\n\n const oldClientState = query.clientState[clientID];\n // Old query was inactivated or never desired by this client.\n if (!oldClientState || oldClientState.inactivatedAt !== undefined) {\n // Reactivated query - record for telemetry\n recordQueryForTelemetry(q);\n needed.add(hash);\n continue;\n }\n\n if (compareTTL(ttl, oldClientState.ttl) > 0) {\n // TTL update only - don't record for telemetry\n needed.add(hash);\n }\n }\n\n if (needed.size === 0) {\n return patches;\n }\n const newVersion = this._ensureNewVersion();\n const mutableClient = this._mutableClient(clientID);\n mutableClient.desiredQueryIDs = toSorted(\n union(current, needed),\n stringCompare,\n );\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 existingQuery = this._cvr.queries[id];\n const query = existingQuery\n ? this._mutableQuery(id)\n : 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._mutableQueries()[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 mutableClient,\n false,\n inactivatedAt,\n ttl,\n );\n }\n return patches;\n });\n }\n\n markDesiredQueriesAsInactive(\n clientID: string,\n queryHashes: string[],\n ttlClock: TTLClock,\n ): PatchToVersion[] {\n return this.#deleteQueries(clientID, queryHashes, ttlClock);\n }\n\n deleteDesiredQueries(\n clientID: string,\n queryHashes: string[],\n ): PatchToVersion[] {\n return this.#deleteQueries(clientID, queryHashes, undefined);\n }\n\n #deleteQueries(\n clientID: string,\n queryHashes: string[],\n inactivatedAt: TTLClock | undefined,\n ): PatchToVersion[] {\n return startSpan(tracer, 'CVRConfigDrivenUpdater.#deleteQueries', () => {\n const patches: PatchToVersion[] = [];\n const client = this.ensureClient(clientID);\n const current = new Set(client.desiredQueryIDs);\n const unwanted = new Set(queryHashes);\n const remove = intersection(unwanted, current);\n if (remove.size === 0) {\n return patches;\n }\n\n const newVersion = this._ensureNewVersion();\n const mutableClient = this._mutableClient(clientID);\n mutableClient.desiredQueryIDs = toSorted(\n difference(current, remove),\n stringCompare,\n );\n\n for (const id of remove) {\n if (!this._cvr.queries[id]) {\n continue; // Query itself has already been removed. Should not happen?\n }\n const query = this._mutableQuery(id);\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 mutableClient,\n true,\n inactivatedAt,\n ttl,\n );\n patches.push({\n toVersion: newVersion,\n patch: {type: 'query', op: 'del', id, clientID},\n });\n }\n return patches;\n });\n }\n\n clearDesiredQueries(clientID: string): PatchToVersion[] {\n const client = this.ensureClient(clientID);\n return this.#deleteQueries(clientID, client.desiredQueryIDs, undefined);\n }\n\n deleteClient(clientID: string, ttlClock: TTLClock): PatchToVersion[] {\n return startSpan(tracer, 'CVRConfigDrivenUpdater.deleteClient', () => {\n // clientID might not be part of this client group but if it is, this delete\n // may generate changes to the desired queries.\n\n const client = this._cvr.clients[clientID];\n if (!client) {\n // Clients in different client groups are no longer deleted, leaving\n // cleanup to inactive CVR purging logic.\n return [];\n }\n\n // When a client is deleted we mark all of its desired queries as inactive.\n // They will then be removed when the queries expire.\n const patches = this.markDesiredQueriesAsInactive(\n clientID,\n client.desiredQueryIDs,\n ttlClock,\n );\n delete this._mutableClients()[clientID];\n this._cvrStore.deleteClient(clientID);\n\n return patches;\n });\n }\n}\n\ntype Hash = string;\nexport type Column = string;\nexport type RefCounts = Record<Hash, number>;\n\ntype RowPatchInfo = {\n rowVersion: string | null; // null for a row-del\n toVersion: CVRVersion; // patchVersion\n};\n\n/**\n * The row-set signatures maintained by the pipeline driver, used by\n * {@link CVRQueryDrivenUpdater.flush} to persist the ones that changed.\n * Implemented by `PipelineDriver`.\n */\nexport interface RowSetSignatureProvider {\n /**\n * IDs of the queries whose signature changed since the last\n * {@link clearChangedRowSetSignatures}. Queries not in this set are not\n * looked at, so that a flush does not have to visit every query.\n */\n changedRowSetSignatures(): Iterable<string>;\n\n /**\n * The current signature for the query. `undefined` means \"no pipeline for\n * this query right now\" — the stored signature on disk is left alone.\n */\n rowSetSignature(queryID: string): bigint | undefined;\n\n /** Called after the changed signatures have been flushed. */\n clearChangedRowSetSignatures(): void;\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 removeTrackedQueries} for optional queries that did not execute\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 #executedQueryIDs = new Set<string>();\n readonly #receivedRows = new CustomKeyMap<RowID, RefCounts | null>(\n rowIDString,\n );\n readonly #lastPatches = new CustomKeyMap<RowID, RowPatchInfo>(rowIDString);\n readonly #rowSetSignature: RowSetSignatureProvider | undefined;\n\n #existingRows: Promise<Iterable<RowRecord>> | undefined = undefined;\n\n /**\n * @param stateVersion The `stateVersion` at which the queries were executed.\n * @param rowSetSignature Optional source of the current row-set\n * signatures, typically the `PipelineDriver`. When provided,\n * {@link flush} persists the signatures that changed.\n */\n constructor(\n cvrStore: CVRStore,\n cvr: CVRSnapshot,\n stateVersion: LexiVersion,\n replicaVersion: string,\n rowSetSignature?: RowSetSignatureProvider,\n ) {\n super(cvrStore, cvr, replicaVersion);\n this.#rowSetSignature = rowSetSignature;\n\n assert(\n // We should either be setting the cvr.replicaVersion for the first time, or it should\n // be something newer than the current cvr.replicaVersion. Otherwise, the CVR should\n // have been rejected by the ViewSyncer.\n (cvr.replicaVersion ?? replicaVersion) <= replicaVersion,\n `Cannot sync from an older replicaVersion: CVR=${cvr.replicaVersion}, DB=${replicaVersion}`,\n );\n assert(\n stateVersion >= cvr.version.stateVersion,\n () =>\n `stateVersion (${stateVersion}) must be >= cvr.version.stateVersion (${cvr.version.stateVersion})`,\n );\n if (stateVersion > cvr.version.stateVersion) {\n this._setVersion({stateVersion});\n }\n }\n\n /**\n * Initiates the tracking of the specified `executed` and `removed` queries.\n * This kicks of a lookup of existing {@link RowRecord}s currently associated\n * with those queries, which will be used to reconcile the rows to keep\n * after all rows have been {@link received()}.\n *\n * \"transformed\" queries are queries that are currently\n * gotten and running in the pipeline driver but\n * received a new transformation hash due to an auth token\n * update.\n *\n * @returns The new CVRVersion to be used when all changes are committed.\n */\n trackQueries(\n lc: LogContext,\n executed: {id: string; transformationHash: string}[],\n removed: {id: string}[],\n ): {newVersion: CVRVersion; queryPatches: PatchToVersion[]} {\n return startSpan(tracer, 'CVRQueryDrivenUpdater.trackQueries', () => {\n assert(this.#existingRows === undefined, `trackQueries already called`);\n\n const queryPatches: Patch[] = [\n executed.map(q => this.#trackExecuted(q.id, q.transformationHash)),\n removed.map(q => this.#trackRemoved(q.id)),\n ].flat(2);\n\n this.#existingRows = this.#lookupRowsForExecutedAndRemovedQueries(lc);\n // Immediately attach a rejection handler to avoid unhandled rejections.\n // The error will surface when this.#existingRows is awaited.\n void this.#existingRows.then(() => {});\n\n const versionBumped =\n cmpVersions(this._orig.version, this._cvr.version) < 0;\n lc.info?.(\n `trackQueries: ${executed.length} executed, ${removed.length} removed, ` +\n `version ${versionBumped ? 'bumped' : 'unchanged'}`,\n );\n\n return {\n newVersion: this._cvr.version,\n queryPatches: queryPatches.map(patch => ({\n patch,\n toVersion: this._cvr.version,\n })),\n };\n });\n }\n\n #lookupRowsForExecutedAndRemovedQueries(\n lc: LogContext,\n ): Promise<Iterable<RowRecord>> {\n return startAsyncSpan(\n tracer,\n 'CVRQueryDrivenUpdater.#lookupRowsForExecutedAndRemovedQueries',\n async () => {\n const results = new CustomKeyMap<RowID, RowRecord>(rowIDString);\n\n if (this.#removedOrExecutedQueryIDs.size === 0) {\n // Query-less update. This can happen for config only changes.\n return [];\n }\n\n // Utilizes the in-memory RowCache.\n const allRowRecords = (await this._cvrStore.getRowRecords()).values();\n let total = 0;\n for (const existing of allRowRecords) {\n total++;\n assert(\n existing.refCounts !== null,\n 'allRowRecords should not include null refCounts',\n );\n for (const id of Object.keys(existing.refCounts)) {\n if (this.#removedOrExecutedQueryIDs.has(id)) {\n results.set(existing.id, existing);\n break;\n }\n }\n }\n\n lc.debug?.(\n `found ${\n results.size\n } (of ${total}) rows for executed / removed queries ${[\n ...this.#removedOrExecutedQueryIDs,\n ]}`,\n );\n return results.values();\n },\n );\n }\n\n /**\n * Tracks an executed query, ensures that it is marked as \"gotten\",\n * updating the CVR and creating put patches if necessary.\n *\n * This must be called for all executed queries.\n */\n #trackExecuted(queryID: string, transformationHash: string): Patch[] {\n return startSpan(tracer, 'CVRQueryDrivenUpdater.#trackExecuted', () => {\n assert(\n !this.#removedOrExecutedQueryIDs.has(queryID),\n () => `Query ${queryID} already tracked as executed or removed`,\n );\n this.#removedOrExecutedQueryIDs.add(queryID);\n this.#executedQueryIDs.add(queryID);\n\n let gotQueryPatch: Patch | undefined;\n const query = this._cvr.queries[queryID];\n if (query.transformationHash !== transformationHash) {\n const mutableQuery = this._mutableQuery(queryID);\n const transformationVersion = this._ensureNewVersion();\n\n if (\n mutableQuery.type !== 'internal' &&\n mutableQuery.patchVersion === undefined\n ) {\n // client query: desired -> gotten\n mutableQuery.patchVersion = transformationVersion;\n gotQueryPatch = {\n type: 'query',\n op: 'put',\n id: mutableQuery.id,\n };\n }\n\n mutableQuery.transformationHash = transformationHash;\n mutableQuery.transformationVersion = transformationVersion;\n this._cvrStore.updateQuery(mutableQuery);\n }\n return gotQueryPatch ? [gotQueryPatch] : [];\n });\n }\n\n /**\n * Converts queries that were tracked as executed into normal query\n * removals. This is used when a hydration budget expires after\n * `trackQueries()` has established the final poke version but before an\n * optional query starts hydration.\n *\n * The query ID remains in `#removedOrExecutedQueryIDs`, so its row\n * references are dropped by {@link deleteUnreferencedRows} exactly as they\n * would be for a query removed up front. Never un-track a query here: rows\n * already flushed by {@link received} have had its refCounts stripped and\n * cannot be restored.\n */\n removeTrackedQueries(queryIDs: Iterable<string>): PatchToVersion[] {\n assert(this.#existingRows !== undefined, 'trackQueries() was not called');\n assert(\n cmpVersions(this._orig.version, this._cvr.version) < 0,\n 'A final CVR version must be set before removing tracked queries',\n );\n\n const patches: PatchToVersion[] = [];\n for (const queryID of queryIDs) {\n assert(\n this.#executedQueryIDs.has(queryID),\n () => `Query ${queryID} was not tracked as executed`,\n );\n const query = must(this._cvr.queries[queryID]);\n assertNotInternal(query);\n assert(\n Object.values(query.clientState).every(\n ({inactivatedAt}) => inactivatedAt !== undefined,\n ),\n () => `Query ${queryID} is active`,\n );\n assert(\n query.patchVersion !== undefined,\n `Query ${queryID} is not gotten`,\n );\n\n delete this._mutableQueries()[queryID];\n const patch = {type: 'query', op: 'del', id: queryID} as const;\n this._cvrStore.markQueryAsDeleted(this._cvr.version, patch);\n patches.push({patch, toVersion: this._cvr.version});\n }\n return patches;\n }\n\n /**\n * Aborts queries that were tracked as executed but whose hydration was cut\n * short (see the view-syncer's hydration circuit breaker).\n *\n * Rows received for an aborted query in this update have that query's\n * references stripped: a row left with no references is deleted, and the\n * 'del' patch cancels the 'put' the client has already received, while a\n * row still referenced by other queries keeps its patch version. The query\n * is then removed from the CVR. It stays tracked as executed, so\n * {@link deleteUnreferencedRows} also drops the references held by rows that\n * were synced for it before this update, exactly as for a removed query.\n *\n * Like {@link removeTrackedQueries}, this must be called after\n * {@link trackQueries} has established the final version, and after all\n * rows of the aborted queries have been {@link received}.\n */\n async abortExecutedQueries(\n lc: LogContext,\n queryIDs: readonly string[],\n ): Promise<PatchToVersion[]> {\n assert(this.#existingRows !== undefined, 'trackQueries() was not called');\n assert(\n cmpVersions(this._orig.version, this._cvr.version) < 0,\n 'A final CVR version must be set before aborting executed queries',\n );\n const aborted = new Set(queryIDs);\n for (const queryID of aborted) {\n assert(\n this.#executedQueryIDs.has(queryID),\n `Query ${queryID} was not tracked as executed`,\n );\n const query = must(this._cvr.queries[queryID]);\n assertNotInternal(query);\n }\n\n // Unref the rows received for the aborted queries. The version of each\n // unref is the version the row currently has in this update, so a row that\n // stays referenced does not get a spurious patch version bump.\n const existingRows = await this._cvrStore.getRowRecords();\n const unrefs = new CustomKeyMap<RowID, RowUpdate>(rowIDString);\n for (const [id, refCounts] of this.#receivedRows) {\n if (refCounts === null) {\n continue;\n }\n let unref: RowUpdate | undefined;\n for (const queryID of aborted) {\n const count = refCounts[queryID];\n if (!count) {\n continue;\n }\n if (!unref) {\n const version =\n this.#lastPatches.get(id)?.rowVersion ??\n existingRows.get(id)?.rowVersion;\n unref = {refCounts: {}, ...(version && {version})};\n unrefs.set(id, unref);\n }\n unref.refCounts[queryID] = -count;\n }\n }\n const patches =\n unrefs.size > 0\n ? await this.received(lc, unrefs)\n : ([] as PatchToVersion[]);\n\n for (const queryID of aborted) {\n delete this._mutableQueries()[queryID];\n const patch = {type: 'query', op: 'del', id: queryID} as const;\n this._cvrStore.markQueryAsDeleted(this._cvr.version, patch);\n patches.push({patch, toVersion: this._cvr.version});\n }\n return patches;\n }\n\n /**\n * Tracks a query removed from the \"gotten\" set. In addition to producing the\n * appropriate patches for deleting the query, the removed query is taken into\n * account when computing the final row records in\n * {@link deleteUnreferencedRows}.\n * Namely, any rows with columns that are no longer referenced by a\n * query are deleted.\n *\n * This must only be called on queries that are not \"desired\" by any client.\n */\n #trackRemoved(queryID: string): Patch[] {\n return startSpan(tracer, 'CVRQueryDrivenUpdater.#trackRemoved', () => {\n const query = this._cvr.queries[queryID];\n assertNotInternal(query);\n\n assert(\n !this.#removedOrExecutedQueryIDs.has(queryID),\n () => `Query ${queryID} already tracked as executed or removed`,\n );\n this.#removedOrExecutedQueryIDs.add(queryID);\n delete this._mutableQueries()[queryID];\n\n const newVersion = this._ensureNewVersion();\n const queryPatch = {type: 'query', op: 'del', id: queryID} as const;\n this._cvrStore.markQueryAsDeleted(newVersion, queryPatch);\n return [queryPatch];\n });\n }\n\n /**\n * Asserts that a new version has already been set.\n *\n * After {@link #executed} and {@link #removed} are called, we must have properly\n * decided on the final CVR version because the poke-start message declares the\n * final cookie (i.e. version), and that must be sent before any poke parts\n * generated from {@link received} are sent.\n */\n #assertNewVersion(\n rowID: RowID,\n existingVersion: string | undefined,\n newVersion: string | undefined,\n refCounts: RefCounts,\n ): CVRVersion {\n assert(\n cmpVersions(this._orig.version, this._cvr.version) < 0,\n () =>\n `Expected CVR version to have been bumped above original` +\n ` (orig=${versionString(this._orig.version)},` +\n ` curr=${versionString(this._cvr.version)}).` +\n ` Row ${JSON.stringify(rowID)}:` +\n ` existing=${existingVersion},` +\n ` new=${newVersion},` +\n ` queries=[${Object.keys(refCounts).join(',')}]`,\n );\n return this._cvr.version;\n }\n\n updatedVersion(): CVRVersion {\n return this._cvr.version;\n }\n\n /**\n * Public alias for {@link _ensureNewVersion}. Forces a `configVersion` bump\n * when the caller needs a new CVR version for reasons outside the built-in\n * tracking (e.g. rowSetSignature drift re-execution).\n */\n ensureNewVersion(): CVRVersion {\n return this._ensureNewVersion();\n }\n\n override async flush(\n lc: LogContext,\n lastConnectTime: number,\n lastActive: number,\n ttlClock: TTLClock,\n verifyNoop = false,\n ): Promise<{cvr: CVRSnapshot; flushed: CVRFlushStats | false}> {\n const signatures = this.#rowSetSignature;\n if (signatures) {\n // Persist the row-set signature of each query whose pipeline-driver\n // signature changed and now differs from what's on disk. Only the\n // changed queries are visited: scanning (and parsing the stored\n // signature of) every query on every flush is a significant source of\n // garbage for client groups with many queries. Queries without an\n // active pipeline (provider returns undefined) keep their stored value.\n //\n // The signatures live in the CVRStore rather than in the QueryRecords,\n // so that persisting one does not copy the CVR's queries.\n for (const queryID of signatures.changedRowSetSignatures()) {\n if (this._cvr.queries[queryID] === undefined) {\n continue;\n }\n const sig = signatures.rowSetSignature(queryID);\n if (sig === undefined) {\n continue;\n }\n const stored = this._cvrStore.rowSetSignature(queryID) ?? 0n;\n if (stored === sig) {\n continue;\n }\n this._cvrStore.updateRowSetSignature(queryID, sig);\n }\n }\n const result = await super.flush(\n lc,\n lastConnectTime,\n lastActive,\n ttlClock,\n verifyNoop,\n );\n // Only forget the changes once they are persisted. If the flush throws\n // they are looked at again by the next flush.\n signatures?.clearChangedRowSetSignatures();\n return result;\n }\n\n /**\n * Tracks rows received from executing queries. This will update row records\n * and row patches if the received rows have a new version. The method also\n * returns (put) patches to be returned to update their state, versioned by\n * patchVersion so that only the patches new to the clients are sent.\n */\n received(\n _lc: LogContext,\n rows: Map<RowID, RowUpdate>,\n ): Promise<PatchToVersion[]> {\n return startAsyncSpan(\n tracer,\n 'CVRQueryDrivenUpdater.received',\n async () => {\n const patches: PatchToVersion[] = [];\n const existingRows = await this._cvrStore.getRowRecords();\n\n for (const [id, update] of rows.entries()) {\n const {contents, version, refCounts} = update;\n\n let existing = existingRows.get(id);\n // Accumulate all received refCounts to determine which rows to prune.\n const previouslyReceived = this.#receivedRows.get(id);\n\n const merged =\n previouslyReceived !== undefined\n ? mergeRefCounts(previouslyReceived, refCounts)\n : mergeRefCounts(\n existing?.refCounts,\n refCounts,\n this.#removedOrExecutedQueryIDs,\n );\n\n this.#receivedRows.set(id, merged);\n\n const newRowVersion = merged === null ? undefined : version;\n const patchVersion =\n existing && existing.rowVersion === newRowVersion\n ? existing.patchVersion // existing row is unchanged\n : this.#assertNewVersion(\n id,\n existing?.rowVersion,\n newRowVersion,\n refCounts,\n );\n\n // Note: for determining what to commit to the CVR store, use the\n // `version` of the update even if `merged` is null (i.e. don't\n // use `newRowVersion`). This will be deduped by the cvr-store flush\n // if it is redundant. In rare cases--namely, if the row key has\n // changed--we _do_ want to add row-put for the new row key with\n // `refCounts: null` in order to correctly record a delete patch\n // for that row, as the row with the old key will be removed.\n const rowVersion = version ?? existing?.rowVersion;\n if (rowVersion) {\n this._cvrStore.putRowRecord({\n id,\n rowVersion,\n patchVersion,\n refCounts: merged,\n });\n } else {\n // This means that a row that was not in the CVR was added during\n // this update, and then subsequently removed. Since there's no\n // corresponding row in the CVR itself, cancel the previous put.\n // Note that we still send a 'del' patch to the client in order to\n // cancel the previous 'put' patch.\n this._cvrStore.delRowRecord(id);\n }\n\n // Dedupe against the lastPatch sent for the row, and ensure that\n // toVersion never backtracks (lest it be undesirably filtered).\n const lastPatch = this.#lastPatches.get(id);\n const toVersion = maxVersion(patchVersion, lastPatch?.toVersion);\n\n if (merged === null) {\n // All refCounts have gone to zero, if row was previously synced\n // delete it.\n if (existing || previouslyReceived) {\n // dedupe\n if (lastPatch?.rowVersion !== null) {\n patches.push({\n patch: {\n type: 'row',\n op: 'del',\n id,\n },\n toVersion,\n });\n this.#lastPatches.set(id, {rowVersion: null, toVersion});\n }\n }\n } else if (contents) {\n assert(\n rowVersion,\n 'rowVersion is required when contents is present',\n );\n // dedupe\n if (!lastPatch?.rowVersion || lastPatch.rowVersion < rowVersion) {\n patches.push({\n patch: {\n type: 'row',\n op: 'put',\n id,\n contents,\n },\n toVersion,\n });\n this.#lastPatches.set(id, {rowVersion, toVersion});\n }\n }\n }\n return patches;\n },\n );\n }\n\n /**\n * Computes and updates the row records based on:\n * * The {@link #executed} queries\n * * The {@link #removed} queries\n * * The {@link received} rows\n *\n * Returns the final delete and patch ops that must be sent to the client\n * to delete rows that are no longer referenced by any query.\n *\n * This is Step [5] of the\n * [CVR Sync Algorithm](https://www.notion.so/replicache/Sync-and-Client-View-Records-CVR-a18e02ec3ec543449ea22070855ff33d?pvs=4#7874f9b80a514be2b8cd5cf538b88d37).\n */\n deleteUnreferencedRows(lc?: LogContext): Promise<PatchToVersion[]> {\n return startAsyncSpan(\n tracer,\n 'CVRQueryDrivenUpdater.deleteUnreferencedRows',\n async () => {\n if (this.#removedOrExecutedQueryIDs.size === 0) {\n // Query-less update. This can happen for config-only changes.\n assert(\n this.#receivedRows.size === 0,\n () =>\n `Expected no received rows for query-less update, got ${this.#receivedRows.size}`,\n );\n return [];\n }\n\n // patches to send to the client.\n const patches: PatchToVersion[] = [];\n\n const start = Date.now();\n assert(this.#existingRows, `trackQueries() was not called`);\n for (const existing of await this.#existingRows) {\n const deletedID = this.#deleteUnreferencedRow(existing);\n if (deletedID === null) {\n continue;\n }\n const lastPatch = this.#lastPatches.get(deletedID);\n if (lastPatch !== undefined && lastPatch.rowVersion === null) {\n continue;\n }\n const toVersion = maxVersion(this._cvr.version, lastPatch?.toVersion);\n patches.push({\n toVersion,\n patch: {type: 'row', op: 'del', id: deletedID},\n });\n this.#lastPatches.set(deletedID, {rowVersion: null, toVersion});\n }\n lc?.debug?.(\n `computed ${patches.length} delete patches (${Date.now() - start} ms)`,\n );\n\n return patches;\n },\n );\n }\n\n #deleteUnreferencedRow(existing: RowRecord): RowID | null {\n return startSpan(\n tracer,\n 'CVRQueryDrivenUpdater.#deleteUnreferencedRow',\n () => {\n // received() has already written the final record (and patch) of a\n // received row, including a row whose refCounts all went to zero.\n // Rebuilding such a tombstone from the pre-update refCounts would\n // resurrect a reference that an untracked query's delta removed.\n if (this.#receivedRows.has(existing.id)) {\n return null;\n }\n\n const newRefCounts = mergeRefCounts(\n existing.refCounts,\n undefined,\n this.#removedOrExecutedQueryIDs,\n );\n\n // If a row is still referenced, we update the refCounts but not the\n // patchVersion (as the existence and contents of the row have not\n // changed from the clients' perspective). If the row is deleted, it\n // gets a new patchVersion (and corresponding poke).\n const patchVersion = newRefCounts\n ? existing.patchVersion\n : this.#assertNewVersion(\n existing.id,\n existing.rowVersion,\n undefined,\n existing.refCounts ?? {},\n );\n const rowRecord: RowRecord = {\n ...existing,\n patchVersion,\n refCounts: newRefCounts,\n };\n\n this._cvrStore.putRowRecord(rowRecord);\n\n // Return the id to delete if no longer referenced.\n return newRefCounts ? null : existing.id;\n },\n );\n }\n}\n\nfunction mergeRefCounts(\n existing: RefCounts | null | undefined,\n received: RefCounts | null | undefined,\n removeHashes?: Set<string>,\n): RefCounts | null {\n let merged: RefCounts = {};\n if (!existing) {\n merged = received ?? {};\n } else {\n [existing, received].forEach((refCounts, i) => {\n if (!refCounts) {\n return;\n }\n for (const [hash, count] of Object.entries(refCounts)) {\n if (i === 0 /* existing */ && removeHashes?.has(hash)) {\n continue; // removeHashes from existing row.\n }\n merged[hash] = (merged[hash] ?? 0) + count;\n if (merged[hash] === 0) {\n delete merged[hash];\n }\n }\n\n return merged;\n });\n }\n\n return Object.values(merged).some(v => v > 0) ? merged : null;\n}\n\n/**\n * The query must be inactive for all clients to be considered inactive.\n * This is because expiration is defined that way: a query is expired for a client group\n * only if it is expired for all clients in the group.\n *\n * If all clients have inactivated the query, we return\n * the one with the expiration furthest in the future.\n */\nexport function getInactiveQueries(cvr: CVR): {\n hash: string;\n inactivatedAt: TTLClock;\n ttl: number;\n}[] {\n // We no longer support a TTL larger than 10 minutes.\n const inactive: Map<\n string,\n {\n hash: string;\n inactivatedAt: TTLClock;\n ttl: number;\n }\n > = new Map();\n for (const [queryID, query] of Object.entries(cvr.queries)) {\n if (query.type === 'internal') {\n continue;\n }\n for (const clientState of Object.values(query.clientState)) {\n // 1. Take the longest TTL\n // 2. If the query is not inactivated (for any client), do not return it\n const {inactivatedAt, ttl} = clientState;\n const existing = inactive.get(queryID);\n if (inactivatedAt === undefined) {\n if (existing) {\n inactive.delete(queryID);\n }\n break;\n }\n\n const clampedTTL = clampTTL(ttl);\n if (existing) {\n // The stored one might be too large because from a previous version of\n // zero\n const existingTTL = clampTTL(existing.ttl);\n // Use the last eviction time.\n if (\n existingTTL + ttlClockAsNumber(existing.inactivatedAt) <\n ttlClockAsNumber(inactivatedAt) + clampedTTL\n ) {\n existing.ttl = clampedTTL;\n existing.inactivatedAt = inactivatedAt;\n }\n } else {\n inactive.set(queryID, {\n hash: queryID,\n inactivatedAt,\n ttl: clampedTTL,\n });\n }\n }\n }\n\n // First sort all the queries that have TTL. Oldest first.\n return toSorted(inactive.values(), (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 type HydrationQueryGroups<T extends QueryRecord = QueryRecord> = {\n required: T[];\n /**\n * Never contains an internal query: those are always required, so callers\n * can rely on `patchVersion` being present on the type.\n */\n optional: Exclude<T, InternalQueryRecord>[];\n};\n\n/**\n * Splits hydration candidates into required and optional queries. Optional\n * queries are ordered from latest to earliest effective expiration; an\n * ownerless query has the lowest priority.\n */\nexport function classifyQueriesForHydration<T extends QueryRecord>(\n queries: Iterable<T>,\n): HydrationQueryGroups<T> {\n type Optional = Exclude<T, InternalQueryRecord>;\n const required: T[] = [];\n const optionalWithExpiration: {\n query: Optional;\n expiration: number | undefined;\n }[] = [];\n\n for (const query of queries) {\n if (\n query.type === 'internal' ||\n Object.values(query.clientState).some(\n state => state.inactivatedAt === undefined,\n )\n ) {\n required.push(query);\n continue;\n }\n\n let expiration: number | undefined;\n for (const {inactivatedAt, ttl} of Object.values(query.clientState)) {\n assert(inactivatedAt !== undefined, 'inactive query has active client');\n const clientExpiration = ttlClockAsNumber(inactivatedAt) + clampTTL(ttl);\n expiration = Math.max(expiration ?? -Infinity, clientExpiration);\n }\n // Narrowed by the `type === 'internal'` branch above.\n optionalWithExpiration.push({query: query as Optional, expiration});\n }\n\n optionalWithExpiration.sort((a, b) => {\n if (a.expiration === undefined) {\n return b.expiration === undefined ? 0 : 1;\n }\n if (b.expiration === undefined) {\n return -1;\n }\n return b.expiration - a.expiration;\n });\n\n return {\n required,\n optional: optionalWithExpiration.map(({query}) => query),\n };\n}\n\nexport function nextEvictionTime(cvr: CVR): TTLClock | undefined {\n let next: number | undefined;\n for (const {inactivatedAt, ttl} of getInactiveQueries(cvr)) {\n const expire = ttlClockAsNumber(inactivatedAt) + ttl;\n if (next === undefined || expire < next) {\n next = expire;\n }\n }\n return next as TTLClock | undefined;\n}\n\nfunction newQueryRecord(\n id: string,\n ast: AST | undefined,\n name: string | undefined,\n args: readonly ReadonlyJSONValue[] | undefined,\n): ClientQueryRecord | CustomQueryRecord {\n if (ast !== undefined) {\n assert(\n name === undefined && args === undefined,\n 'Cannot provide name or args with ast',\n );\n return {\n id,\n type: 'client',\n ast,\n clientState: {},\n } satisfies ClientQueryRecord;\n }\n\n assert(\n name !== undefined && args !== undefined,\n 'Must provide name and args',\n );\n return {\n id,\n type: 'custom',\n name,\n args,\n clientState: {},\n } satisfies CustomQueryRecord;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cvr.js","names":["#shard","#deleteQueries","#removedOrExecutedQueryIDs","#executedQueryIDs","#receivedRows","#lastPatches","#retractedRows","#rowSetSignature","#existingRows","#trackExecuted","#trackRemoved","#lookupRowsForExecutedAndRemovedQueries","#unrefReceivedRows","#receive","#assertNewVersion","#deleteUnreferencedRow"],"sources":["../../../../../../zero-cache/src/services/view-syncer/cvr.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {startAsyncSpan, startSpan} from '../../../../otel/src/span.ts';\nimport {assert} from '../../../../shared/src/asserts.ts';\nimport {type JSONObject} from '../../../../shared/src/bigint-json.ts';\nimport {CustomKeyMap} from '../../../../shared/src/custom-key-map.ts';\nimport {toSorted} from '../../../../shared/src/iterables.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 versionString,\n type ClientQueryRecord,\n type ClientRecord,\n type CustomQueryRecord,\n type CVRVersion,\n type InternalQueryRecord,\n type QueryRecord,\n type RowID,\n type RowRecord,\n} from './schema/types.ts';\nimport {tracer} from './tracer.ts';\nimport {ttlClockAsNumber, type TTLClock} from './ttl-clock.ts';\n\nexport type RowUpdate = {\n version?: string; // Undefined for an unref.\n contents?: JSONObject; // Undefined for an unref.\n refCounts: {[hash: string]: number}; // Counts are negative when a row is unrefed.\n};\n\n/** Internally used mutable CVR type. */\nexport type CVR = {\n id: string;\n version: CVRVersion;\n lastActive: number;\n ttlClock: TTLClock;\n replicaVersion: string | null;\n clients: Record<string, ClientRecord>;\n queries: Record<string, QueryRecord>;\n clientSchema: ClientSchema | null;\n profileID: string | null;\n};\n\n/** Exported immutable CVR type. */\n// TODO: Use Immutable<CVR> when the AST is immutable.\nexport type CVRSnapshot = {\n readonly id: string;\n readonly version: CVRVersion;\n readonly lastActive: number;\n readonly ttlClock: TTLClock;\n readonly replicaVersion: string | null;\n readonly clients: Readonly<Record<string, ClientRecord>>;\n readonly queries: Readonly<Record<string, QueryRecord>>;\n readonly clientSchema: ClientSchema | null;\n readonly profileID: string | null;\n};\n\nconst CLIENT_LMID_QUERY_ID = 'lmids';\nconst CLIENT_MUTATION_RESULTS_QUERY_ID = 'mutationResults';\n\nfunction assertNotInternal(\n query: QueryRecord,\n): asserts query is ClientQueryRecord {\n if (query.type === 'internal') {\n // This should never happen for behaving clients, as query ids should be hashes.\n throw new Error(`Query ID ${query.id} is reserved for internal use`);\n }\n}\n\nexport function getMutationResultsQuery(\n upstreamSchema: string,\n clientGroupID: string,\n): InternalQueryRecord {\n return {\n id: CLIENT_MUTATION_RESULTS_QUERY_ID,\n type: 'internal',\n ast: {\n schema: '',\n table: `${upstreamSchema}.mutations`,\n where: {\n type: 'and',\n conditions: [\n {\n type: 'simple',\n left: {\n type: 'column',\n name: 'clientGroupID',\n },\n op: '=',\n right: {\n type: 'literal',\n value: clientGroupID,\n },\n },\n ],\n },\n orderBy: [\n ['clientGroupID', 'asc'],\n ['clientID', 'asc'],\n ['mutationID', 'asc'],\n ],\n },\n };\n}\n\n/**\n * The base CVRUpdater contains logic common to the {@link CVRConfigDrivenUpdater} and\n * {@link CVRQueryDrivenUpdater}. The CVRUpdater class itself is exported for updating\n * the `lastActive` time of the CVR in the absence of any changes to the CVR contents.\n * Although activity is automatically tracked when the CVR contents change, there may be\n * edge cases in which a client actively connects to a CVR that doesn't itself change.\n * Calling `new CVRUpdater(...).flush()` will explicitly update the active index and\n * prevent the CVR from being garbage collected.\n */\nexport class CVRUpdater {\n protected readonly _orig: CVRSnapshot;\n protected readonly _cvr: CVR;\n\n protected readonly _cvrStore: CVRStore;\n\n /**\n * @param cvrStore The CVRStore to use for storage\n * @param cvr The current CVR\n */\n constructor(\n cvrStore: CVRStore,\n cvr: CVRSnapshot,\n replicaVersion: string | null,\n ) {\n this._cvrStore = cvrStore;\n this._orig = cvr;\n this._cvr = {\n ...cvr,\n replicaVersion,\n };\n }\n\n protected _mutableClients(): Record<string, ClientRecord> {\n if (this._cvr.clients === this._orig.clients) {\n this._cvr.clients = {...this._orig.clients};\n }\n return this._cvr.clients;\n }\n\n protected _mutableClient(id: string): ClientRecord {\n const clients = this._mutableClients();\n let client = clients[id];\n assert(client !== undefined, () => `Client ${id} not found in CVR`);\n if (client === this._orig.clients[id]) {\n client = {\n ...client,\n desiredQueryIDs: [...client.desiredQueryIDs],\n };\n clients[id] = client;\n }\n return client;\n }\n\n protected _mutableQueries(): Record<string, QueryRecord> {\n if (this._cvr.queries === this._orig.queries) {\n this._cvr.queries = {...this._orig.queries};\n }\n return this._cvr.queries;\n }\n\n protected _mutableQuery(id: string): QueryRecord {\n const queries = this._mutableQueries();\n let query = queries[id];\n assert(query !== undefined, () => `Query ${id} not found in CVR`);\n if (query === this._orig.queries[id]) {\n query = {\n ...query,\n ...(query.type !== 'internal'\n ? {clientState: {...query.clientState}}\n : {}),\n };\n queries[id] = query;\n }\n return query;\n }\n\n protected _setVersion(version: CVRVersion) {\n assert(\n cmpVersions(this._cvr.version, version) < 0,\n 'Expected new version to be greater than current version',\n );\n this._cvr.version = version;\n return version;\n }\n\n /**\n * Ensures that the new CVR has a higher version than the original.\n * This method is idempotent in that it will always return the same\n * (possibly bumped) version.\n */\n protected _ensureNewVersion(): CVRVersion {\n if (cmpVersions(this._orig.version, this._cvr.version) === 0) {\n this._setVersion(oneAfter(this._cvr.version));\n }\n return this._cvr.version;\n }\n\n /**\n * @param verifyNoop Check that the original CVR is still current even if\n * there is nothing to write. See {@link CVRStore.flush}.\n */\n async flush(\n lc: LogContext,\n lastConnectTime: number,\n lastActive: number,\n ttlClock: TTLClock,\n verifyNoop = false,\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 verifyNoop,\n );\n if (!flushed) {\n return {cvr: this._orig, flushed: false};\n }\n return {cvr: this._cvr, flushed};\n }\n}\n\n/**\n * A {@link CVRConfigDrivenUpdater} is used for updating a CVR with config-driven\n * changes. Note that this may result in row deletion (e.g. if queries get dropped),\n * but the `stateVersion` of the CVR does not change.\n */\nexport class CVRConfigDrivenUpdater extends CVRUpdater {\n readonly #shard: ShardID;\n\n constructor(cvrStore: CVRStore, cvr: CVRSnapshot, shard: ShardID) {\n super(cvrStore, cvr, cvr.replicaVersion);\n this.#shard = shard;\n }\n\n ensureClient(id: string): ClientRecord {\n return startSpan(tracer, 'CVRConfigDrivenUpdater.ensureClient', () => {\n let client = this._cvr.clients[id];\n if (client) {\n return client;\n }\n // Add the ClientRecord and PutPatch\n client = {id, desiredQueryIDs: []};\n this._mutableClients()[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._mutableQueries()[CLIENT_LMID_QUERY_ID] = lmidsQuery;\n this._cvrStore.putQuery(lmidsQuery);\n }\n if (!this._cvr.queries[CLIENT_MUTATION_RESULTS_QUERY_ID]) {\n const mutationResultsQuery: InternalQueryRecord =\n getMutationResultsQuery(upstreamSchema(this.#shard), this._cvr.id);\n this._mutableQueries()[CLIENT_MUTATION_RESULTS_QUERY_ID] =\n mutationResultsQuery;\n this._cvrStore.putQuery(mutationResultsQuery);\n }\n\n return client;\n });\n }\n\n setClientSchema(lc: LogContext, clientSchema: ClientSchema) {\n startSpan(tracer, 'CVRConfigDrivenUpdater.setClientSchema', () => {\n if (this._cvr.clientSchema === null) {\n this._cvr.clientSchema = clientSchema;\n this._cvrStore.putInstance(this._cvr);\n } else if (!deepEqual(this._cvr.clientSchema, clientSchema)) {\n // This should not be possible with a correct Zero client, as clients\n // of a CVR should all have the same schema (given that the schema hash\n // is part of the idb key). In fact, clients joining an existing group\n // (i.e. non-empty baseCookie) do not send the clientSchema message.\n lc.warn?.(\n `New schema ${JSON.stringify(\n clientSchema,\n )} does not match existing schema ${JSON.stringify(\n this._cvr.clientSchema,\n )}`,\n );\n throw new ProtocolError({\n kind: 'InvalidConnectionRequest',\n message: `Provided schema does not match previous schema`,\n origin: ErrorOrigin.ZeroCache,\n });\n }\n });\n }\n\n setProfileID(lc: LogContext, profileID: string) {\n if (this._cvr.profileID !== profileID) {\n if (\n this._cvr.profileID !== null &&\n !this._cvr.profileID.startsWith('cg')\n ) {\n // We expect profile ID's to change from null or from the back-filled\n // \"cg...\" value. Log a warning otherwise to surface unexpected or\n // pathological conditions.\n lc.warn?.(\n `changing profile ID from ${this._cvr.profileID} to ${profileID}`,\n );\n }\n this._cvr.profileID = profileID;\n this._cvrStore.putInstance(this._cvr);\n }\n }\n\n putDesiredQueries(\n clientID: string,\n queries: Readonly<{\n hash: string;\n ast?: AST | undefined;\n name?: string | undefined;\n args?: readonly ReadonlyJSONValue[] | undefined;\n ttl?: number | undefined;\n }>[],\n ): PatchToVersion[] {\n return startSpan(tracer, 'CVRConfigDrivenUpdater.putDesiredQueries', () => {\n const patches: PatchToVersion[] = [];\n const client = this.ensureClient(clientID);\n const current = new Set(client.desiredQueryIDs);\n\n // Find the new/changed desired queries.\n const needed: Set<string> = new Set();\n\n const recordQueryForTelemetry = (q: (typeof queries)[0]) => {\n const {ast, name, args} = q;\n if (ast) {\n recordQuery('crud');\n } else if (name && args) {\n recordQuery('custom');\n }\n };\n\n for (const q of queries) {\n const {hash, ttl = DEFAULT_TTL_MS} = q;\n const query = this._cvr.queries[hash];\n if (!query) {\n // New query - record for telemetry\n recordQueryForTelemetry(q);\n needed.add(hash);\n continue;\n }\n if (query.type === 'internal') {\n continue;\n }\n\n const oldClientState = query.clientState[clientID];\n // Old query was inactivated or never desired by this client.\n if (!oldClientState || oldClientState.inactivatedAt !== undefined) {\n // Reactivated query - record for telemetry\n recordQueryForTelemetry(q);\n needed.add(hash);\n continue;\n }\n\n if (compareTTL(ttl, oldClientState.ttl) > 0) {\n // TTL update only - don't record for telemetry\n needed.add(hash);\n }\n }\n\n if (needed.size === 0) {\n return patches;\n }\n const newVersion = this._ensureNewVersion();\n const mutableClient = this._mutableClient(clientID);\n mutableClient.desiredQueryIDs = toSorted(\n union(current, needed),\n stringCompare,\n );\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 existingQuery = this._cvr.queries[id];\n const query = existingQuery\n ? this._mutableQuery(id)\n : 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._mutableQueries()[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 mutableClient,\n false,\n inactivatedAt,\n ttl,\n );\n }\n return patches;\n });\n }\n\n markDesiredQueriesAsInactive(\n clientID: string,\n queryHashes: string[],\n ttlClock: TTLClock,\n ): PatchToVersion[] {\n return this.#deleteQueries(clientID, queryHashes, ttlClock);\n }\n\n deleteDesiredQueries(\n clientID: string,\n queryHashes: string[],\n ): PatchToVersion[] {\n return this.#deleteQueries(clientID, queryHashes, undefined);\n }\n\n #deleteQueries(\n clientID: string,\n queryHashes: string[],\n inactivatedAt: TTLClock | undefined,\n ): PatchToVersion[] {\n return startSpan(tracer, 'CVRConfigDrivenUpdater.#deleteQueries', () => {\n const patches: PatchToVersion[] = [];\n const client = this.ensureClient(clientID);\n const current = new Set(client.desiredQueryIDs);\n const unwanted = new Set(queryHashes);\n const remove = intersection(unwanted, current);\n if (remove.size === 0) {\n return patches;\n }\n\n const newVersion = this._ensureNewVersion();\n const mutableClient = this._mutableClient(clientID);\n mutableClient.desiredQueryIDs = toSorted(\n difference(current, remove),\n stringCompare,\n );\n\n for (const id of remove) {\n if (!this._cvr.queries[id]) {\n continue; // Query itself has already been removed. Should not happen?\n }\n const query = this._mutableQuery(id);\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 mutableClient,\n true,\n inactivatedAt,\n ttl,\n );\n patches.push({\n toVersion: newVersion,\n patch: {type: 'query', op: 'del', id, clientID},\n });\n }\n return patches;\n });\n }\n\n clearDesiredQueries(clientID: string): PatchToVersion[] {\n const client = this.ensureClient(clientID);\n return this.#deleteQueries(clientID, client.desiredQueryIDs, undefined);\n }\n\n deleteClient(clientID: string, ttlClock: TTLClock): PatchToVersion[] {\n return startSpan(tracer, 'CVRConfigDrivenUpdater.deleteClient', () => {\n // clientID might not be part of this client group but if it is, this delete\n // may generate changes to the desired queries.\n\n const client = this._cvr.clients[clientID];\n if (!client) {\n // Clients in different client groups are no longer deleted, leaving\n // cleanup to inactive CVR purging logic.\n return [];\n }\n\n // When a client is deleted we mark all of its desired queries as inactive.\n // They will then be removed when the queries expire.\n const patches = this.markDesiredQueriesAsInactive(\n clientID,\n client.desiredQueryIDs,\n ttlClock,\n );\n delete this._mutableClients()[clientID];\n this._cvrStore.deleteClient(clientID);\n\n return patches;\n });\n }\n}\n\ntype Hash = string;\nexport type Column = string;\nexport type RefCounts = Record<Hash, number>;\n\ntype RowPatchInfo = {\n rowVersion: string | null; // null for a row-del\n toVersion: CVRVersion; // patchVersion\n};\n\n/**\n * The row-set signatures maintained by the pipeline driver, used by\n * {@link CVRQueryDrivenUpdater.flush} to persist the ones that changed.\n * Implemented by `PipelineDriver`.\n */\nexport interface RowSetSignatureProvider {\n /**\n * IDs of the queries whose signature changed since the last\n * {@link clearChangedRowSetSignatures}. Queries not in this set are not\n * looked at, so that a flush does not have to visit every query.\n */\n changedRowSetSignatures(): Iterable<string>;\n\n /**\n * The current signature for the query. `undefined` means \"no pipeline for\n * this query right now\" — the stored signature on disk is left alone.\n */\n rowSetSignature(queryID: string): bigint | undefined;\n\n /** Called after the changed signatures have been flushed. */\n clearChangedRowSetSignatures(): void;\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 removeTrackedQueries} for optional queries that did not execute\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 #executedQueryIDs = new Set<string>();\n readonly #receivedRows = new CustomKeyMap<RowID, RefCounts | null>(\n rowIDString,\n );\n readonly #lastPatches = new CustomKeyMap<RowID, RowPatchInfo>(rowIDString);\n /**\n * Rows that {@link retractQueries} left without references, and the patch\n * version of their deletion. The 'del' patches are held back until\n * {@link deleteUnreferencedRows}, because the retracted queries are\n * executed again and usually reference most of these rows again.\n */\n readonly #retractedRows = new CustomKeyMap<RowID, CVRVersion>(rowIDString);\n readonly #rowSetSignature: RowSetSignatureProvider | undefined;\n\n #existingRows: Promise<Iterable<RowRecord>> | undefined = undefined;\n\n /**\n * @param stateVersion The `stateVersion` at which the queries were executed.\n * @param rowSetSignature Optional source of the current row-set\n * signatures, typically the `PipelineDriver`. When provided,\n * {@link flush} persists the signatures that changed.\n */\n constructor(\n cvrStore: CVRStore,\n cvr: CVRSnapshot,\n stateVersion: LexiVersion,\n replicaVersion: string,\n rowSetSignature?: RowSetSignatureProvider,\n ) {\n super(cvrStore, cvr, replicaVersion);\n this.#rowSetSignature = rowSetSignature;\n\n assert(\n // We should either be setting the cvr.replicaVersion for the first time, or it should\n // be something newer than the current cvr.replicaVersion. Otherwise, the CVR should\n // have been rejected by the ViewSyncer.\n (cvr.replicaVersion ?? replicaVersion) <= replicaVersion,\n `Cannot sync from an older replicaVersion: CVR=${cvr.replicaVersion}, DB=${replicaVersion}`,\n );\n assert(\n stateVersion >= cvr.version.stateVersion,\n () =>\n `stateVersion (${stateVersion}) must be >= cvr.version.stateVersion (${cvr.version.stateVersion})`,\n );\n if (stateVersion > cvr.version.stateVersion) {\n this._setVersion({stateVersion});\n }\n }\n\n /**\n * Initiates the tracking of the specified `executed` and `removed` queries.\n * This kicks of a lookup of existing {@link RowRecord}s currently associated\n * with those queries, which will be used to reconcile the rows to keep\n * after all rows have been {@link received()}.\n *\n * \"transformed\" queries are queries that are currently\n * gotten and running in the pipeline driver but\n * received a new transformation hash due to an auth token\n * update.\n *\n * @returns The new CVRVersion to be used when all changes are committed.\n */\n trackQueries(\n lc: LogContext,\n executed: {id: string; transformationHash: string}[],\n removed: {id: string}[],\n ): {newVersion: CVRVersion; queryPatches: PatchToVersion[]} {\n return startSpan(tracer, 'CVRQueryDrivenUpdater.trackQueries', () => {\n assert(this.#existingRows === undefined, `trackQueries already called`);\n\n const queryPatches: Patch[] = [\n executed.map(q => this.#trackExecuted(q.id, q.transformationHash)),\n removed.map(q => this.#trackRemoved(q.id)),\n ].flat(2);\n\n this.#existingRows = this.#lookupRowsForExecutedAndRemovedQueries(lc);\n // Immediately attach a rejection handler to avoid unhandled rejections.\n // The error will surface when this.#existingRows is awaited.\n this.#existingRows.catch(() => {});\n\n const versionBumped =\n cmpVersions(this._orig.version, this._cvr.version) < 0;\n lc.info?.(\n `trackQueries: ${executed.length} executed, ${removed.length} removed, ` +\n `version ${versionBumped ? 'bumped' : 'unchanged'}`,\n );\n\n return {\n newVersion: this._cvr.version,\n queryPatches: queryPatches.map(patch => ({\n patch,\n toVersion: this._cvr.version,\n })),\n };\n });\n }\n\n #lookupRowsForExecutedAndRemovedQueries(\n lc: LogContext,\n ): Promise<Iterable<RowRecord>> {\n return startAsyncSpan(\n tracer,\n 'CVRQueryDrivenUpdater.#lookupRowsForExecutedAndRemovedQueries',\n async () => {\n const results = new CustomKeyMap<RowID, RowRecord>(rowIDString);\n\n if (this.#removedOrExecutedQueryIDs.size === 0) {\n // Query-less update. This can happen for config only changes.\n return [];\n }\n\n // Utilizes the in-memory RowCache.\n const allRowRecords = (await this._cvrStore.getRowRecords()).values();\n let total = 0;\n for (const existing of allRowRecords) {\n total++;\n assert(\n existing.refCounts !== null,\n 'allRowRecords should not include null refCounts',\n );\n for (const id of Object.keys(existing.refCounts)) {\n if (this.#removedOrExecutedQueryIDs.has(id)) {\n results.set(existing.id, existing);\n break;\n }\n }\n }\n\n lc.debug?.(\n `found ${\n results.size\n } (of ${total}) rows for executed / removed queries ${[\n ...this.#removedOrExecutedQueryIDs,\n ]}`,\n );\n return results.values();\n },\n );\n }\n\n /**\n * Tracks an executed query, ensures that it is marked as \"gotten\",\n * updating the CVR and creating put patches if necessary.\n *\n * This must be called for all executed queries.\n */\n #trackExecuted(queryID: string, transformationHash: string): Patch[] {\n return startSpan(tracer, 'CVRQueryDrivenUpdater.#trackExecuted', () => {\n assert(\n !this.#removedOrExecutedQueryIDs.has(queryID),\n () => `Query ${queryID} already tracked as executed or removed`,\n );\n this.#removedOrExecutedQueryIDs.add(queryID);\n this.#executedQueryIDs.add(queryID);\n\n let gotQueryPatch: Patch | undefined;\n const query = this._cvr.queries[queryID];\n if (query.transformationHash !== transformationHash) {\n const mutableQuery = this._mutableQuery(queryID);\n const transformationVersion = this._ensureNewVersion();\n\n if (\n mutableQuery.type !== 'internal' &&\n mutableQuery.patchVersion === undefined\n ) {\n // client query: desired -> gotten\n mutableQuery.patchVersion = transformationVersion;\n gotQueryPatch = {\n type: 'query',\n op: 'put',\n id: mutableQuery.id,\n };\n }\n\n mutableQuery.transformationHash = transformationHash;\n mutableQuery.transformationVersion = transformationVersion;\n this._cvrStore.updateQuery(mutableQuery);\n }\n return gotQueryPatch ? [gotQueryPatch] : [];\n });\n }\n\n /**\n * Converts queries that were tracked as executed into normal query\n * removals. This is used when a hydration budget expires after\n * `trackQueries()` has established the final poke version but before an\n * optional query starts hydration.\n *\n * The query ID remains in `#removedOrExecutedQueryIDs`, so its row\n * references are dropped by {@link deleteUnreferencedRows} exactly as they\n * would be for a query removed up front. Never un-track a query here: rows\n * already flushed by {@link received} have had its refCounts stripped and\n * cannot be restored.\n */\n removeTrackedQueries(queryIDs: Iterable<string>): PatchToVersion[] {\n assert(this.#existingRows !== undefined, 'trackQueries() was not called');\n assert(\n cmpVersions(this._orig.version, this._cvr.version) < 0,\n 'A final CVR version must be set before removing tracked queries',\n );\n\n const patches: PatchToVersion[] = [];\n for (const queryID of queryIDs) {\n assert(\n this.#executedQueryIDs.has(queryID),\n () => `Query ${queryID} was not tracked as executed`,\n );\n const query = must(this._cvr.queries[queryID]);\n assertNotInternal(query);\n assert(\n Object.values(query.clientState).every(\n ({inactivatedAt}) => inactivatedAt !== undefined,\n ),\n () => `Query ${queryID} is active`,\n );\n assert(\n query.patchVersion !== undefined,\n `Query ${queryID} is not gotten`,\n );\n\n delete this._mutableQueries()[queryID];\n const patch = {type: 'query', op: 'del', id: queryID} as const;\n this._cvrStore.markQueryAsDeleted(this._cvr.version, patch);\n patches.push({patch, toVersion: this._cvr.version});\n }\n return patches;\n }\n\n /**\n * Aborts queries that were tracked as executed but whose hydration was cut\n * short (see the view-syncer's hydration circuit breaker).\n *\n * Rows received for an aborted query in this update have that query's\n * references stripped: a row left with no references is deleted, and the\n * 'del' patch cancels the 'put' the client has already received, while a\n * row still referenced by other queries keeps its patch version. The query\n * is then removed from the CVR. It stays tracked as executed, so\n * {@link deleteUnreferencedRows} also drops the references held by rows that\n * were synced for it before this update, exactly as for a removed query.\n *\n * Like {@link removeTrackedQueries}, this must be called after\n * {@link trackQueries} has established the final version, and after all\n * rows of the aborted queries have been {@link received}.\n */\n async abortExecutedQueries(\n lc: LogContext,\n queryIDs: readonly string[],\n ): Promise<PatchToVersion[]> {\n assert(this.#existingRows !== undefined, 'trackQueries() was not called');\n assert(\n cmpVersions(this._orig.version, this._cvr.version) < 0,\n 'A final CVR version must be set before aborting executed queries',\n );\n const aborted = new Set(queryIDs);\n for (const queryID of aborted) {\n assert(\n this.#executedQueryIDs.has(queryID),\n `Query ${queryID} was not tracked as executed`,\n );\n const query = must(this._cvr.queries[queryID]);\n assertNotInternal(query);\n }\n\n const patches = await this.#unrefReceivedRows(lc, aborted);\n\n for (const queryID of aborted) {\n delete this._mutableQueries()[queryID];\n const patch = {type: 'query', op: 'del', id: queryID} as const;\n this._cvrStore.markQueryAsDeleted(this._cvr.version, patch);\n patches.push({patch, toVersion: this._cvr.version});\n }\n return patches;\n }\n\n /**\n * Strips the references of `queryIDs` from every row received so far in\n * this update. A row left with no references is deleted, and the 'del'\n * patch cancels the 'put' the client has already received in this poke. The\n * version of each unref is the version the row currently has in this\n * update, so a row that stays referenced does not get a spurious patch\n * version bump.\n */\n async #unrefReceivedRows(\n lc: LogContext,\n queryIDs: ReadonlySet<string>,\n holdDeletes = false,\n ): Promise<PatchToVersion[]> {\n const existingRows = await this._cvrStore.getRowRecords();\n const unrefs = new CustomKeyMap<RowID, RowUpdate>(rowIDString);\n for (const [id, refCounts] of this.#receivedRows) {\n if (refCounts === null) {\n continue;\n }\n let unref: RowUpdate | undefined;\n for (const queryID of queryIDs) {\n const count = refCounts[queryID];\n if (!count) {\n continue;\n }\n if (!unref) {\n const version =\n this.#lastPatches.get(id)?.rowVersion ??\n existingRows.get(id)?.rowVersion;\n unref = {refCounts: {}, ...(version && {version})};\n unrefs.set(id, unref);\n }\n unref.refCounts[queryID] = -count;\n }\n }\n return unrefs.size > 0 ? this.#receive(lc, unrefs, holdDeletes) : [];\n }\n\n /**\n * Retracts what queries have contributed to this update, so that they can\n * be executed again from scratch in the same update. This is for a query\n * whose changes were being received incrementally (see\n * `PipelineDriver.advance()`) until its pipeline was abandoned in favor of\n * hydrating it again.\n *\n * Rows received so far in this update have the queries' references\n * stripped, as in {@link abortExecutedQueries}, except that a row left\n * with no references is only deleted by {@link deleteUnreferencedRows}, if\n * it is still unreferenced by then. Until then the clients keep the row\n * and the patches they received for it in this update, so a row that is\n * received again unchanged needs no patch at all. The queries are then\n * tracked as executed, without changing them in the CVR: the references\n * they held before this update are replaced, rather than adjusted, by the\n * rows subsequently {@link received} for them, and\n * {@link deleteUnreferencedRows} drops the references of rows that are not\n * received again.\n *\n * Unlike {@link trackQueries}, this can be called on an updater that has\n * not tracked any queries, and after rows have been received. All rows\n * that the queries' pipelines have output must have been {@link received}\n * first, and {@link deleteUnreferencedRows} must be called once the\n * queries' rows have been received again.\n */\n async retractQueries(\n lc: LogContext,\n queryIDs: readonly string[],\n ): Promise<PatchToVersion[]> {\n assert(\n cmpVersions(this._orig.version, this._cvr.version) < 0,\n 'A final CVR version must be set before retracting queries',\n );\n const retracted = new Set(queryIDs);\n for (const queryID of retracted) {\n assert(\n this._cvr.queries[queryID] !== undefined,\n () => `Query ${queryID} is not in the CVR`,\n );\n assert(\n this.#executedQueryIDs.has(queryID) ||\n !this.#removedOrExecutedQueryIDs.has(queryID),\n () => `Query ${queryID} was tracked as removed`,\n );\n }\n\n const patches = await this.#unrefReceivedRows(lc, retracted, true);\n\n let tracked = false;\n for (const queryID of retracted) {\n if (!this.#executedQueryIDs.has(queryID)) {\n this.#removedOrExecutedQueryIDs.add(queryID);\n this.#executedQueryIDs.add(queryID);\n tracked = true;\n }\n }\n if (tracked) {\n // The rows that reference the newly tracked queries have to be\n // reconciled too. The lookup reads the row records as they were before\n // this update, so it can be redone at any point in it.\n this.#existingRows = this.#lookupRowsForExecutedAndRemovedQueries(lc);\n void this.#existingRows.then(() => {});\n }\n return patches;\n }\n\n /**\n * Tracks a query removed from the \"gotten\" set. In addition to producing the\n * appropriate patches for deleting the query, the removed query is taken into\n * account when computing the final row records in\n * {@link deleteUnreferencedRows}.\n * Namely, any rows with columns that are no longer referenced by a\n * query are deleted.\n *\n * This must only be called on queries that are not \"desired\" by any client.\n */\n #trackRemoved(queryID: string): Patch[] {\n return startSpan(tracer, 'CVRQueryDrivenUpdater.#trackRemoved', () => {\n const query = this._cvr.queries[queryID];\n assertNotInternal(query);\n\n assert(\n !this.#removedOrExecutedQueryIDs.has(queryID),\n () => `Query ${queryID} already tracked as executed or removed`,\n );\n this.#removedOrExecutedQueryIDs.add(queryID);\n delete this._mutableQueries()[queryID];\n\n const newVersion = this._ensureNewVersion();\n const queryPatch = {type: 'query', op: 'del', id: queryID} as const;\n this._cvrStore.markQueryAsDeleted(newVersion, queryPatch);\n return [queryPatch];\n });\n }\n\n /**\n * Asserts that a new version has already been set.\n *\n * After {@link #executed} and {@link #removed} are called, we must have properly\n * decided on the final CVR version because the poke-start message declares the\n * final cookie (i.e. version), and that must be sent before any poke parts\n * generated from {@link received} are sent.\n */\n #assertNewVersion(\n rowID: RowID,\n existingVersion: string | undefined,\n newVersion: string | undefined,\n refCounts: RefCounts,\n ): CVRVersion {\n assert(\n cmpVersions(this._orig.version, this._cvr.version) < 0,\n () =>\n `Expected CVR version to have been bumped above original` +\n ` (orig=${versionString(this._orig.version)},` +\n ` curr=${versionString(this._cvr.version)}).` +\n ` Row ${JSON.stringify(rowID)}:` +\n ` existing=${existingVersion},` +\n ` new=${newVersion},` +\n ` queries=[${Object.keys(refCounts).join(',')}]`,\n );\n return this._cvr.version;\n }\n\n updatedVersion(): CVRVersion {\n return this._cvr.version;\n }\n\n /**\n * Public alias for {@link _ensureNewVersion}. Forces a `configVersion` bump\n * when the caller needs a new CVR version for reasons outside the built-in\n * tracking (e.g. rowSetSignature drift re-execution).\n */\n ensureNewVersion(): CVRVersion {\n return this._ensureNewVersion();\n }\n\n override async flush(\n lc: LogContext,\n lastConnectTime: number,\n lastActive: number,\n ttlClock: TTLClock,\n verifyNoop = false,\n ): Promise<{cvr: CVRSnapshot; flushed: CVRFlushStats | false}> {\n const signatures = this.#rowSetSignature;\n if (signatures) {\n // Persist the row-set signature of each query whose pipeline-driver\n // signature changed and now differs from what's on disk. Only the\n // changed queries are visited: scanning (and parsing the stored\n // signature of) every query on every flush is a significant source of\n // garbage for client groups with many queries. Queries without an\n // active pipeline (provider returns undefined) keep their stored value.\n //\n // The signatures live in the CVRStore rather than in the QueryRecords,\n // so that persisting one does not copy the CVR's queries.\n for (const queryID of signatures.changedRowSetSignatures()) {\n if (this._cvr.queries[queryID] === undefined) {\n continue;\n }\n const sig = signatures.rowSetSignature(queryID);\n if (sig === undefined) {\n continue;\n }\n const stored = this._cvrStore.rowSetSignature(queryID) ?? 0n;\n if (stored === sig) {\n continue;\n }\n this._cvrStore.updateRowSetSignature(queryID, sig);\n }\n }\n const result = await super.flush(\n lc,\n lastConnectTime,\n lastActive,\n ttlClock,\n verifyNoop,\n );\n // Only forget the changes once they are persisted. If the flush throws\n // they are looked at again by the next flush.\n signatures?.clearChangedRowSetSignatures();\n return result;\n }\n\n /**\n * Tracks rows received from executing queries. This will update row records\n * and row patches if the received rows have a new version. The method also\n * returns (put) patches to be returned to update their state, versioned by\n * patchVersion so that only the patches new to the clients are sent.\n */\n received(\n lc: LogContext,\n rows: Map<RowID, RowUpdate>,\n ): Promise<PatchToVersion[]> {\n return this.#receive(lc, rows, false);\n }\n\n /**\n * @param holdDeletes Whether to hold back the 'del' patches of rows that\n * are left without references until {@link deleteUnreferencedRows}.\n */\n #receive(\n _lc: LogContext,\n rows: Map<RowID, RowUpdate>,\n holdDeletes: boolean,\n ): Promise<PatchToVersion[]> {\n return startAsyncSpan(\n tracer,\n 'CVRQueryDrivenUpdater.received',\n async () => {\n const patches: PatchToVersion[] = [];\n const existingRows = await this._cvrStore.getRowRecords();\n\n for (const [id, update] of rows.entries()) {\n const {contents, version, refCounts} = update;\n\n let existing = existingRows.get(id);\n // Accumulate all received refCounts to determine which rows to prune.\n const previouslyReceived = this.#receivedRows.get(id);\n\n const merged =\n previouslyReceived !== undefined\n ? mergeRefCounts(previouslyReceived, refCounts)\n : mergeRefCounts(\n existing?.refCounts,\n refCounts,\n this.#removedOrExecutedQueryIDs,\n );\n\n this.#receivedRows.set(id, merged);\n // A held deletion stands until the row is referenced again.\n if (merged !== null) {\n this.#retractedRows.delete(id);\n }\n\n const newRowVersion = merged === null ? undefined : version;\n const patchVersion =\n existing && existing.rowVersion === newRowVersion\n ? existing.patchVersion // existing row is unchanged\n : this.#assertNewVersion(\n id,\n existing?.rowVersion,\n newRowVersion,\n refCounts,\n );\n\n // Note: for determining what to commit to the CVR store, use the\n // `version` of the update even if `merged` is null (i.e. don't\n // use `newRowVersion`). This will be deduped by the cvr-store flush\n // if it is redundant. In rare cases--namely, if the row key has\n // changed--we _do_ want to add row-put for the new row key with\n // `refCounts: null` in order to correctly record a delete patch\n // for that row, as the row with the old key will be removed.\n const rowVersion = version ?? existing?.rowVersion;\n if (rowVersion) {\n this._cvrStore.putRowRecord({\n id,\n rowVersion,\n patchVersion,\n refCounts: merged,\n });\n } else {\n // This means that a row that was not in the CVR was added during\n // this update, and then subsequently removed. Since there's no\n // corresponding row in the CVR itself, cancel the previous put.\n // Note that we still send a 'del' patch to the client in order to\n // cancel the previous 'put' patch.\n this._cvrStore.delRowRecord(id);\n }\n\n // Dedupe against the lastPatch sent for the row, and ensure that\n // toVersion never backtracks (lest it be undesirably filtered).\n const lastPatch = this.#lastPatches.get(id);\n const toVersion = maxVersion(patchVersion, lastPatch?.toVersion);\n\n if (merged === null) {\n // All refCounts have gone to zero, if row was previously synced\n // delete it.\n if (existing || previouslyReceived) {\n if (holdDeletes) {\n this.#retractedRows.set(id, toVersion);\n } else if (lastPatch?.rowVersion !== null) {\n // dedupe\n patches.push({\n patch: {\n type: 'row',\n op: 'del',\n id,\n },\n toVersion,\n });\n this.#lastPatches.set(id, {rowVersion: null, toVersion});\n }\n }\n } else if (contents) {\n assert(\n rowVersion,\n 'rowVersion is required when contents is present',\n );\n // dedupe\n if (!lastPatch?.rowVersion || lastPatch.rowVersion < rowVersion) {\n patches.push({\n patch: {\n type: 'row',\n op: 'put',\n id,\n contents,\n },\n toVersion,\n });\n this.#lastPatches.set(id, {rowVersion, toVersion});\n }\n }\n }\n return patches;\n },\n );\n }\n\n /**\n * Computes and updates the row records based on:\n * * The {@link #executed} queries\n * * The {@link #removed} queries\n * * The {@link received} rows\n *\n * Returns the final delete and patch ops that must be sent to the client\n * to delete rows that are no longer referenced by any query.\n *\n * This is Step [5] of the\n * [CVR Sync Algorithm](https://www.notion.so/replicache/Sync-and-Client-View-Records-CVR-a18e02ec3ec543449ea22070855ff33d?pvs=4#7874f9b80a514be2b8cd5cf538b88d37).\n */\n deleteUnreferencedRows(lc?: LogContext): Promise<PatchToVersion[]> {\n return startAsyncSpan(\n tracer,\n 'CVRQueryDrivenUpdater.deleteUnreferencedRows',\n async () => {\n if (this.#removedOrExecutedQueryIDs.size === 0) {\n // Query-less update. This can happen for config-only changes.\n assert(\n this.#receivedRows.size === 0,\n () =>\n `Expected no received rows for query-less update, got ${this.#receivedRows.size}`,\n );\n return [];\n }\n\n // patches to send to the client.\n const patches: PatchToVersion[] = [];\n\n const start = Date.now();\n assert(this.#existingRows, `trackQueries() was not called`);\n // Rows that retracted queries referenced and that were not received\n // again. Their row records were written when they were retracted.\n for (const [id, deletedVersion] of this.#retractedRows) {\n const lastPatch = this.#lastPatches.get(id);\n if (lastPatch?.rowVersion === null) {\n continue;\n }\n const toVersion = maxVersion(deletedVersion, lastPatch?.toVersion);\n patches.push({toVersion, patch: {type: 'row', op: 'del', id}});\n this.#lastPatches.set(id, {rowVersion: null, toVersion});\n }\n this.#retractedRows.clear();\n\n for (const existing of await this.#existingRows) {\n const deletedID = this.#deleteUnreferencedRow(existing);\n if (deletedID === null) {\n continue;\n }\n const lastPatch = this.#lastPatches.get(deletedID);\n if (lastPatch !== undefined && lastPatch.rowVersion === null) {\n continue;\n }\n const toVersion = maxVersion(this._cvr.version, lastPatch?.toVersion);\n patches.push({\n toVersion,\n patch: {type: 'row', op: 'del', id: deletedID},\n });\n this.#lastPatches.set(deletedID, {rowVersion: null, toVersion});\n }\n lc?.debug?.(\n `computed ${patches.length} delete patches (${Date.now() - start} ms)`,\n );\n\n return patches;\n },\n );\n }\n\n #deleteUnreferencedRow(existing: RowRecord): RowID | null {\n return startSpan(\n tracer,\n 'CVRQueryDrivenUpdater.#deleteUnreferencedRow',\n () => {\n // received() has already written the final record (and patch) of a\n // received row, including a row whose refCounts all went to zero.\n // Rebuilding such a tombstone from the pre-update refCounts would\n // resurrect a reference that an untracked query's delta removed.\n if (this.#receivedRows.has(existing.id)) {\n return null;\n }\n\n const newRefCounts = mergeRefCounts(\n existing.refCounts,\n undefined,\n this.#removedOrExecutedQueryIDs,\n );\n\n // If a row is still referenced, we update the refCounts but not the\n // patchVersion (as the existence and contents of the row have not\n // changed from the clients' perspective). If the row is deleted, it\n // gets a new patchVersion (and corresponding poke).\n const patchVersion = newRefCounts\n ? existing.patchVersion\n : this.#assertNewVersion(\n existing.id,\n existing.rowVersion,\n undefined,\n existing.refCounts ?? {},\n );\n const rowRecord: RowRecord = {\n ...existing,\n patchVersion,\n refCounts: newRefCounts,\n };\n\n this._cvrStore.putRowRecord(rowRecord);\n\n // Return the id to delete if no longer referenced.\n return newRefCounts ? null : existing.id;\n },\n );\n }\n}\n\nfunction mergeRefCounts(\n existing: RefCounts | null | undefined,\n received: RefCounts | null | undefined,\n removeHashes?: Set<string>,\n): RefCounts | null {\n let merged: RefCounts = {};\n if (!existing) {\n merged = received ?? {};\n } else {\n [existing, received].forEach((refCounts, i) => {\n if (!refCounts) {\n return;\n }\n for (const [hash, count] of Object.entries(refCounts)) {\n if (i === 0 /* existing */ && removeHashes?.has(hash)) {\n continue; // removeHashes from existing row.\n }\n merged[hash] = (merged[hash] ?? 0) + count;\n if (merged[hash] === 0) {\n delete merged[hash];\n }\n }\n\n return merged;\n });\n }\n\n return Object.values(merged).some(v => v > 0) ? merged : null;\n}\n\n/**\n * The query must be inactive for all clients to be considered inactive.\n * This is because expiration is defined that way: a query is expired for a client group\n * only if it is expired for all clients in the group.\n *\n * If all clients have inactivated the query, we return\n * the one with the expiration furthest in the future.\n */\nexport function getInactiveQueries(cvr: CVR): {\n hash: string;\n inactivatedAt: TTLClock;\n ttl: number;\n}[] {\n // We no longer support a TTL larger than 10 minutes.\n const inactive: Map<\n string,\n {\n hash: string;\n inactivatedAt: TTLClock;\n ttl: number;\n }\n > = new Map();\n for (const [queryID, query] of Object.entries(cvr.queries)) {\n if (query.type === 'internal') {\n continue;\n }\n for (const clientState of Object.values(query.clientState)) {\n // 1. Take the longest TTL\n // 2. If the query is not inactivated (for any client), do not return it\n const {inactivatedAt, ttl} = clientState;\n const existing = inactive.get(queryID);\n if (inactivatedAt === undefined) {\n if (existing) {\n inactive.delete(queryID);\n }\n break;\n }\n\n const clampedTTL = clampTTL(ttl);\n if (existing) {\n // The stored one might be too large because from a previous version of\n // zero\n const existingTTL = clampTTL(existing.ttl);\n // Use the last eviction time.\n if (\n existingTTL + ttlClockAsNumber(existing.inactivatedAt) <\n ttlClockAsNumber(inactivatedAt) + clampedTTL\n ) {\n existing.ttl = clampedTTL;\n existing.inactivatedAt = inactivatedAt;\n }\n } else {\n inactive.set(queryID, {\n hash: queryID,\n inactivatedAt,\n ttl: clampedTTL,\n });\n }\n }\n }\n\n // First sort all the queries that have TTL. Oldest first.\n return toSorted(inactive.values(), (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 type HydrationQueryGroups<T extends QueryRecord = QueryRecord> = {\n required: T[];\n /**\n * Never contains an internal query: those are always required, so callers\n * can rely on `patchVersion` being present on the type.\n */\n optional: Exclude<T, InternalQueryRecord>[];\n};\n\n/**\n * Splits hydration candidates into required and optional queries. Optional\n * queries are ordered from latest to earliest effective expiration; an\n * ownerless query has the lowest priority.\n */\nexport function classifyQueriesForHydration<T extends QueryRecord>(\n queries: Iterable<T>,\n): HydrationQueryGroups<T> {\n type Optional = Exclude<T, InternalQueryRecord>;\n const required: T[] = [];\n const optionalWithExpiration: {\n query: Optional;\n expiration: number | undefined;\n }[] = [];\n\n for (const query of queries) {\n if (\n query.type === 'internal' ||\n Object.values(query.clientState).some(\n state => state.inactivatedAt === undefined,\n )\n ) {\n required.push(query);\n continue;\n }\n\n let expiration: number | undefined;\n for (const {inactivatedAt, ttl} of Object.values(query.clientState)) {\n assert(inactivatedAt !== undefined, 'inactive query has active client');\n const clientExpiration = ttlClockAsNumber(inactivatedAt) + clampTTL(ttl);\n expiration = Math.max(expiration ?? -Infinity, clientExpiration);\n }\n // Narrowed by the `type === 'internal'` branch above.\n optionalWithExpiration.push({query: query as Optional, expiration});\n }\n\n optionalWithExpiration.sort((a, b) => {\n if (a.expiration === undefined) {\n return b.expiration === undefined ? 0 : 1;\n }\n if (b.expiration === undefined) {\n return -1;\n }\n return b.expiration - a.expiration;\n });\n\n return {\n required,\n optional: optionalWithExpiration.map(({query}) => query),\n };\n}\n\nexport function nextEvictionTime(cvr: CVR): TTLClock | undefined {\n let next: number | undefined;\n for (const {inactivatedAt, ttl} of getInactiveQueries(cvr)) {\n const expire = ttlClockAsNumber(inactivatedAt) + ttl;\n if (next === undefined || expire < next) {\n next = expire;\n }\n }\n return next as TTLClock | undefined;\n}\n\nfunction newQueryRecord(\n id: string,\n ast: AST | undefined,\n name: string | undefined,\n args: readonly ReadonlyJSONValue[] | undefined,\n): ClientQueryRecord | CustomQueryRecord {\n if (ast !== undefined) {\n assert(\n name === undefined && args === undefined,\n 'Cannot provide name or args with ast',\n );\n return {\n id,\n type: 'client',\n ast,\n clientState: {},\n } satisfies ClientQueryRecord;\n }\n\n assert(\n name !== undefined && args !== undefined,\n 'Must provide name and args',\n );\n return {\n id,\n type: 'custom',\n name,\n args,\n clientState: {},\n } satisfies CustomQueryRecord;\n}\n"],"mappings":"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"}
|
|
@@ -13,6 +13,7 @@ import type { InspectorDelegate } from '../../server/inspector-delegate.ts';
|
|
|
13
13
|
import { type RowKey } from '../../types/row-key.ts';
|
|
14
14
|
import { type ShardID } from '../../types/shards.ts';
|
|
15
15
|
import type { DeferredWritesBudget } from './deferred-writes-budget.ts';
|
|
16
|
+
import { type QueryPushProgress, type ShouldCutOffPush } from './push-cutoff.ts';
|
|
16
17
|
import { type QueryShape } from './query-shape.ts';
|
|
17
18
|
import type { QueryStats } from './query-stats.ts';
|
|
18
19
|
import type { Snapshotter } from './snapshotter.ts';
|
|
@@ -49,7 +50,7 @@ export type HydrationStats = {
|
|
|
49
50
|
readonly shape: QueryShape;
|
|
50
51
|
};
|
|
51
52
|
export type PipelineHydrationReason = 'query-set-sync' | 'unchanged-query-rehydrate';
|
|
52
|
-
type PipelineStopReason = 'replace-query' | 'remove-query' | 'reset' | 'destroy';
|
|
53
|
+
type PipelineStopReason = 'replace-query' | 'remove-query' | 'abandon-advance' | 'reset' | 'destroy';
|
|
53
54
|
export type AdvancementProgress = {
|
|
54
55
|
/** Raw ChangeLog entries read, including skipped entries and the current change. */
|
|
55
56
|
readonly changesScanned: number;
|
|
@@ -58,6 +59,55 @@ export type AdvancementProgress = {
|
|
|
58
59
|
/** Raw entries omitted by the snapshot diff. */
|
|
59
60
|
readonly changesSkipped: number;
|
|
60
61
|
};
|
|
62
|
+
/**
|
|
63
|
+
* A query whose pipeline was removed by a {@link CostBasedAdvance}, either
|
|
64
|
+
* before the advancement's changes were pushed or partway through them
|
|
65
|
+
* (`'cutoff'`). Before, it is because pushing them to the query was
|
|
66
|
+
* estimated to cost more than hydrating it again (`'estimate'`), or because
|
|
67
|
+
* reading them was, for the queries that were left to push them to
|
|
68
|
+
* (`'read'`). The row changes it output before then have been returned by
|
|
69
|
+
* the advancement. To bring the query to the advancement's
|
|
70
|
+
* version, hydrate it again with {@link PipelineDriver.addQuery}, passing its
|
|
71
|
+
* `originalAst`, once the advancement's changes have been iterated over in
|
|
72
|
+
* their entirety.
|
|
73
|
+
*/
|
|
74
|
+
export type AbandonedQuery = QueryPushProgress & {
|
|
75
|
+
readonly reason: 'estimate' | 'read' | 'cutoff';
|
|
76
|
+
/** What pushing the advancement's changes to it was estimated to cost. */
|
|
77
|
+
readonly estimatedPushMs?: number | undefined;
|
|
78
|
+
readonly transformationHash: string;
|
|
79
|
+
readonly transformedAst: AST;
|
|
80
|
+
/** The AST the query was added with. */
|
|
81
|
+
readonly originalAst: AST;
|
|
82
|
+
readonly queryName?: string | undefined;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Bounds the cost of an advancement query by query, by hydrating a query
|
|
86
|
+
* again when that is cheaper than pushing the advancement's changes through
|
|
87
|
+
* its pipeline. This is an alternative to the client group's advancement
|
|
88
|
+
* time limit, which does not apply to an advancement that is given this.
|
|
89
|
+
* Such an advancement is never aborted for taking too long.
|
|
90
|
+
*/
|
|
91
|
+
export type CostBasedAdvance = {
|
|
92
|
+
/**
|
|
93
|
+
* Between changes, the pipeline of each query that this says to stop
|
|
94
|
+
* pushing to is removed (see {@link PushCutoff}).
|
|
95
|
+
*/
|
|
96
|
+
readonly shouldCutOff: ShouldCutOffPush;
|
|
97
|
+
/**
|
|
98
|
+
* Before any change is pushed, the pipelines of the queries that are
|
|
99
|
+
* estimated to be cheaper to hydrate again than to push the advancement's
|
|
100
|
+
* changes to are removed (see {@link shouldStartOnRebuild}). This is how
|
|
101
|
+
* many times cheaper.
|
|
102
|
+
*/
|
|
103
|
+
readonly rebuildMargin: number;
|
|
104
|
+
};
|
|
105
|
+
/** How an estimate of a query's push time compared with the actual time. */
|
|
106
|
+
export type PushEstimate = {
|
|
107
|
+
readonly queryID: string;
|
|
108
|
+
readonly estimatedPushMs: number;
|
|
109
|
+
readonly pushTimeMs: number;
|
|
110
|
+
};
|
|
61
111
|
export type Timer = {
|
|
62
112
|
elapsedLap: () => number;
|
|
63
113
|
totalElapsed: () => number;
|
|
@@ -108,8 +158,12 @@ export declare class PipelineDriver {
|
|
|
108
158
|
* {@link reset()} before hydrating. ({@link advance()} detects this when
|
|
109
159
|
* the diff encounters the RESET op; this path skips the diff and so must
|
|
110
160
|
* check explicitly.)
|
|
161
|
+
*
|
|
162
|
+
* @param releasePreviousSnapshot Whether to release the snapshot that the
|
|
163
|
+
* driver was at, which is otherwise left open until the next
|
|
164
|
+
* advance (see `Snapshotter.releasePrevious()`).
|
|
111
165
|
*/
|
|
112
|
-
advanceWithoutDiff(): string;
|
|
166
|
+
advanceWithoutDiff(releasePreviousSnapshot?: boolean): string;
|
|
113
167
|
/**
|
|
114
168
|
* Clears storage used for the pipelines. Call this when the
|
|
115
169
|
* PipelineDriver will no longer be used.
|
|
@@ -117,6 +171,13 @@ export declare class PipelineDriver {
|
|
|
117
171
|
destroy(): void;
|
|
118
172
|
/** @return Map from query ID to PipelineInfo for all added queries. */
|
|
119
173
|
queries(): ReadonlyMap<string, QueryInfo>;
|
|
174
|
+
/**
|
|
175
|
+
* The queries whose pipelines a {@link CostBasedAdvance} removed and that
|
|
176
|
+
* have not been hydrated again since, by query ID. They are not in
|
|
177
|
+
* {@link queries()}. A query whose hydration is started but does not
|
|
178
|
+
* finish is still one of them.
|
|
179
|
+
*/
|
|
180
|
+
abandonedQueries(): ReadonlyMap<string, AbandonedQuery>;
|
|
120
181
|
/**
|
|
121
182
|
* Stats from the hydration of the pipeline for `queryID`, or `undefined` if
|
|
122
183
|
* the query has no pipeline.
|
|
@@ -184,16 +245,37 @@ export declare class PipelineDriver {
|
|
|
184
245
|
* measure the progress of the advancement and abort with a
|
|
185
246
|
* {@link ResetPipelinesSignal} if it is estimated to take longer
|
|
186
247
|
* than a hydration.
|
|
248
|
+
* @param costBased If given, the advancement is bounded query by query
|
|
249
|
+
* instead (see {@link CostBasedAdvance}).
|
|
187
250
|
* @return The resulting row changes for all added queries. Note that the
|
|
188
251
|
* `changes` must be iterated over in their entirety in order to
|
|
189
252
|
* advance the database snapshot.
|
|
253
|
+
* @throws A {@link ResetPipelinesSignal} if `costBased` is given and the
|
|
254
|
+
* changes include a schema change, a truncation or a change of
|
|
255
|
+
* the permissions. It is thrown before any change is pushed or
|
|
256
|
+
* any pipeline is removed, as the caller resets the pipelines
|
|
257
|
+
* either way. Without `costBased`, it is iterating over the
|
|
258
|
+
* `changes` that throws it, on getting to that change.
|
|
190
259
|
*/
|
|
191
|
-
advance(timer: Timer): {
|
|
260
|
+
advance(timer: Timer, costBased?: CostBasedAdvance): {
|
|
192
261
|
version: string;
|
|
193
262
|
numChanges: number;
|
|
194
263
|
changes: Iterable<RowChange | 'yield'>;
|
|
195
264
|
/** Available throughout iteration and after completion or an abort. */
|
|
196
265
|
progress: () => AdvancementProgress;
|
|
266
|
+
/**
|
|
267
|
+
* The queries whose pipelines the {@link CostBasedAdvance} has removed
|
|
268
|
+
* so far. They can be hydrated again once `changes` has been iterated
|
|
269
|
+
* over.
|
|
270
|
+
*/
|
|
271
|
+
abandoned: () => readonly AbandonedQuery[];
|
|
272
|
+
/**
|
|
273
|
+
* How the estimates of the {@link CostBasedAdvance} compared with the
|
|
274
|
+
* actual push times, for the queries that had an estimate and were
|
|
275
|
+
* pushed all of the changes. Available once `changes` has been iterated
|
|
276
|
+
* over.
|
|
277
|
+
*/
|
|
278
|
+
estimates: () => readonly PushEstimate[];
|
|
197
279
|
};
|
|
198
280
|
}
|
|
199
281
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline-driver.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/pipeline-driver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAQjD,OAAO,KAAK,EAAC,GAAG,EAAE,YAAY,EAAC,MAAM,sCAAsC,CAAC;AAC5E,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gDAAgD,CAAC;AACjF,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,uCAAuC,CAAC;AAQ/D,OAAO,EAAC,UAAU,EAAC,MAAM,wCAAwC,CAAC;AAGlE,OAAO,EAIL,KAAK,KAAK,EAIX,MAAM,qCAAqC,CAAC;AAY7C,OAAO,KAAK,EACV,WAAW,EAEZ,MAAM,iDAAiD,CAAC;AAMzD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,4CAA4C,CAAC;AASnF,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAC,SAAS,EAAE,UAAU,EAAC,MAAM,6BAA6B,CAAC;AAEvE,OAAO,KAAK,EAAC,cAAc,EAAgB,MAAM,mBAAmB,CAAC;AAMrE,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAC,KAAK,OAAO,EAAC,MAAM,uBAAuB,CAAC;AAMnD,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"pipeline-driver.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/pipeline-driver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAQjD,OAAO,KAAK,EAAC,GAAG,EAAE,YAAY,EAAC,MAAM,sCAAsC,CAAC;AAC5E,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gDAAgD,CAAC;AACjF,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,uCAAuC,CAAC;AAQ/D,OAAO,EAAC,UAAU,EAAC,MAAM,wCAAwC,CAAC;AAGlE,OAAO,EAIL,KAAK,KAAK,EAIX,MAAM,qCAAqC,CAAC;AAY7C,OAAO,KAAK,EACV,WAAW,EAEZ,MAAM,iDAAiD,CAAC;AAMzD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,4CAA4C,CAAC;AASnF,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAC,SAAS,EAAE,UAAU,EAAC,MAAM,6BAA6B,CAAC;AAEvE,OAAO,KAAK,EAAC,cAAc,EAAgB,MAAM,mBAAmB,CAAC;AAMrE,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAC,KAAK,OAAO,EAAC,MAAM,uBAAuB,CAAC;AAMnD,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,6BAA6B,CAAC;AAQtE,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAa,KAAK,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAC7D,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAkB,WAAW,EAAC,MAAM,kBAAkB,CAAC;AAGnE,KAAK,KAAK,CAAC,EAAE,SAAS,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,KAAK,CAAC,IAAI;IAC1D,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;IAClB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;AAE3C,MAAM,MAAM,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;AAEjD,MAAM,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AAE7C,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AA4BrD,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,cAAc,EAAE,GAAG,CAAC;IAC7B,QAAQ,CAAC,WAAW,CAAC,EAAE,GAAG,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,qCAAqC;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,qEAAqE;IACrE,QAAQ,CAAC,YAAY,EAAE,SAAS,WAAW,EAAE,CAAC;IAC9C,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC5B,CAAC;AAeF,MAAM,MAAM,uBAAuB,GAC/B,gBAAgB,GAChB,2BAA2B,CAAC;AAEhC,KAAK,kBAAkB,GACnB,eAAe,GACf,cAAc,GACd,iBAAiB,GACjB,OAAO,GACP,SAAS,CAAC;AAiCd,MAAM,MAAM,mBAAmB,GAAG;IAChC,oFAAoF;IACpF,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,8DAA8D;IAC9D,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,gDAAgD;IAChD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,cAAc,GAAG,iBAAiB,GAAG;IAC/C,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAC;IAChD,0EAA0E;IAC1E,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9C,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,cAAc,EAAE,GAAG,CAAC;IAC7B,wCAAwC;IACxC,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;;OAGG;IACH,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC,CAAC;AAEF,4EAA4E;AAC5E,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B,CAAC;AA4EF,MAAM,MAAM,KAAK,GAAG;IAClB,UAAU,EAAE,MAAM,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,MAAM,CAAC;CAC5B,CAAC;AA2IF;;GAEG;AACH,qBAAa,cAAc;;IAoGzB,YACE,EAAE,EAAE,UAAU,EACd,SAAS,EAAE,SAAS,EACpB,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,kBAAkB,EAC3B,aAAa,EAAE,MAAM,EACrB,iBAAiB,EAAE,iBAAiB,EACpC,gBAAgB,EAAE,MAAM,MAAM,EAC9B,aAAa,CAAC,EAAE,OAAO,EACvB,MAAM,CAAC,EAAE,UAAU,EACnB,cAAc,CAAC,EAAE,oBAAoB,EACrC,UAAU,CAAC,EAAE,UAAU,GAAG,SAAS,EAqBpC;IAED;;;;;OAKG;IACH,IAAI,CAAC,YAAY,EAAE,YAAY,QAI9B;IAED;;OAEG;IACH,WAAW,IAAI,OAAO,CAErB;IAED;;;;OAIG;IACH,KAAK,CAAC,YAAY,EAAE,YAAY,QAY/B;IAiCD,mFAAmF;IACnF,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED;;;;OAIG;IACH,cAAc,IAAI,MAAM,CAGvB;IAED;;OAEG;IACH,kBAAkB,IAAI,iBAAiB,GAAG,IAAI,CAiB7C;IAED;;;;;;;;;;;;;;;OAeG;IACH,kBAAkB,CAAC,uBAAuB,UAAQ,GAAG,MAAM,CAe1D;IAeD;;;OAGG;IACH,OAAO,SAUN;IAED,uEAAuE;IACvE,OAAO,IAAI,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAExC;IAED;;;;;OAKG;IACH,gBAAgB,IAAI,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,CAEtD;IAED;;;OAGG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAU1D;IA6CD,oBAAoB,IAAI,MAAM,CAM7B;IAED;;;;OAIG;IACH,IAAI,WAAW,IAAI,MAAM,CAMxB;IAED;;;OAGG;IACH,IAAI,YAAY,IAAI,MAAM,CAMzB;IAoJD;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CACN,kBAAkB,EAAE,MAAM,EAC1B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,GAAG,EACV,KAAK,EAAE,KAAK,EACZ,SAAS,CAAC,EAAE,MAAM,EAClB,eAAe,GAAE,uBAA0C,GAC1D,QAAQ,CAAC,SAAS,GAAG,OAAO,CAAC,CAW/B;IAsTD;;;OAGG;IACH,WAAW,CACT,OAAO,EAAE,MAAM,EACf,UAAU,GAAE,kBAAmC,QAGhD;IA2DD;;;;OAIG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEnD;IAED;;;;OAIG;IACH,uBAAuB,IAAI,WAAW,CAAC,MAAM,CAAC,CAE7C;IAED;;OAEG;IACH,4BAA4B,IAAI,IAAI,CAEnC;IA0BD;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS,CAIjD;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACH,OAAO,CACL,KAAK,EAAE,KAAK,EACZ,SAAS,CAAC,EAAE,gBAAgB,GAC3B;QACD,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,QAAQ,CAAC,SAAS,GAAG,OAAO,CAAC,CAAC;QACvC,uEAAuE;QACvE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;QACpC;;;;WAIG;QACH,SAAS,EAAE,MAAM,SAAS,cAAc,EAAE,CAAC;QAC3C;;;;;WAKG;QACH,SAAS,EAAE,MAAM,SAAS,YAAY,EAAE,CAAC;KAC1C,CAsEA;CAkjCF;AA2OD;;;;;GAKG;AACH,wBAAgB,OAAO,CACrB,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GACtC,QAAQ,CAAC,SAAS,GAAG,OAAO,CAAC,CAK/B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,MAAM,GACjB,YAAY,GAAG,IAAI,GAAG,SAAS,CAYjC"}
|