@rocicorp/zero 0.25.10-canary.7 → 0.25.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/zero/package.json.js +1 -1
- package/out/zero-cache/src/custom/fetch.d.ts +3 -3
- package/out/zero-cache/src/custom/fetch.d.ts.map +1 -1
- package/out/zero-cache/src/custom/fetch.js +116 -76
- package/out/zero-cache/src/custom/fetch.js.map +1 -1
- package/out/zero-cache/src/custom-queries/transform-query.d.ts.map +1 -1
- package/out/zero-cache/src/custom-queries/transform-query.js +0 -1
- package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
- package/out/zero-cache/src/server/anonymous-otel-start.d.ts.map +1 -1
- package/out/zero-cache/src/server/anonymous-otel-start.js +1 -0
- package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
- package/out/zero-cache/src/server/inspector-delegate.d.ts.map +1 -1
- package/out/zero-cache/src/server/inspector-delegate.js +2 -2
- package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
- package/out/zero-cache/src/server/priority-op.d.ts +8 -0
- package/out/zero-cache/src/server/priority-op.d.ts.map +1 -0
- package/out/zero-cache/src/server/priority-op.js +29 -0
- package/out/zero-cache/src/server/priority-op.js.map +1 -0
- package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/server/syncer.js +9 -2
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/services/analyze.js +1 -1
- package/out/zero-cache/src/services/analyze.js.map +1 -1
- package/out/zero-cache/src/services/change-source/replica-schema.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/replica-schema.js +5 -1
- package/out/zero-cache/src/services/change-source/replica-schema.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js +10 -6
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +2 -2
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.js +1 -3
- package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.js +60 -22
- package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js +2 -0
- package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +2 -2
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/row-record-cache.d.ts +1 -1
- package/out/zero-cache/src/services/view-syncer/row-record-cache.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/row-record-cache.js +22 -11
- package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +2 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +82 -52
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/error-with-level.d.ts +1 -1
- package/out/zero-cache/src/types/error-with-level.d.ts.map +1 -1
- package/out/zero-cache/src/types/error-with-level.js +1 -1
- package/out/zero-cache/src/types/error-with-level.js.map +1 -1
- package/out/zero-client/src/client/connection-manager.d.ts +3 -0
- package/out/zero-client/src/client/connection-manager.d.ts.map +1 -1
- package/out/zero-client/src/client/connection-manager.js +10 -3
- package/out/zero-client/src/client/connection-manager.js.map +1 -1
- package/out/zero-client/src/client/error.d.ts +5 -1
- package/out/zero-client/src/client/error.d.ts.map +1 -1
- package/out/zero-client/src/client/error.js +3 -3
- package/out/zero-client/src/client/error.js.map +1 -1
- package/out/zero-client/src/client/options.d.ts +1 -1
- package/out/zero-client/src/client/options.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero.d.ts +1 -1
- package/out/zero-client/src/client/zero.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.js +1 -1
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"row-record-cache.js","sources":["../../../../../../zero-cache/src/services/view-syncer/row-record-cache.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {type Resolver, resolver} from '@rocicorp/resolver';\nimport type {PendingQuery, Row} from 'postgres';\nimport {CustomKeyMap} from '../../../../shared/src/custom-key-map.ts';\nimport {must} from '../../../../shared/src/must.ts';\nimport {promiseVoid} from '../../../../shared/src/resolved-promises.ts';\nimport * as Mode from '../../db/mode-enum.ts';\nimport {TransactionPool} from '../../db/transaction-pool.ts';\nimport {\n getOrCreateCounter,\n getOrCreateHistogram,\n} from '../../observability/metrics.ts';\nimport {\n disableStatementTimeout,\n type PostgresDB,\n type PostgresTransaction,\n} from '../../types/pg.ts';\nimport {rowIDString} from '../../types/row-key.ts';\nimport {cvrSchema, type ShardID} from '../../types/shards.ts';\nimport {checkVersion, type CVRFlushStats} from './cvr-store.ts';\nimport type {CVRSnapshot} from './cvr.ts';\nimport {\n rowRecordToRowsRow,\n type RowsRow,\n rowsRowToRowRecord,\n} from './schema/cvr.ts';\nimport {\n cmpVersions,\n type CVRVersion,\n type NullableCVRVersion,\n type RowID,\n type RowRecord,\n versionString,\n versionToNullableCookie,\n} from './schema/types.ts';\n\nconst FLUSH_TYPE_ATTRIBUTE = 'flush.type';\n\n/**\n * The RowRecordCache is an in-memory cache of the `cvr.rows` tables that\n * operates as both a write-through and write-back cache.\n *\n * For \"small\" CVR updates (i.e. zero or small numbers of rows) the\n * RowRecordCache operates as write-through, executing commits in\n * {@link executeRowUpdates()} before they are {@link apply}-ed to the\n * in-memory state.\n *\n * For \"large\" CVR updates (i.e. with many rows), the cache switches to a\n * write-back mode of operation, in which {@link executeRowUpdates()} is a\n * no-op, and {@link apply()} initiates a background task to flush the pending\n * row changes to the store. This allows the client poke to be completed and\n * committed on the client without waiting for the heavyweight operation of\n * committing the row records to the CVR store.\n *\n * Note that when the cache is in write-back mode, all updates become\n * write-back (i.e. asynchronously flushed) until the pending update queue is\n * fully flushed. This is required because updates must be applied in version\n * order. As with all pending work systems in zero-cache, multiple pending\n * updates are coalesced to reduce buildup of work.\n *\n * ### High level consistency\n *\n * Note that the above caching scheme only applies to the row data in `cvr.rows`\n * and corresponding `cvr.rowsVersion` tables. CVR metadata and query\n * information, on the other hand, are always committed before completing the\n * client poke. In this manner, the difference between the `version` column in\n * `cvr.instances` and the analogous column in `cvr.rowsVersion` determines\n * whether the data in the store is consistent, or whether it is awaiting a\n * pending update.\n *\n * The logic in {@link CVRStore#load()} takes this into account by loading both\n * the `cvr.instances` version and the `cvr.rowsVersion` version and checking\n * if they are in sync, waiting for a configurable delay until they are.\n *\n * ### Eventual conversion\n *\n * In the event of a continual stream of mutations (e.g. an animation-style\n * app), it is conceivable that the row record data be continually behind\n * the CVR metadata. In order to effect eventual convergence, a new view-syncer\n * signals the current view-syncer to stop updating by writing new `owner`\n * information to the `cvr.instances` row. This effectively stops the mutation\n * processing (in {@link CVRStore.#checkVersionAndOwnership}) so that the row\n * data can eventually catch up, allowing the new view-syncer to take over.\n *\n * Of course, there is the pathological situation in which a view-syncer\n * process crashes before the pending row updates are flushed. In this case,\n * the wait timeout will elapse and the CVR considered invalid.\n */\nexport class RowRecordCache {\n // The state in the #cache is always in sync with the CVR metadata\n // (i.e. cvr.instances). It may contain information that has not yet\n // been flushed to cvr.rows.\n #cache: Promise<CustomKeyMap<RowID, RowRecord>> | undefined;\n readonly #lc: LogContext;\n readonly #db: PostgresDB;\n readonly #schema: string;\n readonly #cvrID: string;\n readonly #failService: (e: unknown) => void;\n readonly #deferredRowFlushThreshold: number;\n readonly #setTimeout: typeof setTimeout;\n\n // Write-back cache state.\n readonly #pending = new CustomKeyMap<RowID, RowRecord | null>(rowIDString);\n #pendingRowsVersion: CVRVersion | null = null;\n #flushedRowsVersion: CVRVersion | null = null;\n #flushing: Resolver<void> | null = null;\n\n readonly #cvrFlushTime = getOrCreateHistogram('sync', 'cvr.flush-time', {\n description:\n 'Time to flush a CVR transaction. This includes both synchronous ' +\n 'and asynchronous flushes, distinguished by the flush.type attribute',\n unit: 's',\n });\n readonly #cvrRowsFlushed = getOrCreateCounter(\n 'sync',\n 'cvr.rows-flushed',\n 'Number of (changed) rows flushed to a CVR',\n );\n\n constructor(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardID,\n cvrID: string,\n failService: (e: unknown) => void,\n deferredRowFlushThreshold = 100,\n setTimeoutFn = setTimeout,\n ) {\n this.#lc = lc;\n this.#db = db;\n this.#schema = cvrSchema(shard);\n this.#cvrID = cvrID;\n this.#failService = failService;\n this.#deferredRowFlushThreshold = deferredRowFlushThreshold;\n this.#setTimeout = setTimeoutFn;\n }\n\n recordSyncFlushStats(stats: CVRFlushStats, elapsedMs: number) {\n this.#cvrFlushTime.record(elapsedMs / 1000, {\n [FLUSH_TYPE_ATTRIBUTE]: 'sync',\n });\n if (stats.rowsDeferred === 0) {\n this.#cvrRowsFlushed.add(stats.rows);\n }\n }\n\n #recordAsyncFlushStats(rows: number, elapsedMs: number) {\n this.#cvrFlushTime.record(elapsedMs / 1000, {\n [FLUSH_TYPE_ATTRIBUTE]: 'async',\n });\n this.#cvrRowsFlushed.add(rows);\n }\n\n #cvr(table: string) {\n return this.#db(`${this.#schema}.${table}`);\n }\n\n async #ensureLoaded(): Promise<CustomKeyMap<RowID, RowRecord>> {\n if (this.#cache) {\n return this.#cache;\n }\n const r = resolver<CustomKeyMap<RowID, RowRecord>>();\n // Set this.#cache immediately (before await) so that only one db\n // query is made even if there are multiple callers.\n this.#cache = r.promise;\n\n const cache: CustomKeyMap<RowID, RowRecord> = new CustomKeyMap(rowIDString);\n for await (const rows of this.#db<RowsRow[]>`\n SELECT * FROM ${this.#cvr(`rows`)} \n WHERE \"clientGroupID\" = ${this.#cvrID} AND \"refCounts\" IS NOT NULL`\n // TODO(arv): Arbitrary page size\n .cursor(5000)) {\n for (const row of rows) {\n const rowRecord = rowsRowToRowRecord(row);\n cache.set(rowRecord.id, rowRecord);\n }\n }\n r.resolve(cache);\n return this.#cache;\n }\n\n getRowRecords(): Promise<ReadonlyMap<RowID, RowRecord>> {\n return this.#ensureLoaded();\n }\n\n /**\n * Applies the `rowRecords` corresponding to the `rowsVersion`\n * to the cache, indicating whether the corresponding updates\n * (generated by {@link executeRowUpdates}) were `flushed`.\n *\n * If `flushed` is false, the RowRecordCache will flush the records\n * asynchronously.\n *\n * Note that `apply()` indicates that the CVR metadata associated with\n * the `rowRecords` was successfully committed, which essentially means\n * that this process has the unconditional right (and responsibility) of\n * following up with a flush of the `rowRecords`. In particular, the\n * commit of row records are not conditioned on the version or ownership\n * columns of the `cvr.instances` row.\n */\n async apply(\n rowRecords: Map<RowID, RowRecord | null>,\n rowsVersion: CVRVersion,\n flushed: boolean,\n ): Promise<number> {\n const cache = await this.#ensureLoaded();\n for (const [id, row] of rowRecords.entries()) {\n if (row === null || row.refCounts === null) {\n cache.delete(id);\n } else {\n cache.set(id, row);\n }\n if (!flushed) {\n this.#pending.set(id, row);\n }\n }\n this.#pendingRowsVersion = rowsVersion;\n // Initiate a flush if not already flushing.\n if (!flushed && this.#flushing === null) {\n this.#flushing = resolver();\n this.#setTimeout(() => this.#flush(), 0);\n }\n return cache.size;\n }\n\n async #flush() {\n const flushing = must(this.#flushing);\n try {\n while (this.#pendingRowsVersion !== this.#flushedRowsVersion) {\n const start = performance.now();\n\n const {rows, rowsVersion} = await this.#db.begin(\n Mode.READ_COMMITTED,\n tx => {\n disableStatementTimeout(tx);\n\n // Note: This code block is synchronous, guaranteeing that the\n // #pendingRowsVersion is consistent with the #pending rows.\n const rows = this.#pending.size;\n const rowsVersion = must(this.#pendingRowsVersion);\n // Awaiting all of the individual statements incurs too much\n // overhead. Instead, just catch and log exception(s); the outer\n // transaction will properly fail.\n void Promise.all(\n this.executeRowUpdates(tx, rowsVersion, this.#pending, 'force'),\n ).catch(e => this.#lc.error?.(`error flushing cvr rows`, e));\n\n this.#pending.clear();\n return {rows, rowsVersion};\n },\n );\n const elapsed = performance.now() - start;\n this.#lc.debug?.(\n `flushed ${rows} rows@${versionString(rowsVersion)} (${elapsed} ms)`,\n );\n this.#recordAsyncFlushStats(rows, elapsed);\n this.#flushedRowsVersion = rowsVersion;\n // Note: apply() may have called while the transaction was committing,\n // which will result in looping to commit the next #pendingRowsVersion.\n }\n this.#lc.debug?.(\n `up to date rows@${versionToNullableCookie(this.#flushedRowsVersion)}`,\n );\n flushing.resolve();\n this.#flushing = null;\n } catch (e) {\n flushing.reject(e);\n this.#failService(e);\n }\n }\n\n hasPendingUpdates() {\n return this.#flushing !== null;\n }\n\n /**\n * Returns a promise that resolves when all outstanding row-records\n * have been committed.\n */\n flushed(lc: LogContext): Promise<void> {\n if (this.#flushing) {\n lc.debug?.('awaiting pending row flush');\n return this.#flushing.promise;\n }\n return promiseVoid;\n }\n\n clear() {\n // Note: Only the #cache is cleared. #pending updates, on the other hand,\n // comprise canonical (i.e. already flushed) data and must be flushed\n // even if the snapshot of the present state (the #cache) is cleared.\n this.#cache = undefined;\n }\n\n async *catchupRowPatches(\n lc: LogContext,\n afterVersion: NullableCVRVersion,\n upToCVR: CVRSnapshot,\n current: CVRVersion,\n excludeQueryHashes: string[] = [],\n ): AsyncGenerator<RowsRow[], void, undefined> {\n if (cmpVersions(afterVersion, upToCVR.version) >= 0) {\n return;\n }\n\n const startMs = Date.now();\n const start = afterVersion ? versionString(afterVersion) : '';\n const end = versionString(upToCVR.version);\n lc.debug?.(`scanning row patches for clients from ${start}`);\n\n // Before accessing the CVR db, pending row records must be flushed.\n // Note that because catchupRowPatches() is called from within the\n // view syncer lock, this flush is guaranteed to complete since no\n // new CVR updates can happen while the lock is held.\n await this.flushed(lc);\n const flushMs = Date.now() - startMs;\n\n const reader = new TransactionPool(lc, Mode.READONLY).run(this.#db);\n try {\n // Verify that we are reading the right version of the CVR.\n await reader.processReadTask(tx =>\n checkVersion(tx, this.#schema, this.#cvrID, current),\n );\n\n const {query} = await reader.processReadTask(tx => {\n const query =\n excludeQueryHashes.length === 0\n ? tx<RowsRow[]>`SELECT * FROM ${this.#cvr('rows')}\n WHERE \"clientGroupID\" = ${this.#cvrID}\n AND \"patchVersion\" > ${start}\n AND \"patchVersion\" <= ${end}`\n : // Exclude rows that were already sent as part of query hydration.\n tx<RowsRow[]>`SELECT * FROM ${this.#cvr('rows')}\n WHERE \"clientGroupID\" = ${this.#cvrID}\n AND \"patchVersion\" > ${start}\n AND \"patchVersion\" <= ${end}\n AND (\"refCounts\" IS NULL OR NOT \"refCounts\" ?| ${excludeQueryHashes})`;\n return {query};\n });\n\n yield* query.cursor(10000);\n } finally {\n reader.setDone();\n }\n\n const totalMs = Date.now() - startMs;\n lc.debug?.(\n `finished row catchup (flush: ${flushMs} ms, total: ${totalMs} ms)`,\n );\n }\n\n executeRowUpdates(\n tx: PostgresTransaction,\n version: CVRVersion,\n rowUpdates: Map<RowID, RowRecord | null>,\n mode: 'allow-defer' | 'force',\n ): PendingQuery<Row[]>[] {\n if (\n mode === 'allow-defer' &&\n // defer if pending rows are being flushed\n (this.#flushing !== null ||\n // or if the new batch is above the limit.\n rowUpdates.size > this.#deferredRowFlushThreshold)\n ) {\n return [];\n }\n const rowsVersion = {\n clientGroupID: this.#cvrID,\n version: versionString(version),\n };\n const pending: PendingQuery<Row[]>[] = [\n tx`INSERT INTO ${this.#cvr('rowsVersion')} ${tx(rowsVersion)}\n ON CONFLICT (\"clientGroupID\") \n DO UPDATE SET ${tx(rowsVersion)}`.execute(),\n ];\n\n const rowRecordRows: RowsRow[] = [];\n for (const [id, row] of rowUpdates.entries()) {\n if (row === null) {\n pending.push(\n tx`\n DELETE FROM ${this.#cvr('rows')}\n WHERE \"clientGroupID\" = ${this.#cvrID}\n AND \"schema\" = ${id.schema}\n AND \"table\" = ${id.table}\n AND \"rowKey\" = ${id.rowKey}\n `.execute(),\n );\n } else {\n rowRecordRows.push(rowRecordToRowsRow(this.#cvrID, row));\n }\n }\n if (rowRecordRows.length) {\n pending.push(\n tx`\n INSERT INTO ${this.#cvr('rows')}(\n \"clientGroupID\", \"schema\", \"table\", \"rowKey\", \"rowVersion\", \"patchVersion\", \"refCounts\"\n ) SELECT\n \"clientGroupID\", \"schema\", \"table\", \"rowKey\", \"rowVersion\", \"patchVersion\", \"refCounts\"\n FROM json_to_recordset(${rowRecordRows}) AS x(\n \"clientGroupID\" TEXT,\n \"schema\" TEXT,\n \"table\" TEXT,\n \"rowKey\" JSONB,\n \"rowVersion\" TEXT,\n \"patchVersion\" TEXT,\n \"refCounts\" JSONB\n ) ON CONFLICT (\"clientGroupID\", \"schema\", \"table\", \"rowKey\")\n DO UPDATE SET \"rowVersion\" = excluded.\"rowVersion\",\n \"patchVersion\" = excluded.\"patchVersion\",\n \"refCounts\" = excluded.\"refCounts\"\n `.execute(),\n );\n this.#lc.debug?.(\n `flushing ${rowUpdates.size} rows (${rowRecordRows.length} inserts, ${\n rowUpdates.size - rowRecordRows.length\n } deletes)`,\n );\n }\n return pending;\n }\n}\n"],"names":["Mode.READ_COMMITTED","rows","rowsVersion","Mode.READONLY","query"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"row-record-cache.js","sources":["../../../../../../zero-cache/src/services/view-syncer/row-record-cache.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {type Resolver, resolver} from '@rocicorp/resolver';\nimport type {PendingQuery, Row} from 'postgres';\nimport {CustomKeyMap} from '../../../../shared/src/custom-key-map.ts';\nimport {must} from '../../../../shared/src/must.ts';\nimport {promiseVoid} from '../../../../shared/src/resolved-promises.ts';\nimport * as Mode from '../../db/mode-enum.ts';\nimport {TransactionPool} from '../../db/transaction-pool.ts';\nimport {\n getOrCreateCounter,\n getOrCreateHistogram,\n} from '../../observability/metrics.ts';\nimport {\n disableStatementTimeout,\n type PostgresDB,\n type PostgresTransaction,\n} from '../../types/pg.ts';\nimport {rowIDString} from '../../types/row-key.ts';\nimport {cvrSchema, type ShardID} from '../../types/shards.ts';\nimport {checkVersion, type CVRFlushStats} from './cvr-store.ts';\nimport type {CVRSnapshot} from './cvr.ts';\nimport {\n rowRecordToRowsRow,\n type RowsRow,\n rowsRowToRowRecord,\n} from './schema/cvr.ts';\nimport {\n cmpVersions,\n type CVRVersion,\n type NullableCVRVersion,\n type RowID,\n type RowRecord,\n versionString,\n versionToNullableCookie,\n} from './schema/types.ts';\n\nconst FLUSH_TYPE_ATTRIBUTE = 'flush.type';\n\n/**\n * The RowRecordCache is an in-memory cache of the `cvr.rows` tables that\n * operates as both a write-through and write-back cache.\n *\n * For \"small\" CVR updates (i.e. zero or small numbers of rows) the\n * RowRecordCache operates as write-through, executing commits in\n * {@link executeRowUpdates()} before they are {@link apply}-ed to the\n * in-memory state.\n *\n * For \"large\" CVR updates (i.e. with many rows), the cache switches to a\n * write-back mode of operation, in which {@link executeRowUpdates()} is a\n * no-op, and {@link apply()} initiates a background task to flush the pending\n * row changes to the store. This allows the client poke to be completed and\n * committed on the client without waiting for the heavyweight operation of\n * committing the row records to the CVR store.\n *\n * Note that when the cache is in write-back mode, all updates become\n * write-back (i.e. asynchronously flushed) until the pending update queue is\n * fully flushed. This is required because updates must be applied in version\n * order. As with all pending work systems in zero-cache, multiple pending\n * updates are coalesced to reduce buildup of work.\n *\n * ### High level consistency\n *\n * Note that the above caching scheme only applies to the row data in `cvr.rows`\n * and corresponding `cvr.rowsVersion` tables. CVR metadata and query\n * information, on the other hand, are always committed before completing the\n * client poke. In this manner, the difference between the `version` column in\n * `cvr.instances` and the analogous column in `cvr.rowsVersion` determines\n * whether the data in the store is consistent, or whether it is awaiting a\n * pending update.\n *\n * The logic in {@link CVRStore#load()} takes this into account by loading both\n * the `cvr.instances` version and the `cvr.rowsVersion` version and checking\n * if they are in sync, waiting for a configurable delay until they are.\n *\n * ### Eventual conversion\n *\n * In the event of a continual stream of mutations (e.g. an animation-style\n * app), it is conceivable that the row record data be continually behind\n * the CVR metadata. In order to effect eventual convergence, a new view-syncer\n * signals the current view-syncer to stop updating by writing new `owner`\n * information to the `cvr.instances` row. This effectively stops the mutation\n * processing (in {@link CVRStore.#checkVersionAndOwnership}) so that the row\n * data can eventually catch up, allowing the new view-syncer to take over.\n *\n * Of course, there is the pathological situation in which a view-syncer\n * process crashes before the pending row updates are flushed. In this case,\n * the wait timeout will elapse and the CVR considered invalid.\n */\nexport class RowRecordCache {\n // The state in the #cache is always in sync with the CVR metadata\n // (i.e. cvr.instances). It may contain information that has not yet\n // been flushed to cvr.rows.\n #cache: Promise<CustomKeyMap<RowID, RowRecord>> | undefined;\n readonly #lc: LogContext;\n readonly #db: PostgresDB;\n readonly #schema: string;\n readonly #cvrID: string;\n readonly #failService: (e: unknown) => void;\n readonly #deferredRowFlushThreshold: number;\n readonly #setTimeout: typeof setTimeout;\n\n // Write-back cache state.\n readonly #pending = new CustomKeyMap<RowID, RowRecord | null>(rowIDString);\n #pendingRowsVersion: CVRVersion | null = null;\n #flushedRowsVersion: CVRVersion | null = null;\n #flushing: Resolver<void> | null = null;\n\n readonly #cvrFlushTime = getOrCreateHistogram('sync', 'cvr.flush-time', {\n description:\n 'Time to flush a CVR transaction. This includes both synchronous ' +\n 'and asynchronous flushes, distinguished by the flush.type attribute',\n unit: 's',\n });\n readonly #cvrRowsFlushed = getOrCreateCounter(\n 'sync',\n 'cvr.rows-flushed',\n 'Number of (changed) rows flushed to a CVR',\n );\n\n constructor(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardID,\n cvrID: string,\n failService: (e: unknown) => void,\n deferredRowFlushThreshold = 100,\n setTimeoutFn = setTimeout,\n ) {\n this.#lc = lc;\n this.#db = db;\n this.#schema = cvrSchema(shard);\n this.#cvrID = cvrID;\n this.#failService = failService;\n this.#deferredRowFlushThreshold = deferredRowFlushThreshold;\n this.#setTimeout = setTimeoutFn;\n }\n\n recordSyncFlushStats(stats: CVRFlushStats, elapsedMs: number) {\n this.#cvrFlushTime.record(elapsedMs / 1000, {\n [FLUSH_TYPE_ATTRIBUTE]: 'sync',\n });\n if (stats.rowsDeferred === 0) {\n this.#cvrRowsFlushed.add(stats.rows);\n }\n }\n\n #recordAsyncFlushStats(rows: number, elapsedMs: number) {\n this.#cvrFlushTime.record(elapsedMs / 1000, {\n [FLUSH_TYPE_ATTRIBUTE]: 'async',\n });\n this.#cvrRowsFlushed.add(rows);\n }\n\n #cvr(table: string) {\n return this.#db(`${this.#schema}.${table}`);\n }\n\n async #ensureLoaded(): Promise<CustomKeyMap<RowID, RowRecord>> {\n if (this.#cache) {\n return this.#cache;\n }\n const start = Date.now();\n const r = resolver<CustomKeyMap<RowID, RowRecord>>();\n // Set this.#cache immediately (before await) so that only one db\n // query is made even if there are multiple callers.\n this.#cache = r.promise;\n try {\n const cache: CustomKeyMap<RowID, RowRecord> = new CustomKeyMap(\n rowIDString,\n );\n for await (const rows of this.#db<RowsRow[]>`\n SELECT * FROM ${this.#cvr(`rows`)}\n WHERE \"clientGroupID\" = ${this.#cvrID} AND \"refCounts\" IS NOT NULL`\n // TODO(arv): Arbitrary page size\n .cursor(5000)) {\n for (const row of rows) {\n const rowRecord = rowsRowToRowRecord(row);\n cache.set(rowRecord.id, rowRecord);\n }\n }\n this.#lc.debug?.(\n `Loaded ${cache.size} row records in ${Date.now() - start} ms`,\n );\n r.resolve(cache);\n return this.#cache;\n } catch (e) {\n r.reject(e); // Make sure the error is reflected in the cached promise\n throw e;\n }\n }\n\n getRowRecords(): Promise<ReadonlyMap<RowID, RowRecord>> {\n return this.#ensureLoaded();\n }\n\n /**\n * Applies the `rowRecords` corresponding to the `rowsVersion`\n * to the cache, indicating whether the corresponding updates\n * (generated by {@link executeRowUpdates}) were `flushed`.\n *\n * If `flushed` is false, the RowRecordCache will flush the records\n * asynchronously.\n *\n * Note that `apply()` indicates that the CVR metadata associated with\n * the `rowRecords` was successfully committed, which essentially means\n * that this process has the unconditional right (and responsibility) of\n * following up with a flush of the `rowRecords`. In particular, the\n * commit of row records are not conditioned on the version or ownership\n * columns of the `cvr.instances` row.\n */\n async apply(\n rowRecords: Map<RowID, RowRecord | null>,\n rowsVersion: CVRVersion,\n flushed: boolean,\n ): Promise<number> {\n const cache = await this.#ensureLoaded();\n for (const [id, row] of rowRecords.entries()) {\n if (row === null || row.refCounts === null) {\n cache.delete(id);\n } else {\n cache.set(id, row);\n }\n if (!flushed) {\n this.#pending.set(id, row);\n }\n }\n this.#pendingRowsVersion = rowsVersion;\n // Initiate a flush if not already flushing.\n if (!flushed && this.#flushing === null) {\n this.#flushing = resolver();\n this.#setTimeout(() => this.#flush(), 0);\n }\n return cache.size;\n }\n\n async #flush() {\n const flushing = must(this.#flushing);\n try {\n while (this.#pendingRowsVersion !== this.#flushedRowsVersion) {\n const start = performance.now();\n\n const {rows, rowsVersion} = await this.#db.begin(\n Mode.READ_COMMITTED,\n tx => {\n disableStatementTimeout(tx);\n\n // Note: This code block is synchronous, guaranteeing that the\n // #pendingRowsVersion is consistent with the #pending rows.\n const rows = this.#pending.size;\n const rowsVersion = must(this.#pendingRowsVersion);\n // Awaiting all of the individual statements incurs too much\n // overhead. Instead, just catch and log exception(s); the outer\n // transaction will properly fail.\n void Promise.all(\n this.executeRowUpdates(tx, rowsVersion, this.#pending, 'force'),\n ).catch(e => this.#lc.error?.(`error flushing cvr rows`, e));\n\n this.#pending.clear();\n return {rows, rowsVersion};\n },\n );\n const elapsed = performance.now() - start;\n this.#lc.debug?.(\n `flushed ${rows} rows@${versionString(rowsVersion)} (${elapsed} ms)`,\n );\n this.#recordAsyncFlushStats(rows, elapsed);\n this.#flushedRowsVersion = rowsVersion;\n // Note: apply() may have called while the transaction was committing,\n // which will result in looping to commit the next #pendingRowsVersion.\n }\n this.#lc.debug?.(\n `up to date rows@${versionToNullableCookie(this.#flushedRowsVersion)}`,\n );\n flushing.resolve();\n this.#flushing = null;\n } catch (e) {\n flushing.reject(e);\n this.#failService(e);\n }\n }\n\n hasPendingUpdates() {\n return this.#flushing !== null;\n }\n\n /**\n * Returns a promise that resolves when all outstanding row-records\n * have been committed.\n */\n flushed(lc: LogContext): Promise<void> {\n if (this.#flushing) {\n lc.debug?.('awaiting pending row flush');\n return this.#flushing.promise;\n }\n return promiseVoid;\n }\n\n clear() {\n // Note: Only the #cache is cleared. #pending updates, on the other hand,\n // comprise canonical (i.e. already flushed) data and must be flushed\n // even if the snapshot of the present state (the #cache) is cleared.\n this.#cache = undefined;\n }\n\n async *catchupRowPatches(\n lc: LogContext,\n afterVersion: NullableCVRVersion,\n upToCVR: CVRSnapshot,\n current: CVRVersion,\n excludeQueryHashes: string[] = [],\n ): AsyncGenerator<RowsRow[], void, undefined> {\n if (cmpVersions(afterVersion, upToCVR.version) >= 0) {\n return;\n }\n\n const startMs = Date.now();\n const start = afterVersion ? versionString(afterVersion) : '';\n const end = versionString(upToCVR.version);\n lc.debug?.(`scanning row patches for clients from ${start}`);\n\n // Before accessing the CVR db, pending row records must be flushed.\n // Note that because catchupRowPatches() is called from within the\n // view syncer lock, this flush is guaranteed to complete since no\n // new CVR updates can happen while the lock is held.\n await this.flushed(lc);\n const flushMs = Date.now() - startMs;\n\n const reader = new TransactionPool(lc, Mode.READONLY).run(this.#db);\n try {\n // Verify that we are reading the right version of the CVR.\n await reader.processReadTask(tx =>\n checkVersion(tx, this.#schema, this.#cvrID, current),\n );\n\n const {query} = await reader.processReadTask(tx => {\n const query =\n excludeQueryHashes.length === 0\n ? tx<RowsRow[]>`SELECT * FROM ${this.#cvr('rows')}\n WHERE \"clientGroupID\" = ${this.#cvrID}\n AND \"patchVersion\" > ${start}\n AND \"patchVersion\" <= ${end}`\n : // Exclude rows that were already sent as part of query hydration.\n tx<RowsRow[]>`SELECT * FROM ${this.#cvr('rows')}\n WHERE \"clientGroupID\" = ${this.#cvrID}\n AND \"patchVersion\" > ${start}\n AND \"patchVersion\" <= ${end}\n AND (\"refCounts\" IS NULL OR NOT \"refCounts\" ?| ${excludeQueryHashes})`;\n return {query};\n });\n\n yield* query.cursor(10000);\n } finally {\n reader.setDone();\n }\n\n const totalMs = Date.now() - startMs;\n lc.debug?.(\n `finished row catchup (flush: ${flushMs} ms, total: ${totalMs} ms)`,\n );\n }\n\n executeRowUpdates(\n tx: PostgresTransaction,\n version: CVRVersion,\n rowUpdates: Map<RowID, RowRecord | null>,\n mode: 'allow-defer' | 'force',\n lc = this.#lc,\n ): PendingQuery<Row[]>[] {\n if (\n mode === 'allow-defer' &&\n // defer if pending rows are being flushed\n (this.#flushing !== null ||\n // or if the new batch is above the limit.\n rowUpdates.size > this.#deferredRowFlushThreshold)\n ) {\n return [];\n }\n const rowsVersion = {\n clientGroupID: this.#cvrID,\n version: versionString(version),\n };\n const pending: PendingQuery<Row[]>[] = [\n tx`INSERT INTO ${this.#cvr('rowsVersion')} ${tx(rowsVersion)}\n ON CONFLICT (\"clientGroupID\") \n DO UPDATE SET ${tx(rowsVersion)}`.execute(),\n ];\n\n const rowRecordRows: RowsRow[] = [];\n for (const [id, row] of rowUpdates.entries()) {\n if (row === null) {\n pending.push(\n tx`\n DELETE FROM ${this.#cvr('rows')}\n WHERE \"clientGroupID\" = ${this.#cvrID}\n AND \"schema\" = ${id.schema}\n AND \"table\" = ${id.table}\n AND \"rowKey\" = ${id.rowKey}\n `.execute(),\n );\n } else {\n rowRecordRows.push(rowRecordToRowsRow(this.#cvrID, row));\n }\n }\n if (rowRecordRows.length) {\n pending.push(\n tx`\n INSERT INTO ${this.#cvr('rows')}(\n \"clientGroupID\", \"schema\", \"table\", \"rowKey\", \"rowVersion\", \"patchVersion\", \"refCounts\"\n ) SELECT\n \"clientGroupID\", \"schema\", \"table\", \"rowKey\", \"rowVersion\", \"patchVersion\", \"refCounts\"\n FROM json_to_recordset(${rowRecordRows}) AS x(\n \"clientGroupID\" TEXT,\n \"schema\" TEXT,\n \"table\" TEXT,\n \"rowKey\" JSONB,\n \"rowVersion\" TEXT,\n \"patchVersion\" TEXT,\n \"refCounts\" JSONB\n ) ON CONFLICT (\"clientGroupID\", \"schema\", \"table\", \"rowKey\")\n DO UPDATE SET \"rowVersion\" = excluded.\"rowVersion\",\n \"patchVersion\" = excluded.\"patchVersion\",\n \"refCounts\" = excluded.\"refCounts\"\n `.execute(),\n );\n lc.debug?.(\n `flushing ${rowUpdates.size} rows (${rowRecordRows.length} inserts, ${\n rowUpdates.size - rowRecordRows.length\n } deletes)`,\n );\n }\n return pending;\n }\n}\n"],"names":["Mode.READ_COMMITTED","rows","rowsVersion","Mode.READONLY","query"],"mappings":"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"}
|
|
@@ -56,7 +56,7 @@ export declare class ViewSyncerService implements ViewSyncer, ActivityBasedServi
|
|
|
56
56
|
readonly id: string;
|
|
57
57
|
userQueryURL?: string | undefined;
|
|
58
58
|
userQueryHeaders?: Record<string, string> | undefined;
|
|
59
|
-
constructor(config: NormalizedZeroConfig, lc: LogContext, shard: ShardID, taskID: string, clientGroupID: string, cvrDb: PostgresDB, upstreamDb: PostgresDB | undefined, pipelineDriver: PipelineDriver, versionChanges: Subscription<ReplicaState>, drainCoordinator: DrainCoordinator, slowHydrateThreshold: number, inspectorDelegate: InspectorDelegate, customQueryTransformer: CustomQueryTransformer | undefined, keepaliveMs?: number, setTimeoutFn?: SetTimeout);
|
|
59
|
+
constructor(config: NormalizedZeroConfig, lc: LogContext, shard: ShardID, taskID: string, clientGroupID: string, cvrDb: PostgresDB, upstreamDb: PostgresDB | undefined, pipelineDriver: PipelineDriver, versionChanges: Subscription<ReplicaState>, drainCoordinator: DrainCoordinator, slowHydrateThreshold: number, inspectorDelegate: InspectorDelegate, customQueryTransformer: CustomQueryTransformer | undefined, runPriorityOp: <T>(lc: LogContext, description: string, op: () => Promise<T>) => Promise<T>, keepaliveMs?: number, setTimeoutFn?: SetTimeout);
|
|
60
60
|
readyState(): Promise<'initialized' | 'draining'>;
|
|
61
61
|
run(): Promise<void>;
|
|
62
62
|
/**
|
|
@@ -83,6 +83,7 @@ export declare class ViewSyncerService implements ViewSyncer, ActivityBasedServi
|
|
|
83
83
|
export declare function pickToken(lc: LogContext, previousToken: TokenData | undefined, newToken: TokenData | undefined): TokenData | undefined;
|
|
84
84
|
export declare class TimeSliceTimer {
|
|
85
85
|
#private;
|
|
86
|
+
constructor(lc: LogContext);
|
|
86
87
|
start(): Promise<this>;
|
|
87
88
|
startWithoutYielding(): this;
|
|
88
89
|
yieldProcess(_msgForTesting?: string): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"view-syncer.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/view-syncer.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"view-syncer.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/view-syncer.ts"],"names":[],"mappings":"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"}
|
|
@@ -155,7 +155,8 @@ class ViewSyncerService {
|
|
|
155
155
|
);
|
|
156
156
|
#inspectorDelegate;
|
|
157
157
|
#config;
|
|
158
|
-
|
|
158
|
+
#runPriorityOp;
|
|
159
|
+
constructor(config, lc, shard, taskID, clientGroupID, cvrDb, upstreamDb, pipelineDriver, versionChanges, drainCoordinator, slowHydrateThreshold, inspectorDelegate, customQueryTransformer, runPriorityOp, keepaliveMs = DEFAULT_KEEPALIVE_MS, setTimeoutFn = setTimeout.bind(globalThis)) {
|
|
159
160
|
const queryConfig = config.query?.url ? config.query : config.getQueries;
|
|
160
161
|
this.#config = config;
|
|
161
162
|
this.id = clientGroupID;
|
|
@@ -181,6 +182,7 @@ class ViewSyncerService {
|
|
|
181
182
|
() => this.#stateChanges.cancel()
|
|
182
183
|
);
|
|
183
184
|
this.#setTimeout = setTimeoutFn;
|
|
185
|
+
this.#runPriorityOp = runPriorityOp;
|
|
184
186
|
this.keepalive();
|
|
185
187
|
}
|
|
186
188
|
#getHeaderOptions(forwardCookie) {
|
|
@@ -200,11 +202,14 @@ class ViewSyncerService {
|
|
|
200
202
|
if (!this.#stateChanges.active) {
|
|
201
203
|
this.#lc.debug?.("state changes are inactive");
|
|
202
204
|
clearTimeout(this.#expiredQueriesTimer);
|
|
203
|
-
throw new ProtocolErrorWithLevel(
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
205
|
+
throw new ProtocolErrorWithLevel(
|
|
206
|
+
{
|
|
207
|
+
kind: Rehome,
|
|
208
|
+
message: "Reconnect required",
|
|
209
|
+
origin: ZeroCache
|
|
210
|
+
},
|
|
211
|
+
"info"
|
|
212
|
+
);
|
|
208
213
|
}
|
|
209
214
|
if (await this.#checkForShutdownConditionsInLock()) {
|
|
210
215
|
this.#lc.info?.(`closing clientGroupID=${this.id}`);
|
|
@@ -212,8 +217,12 @@ class ViewSyncerService {
|
|
|
212
217
|
return;
|
|
213
218
|
}
|
|
214
219
|
if (!this.#cvr) {
|
|
215
|
-
this.#lc.debug?.("loading
|
|
216
|
-
this.#cvr = await this.#
|
|
220
|
+
this.#lc.debug?.("loading cvr");
|
|
221
|
+
this.#cvr = await this.#runPriorityOp(
|
|
222
|
+
lc,
|
|
223
|
+
"loading cvr",
|
|
224
|
+
() => this.#cvrStore.load(lc, this.#lastConnectTime)
|
|
225
|
+
);
|
|
217
226
|
this.#ttlClock = this.#cvr.ttlClock;
|
|
218
227
|
this.#ttlClockBase = Date.now();
|
|
219
228
|
} else {
|
|
@@ -269,6 +278,7 @@ class ViewSyncerService {
|
|
|
269
278
|
}
|
|
270
279
|
lc.info?.(`resetting pipelines: ${result.message}`);
|
|
271
280
|
this.#pipelines.reset(clientSchema);
|
|
281
|
+
this.#pipelinesSynced = false;
|
|
272
282
|
}
|
|
273
283
|
const version2 = this.#pipelines.advanceWithoutDiff();
|
|
274
284
|
const cvrVer = versionString(cvr.version);
|
|
@@ -303,8 +313,9 @@ class ViewSyncerService {
|
|
|
303
313
|
if (hasExpiredQueries(cvr)) {
|
|
304
314
|
lc = lc.withContext("method", "#removeExpiredQueries");
|
|
305
315
|
lc.debug?.("Queries have expired");
|
|
306
|
-
|
|
307
|
-
|
|
316
|
+
if (this.#pipelinesSynced) {
|
|
317
|
+
await this.#syncQueryPipelineSet(lc, cvr);
|
|
318
|
+
}
|
|
308
319
|
}
|
|
309
320
|
this.#scheduleExpireEviction(lc, cvr);
|
|
310
321
|
};
|
|
@@ -440,11 +451,14 @@ class ViewSyncerService {
|
|
|
440
451
|
initConnectionMessage,
|
|
441
452
|
async (lc2, clientID2, msg, cvr) => {
|
|
442
453
|
if (cvr.clientSchema === null && !msg.clientSchema) {
|
|
443
|
-
throw new ProtocolErrorWithLevel(
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
454
|
+
throw new ProtocolErrorWithLevel(
|
|
455
|
+
{
|
|
456
|
+
kind: InvalidConnectionRequest,
|
|
457
|
+
message: "The initConnection message for a new client group must include client schema.",
|
|
458
|
+
origin: ZeroCache
|
|
459
|
+
},
|
|
460
|
+
"warn"
|
|
461
|
+
);
|
|
448
462
|
}
|
|
449
463
|
await this.#handleConfigUpdate(
|
|
450
464
|
lc2,
|
|
@@ -488,19 +502,21 @@ class ViewSyncerService {
|
|
|
488
502
|
this.#ttlClockBase = now;
|
|
489
503
|
return ttlClock;
|
|
490
504
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
505
|
+
#flushUpdater(lc, updater) {
|
|
506
|
+
return this.#runPriorityOp(lc, "flushing cvr", async () => {
|
|
507
|
+
const now = Date.now();
|
|
508
|
+
const ttlClock = this.#getTTLClock(now);
|
|
509
|
+
const { cvr, flushed } = await updater.flush(
|
|
510
|
+
lc,
|
|
511
|
+
this.#lastConnectTime,
|
|
512
|
+
now,
|
|
513
|
+
ttlClock
|
|
514
|
+
);
|
|
515
|
+
if (flushed) {
|
|
516
|
+
this.#startTTLClockInterval(lc);
|
|
517
|
+
}
|
|
518
|
+
return cvr;
|
|
519
|
+
});
|
|
504
520
|
}
|
|
505
521
|
#startTTLClockInterval(lc) {
|
|
506
522
|
this.#stopTTLClockInterval();
|
|
@@ -757,11 +773,16 @@ class ViewSyncerService {
|
|
|
757
773
|
"Custom/named queries were requested but no `ZERO_QUERY_URL` is configured for Zero Cache."
|
|
758
774
|
);
|
|
759
775
|
}
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
776
|
+
const customQueryTransformer = this.#customQueryTransformer;
|
|
777
|
+
if (customQueryTransformer && customQueries.size > 0) {
|
|
778
|
+
const transformedCustomQueries = await this.#runPriorityOp(
|
|
779
|
+
lc,
|
|
780
|
+
"#hydrateUnchangedQueries transforming custom queries",
|
|
781
|
+
() => customQueryTransformer.transform(
|
|
782
|
+
this.#getHeaderOptions(this.#queryConfig.forwardCookies),
|
|
783
|
+
customQueries.values(),
|
|
784
|
+
this.userQueryURL
|
|
785
|
+
)
|
|
765
786
|
);
|
|
766
787
|
this.#processTransformedCustomQueries(
|
|
767
788
|
lc,
|
|
@@ -790,7 +811,7 @@ class ViewSyncerService {
|
|
|
790
811
|
transformationHash,
|
|
791
812
|
transformedAst
|
|
792
813
|
} of transformedQueries) {
|
|
793
|
-
const timer = new TimeSliceTimer();
|
|
814
|
+
const timer = new TimeSliceTimer(lc);
|
|
794
815
|
let count = 0;
|
|
795
816
|
await startAsyncSpan(
|
|
796
817
|
tracer,
|
|
@@ -936,16 +957,25 @@ class ViewSyncerService {
|
|
|
936
957
|
);
|
|
937
958
|
}
|
|
938
959
|
let erroredQueryIDs;
|
|
939
|
-
|
|
960
|
+
const customQueryTransformer = this.#customQueryTransformer;
|
|
961
|
+
if (customQueryTransformer && customQueries.size > 0) {
|
|
940
962
|
const transformStart = performance.now();
|
|
941
963
|
let transformedCustomQueries;
|
|
942
964
|
try {
|
|
943
|
-
transformedCustomQueries = await this.#
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
965
|
+
transformedCustomQueries = await this.#runPriorityOp(
|
|
966
|
+
lc,
|
|
967
|
+
"#syncQueryPipelineSet transforming custom queries",
|
|
968
|
+
() => customQueryTransformer.transform(
|
|
969
|
+
this.#getHeaderOptions(true),
|
|
970
|
+
customQueries.values(),
|
|
971
|
+
this.userQueryURL
|
|
972
|
+
)
|
|
947
973
|
);
|
|
948
|
-
|
|
974
|
+
if (!Array.isArray(transformedCustomQueries) && transformedCustomQueries.kind === TransformFailed) {
|
|
975
|
+
throw new ProtocolErrorWithLevel(transformedCustomQueries, "warn");
|
|
976
|
+
} else {
|
|
977
|
+
this.#queryTransformations.add(1, { result: "success" });
|
|
978
|
+
}
|
|
949
979
|
} catch (e) {
|
|
950
980
|
this.#queryTransformations.add(1, { result: "error" });
|
|
951
981
|
throw e;
|
|
@@ -953,12 +983,6 @@ class ViewSyncerService {
|
|
|
953
983
|
const transformDuration = (performance.now() - transformStart) / 1e3;
|
|
954
984
|
this.#queryTransformationTime.record(transformDuration);
|
|
955
985
|
}
|
|
956
|
-
if (!Array.isArray(transformedCustomQueries) && transformedCustomQueries.kind === TransformFailed) {
|
|
957
|
-
throw new ProtocolErrorWithLevel(
|
|
958
|
-
transformedCustomQueries,
|
|
959
|
-
getLogLevel(transformedCustomQueries.kind)
|
|
960
|
-
);
|
|
961
|
-
}
|
|
962
986
|
const successfullyTransformed = /* @__PURE__ */ new Map();
|
|
963
987
|
erroredQueryIDs = this.#processTransformedCustomQueries(
|
|
964
988
|
lc,
|
|
@@ -1133,12 +1157,12 @@ class ViewSyncerService {
|
|
|
1133
1157
|
}
|
|
1134
1158
|
}
|
|
1135
1159
|
let totalProcessTime = 0;
|
|
1136
|
-
const timer = new TimeSliceTimer();
|
|
1160
|
+
const timer = new TimeSliceTimer(lc);
|
|
1137
1161
|
const pipelines = this.#pipelines;
|
|
1138
1162
|
const hydrations = this.#hydrations;
|
|
1139
1163
|
const hydrationTime = this.#hydrationTime;
|
|
1140
1164
|
const self = this;
|
|
1141
|
-
await yieldProcess();
|
|
1165
|
+
await yieldProcess(lc);
|
|
1142
1166
|
function* generateRowChanges(slowHydrateThreshold) {
|
|
1143
1167
|
for (const q of addQueries) {
|
|
1144
1168
|
lc = lc.withContext("hash", q.id).withContext("transformationHash", q.transformationHash);
|
|
@@ -1236,6 +1260,8 @@ class ViewSyncerService {
|
|
|
1236
1260
|
cvr,
|
|
1237
1261
|
current
|
|
1238
1262
|
);
|
|
1263
|
+
configPatches.catch(() => {
|
|
1264
|
+
});
|
|
1239
1265
|
let rowPatchCount = 0;
|
|
1240
1266
|
for await (const rows of rowPatches) {
|
|
1241
1267
|
for (const row of rows) {
|
|
@@ -1357,7 +1383,7 @@ class ViewSyncerService {
|
|
|
1357
1383
|
"pipelines must be initialized (advancePipelines"
|
|
1358
1384
|
);
|
|
1359
1385
|
const start = performance.now();
|
|
1360
|
-
const timer = new TimeSliceTimer();
|
|
1386
|
+
const timer = new TimeSliceTimer(lc);
|
|
1361
1387
|
const { version: version2, numChanges, changes } = this.#pipelines.advance(timer);
|
|
1362
1388
|
lc = lc.withContext("newVersion", version2);
|
|
1363
1389
|
const updater = new CVRQueryDrivenUpdater(
|
|
@@ -1462,7 +1488,7 @@ function createHashToIDs(cvr) {
|
|
|
1462
1488
|
return hashToIDs;
|
|
1463
1489
|
}
|
|
1464
1490
|
const timeSliceQueue = new Lock();
|
|
1465
|
-
function yieldProcess() {
|
|
1491
|
+
function yieldProcess(_lc) {
|
|
1466
1492
|
return timeSliceQueue.withLock(() => new Promise(setImmediate));
|
|
1467
1493
|
}
|
|
1468
1494
|
function contentsAndVersion(row) {
|
|
@@ -1550,8 +1576,12 @@ function hasExpiredQueries(cvr) {
|
|
|
1550
1576
|
class TimeSliceTimer {
|
|
1551
1577
|
#total = 0;
|
|
1552
1578
|
#start = 0;
|
|
1579
|
+
#lc;
|
|
1580
|
+
constructor(lc) {
|
|
1581
|
+
this.#lc = lc;
|
|
1582
|
+
}
|
|
1553
1583
|
async start() {
|
|
1554
|
-
await yieldProcess();
|
|
1584
|
+
await yieldProcess(this.#lc);
|
|
1555
1585
|
return this.startWithoutYielding();
|
|
1556
1586
|
}
|
|
1557
1587
|
startWithoutYielding() {
|
|
@@ -1561,7 +1591,7 @@ class TimeSliceTimer {
|
|
|
1561
1591
|
}
|
|
1562
1592
|
async yieldProcess(_msgForTesting) {
|
|
1563
1593
|
this.#stopLap();
|
|
1564
|
-
await yieldProcess();
|
|
1594
|
+
await yieldProcess(this.#lc);
|
|
1565
1595
|
this.#startLap();
|
|
1566
1596
|
}
|
|
1567
1597
|
#startLap() {
|