@rocicorp/zero 1.5.0-canary.3 → 1.6.0-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/out/analyze-query/src/analyze-cli.js +2 -2
  2. package/out/analyze-query/src/analyze-cli.js.map +1 -1
  3. package/out/replicache/src/btree/node.d.ts +3 -0
  4. package/out/replicache/src/btree/node.d.ts.map +1 -1
  5. package/out/replicache/src/btree/node.js +114 -1
  6. package/out/replicache/src/btree/node.js.map +1 -1
  7. package/out/replicache/src/btree/write.d.ts +7 -0
  8. package/out/replicache/src/btree/write.d.ts.map +1 -1
  9. package/out/replicache/src/btree/write.js +50 -0
  10. package/out/replicache/src/btree/write.js.map +1 -1
  11. package/out/replicache/src/db/write.d.ts +8 -0
  12. package/out/replicache/src/db/write.d.ts.map +1 -1
  13. package/out/replicache/src/db/write.js +15 -0
  14. package/out/replicache/src/db/write.js.map +1 -1
  15. package/out/replicache/src/kv/sqlite-store.d.ts +2 -5
  16. package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
  17. package/out/replicache/src/kv/sqlite-store.js +21 -24
  18. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  19. package/out/replicache/src/replicache-impl.d.ts.map +1 -1
  20. package/out/replicache/src/replicache-impl.js.map +1 -1
  21. package/out/replicache/src/sync/patch.d.ts +15 -0
  22. package/out/replicache/src/sync/patch.d.ts.map +1 -1
  23. package/out/replicache/src/sync/patch.js +85 -26
  24. package/out/replicache/src/sync/patch.js.map +1 -1
  25. package/out/shared/src/testing.d.ts +3 -0
  26. package/out/shared/src/testing.d.ts.map +1 -0
  27. package/out/zero/package.js +5 -6
  28. package/out/zero/package.js.map +1 -1
  29. package/out/zero-cache/src/auth/write-authorizer.js +1 -1
  30. package/out/zero-cache/src/config/zero-config.d.ts +4 -0
  31. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  32. package/out/zero-cache/src/config/zero-config.js +8 -0
  33. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  34. package/out/zero-cache/src/server/inspector-delegate.d.ts +3 -2
  35. package/out/zero-cache/src/server/inspector-delegate.d.ts.map +1 -1
  36. package/out/zero-cache/src/server/inspector-delegate.js +19 -9
  37. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  38. package/out/zero-cache/src/server/runner/run-worker.js +1 -1
  39. package/out/zero-cache/src/services/change-source/custom/change-source.js +2 -2
  40. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  41. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +7 -6
  42. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  43. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  44. package/out/zero-cache/src/services/change-source/pg/change-source.js +49 -66
  45. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  46. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +0 -8
  47. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  48. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +22 -52
  49. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  50. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +57 -0
  51. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -0
  52. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +162 -0
  53. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -0
  54. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
  55. package/out/zero-cache/src/services/change-source/pg/schema/init.js +18 -0
  56. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  57. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +17 -3
  58. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
  59. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +43 -16
  60. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  61. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +2 -3
  62. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  63. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +5 -5
  64. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  65. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +10 -1
  66. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
  67. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +13 -3
  68. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  69. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +6 -11
  70. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
  71. package/out/zero-cache/src/services/change-streamer/change-streamer.js +0 -1
  72. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  73. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts.map +1 -1
  74. package/out/zero-cache/src/services/change-streamer/forwarder.js +2 -2
  75. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  76. package/out/zero-cache/src/services/change-streamer/storer.d.ts +12 -5
  77. package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
  78. package/out/zero-cache/src/services/change-streamer/storer.js +43 -21
  79. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  80. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +4 -5
  81. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
  82. package/out/zero-cache/src/services/change-streamer/subscriber.js +18 -16
  83. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  84. package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
  85. package/out/zero-cache/src/services/litestream/commands.js +3 -2
  86. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  87. package/out/zero-cache/src/services/litestream/config.yml +1 -0
  88. package/out/zero-cache/src/services/mutagen/pusher.d.ts +2 -2
  89. package/out/zero-cache/src/services/view-syncer/cvr-store.js +2 -2
  90. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  91. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +1 -1
  92. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  93. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  94. package/out/zero-cache/src/services/view-syncer/view-syncer.js +5 -6
  95. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  96. package/out/zero-cache/src/types/streams.d.ts +4 -0
  97. package/out/zero-cache/src/types/streams.d.ts.map +1 -1
  98. package/out/zero-cache/src/types/streams.js +13 -10
  99. package/out/zero-cache/src/types/streams.js.map +1 -1
  100. package/out/zero-cache/src/workers/connection.js +5 -5
  101. package/out/zero-cache/src/workers/connection.js.map +1 -1
  102. package/out/zero-client/src/client/inspector/inspector.d.ts.map +1 -1
  103. package/out/zero-client/src/client/inspector/inspector.js +15 -2
  104. package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
  105. package/out/zero-client/src/client/inspector/lazy-inspector.d.ts +9 -3
  106. package/out/zero-client/src/client/inspector/lazy-inspector.d.ts.map +1 -1
  107. package/out/zero-client/src/client/inspector/lazy-inspector.js +27 -6
  108. package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
  109. package/out/zero-client/src/client/inspector/query.d.ts.map +1 -1
  110. package/out/zero-client/src/client/inspector/query.js +3 -3
  111. package/out/zero-client/src/client/inspector/query.js.map +1 -1
  112. package/out/zero-client/src/client/ivm-branch.d.ts.map +1 -1
  113. package/out/zero-client/src/client/ivm-branch.js +16 -2
  114. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  115. package/out/zero-client/src/client/options.d.ts +12 -4
  116. package/out/zero-client/src/client/options.d.ts.map +1 -1
  117. package/out/zero-client/src/client/options.js.map +1 -1
  118. package/out/zero-client/src/client/query-manager.d.ts +8 -1
  119. package/out/zero-client/src/client/query-manager.d.ts.map +1 -1
  120. package/out/zero-client/src/client/query-manager.js +28 -3
  121. package/out/zero-client/src/client/query-manager.js.map +1 -1
  122. package/out/zero-client/src/client/version.js +1 -1
  123. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  124. package/out/zero-client/src/client/zero.js +12 -11
  125. package/out/zero-client/src/client/zero.js.map +1 -1
  126. package/out/zero-protocol/src/down.d.ts +1 -1
  127. package/out/zero-protocol/src/inspect-down.d.ts +15 -4
  128. package/out/zero-protocol/src/inspect-down.d.ts.map +1 -1
  129. package/out/zero-protocol/src/inspect-down.js +11 -1
  130. package/out/zero-protocol/src/inspect-down.js.map +1 -1
  131. package/out/zero-protocol/src/protocol-version.d.ts +1 -1
  132. package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
  133. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  134. package/out/zero-react/src/use-query.d.ts.map +1 -1
  135. package/out/zero-react/src/use-query.js.map +1 -1
  136. package/out/zero-react/src/zero-provider.d.ts +6 -0
  137. package/out/zero-react/src/zero-provider.d.ts.map +1 -1
  138. package/out/zero-react/src/zero-provider.js +21 -1
  139. package/out/zero-react/src/zero-provider.js.map +1 -1
  140. package/out/zero-solid/src/use-zero.d.ts +6 -0
  141. package/out/zero-solid/src/use-zero.d.ts.map +1 -1
  142. package/out/zero-solid/src/use-zero.js +24 -4
  143. package/out/zero-solid/src/use-zero.js.map +1 -1
  144. package/out/zql/src/builder/builder.d.ts.map +1 -1
  145. package/out/zql/src/builder/builder.js +18 -8
  146. package/out/zql/src/builder/builder.js.map +1 -1
  147. package/out/zql/src/ivm/cap.d.ts +32 -0
  148. package/out/zql/src/ivm/cap.d.ts.map +1 -0
  149. package/out/zql/src/ivm/cap.js +205 -0
  150. package/out/zql/src/ivm/cap.js.map +1 -0
  151. package/out/zql/src/ivm/constraint.d.ts.map +1 -1
  152. package/out/zql/src/ivm/constraint.js.map +1 -1
  153. package/out/zql/src/ivm/flipped-join.d.ts +9 -0
  154. package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
  155. package/out/zql/src/ivm/flipped-join.js +56 -69
  156. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  157. package/out/zql/src/ivm/memory-source.d.ts +24 -3
  158. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  159. package/out/zql/src/ivm/memory-source.js +162 -7
  160. package/out/zql/src/ivm/memory-source.js.map +1 -1
  161. package/out/zql/src/ivm/operator.d.ts +26 -0
  162. package/out/zql/src/ivm/operator.d.ts.map +1 -1
  163. package/out/zql/src/ivm/operator.js.map +1 -1
  164. package/out/zql/src/ivm/take.js +2 -2
  165. package/out/zqlite/src/query-builder.d.ts +14 -2
  166. package/out/zqlite/src/query-builder.d.ts.map +1 -1
  167. package/out/zqlite/src/query-builder.js +32 -1
  168. package/out/zqlite/src/query-builder.js.map +1 -1
  169. package/out/zqlite/src/table-source.d.ts.map +1 -1
  170. package/out/zqlite/src/table-source.js +4 -4
  171. package/out/zqlite/src/table-source.js.map +1 -1
  172. package/package.json +5 -6
@@ -1 +1 @@
1
- {"version":3,"file":"cvr-store.js","names":["#schema","#taskID","#id","#failService","#db","#writes","#pendingRowRecordUpdates","#forceUpdates","#rowCache","#loadAttemptIntervalMs","#maxLoadAttempts","#pendingQueryUpdates","#pendingDesireUpdates","#pendingQueryPartialUpdates","#load","#cvr","#pendingInstanceWrite","#updateQueryFields","#checkVersionAndOwnership","#flush","#rowCount","#flushDesires","#flushQueries"],"sources":["../../../../../../zero-cache/src/services/view-syncer/cvr-store.ts"],"sourcesContent":["import {trace} from '@opentelemetry/api';\nimport type {LogContext} from '@rocicorp/logger';\nimport type {MaybeRow, PendingQuery, Row} from 'postgres';\nimport {startAsyncSpan} from '../../../../otel/src/span.ts';\nimport {version} from '../../../../otel/src/version.ts';\nimport {assert} from '../../../../shared/src/asserts.ts';\nimport {CustomKeyMap} from '../../../../shared/src/custom-key-map.ts';\nimport {CustomKeySet} from '../../../../shared/src/custom-key-set.ts';\nimport {\n deepEqual,\n type ReadonlyJSONValue,\n} from '../../../../shared/src/json.ts';\nimport {sleep} from '../../../../shared/src/sleep.ts';\nimport * as v from '../../../../shared/src/valita.ts';\nimport {astSchema} from '../../../../zero-protocol/src/ast.ts';\nimport {clientSchemaSchema} from '../../../../zero-protocol/src/client-schema.ts';\nimport {ErrorKind} from '../../../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../../../zero-protocol/src/error-origin.ts';\nimport type {InspectQueryRow} from '../../../../zero-protocol/src/inspect-down.ts';\nimport {clampTTL, DEFAULT_TTL_MS} from '../../../../zql/src/query/ttl.ts';\nimport * as Mode from '../../db/mode-enum.ts';\nimport {runTx} from '../../db/run-transaction.ts';\nimport {TransactionPool} from '../../db/transaction-pool.ts';\nimport {recordRowsSynced} from '../../server/anonymous-otel-start.ts';\nimport {ProtocolErrorWithLevel} from '../../types/error-with-level.ts';\nimport type {PostgresDB, PostgresTransaction} from '../../types/pg.ts';\nimport {rowIDString} from '../../types/row-key.ts';\nimport {cvrSchema, type ShardID} from '../../types/shards.ts';\nimport type {Patch, PatchToVersion} from './client-handler.ts';\nimport type {CVR, CVRSnapshot} from './cvr.ts';\nimport {RowRecordCache} from './row-record-cache.ts';\nimport {\n type ClientsRow,\n type DesiresRow,\n type InstancesRow,\n type QueriesRow,\n type RowsRow,\n} from './schema/cvr.ts';\nimport {\n type ClientQueryRecord,\n type ClientRecord,\n cmpVersions,\n type CustomQueryRecord,\n type CVRVersion,\n EMPTY_CVR_VERSION,\n type InternalQueryRecord,\n type NullableCVRVersion,\n type QueryPatch,\n type QueryRecord,\n queryRecordToQueryRow,\n type RowID,\n type RowRecord,\n versionFromString,\n versionString,\n} from './schema/types.ts';\nimport {\n type TTLClock,\n ttlClockAsNumber,\n ttlClockFromNumber,\n} from './ttl-clock.ts';\n\nexport type CVRFlushStats = {\n instances: number;\n queries: number;\n desires: number;\n clients: number;\n rows: number;\n rowsDeferred: number;\n statements: number;\n};\n\nlet flushCounter = 0;\n\n/**\n * Convert TTL/timestamp values for both old (seconds-based) and new (ms-based) columns.\n * Old columns: inactivatedAt (TIMESTAMPTZ), ttl (INTERVAL) - need conversion ms->seconds\n * New columns: inactivatedAtMs (DOUBLE PRECISION), ttlMs (DOUBLE PRECISION) - store ms directly\n */\nfunction convertTTLValues(\n inactivatedAt: TTLClock | undefined,\n ttl: number,\n): {\n ttlInterval: number | null;\n ttlMs: number | null;\n inactivatedAtTimestamp: TTLClock | null;\n inactivatedAtMs: TTLClock | null;\n} {\n return {\n ttlInterval: ttl < 0 ? null : ttl / 1000, // INTERVAL needs seconds\n ttlMs: ttl < 0 ? null : ttl, // New column stores ms directly\n inactivatedAtTimestamp:\n inactivatedAt === undefined\n ? null\n : ttlClockFromNumber(ttlClockAsNumber(inactivatedAt) / 1000),\n inactivatedAtMs: inactivatedAt ?? null,\n };\n}\n\nconst tracer = trace.getTracer('cvr-store', version);\n\n/**\n * QueriesRow with queryArgs as a stringified JSON value.\n * Used for batched config writes where queryArgs are pre-stringified\n * to handle the postgres.js boolean array bug.\n */\ntype StringifiedQueriesRow = Omit<QueriesRow, 'queryArgs'> & {\n queryArgs: string | null;\n};\n\nfunction asQuery(row: QueriesRow): QueryRecord {\n const maybeVersion = (s: string | null) =>\n s === null ? undefined : versionFromString(s);\n\n // Only attach rowSetSignature when the column is non-null, so existing\n // snapshots that don't include the field don't break.\n const sigField = row.rowSetSignature\n ? {rowSetSignature: row.rowSetSignature}\n : {};\n\n if (row.clientAST === null) {\n // custom query\n assert(\n row.queryName !== null && row.queryArgs !== null,\n 'queryName and queryArgs must be set for custom queries',\n );\n return {\n type: 'custom',\n id: row.queryHash,\n name: row.queryName,\n args: row.queryArgs,\n patchVersion: maybeVersion(row.patchVersion),\n clientState: {},\n transformationHash: row.transformationHash ?? undefined,\n transformationVersion: maybeVersion(row.transformationVersion),\n ...sigField,\n } satisfies CustomQueryRecord;\n }\n\n const ast = astSchema.parse(row.clientAST);\n return row.internal\n ? ({\n type: 'internal',\n id: row.queryHash,\n ast,\n transformationHash: row.transformationHash ?? undefined,\n transformationVersion: maybeVersion(row.transformationVersion),\n ...sigField,\n } satisfies InternalQueryRecord)\n : ({\n type: 'client',\n id: row.queryHash,\n ast,\n patchVersion: maybeVersion(row.patchVersion),\n clientState: {},\n transformationHash: row.transformationHash ?? undefined,\n transformationVersion: maybeVersion(row.transformationVersion),\n ...sigField,\n } satisfies ClientQueryRecord);\n}\n\n// The time to wait between load attempts.\nconst LOAD_ATTEMPT_INTERVAL_MS = 500;\n// The maximum number of load() attempts if the rowsVersion is behind.\n// This currently results in a maximum catchup time of ~5 seconds, after\n// which we give up and consider the CVR invalid.\n//\n// TODO: Make this configurable with something like --max-catchup-wait-ms,\n// as it is technically application specific.\nconst MAX_LOAD_ATTEMPTS = 10;\n\nexport class CVRStore {\n readonly #schema: string;\n readonly #taskID: string;\n readonly #id: string;\n readonly #failService: (e: unknown) => void;\n readonly #db: PostgresDB;\n readonly #writes: Set<{\n stats: Partial<CVRFlushStats>;\n write: (\n tx: PostgresTransaction,\n lastConnectTime: number,\n ) => PendingQuery<MaybeRow[]>;\n }> = new Set();\n // Stored separately so repeated putInstance() calls (e.g. setClientSchema,\n // setProfileID, and the final call in #flush) replace each other rather than\n // accumulating as independent statements in #writes.\n #pendingInstanceWrite:\n | ((\n tx: PostgresTransaction,\n lastConnectTime: number,\n ) => PendingQuery<MaybeRow[]>)\n | undefined = undefined;\n readonly #pendingRowRecordUpdates = new CustomKeyMap<RowID, RowRecord | null>(\n rowIDString,\n );\n readonly #forceUpdates = new CustomKeySet<RowID>(rowIDString);\n readonly #rowCache: RowRecordCache;\n readonly #loadAttemptIntervalMs: number;\n readonly #maxLoadAttempts: number;\n #rowCount: number = 0;\n readonly #pendingQueryUpdates = new Map<string, StringifiedQueriesRow>();\n readonly #pendingDesireUpdates = new Map<string, DesiresRow>();\n readonly #pendingQueryPartialUpdates = new Map<string, Partial<QueriesRow>>();\n\n constructor(\n lc: LogContext,\n cvrDb: PostgresDB,\n shard: ShardID,\n taskID: string,\n cvrID: string,\n failService: (e: unknown) => void,\n loadAttemptIntervalMs = LOAD_ATTEMPT_INTERVAL_MS,\n maxLoadAttempts = MAX_LOAD_ATTEMPTS,\n deferredRowFlushThreshold = 100, // somewhat arbitrary\n setTimeoutFn = setTimeout,\n ) {\n this.#failService = failService;\n this.#db = cvrDb;\n this.#schema = cvrSchema(shard);\n this.#taskID = taskID;\n this.#id = cvrID;\n this.#rowCache = new RowRecordCache(\n lc,\n cvrDb,\n shard,\n cvrID,\n failService,\n deferredRowFlushThreshold,\n setTimeoutFn,\n );\n this.#loadAttemptIntervalMs = loadAttemptIntervalMs;\n this.#maxLoadAttempts = maxLoadAttempts;\n }\n\n #cvr(table: string) {\n return this.#db(`${this.#schema}.${table}`);\n }\n\n #updateQueryFields(queryHash: string, fields: Partial<QueriesRow>): void {\n // Track as partial-only update for batched flush. Merge into any\n // pre-existing partial update for the same hash so independent callers\n // (e.g. updateQuery + updateRowSetSignature) don't clobber each other.\n const existing = this.#pendingQueryPartialUpdates.get(queryHash);\n this.#pendingQueryPartialUpdates.set(\n queryHash,\n existing ? {...existing, ...fields} : fields,\n );\n }\n\n load(lc: LogContext, lastConnectTime: number): Promise<CVR> {\n return startAsyncSpan(tracer, 'cvr.load', async () => {\n let err: RowsVersionBehindError | undefined;\n for (let i = 0; i < this.#maxLoadAttempts; i++) {\n if (i > 0) {\n await sleep(this.#loadAttemptIntervalMs);\n }\n const result = await this.#load(lc, lastConnectTime);\n if (result instanceof RowsVersionBehindError) {\n lc.info?.(`attempt ${i + 1}: ${String(result)}`);\n err = result;\n continue;\n }\n return result;\n }\n assert(err, 'Expected error to be set after retry loop exhausted');\n throw new ClientNotFoundError(\n `max attempts exceeded waiting for CVR@${err.cvrVersion} to catch up from ${err.rowsVersion}`,\n );\n });\n }\n\n async #load(\n lc: LogContext,\n lastConnectTime: number,\n ): Promise<CVR | RowsVersionBehindError> {\n const start = Date.now();\n\n const id = this.#id;\n const cvr: CVR = {\n id,\n version: EMPTY_CVR_VERSION,\n lastActive: 0,\n ttlClock: ttlClockFromNumber(0), // TTL clock starts at 0, not Date.now()\n replicaVersion: null,\n clients: {},\n queries: {},\n clientSchema: null,\n profileID: null,\n };\n\n const [instance, clientsRows, queryRows, desiresRows] = await runTx(\n this.#db,\n tx => {\n lc.debug?.(`CVR tx started after ${Date.now() - start} ms`);\n return [\n tx<\n (Omit<InstancesRow, 'clientGroupID'> & {\n profileID: string | null;\n deleted: boolean;\n rowsVersion: string | null;\n })[]\n >`SELECT cvr.\"version\",\n \"lastActive\",\n \"ttlClock\",\n \"replicaVersion\",\n \"owner\",\n \"grantedAt\",\n \"clientSchema\",\n \"profileID\",\n \"deleted\",\n rows.\"version\" as \"rowsVersion\"\n FROM ${this.#cvr('instances')} AS cvr\n LEFT JOIN ${this.#cvr('rowsVersion')} AS rows\n ON cvr.\"clientGroupID\" = rows.\"clientGroupID\"\n WHERE cvr.\"clientGroupID\" = ${id}`,\n tx<Pick<ClientsRow, 'clientID'>[]>`SELECT \"clientID\" FROM ${this.#cvr(\n 'clients',\n )}\n WHERE \"clientGroupID\" = ${id}`,\n tx<QueriesRow[]>`SELECT\n \"clientGroupID\",\n \"queryHash\",\n \"clientAST\",\n \"queryName\",\n \"queryArgs\",\n \"patchVersion\",\n \"transformationHash\",\n \"transformationVersion\",\n \"internal\",\n \"deleted\",\n \"rowSetSignature\"\n FROM ${this.#cvr('queries')}\n WHERE \"clientGroupID\" = ${id} AND deleted IS DISTINCT FROM true`,\n tx<DesiresRow[]>`SELECT\n \"clientGroupID\",\n \"clientID\",\n \"queryHash\",\n \"patchVersion\",\n \"deleted\",\n \"ttlMs\" AS \"ttl\",\n \"inactivatedAtMs\" AS \"inactivatedAt\"\n FROM ${this.#cvr('desires')}\n WHERE \"clientGroupID\" = ${id}`,\n ];\n },\n {mode: Mode.READONLY},\n );\n lc.debug?.(\n `CVR tx completed after ${Date.now() - start} ms ` +\n `(${clientsRows.length} clients, ${queryRows.length} queries, ${desiresRows.length} desires)`,\n );\n\n if (instance.length === 0) {\n // This is the first time we see this CVR.\n this.putInstance({\n version: cvr.version,\n lastActive: 0,\n ttlClock: ttlClockFromNumber(0), // TTL clock starts at 0 for new instances\n replicaVersion: null,\n clientSchema: null,\n profileID: null,\n });\n } else {\n assert(\n instance.length === 1,\n () => `Expected exactly one CVR instance, got ${instance.length}`,\n );\n const {\n version,\n lastActive,\n ttlClock,\n replicaVersion,\n owner,\n grantedAt,\n rowsVersion,\n clientSchema,\n profileID,\n deleted,\n } = instance[0];\n\n if (deleted) {\n throw new ClientNotFoundError(\n 'Client has been purged due to inactivity',\n );\n }\n\n if (owner !== this.#taskID) {\n if ((grantedAt ?? 0) > lastConnectTime) {\n throw new OwnershipError(owner, grantedAt, lastConnectTime);\n } else {\n // Fire-and-forget an ownership change to signal the current owner.\n // Note that the query is structured such that it only succeeds in the\n // correct conditions (i.e. gated on `grantedAt`).\n void this.#db`\n UPDATE ${this.#cvr('instances')} \n SET \"owner\" = ${this.#taskID}, \n \"grantedAt\" = ${lastConnectTime}\n WHERE \"clientGroupID\" = ${this.#id} AND\n (\"grantedAt\" IS NULL OR\n \"grantedAt\" <= to_timestamp(${lastConnectTime / 1000}))\n `\n .execute()\n .catch(this.#failService);\n }\n }\n\n if (version !== (rowsVersion ?? EMPTY_CVR_VERSION.stateVersion)) {\n // This will cause the load() method to wait for row catchup and retry.\n // Assuming the ownership signal succeeds, the current owner will stop\n // modifying the CVR and flush its pending row changes.\n return new RowsVersionBehindError(version, rowsVersion);\n }\n\n cvr.version = versionFromString(version);\n cvr.lastActive = lastActive;\n cvr.ttlClock = ttlClock;\n cvr.replicaVersion = replicaVersion;\n cvr.profileID = profileID;\n\n try {\n cvr.clientSchema =\n clientSchema === null\n ? null\n : v.parse(clientSchema, clientSchemaSchema);\n } catch (e) {\n throw new InvalidClientSchemaError(e);\n }\n }\n\n for (const row of clientsRows) {\n cvr.clients[row.clientID] = {\n id: row.clientID,\n desiredQueryIDs: [],\n };\n }\n\n for (const row of queryRows) {\n const query = asQuery(row);\n cvr.queries[row.queryHash] = query;\n }\n\n for (const row of desiresRows) {\n const client = cvr.clients[row.clientID];\n // Note: row.inactivatedAt is mapped from inactivatedAtMs in the SQL query\n if (!row.deleted && row.inactivatedAt === null) {\n if (client) {\n client.desiredQueryIDs.push(row.queryHash);\n } else {\n // This can happen if the client was deleted but the queries are still alive.\n lc.debug?.(\n `Not adding to desiredQueryIDs for client ${row.clientID} because it has been deleted.`,\n );\n }\n }\n\n const query = cvr.queries[row.queryHash];\n if (\n query &&\n query.type !== 'internal' &&\n (!row.deleted || row.inactivatedAt !== null)\n ) {\n query.clientState[row.clientID] = {\n inactivatedAt: row.inactivatedAt ?? undefined,\n ttl: clampTTL(row.ttl ?? DEFAULT_TTL_MS),\n version: versionFromString(row.patchVersion),\n };\n }\n }\n\n lc.info?.(\n `loaded cvr@${versionString(cvr.version)} (${Date.now() - start} ms)`,\n );\n\n // why do we not sort `desiredQueryIDs` here?\n\n return cvr;\n }\n\n getRowRecords(): Promise<ReadonlyMap<RowID, RowRecord>> {\n return this.#rowCache.getRowRecords();\n }\n\n putRowRecord(row: RowRecord): void {\n this.#pendingRowRecordUpdates.set(row.id, row);\n }\n\n /**\n * Note: Removing a row from the CVR should be represented by a\n * {@link putRowRecord()} with `refCounts: null` in order to properly\n * produce the appropriate delete patch when catching up old clients.\n *\n * This `delRowRecord()` method, on the other hand, is only used for\n * \"canceling\" the put of a row that was not in the CVR in the first place.\n */\n delRowRecord(id: RowID): void {\n this.#pendingRowRecordUpdates.set(id, null);\n }\n\n /**\n * Overrides the default logic that removes no-op writes and forces\n * the updates for the given row `ids`. This has no effect if there\n * are no corresponding puts or dels for the associated row records.\n */\n forceUpdates(...ids: RowID[]) {\n for (const id of ids) {\n this.#forceUpdates.add(id);\n }\n }\n\n /**\n * Updates the `ttlClock` of the CVR instance. The ttlClock starts at 0 when\n * the CVR instance is first created and increments based on elapsed time\n * since the base time established by the ViewSyncerService.\n */\n async updateTTLClock(ttlClock: TTLClock, lastActive: number): Promise<void> {\n await this.#db`UPDATE ${this.#cvr('instances')}\n SET \"lastActive\" = ${lastActive},\n \"ttlClock\" = ${ttlClock}\n WHERE \"clientGroupID\" = ${this.#id}`.execute();\n }\n\n /**\n * @returns This returns the current `ttlClock` of the CVR instance. The ttlClock\n * represents elapsed time since the instance was created (starting from 0).\n * If the CVR has never been initialized for this client group, it returns\n * `undefined`.\n */\n async getTTLClock(): Promise<TTLClock | undefined> {\n const result = await this.#db<Pick<InstancesRow, 'ttlClock'>[]>`\n SELECT \"ttlClock\" FROM ${this.#cvr('instances')}\n WHERE \"clientGroupID\" = ${this.#id}`.values();\n if (result.length === 0) {\n // This can happen if the CVR has not been initialized yet.\n return undefined;\n }\n assert(\n result.length === 1,\n () => `Expected exactly one rowsVersion result, got ${result.length}`,\n );\n return result[0][0];\n }\n\n putInstance({\n version,\n replicaVersion,\n lastActive,\n clientSchema,\n profileID,\n ttlClock,\n }: Pick<\n CVRSnapshot,\n | 'version'\n | 'replicaVersion'\n | 'lastActive'\n | 'clientSchema'\n | 'profileID'\n | 'ttlClock'\n >): void {\n // Overwrite any previously queued instance write — only the last call\n // matters since they all target the same row.\n this.#pendingInstanceWrite = (tx, lastConnectTime) => {\n const change: InstancesRow = {\n clientGroupID: this.#id,\n version: versionString(version),\n lastActive,\n ttlClock,\n replicaVersion,\n owner: this.#taskID,\n grantedAt: lastConnectTime,\n clientSchema,\n profileID,\n };\n return tx`\n INSERT INTO ${this.#cvr('instances')} ${tx(change)} \n ON CONFLICT (\"clientGroupID\") DO UPDATE SET ${tx(change)}`;\n };\n }\n\n markQueryAsDeleted(version: CVRVersion, queryPatch: QueryPatch): void {\n this.#updateQueryFields(queryPatch.id, {\n patchVersion: versionString(version),\n deleted: true,\n transformationHash: null,\n transformationVersion: null,\n });\n }\n\n putQuery(query: QueryRecord): void {\n const change = queryRecordToQueryRow(this.#id, query);\n\n const c = {\n ...change,\n // Pre-stringify queryArgs to handle postgres.js boolean array bug\n queryArgs:\n change.queryArgs !== null ? JSON.stringify(change.queryArgs) : null,\n transformationHash: change.transformationHash ?? null,\n transformationVersion: change.transformationVersion ?? null,\n deleted: change.deleted ?? false,\n };\n this.#pendingQueryUpdates.set(query.id, c);\n }\n\n updateQuery(query: QueryRecord) {\n const maybeVersionString = (v: CVRVersion | undefined) =>\n v ? versionString(v) : null;\n this.#updateQueryFields(query.id, {\n patchVersion:\n query.type === 'internal'\n ? null\n : maybeVersionString(query.patchVersion),\n transformationHash: query.transformationHash ?? null,\n transformationVersion: maybeVersionString(query.transformationVersion),\n deleted: false,\n });\n }\n\n updateRowSetSignature(queryHash: string, signature: string): void {\n this.#updateQueryFields(queryHash, {rowSetSignature: signature});\n }\n\n insertClient(client: ClientRecord): void {\n const change: ClientsRow = {\n clientGroupID: this.#id,\n clientID: client.id,\n };\n\n this.#writes.add({\n stats: {clients: 1},\n write: tx => tx`INSERT INTO ${this.#cvr('clients')} ${tx(change)}`,\n });\n }\n\n deleteClient(clientID: string) {\n this.#writes.add({\n stats: {clients: 1},\n write: sql =>\n sql`DELETE FROM ${this.#cvr('clients')}\n WHERE \"clientGroupID\" = ${this.#id}\n AND \"clientID\" = ${clientID}`,\n });\n }\n\n putDesiredQuery(\n newVersion: CVRVersion,\n query: {id: string},\n client: {id: string},\n deleted: boolean,\n inactivatedAt: TTLClock | undefined,\n ttl: number,\n ): void {\n const {ttlMs, inactivatedAtMs} = convertTTLValues(inactivatedAt, ttl);\n\n const change: DesiresRow = {\n clientGroupID: this.#id,\n clientID: client.id,\n deleted,\n inactivatedAt: inactivatedAtMs,\n patchVersion: versionString(newVersion),\n queryHash: query.id,\n ttl: ttlMs,\n };\n\n // Use composite key to deduplicate/replace entries for the same client-query pair\n const key = `${client.id}:${query.id}`;\n this.#pendingDesireUpdates.set(key, change);\n }\n\n catchupRowPatches(\n lc: LogContext,\n afterVersion: NullableCVRVersion,\n upToCVR: CVRSnapshot,\n current: CVRVersion,\n excludeQueryHashes: string[] = [],\n ): AsyncGenerator<RowsRow[], void, undefined> {\n return this.#rowCache.catchupRowPatches(\n lc,\n afterVersion,\n upToCVR,\n current,\n excludeQueryHashes,\n );\n }\n\n async catchupConfigPatches(\n lc: LogContext,\n afterVersion: NullableCVRVersion,\n upToCVR: CVRSnapshot,\n current: CVRVersion,\n ): Promise<PatchToVersion[]> {\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 config patches for clients from ${start}`);\n\n const reader = new TransactionPool(lc, {mode: 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.#id, current),\n );\n\n const [allDesires, queryRows] = await reader.processReadTask(tx =>\n Promise.all([\n tx<DesiresRow[]>`\n SELECT\n \"clientGroupID\",\n \"clientID\",\n \"queryHash\",\n \"patchVersion\",\n \"deleted\",\n \"ttl\",\n \"inactivatedAt\"\n FROM ${this.#cvr('desires')}\n WHERE \"clientGroupID\" = ${this.#id}\n AND \"patchVersion\" > ${start}\n AND \"patchVersion\" <= ${end}`,\n tx<Pick<QueriesRow, 'deleted' | 'queryHash' | 'patchVersion'>[]>`\n SELECT deleted, \"queryHash\", \"patchVersion\" FROM ${this.#cvr('queries')}\n WHERE \"clientGroupID\" = ${this.#id}\n AND \"patchVersion\" > ${start}\n AND \"patchVersion\" <= ${end}`,\n ]),\n );\n\n const patches: PatchToVersion[] = [];\n for (const row of queryRows) {\n const {queryHash: id} = row;\n const patch: Patch = row.deleted\n ? {type: 'query', op: 'del', id}\n : {type: 'query', op: 'put', id};\n const v = row.patchVersion;\n assert(v, 'patchVersion must be set for query patches');\n patches.push({patch, toVersion: versionFromString(v)});\n }\n for (const row of allDesires) {\n const {clientID, queryHash: id} = row;\n const patch: Patch = row.deleted\n ? {type: 'query', op: 'del', id, clientID}\n : {type: 'query', op: 'put', id, clientID};\n patches.push({patch, toVersion: versionFromString(row.patchVersion)});\n }\n\n lc.debug?.(\n `${patches.length} config patches (${Date.now() - startMs} ms)`,\n );\n return patches;\n } finally {\n reader.setDone();\n }\n }\n\n #flushQueries(\n tx: PostgresTransaction,\n lc: LogContext,\n ): PendingQuery<Row[]>[] {\n // Merge partial updates into full updates\n const partialOnly = new Map<string, Partial<QueriesRow>>();\n for (const [queryHash, partial] of this.#pendingQueryPartialUpdates) {\n const existing = this.#pendingQueryUpdates.get(queryHash);\n if (existing) {\n // Merge partial into full update\n Object.assign(existing, partial);\n } else {\n // Track partial-only updates to batch separately\n partialOnly.set(queryHash, partial);\n }\n }\n\n const queries: PendingQuery<Row[]>[] = [];\n\n // Batch full updates\n if (this.#pendingQueryUpdates.size > 0) {\n const rows = [...this.#pendingQueryUpdates.values()];\n lc.debug?.(`Batch flushing ${rows.length} full query updates`);\n\n queries.push(tx`\n INSERT INTO ${this.#cvr('queries')} (\n \"clientGroupID\",\n \"queryHash\",\n \"clientAST\",\n \"queryName\",\n \"queryArgs\",\n \"patchVersion\",\n \"transformationHash\",\n \"transformationVersion\",\n \"internal\",\n \"deleted\",\n \"rowSetSignature\"\n )\n SELECT\n \"clientGroupID\",\n \"queryHash\",\n \"clientAST\",\n \"queryName\",\n CASE\n WHEN \"queryArgs\" IS NULL THEN NULL\n ELSE \"queryArgs\"::json\n END,\n \"patchVersion\",\n \"transformationHash\",\n \"transformationVersion\",\n \"internal\",\n \"deleted\",\n \"rowSetSignature\"\n FROM json_to_recordset(${rows}) AS x(\n \"clientGroupID\" TEXT,\n \"queryHash\" TEXT,\n \"clientAST\" JSONB,\n \"queryName\" TEXT,\n \"queryArgs\" TEXT,\n \"patchVersion\" TEXT,\n \"transformationHash\" TEXT,\n \"transformationVersion\" TEXT,\n \"internal\" BOOLEAN,\n \"deleted\" BOOLEAN,\n \"rowSetSignature\" TEXT\n )\n ON CONFLICT (\"clientGroupID\", \"queryHash\") DO UPDATE SET\n \"clientAST\" = excluded.\"clientAST\",\n \"queryName\" = excluded.\"queryName\",\n \"queryArgs\" = CASE\n WHEN excluded.\"queryArgs\" IS NULL THEN NULL\n ELSE excluded.\"queryArgs\"::json\n END,\n \"patchVersion\" = excluded.\"patchVersion\",\n \"transformationHash\" = excluded.\"transformationHash\",\n \"transformationVersion\" = excluded.\"transformationVersion\",\n \"internal\" = excluded.\"internal\",\n \"deleted\" = excluded.\"deleted\",\n \"rowSetSignature\" = excluded.\"rowSetSignature\"\n `);\n }\n\n // Batch partial-only updates\n if (partialOnly.size > 0) {\n lc.debug?.(`Batch flushing ${partialOnly.size} partial query updates`);\n const rows = Array.from(\n partialOnly.entries(),\n ([queryHash, partial]) => ({\n clientGroupID: this.#id,\n queryHash,\n patchVersionSet: partial.patchVersion !== undefined,\n patchVersion: partial.patchVersion ?? null,\n deletedSet: partial.deleted !== undefined,\n deleted: partial.deleted ?? null,\n transformationHashSet: partial.transformationHash !== undefined,\n transformationHash: partial.transformationHash ?? null,\n transformationVersionSet: partial.transformationVersion !== undefined,\n transformationVersion: partial.transformationVersion ?? null,\n rowSetSignatureSet: partial.rowSetSignature !== undefined,\n rowSetSignature: partial.rowSetSignature ?? null,\n }),\n );\n queries.push(tx`\n UPDATE ${this.#cvr('queries')} AS q\n SET\n \"patchVersion\" = CASE\n WHEN u.\"patchVersionSet\" THEN u.\"patchVersion\"\n ELSE q.\"patchVersion\"\n END,\n \"deleted\" = CASE\n WHEN u.\"deletedSet\" THEN u.\"deleted\"\n ELSE q.\"deleted\"\n END,\n \"transformationHash\" = CASE\n WHEN u.\"transformationHashSet\" THEN u.\"transformationHash\"\n ELSE q.\"transformationHash\"\n END,\n \"transformationVersion\" = CASE\n WHEN u.\"transformationVersionSet\" THEN u.\"transformationVersion\"\n ELSE q.\"transformationVersion\"\n END,\n \"rowSetSignature\" = CASE\n WHEN u.\"rowSetSignatureSet\" THEN u.\"rowSetSignature\"\n ELSE q.\"rowSetSignature\"\n END\n FROM json_to_recordset(${rows}) AS u(\n \"clientGroupID\" TEXT,\n \"queryHash\" TEXT,\n \"patchVersionSet\" BOOLEAN,\n \"patchVersion\" TEXT,\n \"deletedSet\" BOOLEAN,\n \"deleted\" BOOLEAN,\n \"transformationHashSet\" BOOLEAN,\n \"transformationHash\" TEXT,\n \"transformationVersionSet\" BOOLEAN,\n \"transformationVersion\" TEXT,\n \"rowSetSignatureSet\" BOOLEAN,\n \"rowSetSignature\" TEXT\n )\n WHERE q.\"clientGroupID\" = u.\"clientGroupID\"\n AND q.\"queryHash\" = u.\"queryHash\"\n `);\n }\n\n return queries;\n }\n\n #flushDesires(\n tx: PostgresTransaction,\n lc: LogContext,\n ): PendingQuery<Row[]> | null {\n if (this.#pendingDesireUpdates.size === 0) {\n return null;\n }\n\n const rows = Array.from(this.#pendingDesireUpdates.values(), row => {\n const {ttlInterval, ttlMs, inactivatedAtTimestamp, inactivatedAtMs} =\n convertTTLValues(row.inactivatedAt ?? undefined, row.ttl ?? -1);\n return {\n clientGroupID: row.clientGroupID,\n clientID: row.clientID,\n queryHash: row.queryHash,\n patchVersion: row.patchVersion,\n deleted: row.deleted,\n ttl: ttlInterval,\n ttlMs,\n inactivatedAt: inactivatedAtTimestamp,\n inactivatedAtMs,\n };\n });\n\n lc.debug?.(`Batch flushing ${rows.length} desire updates`);\n\n return tx`\n INSERT INTO ${this.#cvr('desires')} (\n \"clientGroupID\",\n \"clientID\",\n \"queryHash\",\n \"patchVersion\",\n \"deleted\",\n \"ttl\",\n \"ttlMs\",\n \"inactivatedAt\",\n \"inactivatedAtMs\"\n )\n SELECT\n \"clientGroupID\",\n \"clientID\",\n \"queryHash\",\n \"patchVersion\",\n \"deleted\",\n \"ttl\",\n \"ttlMs\",\n CASE\n WHEN \"inactivatedAt\" IS NULL THEN NULL\n -- Divide by 1000 because postgres.js serializeTimestamp treats numbers as ms\n -- and to_timestamp expects seconds. This matches non-batched behavior.\n ELSE to_timestamp(\"inactivatedAt\" / 1000.0)\n END,\n \"inactivatedAtMs\"\n FROM json_to_recordset(${rows}) AS x(\n \"clientGroupID\" TEXT,\n \"clientID\" TEXT,\n \"queryHash\" TEXT,\n \"patchVersion\" TEXT,\n \"deleted\" BOOLEAN,\n \"ttl\" INTERVAL,\n \"ttlMs\" DOUBLE PRECISION,\n \"inactivatedAt\" DOUBLE PRECISION,\n \"inactivatedAtMs\" DOUBLE PRECISION\n )\n ON CONFLICT (\"clientGroupID\", \"clientID\", \"queryHash\") DO UPDATE SET\n \"patchVersion\" = excluded.\"patchVersion\",\n \"deleted\" = excluded.\"deleted\",\n \"ttl\" = excluded.\"ttl\",\n \"ttlMs\" = excluded.\"ttlMs\",\n \"inactivatedAt\" = excluded.\"inactivatedAt\",\n \"inactivatedAtMs\" = excluded.\"inactivatedAtMs\"\n `;\n }\n\n async #checkVersionAndOwnership(\n lc: LogContext,\n tx: PostgresTransaction,\n expectedCurrentVersion: CVRVersion,\n lastConnectTime: number,\n ): Promise<void> {\n const start = Date.now();\n lc.debug?.('checking cvr version and ownership');\n const result = await tx<\n Pick<InstancesRow, 'version' | 'owner' | 'grantedAt'>[]\n >`SELECT \"version\", \"owner\", \"grantedAt\" FROM ${this.#cvr('instances')}\n WHERE \"clientGroupID\" = ${this.#id}\n FOR UPDATE`;\n const expected = versionString(expectedCurrentVersion);\n const {version, owner, grantedAt} =\n result.length > 0\n ? result[0]\n : {\n version: EMPTY_CVR_VERSION.stateVersion,\n owner: null,\n grantedAt: null,\n };\n lc.debug?.(\n 'checked cvr version and ownership in ' + (Date.now() - start) + ' ms',\n );\n if (owner !== this.#taskID && (grantedAt ?? 0) > lastConnectTime) {\n throw new OwnershipError(owner, grantedAt, lastConnectTime);\n }\n if (version !== expected) {\n throw new ConcurrentModificationException(expected, version);\n }\n }\n\n async #flush(\n lc: LogContext,\n expectedCurrentVersion: CVRVersion,\n cvr: CVRSnapshot,\n lastConnectTime: number,\n ): Promise<CVRFlushStats | null> {\n const stats: CVRFlushStats = {\n instances: 0,\n queries: 0,\n desires: 0,\n clients: 0,\n rows: 0,\n rowsDeferred: 0,\n statements: 0,\n };\n if (this.#pendingRowRecordUpdates.size) {\n const existingRowRecords = await this.getRowRecords();\n this.#rowCount = existingRowRecords.size;\n for (const [id, row] of this.#pendingRowRecordUpdates.entries()) {\n if (this.#forceUpdates.has(id)) {\n continue;\n }\n const existing = existingRowRecords.get(id);\n if (\n // Don't delete or add an unreferenced row if it's not in the CVR.\n (existing === undefined && !row?.refCounts) ||\n // Don't write a row record that exactly matches what's in the CVR.\n deepEqual(\n (row ?? undefined) as ReadonlyJSONValue | undefined,\n existing as ReadonlyJSONValue | undefined,\n )\n ) {\n this.#pendingRowRecordUpdates.delete(id);\n }\n }\n }\n if (\n this.#pendingRowRecordUpdates.size === 0 &&\n this.#writes.size === 0 &&\n this.#pendingInstanceWrite === undefined &&\n this.#pendingQueryUpdates.size === 0 &&\n this.#pendingQueryPartialUpdates.size === 0 &&\n this.#pendingDesireUpdates.size === 0\n ) {\n return null;\n }\n // Note: The CVR instance itself is only updated if there are material\n // changes (i.e. changes to the CVR contents) to flush.\n this.putInstance(cvr);\n const start = Date.now();\n lc.debug?.('flush tx beginning');\n\n // Use an async callback so we can await the version/ownership check and\n // validate it INSIDE the transaction. If validation fails, the exception\n // causes postgres.js to ROLLBACK, ensuring no writes are committed on error.\n const results = await runTx(\n this.#db,\n async tx => {\n lc.debug?.(`flush tx begun after ${Date.now() - start} ms`);\n\n // Acquire row-level lock and validate version/ownership before queuing writes.\n // Throwing here (inside the begin callback) rolls back the transaction so that\n // no writes are committed when concurrent modification or ownership errors occur.\n await this.#checkVersionAndOwnership(\n lc,\n tx,\n expectedCurrentVersion,\n lastConnectTime,\n );\n\n const writeQueries = [];\n if (this.#pendingInstanceWrite) {\n writeQueries.push(this.#pendingInstanceWrite(tx, lastConnectTime));\n stats.instances++;\n stats.statements++;\n }\n for (const write of this.#writes) {\n stats.clients += write.stats.clients ?? 0;\n stats.rows += write.stats.rows ?? 0;\n\n writeQueries.push(write.write(tx, lastConnectTime));\n stats.statements++;\n }\n\n // Batch flush config writes\n // Flush queries first (desires depend on queries via foreign key)\n const hasQueryUpdates =\n this.#pendingQueryUpdates.size > 0 ||\n this.#pendingQueryPartialUpdates.size > 0;\n\n const desireFlush = this.#flushDesires(tx, lc);\n\n let queryFlushes: PendingQuery<Row[]>[] = [];\n if (hasQueryUpdates) {\n queryFlushes = this.#flushQueries(tx, lc);\n\n // Count both full updates and partial-only updates\n const partialOnlyCount = [\n ...this.#pendingQueryPartialUpdates.keys(),\n ].filter(key => !this.#pendingQueryUpdates.has(key)).length;\n\n stats.queries = this.#pendingQueryUpdates.size + partialOnlyCount;\n stats.statements +=\n (this.#pendingQueryUpdates.size > 0 ? 1 : 0) +\n (partialOnlyCount > 0 ? 1 : 0);\n\n if (desireFlush) {\n stats.desires = this.#pendingDesireUpdates.size;\n stats.statements++;\n }\n } else if (desireFlush) {\n stats.desires = this.#pendingDesireUpdates.size;\n stats.statements++;\n }\n\n const rowUpdates = this.#rowCache.executeRowUpdates(\n tx,\n cvr.version,\n this.#pendingRowRecordUpdates,\n 'allow-defer',\n lc,\n );\n stats.statements += rowUpdates.length;\n\n // Pipeline writes now that the version check has passed.\n const pipelined = [\n ...writeQueries,\n ...queryFlushes,\n ...(desireFlush ? [desireFlush] : []),\n ...rowUpdates,\n ];\n\n lc.debug?.(`returning ${pipelined.length} queries for pipelining`);\n\n // Explicitly await all pipelined queries. When the begin callback is async,\n // postgres.js does not call Promise.all() on the return value the way it does\n // for sync callbacks, so we must do it ourselves.\n return Promise.all(pipelined);\n },\n {mode: Mode.READ_COMMITTED},\n );\n\n lc.debug?.(`flush tx completed after ${Date.now() - start} ms`);\n\n // Calculate how many row update queries were in the pipeline.\n // Note: the version check was awaited separately and is not in the results array.\n const baseQueries =\n (this.#pendingInstanceWrite ? 1 : 0) +\n this.#writes.size +\n (this.#pendingQueryUpdates.size > 0 ? 1 : 0) +\n ([...this.#pendingQueryPartialUpdates.keys()].filter(\n key => !this.#pendingQueryUpdates.has(key),\n ).length > 0\n ? 1\n : 0) +\n (this.#pendingDesireUpdates.size > 0 ? 1 : 0);\n const rowUpdateCount = results.length - baseQueries;\n\n const rowsFlushed = rowUpdateCount > 0;\n if (!rowsFlushed) {\n stats.rowsDeferred = this.#pendingRowRecordUpdates.size;\n } else {\n stats.rows += this.#pendingRowRecordUpdates.size;\n }\n\n this.#rowCount = await this.#rowCache.apply(\n this.#pendingRowRecordUpdates,\n cvr.version,\n rowsFlushed,\n );\n recordRowsSynced(this.#rowCount);\n\n return stats;\n }\n\n get rowCount(): number {\n return this.#rowCount;\n }\n\n async flush(\n lc: LogContext,\n expectedCurrentVersion: CVRVersion,\n cvr: CVRSnapshot,\n lastConnectTime: number,\n ): Promise<CVRFlushStats | null> {\n const start = performance.now();\n lc = lc.withContext('cvrFlushID', flushCounter++);\n try {\n const stats = await this.#flush(\n lc,\n expectedCurrentVersion,\n cvr,\n lastConnectTime,\n );\n if (stats) {\n const elapsed = performance.now() - start;\n lc.info?.(\n `flushed cvr@${versionString(cvr.version)} ` +\n `${JSON.stringify(stats)} in (${elapsed} ms)`,\n );\n this.#rowCache.recordSyncFlushStats(stats, elapsed);\n }\n return stats;\n } catch (e) {\n // Clear cached state if an error (e.g. ConcurrentModificationException) is encountered.\n this.#rowCache.clear();\n throw e;\n } finally {\n this.#writes.clear();\n this.#pendingInstanceWrite = undefined;\n this.#pendingRowRecordUpdates.clear();\n this.#forceUpdates.clear();\n this.#pendingQueryUpdates.clear();\n this.#pendingDesireUpdates.clear();\n this.#pendingQueryPartialUpdates.clear();\n }\n }\n\n hasPendingUpdates(): boolean {\n return this.#rowCache.hasPendingUpdates();\n }\n\n /** Resolves when all pending updates are flushed. */\n flushed(lc: LogContext): Promise<void> {\n return this.#rowCache.flushed(lc);\n }\n\n async inspectQueries(\n lc: LogContext,\n ttlClock: TTLClock,\n clientID?: string,\n ): Promise<InspectQueryRow[]> {\n const db = this.#db;\n const clientGroupID = this.#id;\n\n const reader = new TransactionPool(lc, {mode: Mode.READONLY}).run(db);\n try {\n return await reader.processReadTask(\n tx => tx<InspectQueryRow[]>`\n SELECT DISTINCT ON (d.\"clientID\", d.\"queryHash\")\n d.\"clientID\",\n d.\"queryHash\" AS \"queryID\",\n COALESCE(d.\"ttlMs\", ${DEFAULT_TTL_MS}) AS \"ttl\",\n d.\"inactivatedAtMs\" AS \"inactivatedAt\",\n (SELECT COUNT(*)::INT FROM ${this.#cvr('rows')} r \n WHERE r.\"clientGroupID\" = d.\"clientGroupID\" \n AND r.\"refCounts\" ? d.\"queryHash\") AS \"rowCount\",\n q.\"clientAST\" AS \"ast\",\n (q.\"patchVersion\" IS NOT NULL) AS \"got\",\n COALESCE(d.\"deleted\", FALSE) AS \"deleted\",\n q.\"queryName\" AS \"name\",\n q.\"queryArgs\" AS \"args\"\n FROM ${this.#cvr('desires')} d\n LEFT JOIN ${this.#cvr('queries')} q\n ON q.\"clientGroupID\" = d.\"clientGroupID\"\n AND q.\"queryHash\" = d.\"queryHash\"\n WHERE d.\"clientGroupID\" = ${clientGroupID}\n ${clientID ? tx`AND d.\"clientID\" = ${clientID}` : tx``}\n AND NOT (\n d.\"inactivatedAtMs\" IS NOT NULL \n AND d.\"ttlMs\" IS NOT NULL \n AND (d.\"inactivatedAtMs\" + d.\"ttlMs\") <= ${ttlClockAsNumber(ttlClock)}\n )\n ORDER BY d.\"clientID\", d.\"queryHash\"`,\n );\n } finally {\n reader.setDone();\n }\n }\n}\n\n/**\n * This is similar to {@link CVRStore.#checkVersionAndOwnership} except\n * that it only checks the version and is suitable for snapshot reads\n * (i.e. by doing a plain `SELECT` rather than a `SELECT ... FOR UPDATE`).\n */\nexport async function checkVersion(\n tx: PostgresTransaction,\n schema: string,\n clientGroupID: string,\n expectedCurrentVersion: CVRVersion,\n): Promise<void> {\n const expected = versionString(expectedCurrentVersion);\n const result = await tx<Pick<InstancesRow, 'version'>[]>`\n SELECT version FROM ${tx(schema)}.instances \n WHERE \"clientGroupID\" = ${clientGroupID}`;\n const {version} =\n result.length > 0 ? result[0] : {version: EMPTY_CVR_VERSION.stateVersion};\n if (version !== expected) {\n throw new ConcurrentModificationException(expected, version);\n }\n}\n\nexport class ClientNotFoundError extends ProtocolErrorWithLevel {\n constructor(message: string) {\n super(\n {\n kind: ErrorKind.ClientNotFound,\n message,\n origin: ErrorOrigin.ZeroCache,\n },\n 'warn',\n );\n }\n}\n\nexport class ConcurrentModificationException extends ProtocolErrorWithLevel {\n readonly name = 'ConcurrentModificationException';\n\n constructor(expectedVersion: string, actualVersion: string) {\n super(\n {\n kind: ErrorKind.Internal,\n message: `CVR has been concurrently modified. Expected ${expectedVersion}, got ${actualVersion}`,\n origin: ErrorOrigin.ZeroCache,\n },\n 'warn',\n );\n }\n}\n\nexport class OwnershipError extends ProtocolErrorWithLevel {\n readonly name = 'OwnershipError';\n\n constructor(\n owner: string | null,\n grantedAt: number | null,\n lastConnectTime: number,\n ) {\n super(\n {\n kind: ErrorKind.Rehome,\n message:\n `CVR ownership was transferred to ${owner} at ` +\n `${new Date(grantedAt ?? 0).toISOString()} ` +\n `(last connect time: ${new Date(lastConnectTime).toISOString()})`,\n maxBackoffMs: 0,\n origin: ErrorOrigin.ZeroCache,\n },\n 'info',\n );\n }\n}\n\nexport class InvalidClientSchemaError extends ProtocolErrorWithLevel {\n readonly name = 'InvalidClientSchemaError';\n\n constructor(cause: unknown) {\n super(\n {\n kind: ErrorKind.SchemaVersionNotSupported,\n message: `Could not parse clientSchema stored in CVR: ${String(cause)}`,\n origin: ErrorOrigin.ZeroCache,\n },\n 'warn',\n {cause},\n );\n }\n}\n\nexport class RowsVersionBehindError extends Error {\n readonly name = 'RowsVersionBehindError';\n readonly cvrVersion: string;\n readonly rowsVersion: string | null;\n\n constructor(cvrVersion: string, rowsVersion: string | null) {\n super(`rowsVersion (${rowsVersion}) is behind CVR ${cvrVersion}`);\n this.cvrVersion = cvrVersion;\n this.rowsVersion = rowsVersion;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cvr-store.js","names":["#schema","#taskID","#id","#failService","#db","#writes","#pendingRowRecordUpdates","#forceUpdates","#rowCache","#loadAttemptIntervalMs","#maxLoadAttempts","#pendingQueryUpdates","#pendingDesireUpdates","#pendingQueryPartialUpdates","#load","#cvr","#pendingInstanceWrite","#updateQueryFields","#checkVersionAndOwnership","#flush","#rowCount","#flushDesires","#flushQueries"],"sources":["../../../../../../zero-cache/src/services/view-syncer/cvr-store.ts"],"sourcesContent":["import {trace} from '@opentelemetry/api';\nimport type {LogContext} from '@rocicorp/logger';\nimport type {MaybeRow, PendingQuery, Row} from 'postgres';\nimport {startAsyncSpan} from '../../../../otel/src/span.ts';\nimport {version} from '../../../../otel/src/version.ts';\nimport {assert} from '../../../../shared/src/asserts.ts';\nimport {CustomKeyMap} from '../../../../shared/src/custom-key-map.ts';\nimport {CustomKeySet} from '../../../../shared/src/custom-key-set.ts';\nimport {\n deepEqual,\n type ReadonlyJSONValue,\n} from '../../../../shared/src/json.ts';\nimport {sleep} from '../../../../shared/src/sleep.ts';\nimport * as v from '../../../../shared/src/valita.ts';\nimport {astSchema} from '../../../../zero-protocol/src/ast.ts';\nimport {clientSchemaSchema} from '../../../../zero-protocol/src/client-schema.ts';\nimport {ErrorKind} from '../../../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../../../zero-protocol/src/error-origin.ts';\nimport type {InspectQueryRow} from '../../../../zero-protocol/src/inspect-down.ts';\nimport {clampTTL, DEFAULT_TTL_MS} from '../../../../zql/src/query/ttl.ts';\nimport * as Mode from '../../db/mode-enum.ts';\nimport {runTx} from '../../db/run-transaction.ts';\nimport {TransactionPool} from '../../db/transaction-pool.ts';\nimport {recordRowsSynced} from '../../server/anonymous-otel-start.ts';\nimport {ProtocolErrorWithLevel} from '../../types/error-with-level.ts';\nimport type {PostgresDB, PostgresTransaction} from '../../types/pg.ts';\nimport {rowIDString} from '../../types/row-key.ts';\nimport {cvrSchema, type ShardID} from '../../types/shards.ts';\nimport type {Patch, PatchToVersion} from './client-handler.ts';\nimport type {CVR, CVRSnapshot} from './cvr.ts';\nimport {RowRecordCache} from './row-record-cache.ts';\nimport {\n type ClientsRow,\n type DesiresRow,\n type InstancesRow,\n type QueriesRow,\n type RowsRow,\n} from './schema/cvr.ts';\nimport {\n type ClientQueryRecord,\n type ClientRecord,\n cmpVersions,\n type CustomQueryRecord,\n type CVRVersion,\n EMPTY_CVR_VERSION,\n type InternalQueryRecord,\n type NullableCVRVersion,\n type QueryPatch,\n type QueryRecord,\n queryRecordToQueryRow,\n type RowID,\n type RowRecord,\n versionFromString,\n versionString,\n} from './schema/types.ts';\nimport {\n type TTLClock,\n ttlClockAsNumber,\n ttlClockFromNumber,\n} from './ttl-clock.ts';\n\nexport type CVRFlushStats = {\n instances: number;\n queries: number;\n desires: number;\n clients: number;\n rows: number;\n rowsDeferred: number;\n statements: number;\n};\n\nlet flushCounter = 0;\n\n/**\n * Convert TTL/timestamp values for both old (seconds-based) and new (ms-based) columns.\n * Old columns: inactivatedAt (TIMESTAMPTZ), ttl (INTERVAL) - need conversion ms->seconds\n * New columns: inactivatedAtMs (DOUBLE PRECISION), ttlMs (DOUBLE PRECISION) - store ms directly\n */\nfunction convertTTLValues(\n inactivatedAt: TTLClock | undefined,\n ttl: number,\n): {\n ttlInterval: number | null;\n ttlMs: number | null;\n inactivatedAtTimestamp: TTLClock | null;\n inactivatedAtMs: TTLClock | null;\n} {\n return {\n ttlInterval: ttl < 0 ? null : ttl / 1000, // INTERVAL needs seconds\n ttlMs: ttl < 0 ? null : ttl, // New column stores ms directly\n inactivatedAtTimestamp:\n inactivatedAt === undefined\n ? null\n : ttlClockFromNumber(ttlClockAsNumber(inactivatedAt) / 1000),\n inactivatedAtMs: inactivatedAt ?? null,\n };\n}\n\nconst tracer = trace.getTracer('cvr-store', version);\n\n/**\n * QueriesRow with queryArgs as a stringified JSON value.\n * Used for batched config writes where queryArgs are pre-stringified\n * to handle the postgres.js boolean array bug.\n */\ntype StringifiedQueriesRow = Omit<QueriesRow, 'queryArgs'> & {\n queryArgs: string | null;\n};\n\nfunction asQuery(row: QueriesRow): QueryRecord {\n const maybeVersion = (s: string | null) =>\n s === null ? undefined : versionFromString(s);\n\n // Only attach rowSetSignature when the column is non-null, so existing\n // snapshots that don't include the field don't break.\n const sigField = row.rowSetSignature\n ? {rowSetSignature: row.rowSetSignature}\n : {};\n\n if (row.clientAST === null) {\n // custom query\n assert(\n row.queryName !== null && row.queryArgs !== null,\n 'queryName and queryArgs must be set for custom queries',\n );\n return {\n type: 'custom',\n id: row.queryHash,\n name: row.queryName,\n args: row.queryArgs,\n patchVersion: maybeVersion(row.patchVersion),\n clientState: {},\n transformationHash: row.transformationHash ?? undefined,\n transformationVersion: maybeVersion(row.transformationVersion),\n ...sigField,\n } satisfies CustomQueryRecord;\n }\n\n const ast = astSchema.parse(row.clientAST);\n return row.internal\n ? ({\n type: 'internal',\n id: row.queryHash,\n ast,\n transformationHash: row.transformationHash ?? undefined,\n transformationVersion: maybeVersion(row.transformationVersion),\n ...sigField,\n } satisfies InternalQueryRecord)\n : ({\n type: 'client',\n id: row.queryHash,\n ast,\n patchVersion: maybeVersion(row.patchVersion),\n clientState: {},\n transformationHash: row.transformationHash ?? undefined,\n transformationVersion: maybeVersion(row.transformationVersion),\n ...sigField,\n } satisfies ClientQueryRecord);\n}\n\n// The time to wait between load attempts.\nconst LOAD_ATTEMPT_INTERVAL_MS = 500;\n// The maximum number of load() attempts if the rowsVersion is behind.\n// This currently results in a maximum catchup time of ~5 seconds, after\n// which we give up and consider the CVR invalid.\n//\n// TODO: Make this configurable with something like --max-catchup-wait-ms,\n// as it is technically application specific.\nconst MAX_LOAD_ATTEMPTS = 10;\n\nexport class CVRStore {\n readonly #schema: string;\n readonly #taskID: string;\n readonly #id: string;\n readonly #failService: (e: unknown) => void;\n readonly #db: PostgresDB;\n readonly #writes: Set<{\n stats: Partial<CVRFlushStats>;\n write: (\n tx: PostgresTransaction,\n lastConnectTime: number,\n ) => PendingQuery<MaybeRow[]>;\n }> = new Set();\n // Stored separately so repeated putInstance() calls (e.g. setClientSchema,\n // setProfileID, and the final call in #flush) replace each other rather than\n // accumulating as independent statements in #writes.\n #pendingInstanceWrite:\n | ((\n tx: PostgresTransaction,\n lastConnectTime: number,\n ) => PendingQuery<MaybeRow[]>)\n | undefined = undefined;\n readonly #pendingRowRecordUpdates = new CustomKeyMap<RowID, RowRecord | null>(\n rowIDString,\n );\n readonly #forceUpdates = new CustomKeySet<RowID>(rowIDString);\n readonly #rowCache: RowRecordCache;\n readonly #loadAttemptIntervalMs: number;\n readonly #maxLoadAttempts: number;\n #rowCount: number = 0;\n readonly #pendingQueryUpdates = new Map<string, StringifiedQueriesRow>();\n readonly #pendingDesireUpdates = new Map<string, DesiresRow>();\n readonly #pendingQueryPartialUpdates = new Map<string, Partial<QueriesRow>>();\n\n constructor(\n lc: LogContext,\n cvrDb: PostgresDB,\n shard: ShardID,\n taskID: string,\n cvrID: string,\n failService: (e: unknown) => void,\n loadAttemptIntervalMs = LOAD_ATTEMPT_INTERVAL_MS,\n maxLoadAttempts = MAX_LOAD_ATTEMPTS,\n deferredRowFlushThreshold = 100, // somewhat arbitrary\n setTimeoutFn = setTimeout,\n ) {\n this.#failService = failService;\n this.#db = cvrDb;\n this.#schema = cvrSchema(shard);\n this.#taskID = taskID;\n this.#id = cvrID;\n this.#rowCache = new RowRecordCache(\n lc,\n cvrDb,\n shard,\n cvrID,\n failService,\n deferredRowFlushThreshold,\n setTimeoutFn,\n );\n this.#loadAttemptIntervalMs = loadAttemptIntervalMs;\n this.#maxLoadAttempts = maxLoadAttempts;\n }\n\n #cvr(table: string) {\n return this.#db(`${this.#schema}.${table}`);\n }\n\n #updateQueryFields(queryHash: string, fields: Partial<QueriesRow>): void {\n // Track as partial-only update for batched flush. Merge into any\n // pre-existing partial update for the same hash so independent callers\n // (e.g. updateQuery + updateRowSetSignature) don't clobber each other.\n const existing = this.#pendingQueryPartialUpdates.get(queryHash);\n this.#pendingQueryPartialUpdates.set(\n queryHash,\n existing ? {...existing, ...fields} : fields,\n );\n }\n\n load(lc: LogContext, lastConnectTime: number): Promise<CVR> {\n return startAsyncSpan(tracer, 'cvr.load', async () => {\n let err: RowsVersionBehindError | undefined;\n for (let i = 0; i < this.#maxLoadAttempts; i++) {\n if (i > 0) {\n await sleep(this.#loadAttemptIntervalMs);\n }\n const result = await this.#load(lc, lastConnectTime);\n if (result instanceof RowsVersionBehindError) {\n lc.info?.(`attempt ${i + 1}: ${String(result)}`);\n err = result;\n continue;\n }\n return result;\n }\n assert(err, 'Expected error to be set after retry loop exhausted');\n throw new ClientNotFoundError(\n `max attempts exceeded waiting for CVR@${err.cvrVersion} to catch up from ${err.rowsVersion}`,\n );\n });\n }\n\n async #load(\n lc: LogContext,\n lastConnectTime: number,\n ): Promise<CVR | RowsVersionBehindError> {\n const start = Date.now();\n\n const id = this.#id;\n const cvr: CVR = {\n id,\n version: EMPTY_CVR_VERSION,\n lastActive: 0,\n ttlClock: ttlClockFromNumber(0), // TTL clock starts at 0, not Date.now()\n replicaVersion: null,\n clients: {},\n queries: {},\n clientSchema: null,\n profileID: null,\n };\n\n const [instance, clientsRows, queryRows, desiresRows] = await runTx(\n this.#db,\n tx => {\n lc.debug?.(`CVR tx started after ${Date.now() - start} ms`);\n return [\n tx<\n (Omit<InstancesRow, 'clientGroupID'> & {\n profileID: string | null;\n deleted: boolean;\n rowsVersion: string | null;\n })[]\n >`SELECT cvr.\"version\",\n \"lastActive\",\n \"ttlClock\",\n \"replicaVersion\",\n \"owner\",\n \"grantedAt\",\n \"clientSchema\",\n \"profileID\",\n \"deleted\",\n rows.\"version\" as \"rowsVersion\"\n FROM ${this.#cvr('instances')} AS cvr\n LEFT JOIN ${this.#cvr('rowsVersion')} AS rows\n ON cvr.\"clientGroupID\" = rows.\"clientGroupID\"\n WHERE cvr.\"clientGroupID\" = ${id}`,\n tx<Pick<ClientsRow, 'clientID'>[]>`SELECT \"clientID\" FROM ${this.#cvr(\n 'clients',\n )}\n WHERE \"clientGroupID\" = ${id}`,\n tx<QueriesRow[]>`SELECT\n \"clientGroupID\",\n \"queryHash\",\n \"clientAST\",\n \"queryName\",\n \"queryArgs\",\n \"patchVersion\",\n \"transformationHash\",\n \"transformationVersion\",\n \"internal\",\n \"deleted\",\n \"rowSetSignature\"\n FROM ${this.#cvr('queries')}\n WHERE \"clientGroupID\" = ${id} AND deleted IS DISTINCT FROM true`,\n tx<DesiresRow[]>`SELECT\n \"clientGroupID\",\n \"clientID\",\n \"queryHash\",\n \"patchVersion\",\n \"deleted\",\n \"ttlMs\" AS \"ttl\",\n \"inactivatedAtMs\" AS \"inactivatedAt\"\n FROM ${this.#cvr('desires')}\n WHERE \"clientGroupID\" = ${id}`,\n ];\n },\n {mode: Mode.READONLY},\n );\n lc.debug?.(\n `CVR tx completed after ${Date.now() - start} ms ` +\n `(${clientsRows.length} clients, ${queryRows.length} queries, ${desiresRows.length} desires)`,\n );\n\n if (instance.length === 0) {\n // This is the first time we see this CVR.\n this.putInstance({\n version: cvr.version,\n lastActive: 0,\n ttlClock: ttlClockFromNumber(0), // TTL clock starts at 0 for new instances\n replicaVersion: null,\n clientSchema: null,\n profileID: null,\n });\n } else {\n assert(\n instance.length === 1,\n () => `Expected exactly one CVR instance, got ${instance.length}`,\n );\n const {\n version,\n lastActive,\n ttlClock,\n replicaVersion,\n owner,\n grantedAt,\n rowsVersion,\n clientSchema,\n profileID,\n deleted,\n } = instance[0];\n\n if (deleted) {\n throw new ClientNotFoundError(\n 'Client has been purged due to inactivity',\n );\n }\n\n if (owner !== this.#taskID) {\n if ((grantedAt ?? 0) > lastConnectTime) {\n throw new OwnershipError(owner, grantedAt, lastConnectTime);\n } else {\n // Fire-and-forget an ownership change to signal the current owner.\n // Note that the query is structured such that it only succeeds in the\n // correct conditions (i.e. gated on `grantedAt`).\n void this.#db`\n UPDATE ${this.#cvr('instances')} \n SET \"owner\" = ${this.#taskID}, \n \"grantedAt\" = ${lastConnectTime}\n WHERE \"clientGroupID\" = ${this.#id} AND\n (\"grantedAt\" IS NULL OR\n \"grantedAt\" <= to_timestamp(${lastConnectTime / 1000}))\n `\n .execute()\n .catch(this.#failService);\n }\n }\n\n if (version !== (rowsVersion ?? EMPTY_CVR_VERSION.stateVersion)) {\n // This will cause the load() method to wait for row catchup and retry.\n // Assuming the ownership signal succeeds, the current owner will stop\n // modifying the CVR and flush its pending row changes.\n return new RowsVersionBehindError(version, rowsVersion);\n }\n\n cvr.version = versionFromString(version);\n cvr.lastActive = lastActive;\n cvr.ttlClock = ttlClock;\n cvr.replicaVersion = replicaVersion;\n cvr.profileID = profileID;\n\n try {\n cvr.clientSchema =\n clientSchema === null\n ? null\n : v.parse(clientSchema, clientSchemaSchema);\n } catch (e) {\n throw new InvalidClientSchemaError(e);\n }\n }\n\n for (const row of clientsRows) {\n cvr.clients[row.clientID] = {\n id: row.clientID,\n desiredQueryIDs: [],\n };\n }\n\n for (const row of queryRows) {\n const query = asQuery(row);\n cvr.queries[row.queryHash] = query;\n }\n\n for (const row of desiresRows) {\n const client = cvr.clients[row.clientID];\n // Note: row.inactivatedAt is mapped from inactivatedAtMs in the SQL query\n if (!row.deleted && row.inactivatedAt === null) {\n if (client) {\n client.desiredQueryIDs.push(row.queryHash);\n } else {\n // This can happen if the client was deleted but the queries are still alive.\n lc.debug?.(\n `Not adding to desiredQueryIDs for client ${row.clientID} because it has been deleted.`,\n );\n }\n }\n\n const query = cvr.queries[row.queryHash];\n if (\n query &&\n query.type !== 'internal' &&\n (!row.deleted || row.inactivatedAt !== null)\n ) {\n query.clientState[row.clientID] = {\n inactivatedAt: row.inactivatedAt ?? undefined,\n ttl: clampTTL(row.ttl ?? DEFAULT_TTL_MS),\n version: versionFromString(row.patchVersion),\n };\n }\n }\n\n lc.info?.(\n `loaded cvr@${versionString(cvr.version)} (${Date.now() - start} ms)`,\n );\n\n // why do we not sort `desiredQueryIDs` here?\n\n return cvr;\n }\n\n getRowRecords(): Promise<ReadonlyMap<RowID, RowRecord>> {\n return this.#rowCache.getRowRecords();\n }\n\n putRowRecord(row: RowRecord): void {\n this.#pendingRowRecordUpdates.set(row.id, row);\n }\n\n /**\n * Note: Removing a row from the CVR should be represented by a\n * {@link putRowRecord()} with `refCounts: null` in order to properly\n * produce the appropriate delete patch when catching up old clients.\n *\n * This `delRowRecord()` method, on the other hand, is only used for\n * \"canceling\" the put of a row that was not in the CVR in the first place.\n */\n delRowRecord(id: RowID): void {\n this.#pendingRowRecordUpdates.set(id, null);\n }\n\n /**\n * Overrides the default logic that removes no-op writes and forces\n * the updates for the given row `ids`. This has no effect if there\n * are no corresponding puts or dels for the associated row records.\n */\n forceUpdates(...ids: RowID[]) {\n for (const id of ids) {\n this.#forceUpdates.add(id);\n }\n }\n\n /**\n * Updates the `ttlClock` of the CVR instance. The ttlClock starts at 0 when\n * the CVR instance is first created and increments based on elapsed time\n * since the base time established by the ViewSyncerService.\n */\n async updateTTLClock(ttlClock: TTLClock, lastActive: number): Promise<void> {\n await this.#db`UPDATE ${this.#cvr('instances')}\n SET \"lastActive\" = ${lastActive},\n \"ttlClock\" = ${ttlClock}\n WHERE \"clientGroupID\" = ${this.#id}`.execute();\n }\n\n /**\n * @returns This returns the current `ttlClock` of the CVR instance. The ttlClock\n * represents elapsed time since the instance was created (starting from 0).\n * If the CVR has never been initialized for this client group, it returns\n * `undefined`.\n */\n async getTTLClock(): Promise<TTLClock | undefined> {\n const result = await this.#db<Pick<InstancesRow, 'ttlClock'>[]>`\n SELECT \"ttlClock\" FROM ${this.#cvr('instances')}\n WHERE \"clientGroupID\" = ${this.#id}`.values();\n if (result.length === 0) {\n // This can happen if the CVR has not been initialized yet.\n return undefined;\n }\n assert(\n result.length === 1,\n () => `Expected exactly one rowsVersion result, got ${result.length}`,\n );\n return result[0][0];\n }\n\n putInstance({\n version,\n replicaVersion,\n lastActive,\n clientSchema,\n profileID,\n ttlClock,\n }: Pick<\n CVRSnapshot,\n | 'version'\n | 'replicaVersion'\n | 'lastActive'\n | 'clientSchema'\n | 'profileID'\n | 'ttlClock'\n >): void {\n // Overwrite any previously queued instance write — only the last call\n // matters since they all target the same row.\n this.#pendingInstanceWrite = (tx, lastConnectTime) => {\n const change: InstancesRow = {\n clientGroupID: this.#id,\n version: versionString(version),\n lastActive,\n ttlClock,\n replicaVersion,\n owner: this.#taskID,\n grantedAt: lastConnectTime,\n clientSchema,\n profileID,\n };\n return tx`\n INSERT INTO ${this.#cvr('instances')} ${tx(change)} \n ON CONFLICT (\"clientGroupID\") DO UPDATE SET ${tx(change)}`;\n };\n }\n\n markQueryAsDeleted(version: CVRVersion, queryPatch: QueryPatch): void {\n this.#updateQueryFields(queryPatch.id, {\n patchVersion: versionString(version),\n deleted: true,\n transformationHash: null,\n transformationVersion: null,\n });\n }\n\n putQuery(query: QueryRecord): void {\n const change = queryRecordToQueryRow(this.#id, query);\n\n const c = {\n ...change,\n // Pre-stringify queryArgs to handle postgres.js boolean array bug\n queryArgs:\n change.queryArgs !== null ? JSON.stringify(change.queryArgs) : null,\n transformationHash: change.transformationHash ?? null,\n transformationVersion: change.transformationVersion ?? null,\n deleted: change.deleted ?? false,\n };\n this.#pendingQueryUpdates.set(query.id, c);\n }\n\n updateQuery(query: QueryRecord) {\n const maybeVersionString = (v: CVRVersion | undefined) =>\n v ? versionString(v) : null;\n this.#updateQueryFields(query.id, {\n patchVersion:\n query.type === 'internal'\n ? null\n : maybeVersionString(query.patchVersion),\n transformationHash: query.transformationHash ?? null,\n transformationVersion: maybeVersionString(query.transformationVersion),\n deleted: false,\n });\n }\n\n updateRowSetSignature(queryHash: string, signature: string): void {\n this.#updateQueryFields(queryHash, {rowSetSignature: signature});\n }\n\n insertClient(client: ClientRecord): void {\n const change: ClientsRow = {\n clientGroupID: this.#id,\n clientID: client.id,\n };\n\n this.#writes.add({\n stats: {clients: 1},\n write: tx => tx`INSERT INTO ${this.#cvr('clients')} ${tx(change)}`,\n });\n }\n\n deleteClient(clientID: string) {\n this.#writes.add({\n stats: {clients: 1},\n write: sql =>\n sql`DELETE FROM ${this.#cvr('clients')}\n WHERE \"clientGroupID\" = ${this.#id}\n AND \"clientID\" = ${clientID}`,\n });\n }\n\n putDesiredQuery(\n newVersion: CVRVersion,\n query: {id: string},\n client: {id: string},\n deleted: boolean,\n inactivatedAt: TTLClock | undefined,\n ttl: number,\n ): void {\n const {ttlMs, inactivatedAtMs} = convertTTLValues(inactivatedAt, ttl);\n\n const change: DesiresRow = {\n clientGroupID: this.#id,\n clientID: client.id,\n deleted,\n inactivatedAt: inactivatedAtMs,\n patchVersion: versionString(newVersion),\n queryHash: query.id,\n ttl: ttlMs,\n };\n\n // Use composite key to deduplicate/replace entries for the same client-query pair\n const key = `${client.id}:${query.id}`;\n this.#pendingDesireUpdates.set(key, change);\n }\n\n catchupRowPatches(\n lc: LogContext,\n afterVersion: NullableCVRVersion,\n upToCVR: CVRSnapshot,\n current: CVRVersion,\n excludeQueryHashes: string[] = [],\n ): AsyncGenerator<RowsRow[], void, undefined> {\n return this.#rowCache.catchupRowPatches(\n lc,\n afterVersion,\n upToCVR,\n current,\n excludeQueryHashes,\n );\n }\n\n async catchupConfigPatches(\n lc: LogContext,\n afterVersion: NullableCVRVersion,\n upToCVR: CVRSnapshot,\n current: CVRVersion,\n ): Promise<PatchToVersion[]> {\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 config patches for clients from ${start}`);\n\n const reader = new TransactionPool(lc, {mode: 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.#id, current),\n );\n\n const [allDesires, queryRows] = await reader.processReadTask(tx =>\n Promise.all([\n tx<DesiresRow[]>`\n SELECT\n \"clientGroupID\",\n \"clientID\",\n \"queryHash\",\n \"patchVersion\",\n \"deleted\",\n \"ttl\",\n \"inactivatedAt\"\n FROM ${this.#cvr('desires')}\n WHERE \"clientGroupID\" = ${this.#id}\n AND \"patchVersion\" > ${start}\n AND \"patchVersion\" <= ${end}`,\n tx<Pick<QueriesRow, 'deleted' | 'queryHash' | 'patchVersion'>[]>`\n SELECT deleted, \"queryHash\", \"patchVersion\" FROM ${this.#cvr('queries')}\n WHERE \"clientGroupID\" = ${this.#id}\n AND \"patchVersion\" > ${start}\n AND \"patchVersion\" <= ${end}`,\n ]),\n );\n\n const patches: PatchToVersion[] = [];\n for (const row of queryRows) {\n const {queryHash: id} = row;\n const patch: Patch = row.deleted\n ? {type: 'query', op: 'del', id}\n : {type: 'query', op: 'put', id};\n const v = row.patchVersion;\n assert(v, 'patchVersion must be set for query patches');\n patches.push({patch, toVersion: versionFromString(v)});\n }\n for (const row of allDesires) {\n const {clientID, queryHash: id} = row;\n const patch: Patch = row.deleted\n ? {type: 'query', op: 'del', id, clientID}\n : {type: 'query', op: 'put', id, clientID};\n patches.push({patch, toVersion: versionFromString(row.patchVersion)});\n }\n\n lc.debug?.(\n `${patches.length} config patches (${Date.now() - startMs} ms)`,\n );\n return patches;\n } finally {\n reader.setDone();\n }\n }\n\n #flushQueries(\n tx: PostgresTransaction,\n lc: LogContext,\n ): PendingQuery<Row[]>[] {\n // Merge partial updates into full updates\n const partialOnly = new Map<string, Partial<QueriesRow>>();\n for (const [queryHash, partial] of this.#pendingQueryPartialUpdates) {\n const existing = this.#pendingQueryUpdates.get(queryHash);\n if (existing) {\n // Merge partial into full update\n Object.assign(existing, partial);\n } else {\n // Track partial-only updates to batch separately\n partialOnly.set(queryHash, partial);\n }\n }\n\n const queries: PendingQuery<Row[]>[] = [];\n\n // Batch full updates\n if (this.#pendingQueryUpdates.size > 0) {\n const rows = [...this.#pendingQueryUpdates.values()];\n lc.debug?.(`Batch flushing ${rows.length} full query updates`);\n\n queries.push(tx`\n INSERT INTO ${this.#cvr('queries')} (\n \"clientGroupID\",\n \"queryHash\",\n \"clientAST\",\n \"queryName\",\n \"queryArgs\",\n \"patchVersion\",\n \"transformationHash\",\n \"transformationVersion\",\n \"internal\",\n \"deleted\",\n \"rowSetSignature\"\n )\n SELECT\n \"clientGroupID\",\n \"queryHash\",\n \"clientAST\",\n \"queryName\",\n CASE\n WHEN \"queryArgs\" IS NULL THEN NULL\n ELSE \"queryArgs\"::json\n END,\n \"patchVersion\",\n \"transformationHash\",\n \"transformationVersion\",\n \"internal\",\n \"deleted\",\n \"rowSetSignature\"\n FROM json_to_recordset(${rows}) AS x(\n \"clientGroupID\" TEXT,\n \"queryHash\" TEXT,\n \"clientAST\" JSONB,\n \"queryName\" TEXT,\n \"queryArgs\" TEXT,\n \"patchVersion\" TEXT,\n \"transformationHash\" TEXT,\n \"transformationVersion\" TEXT,\n \"internal\" BOOLEAN,\n \"deleted\" BOOLEAN,\n \"rowSetSignature\" TEXT\n )\n ON CONFLICT (\"clientGroupID\", \"queryHash\") DO UPDATE SET\n \"clientAST\" = excluded.\"clientAST\",\n \"queryName\" = excluded.\"queryName\",\n \"queryArgs\" = CASE\n WHEN excluded.\"queryArgs\" IS NULL THEN NULL\n ELSE excluded.\"queryArgs\"::json\n END,\n \"patchVersion\" = excluded.\"patchVersion\",\n \"transformationHash\" = excluded.\"transformationHash\",\n \"transformationVersion\" = excluded.\"transformationVersion\",\n \"internal\" = excluded.\"internal\",\n \"deleted\" = excluded.\"deleted\",\n \"rowSetSignature\" = excluded.\"rowSetSignature\"\n `);\n }\n\n // Batch partial-only updates\n if (partialOnly.size > 0) {\n lc.debug?.(`Batch flushing ${partialOnly.size} partial query updates`);\n const rows = Array.from(\n partialOnly.entries(),\n ([queryHash, partial]) => ({\n clientGroupID: this.#id,\n queryHash,\n patchVersionSet: partial.patchVersion !== undefined,\n patchVersion: partial.patchVersion ?? null,\n deletedSet: partial.deleted !== undefined,\n deleted: partial.deleted ?? null,\n transformationHashSet: partial.transformationHash !== undefined,\n transformationHash: partial.transformationHash ?? null,\n transformationVersionSet: partial.transformationVersion !== undefined,\n transformationVersion: partial.transformationVersion ?? null,\n rowSetSignatureSet: partial.rowSetSignature !== undefined,\n rowSetSignature: partial.rowSetSignature ?? null,\n }),\n );\n queries.push(tx`\n UPDATE ${this.#cvr('queries')} AS q\n SET\n \"patchVersion\" = CASE\n WHEN u.\"patchVersionSet\" THEN u.\"patchVersion\"\n ELSE q.\"patchVersion\"\n END,\n \"deleted\" = CASE\n WHEN u.\"deletedSet\" THEN u.\"deleted\"\n ELSE q.\"deleted\"\n END,\n \"transformationHash\" = CASE\n WHEN u.\"transformationHashSet\" THEN u.\"transformationHash\"\n ELSE q.\"transformationHash\"\n END,\n \"transformationVersion\" = CASE\n WHEN u.\"transformationVersionSet\" THEN u.\"transformationVersion\"\n ELSE q.\"transformationVersion\"\n END,\n \"rowSetSignature\" = CASE\n WHEN u.\"rowSetSignatureSet\" THEN u.\"rowSetSignature\"\n ELSE q.\"rowSetSignature\"\n END\n FROM json_to_recordset(${rows}) AS u(\n \"clientGroupID\" TEXT,\n \"queryHash\" TEXT,\n \"patchVersionSet\" BOOLEAN,\n \"patchVersion\" TEXT,\n \"deletedSet\" BOOLEAN,\n \"deleted\" BOOLEAN,\n \"transformationHashSet\" BOOLEAN,\n \"transformationHash\" TEXT,\n \"transformationVersionSet\" BOOLEAN,\n \"transformationVersion\" TEXT,\n \"rowSetSignatureSet\" BOOLEAN,\n \"rowSetSignature\" TEXT\n )\n WHERE q.\"clientGroupID\" = u.\"clientGroupID\"\n AND q.\"queryHash\" = u.\"queryHash\"\n `);\n }\n\n return queries;\n }\n\n #flushDesires(\n tx: PostgresTransaction,\n lc: LogContext,\n ): PendingQuery<Row[]> | null {\n if (this.#pendingDesireUpdates.size === 0) {\n return null;\n }\n\n const rows = Array.from(this.#pendingDesireUpdates.values(), row => {\n const {ttlInterval, ttlMs, inactivatedAtTimestamp, inactivatedAtMs} =\n convertTTLValues(row.inactivatedAt ?? undefined, row.ttl ?? -1);\n return {\n clientGroupID: row.clientGroupID,\n clientID: row.clientID,\n queryHash: row.queryHash,\n patchVersion: row.patchVersion,\n deleted: row.deleted,\n ttl: ttlInterval,\n ttlMs,\n inactivatedAt: inactivatedAtTimestamp,\n inactivatedAtMs,\n };\n });\n\n lc.debug?.(`Batch flushing ${rows.length} desire updates`);\n\n return tx`\n INSERT INTO ${this.#cvr('desires')} (\n \"clientGroupID\",\n \"clientID\",\n \"queryHash\",\n \"patchVersion\",\n \"deleted\",\n \"ttl\",\n \"ttlMs\",\n \"inactivatedAt\",\n \"inactivatedAtMs\"\n )\n SELECT\n \"clientGroupID\",\n \"clientID\",\n \"queryHash\",\n \"patchVersion\",\n \"deleted\",\n \"ttl\",\n \"ttlMs\",\n CASE\n WHEN \"inactivatedAt\" IS NULL THEN NULL\n -- Divide by 1000 because postgres.js serializeTimestamp treats numbers as ms\n -- and to_timestamp expects seconds. This matches non-batched behavior.\n ELSE to_timestamp(\"inactivatedAt\" / 1000.0)\n END,\n \"inactivatedAtMs\"\n FROM json_to_recordset(${rows}) AS x(\n \"clientGroupID\" TEXT,\n \"clientID\" TEXT,\n \"queryHash\" TEXT,\n \"patchVersion\" TEXT,\n \"deleted\" BOOLEAN,\n \"ttl\" INTERVAL,\n \"ttlMs\" DOUBLE PRECISION,\n \"inactivatedAt\" DOUBLE PRECISION,\n \"inactivatedAtMs\" DOUBLE PRECISION\n )\n ON CONFLICT (\"clientGroupID\", \"clientID\", \"queryHash\") DO UPDATE SET\n \"patchVersion\" = excluded.\"patchVersion\",\n \"deleted\" = excluded.\"deleted\",\n \"ttl\" = excluded.\"ttl\",\n \"ttlMs\" = excluded.\"ttlMs\",\n \"inactivatedAt\" = excluded.\"inactivatedAt\",\n \"inactivatedAtMs\" = excluded.\"inactivatedAtMs\"\n `;\n }\n\n async #checkVersionAndOwnership(\n lc: LogContext,\n tx: PostgresTransaction,\n expectedCurrentVersion: CVRVersion,\n lastConnectTime: number,\n ): Promise<void> {\n const start = Date.now();\n lc.debug?.('checking cvr version and ownership');\n const result = await tx<\n Pick<InstancesRow, 'version' | 'owner' | 'grantedAt'>[]\n >`SELECT \"version\", \"owner\", \"grantedAt\" FROM ${this.#cvr('instances')}\n WHERE \"clientGroupID\" = ${this.#id}\n FOR UPDATE`;\n const expected = versionString(expectedCurrentVersion);\n const {version, owner, grantedAt} =\n result.length > 0\n ? result[0]\n : {\n version: EMPTY_CVR_VERSION.stateVersion,\n owner: null,\n grantedAt: null,\n };\n lc.debug?.(\n 'checked cvr version and ownership in ' + (Date.now() - start) + ' ms',\n );\n if (owner !== this.#taskID && (grantedAt ?? 0) > lastConnectTime) {\n throw new OwnershipError(owner, grantedAt, lastConnectTime);\n }\n if (version !== expected) {\n throw new ConcurrentModificationException(expected, version);\n }\n }\n\n async #flush(\n lc: LogContext,\n expectedCurrentVersion: CVRVersion,\n cvr: CVRSnapshot,\n lastConnectTime: number,\n ): Promise<CVRFlushStats | null> {\n const stats: CVRFlushStats = {\n instances: 0,\n queries: 0,\n desires: 0,\n clients: 0,\n rows: 0,\n rowsDeferred: 0,\n statements: 0,\n };\n if (this.#pendingRowRecordUpdates.size) {\n const existingRowRecords = await this.getRowRecords();\n this.#rowCount = existingRowRecords.size;\n for (const [id, row] of this.#pendingRowRecordUpdates.entries()) {\n if (this.#forceUpdates.has(id)) {\n continue;\n }\n const existing = existingRowRecords.get(id);\n if (\n // Don't delete or add an unreferenced row if it's not in the CVR.\n (existing === undefined && !row?.refCounts) ||\n // Don't write a row record that exactly matches what's in the CVR.\n deepEqual(\n (row ?? undefined) as ReadonlyJSONValue | undefined,\n existing as ReadonlyJSONValue | undefined,\n )\n ) {\n this.#pendingRowRecordUpdates.delete(id);\n }\n }\n }\n if (\n this.#pendingRowRecordUpdates.size === 0 &&\n this.#writes.size === 0 &&\n this.#pendingInstanceWrite === undefined &&\n this.#pendingQueryUpdates.size === 0 &&\n this.#pendingQueryPartialUpdates.size === 0 &&\n this.#pendingDesireUpdates.size === 0\n ) {\n return null;\n }\n // Note: The CVR instance itself is only updated if there are material\n // changes (i.e. changes to the CVR contents) to flush.\n this.putInstance(cvr);\n const start = Date.now();\n lc.debug?.('flush tx beginning');\n\n // Use an async callback so we can await the version/ownership check and\n // validate it INSIDE the transaction. If validation fails, the exception\n // causes postgres.js to ROLLBACK, ensuring no writes are committed on error.\n const results = await runTx(\n this.#db,\n async tx => {\n lc.debug?.(`flush tx begun after ${Date.now() - start} ms`);\n\n // Acquire row-level lock and validate version/ownership before queuing writes.\n // Throwing here (inside the begin callback) rolls back the transaction so that\n // no writes are committed when concurrent modification or ownership errors occur.\n await this.#checkVersionAndOwnership(\n lc,\n tx,\n expectedCurrentVersion,\n lastConnectTime,\n );\n\n const writeQueries = [];\n if (this.#pendingInstanceWrite) {\n writeQueries.push(this.#pendingInstanceWrite(tx, lastConnectTime));\n stats.instances++;\n stats.statements++;\n }\n for (const write of this.#writes) {\n stats.clients += write.stats.clients ?? 0;\n stats.rows += write.stats.rows ?? 0;\n\n writeQueries.push(write.write(tx, lastConnectTime));\n stats.statements++;\n }\n\n // Batch flush config writes\n // Flush queries first (desires depend on queries via foreign key)\n const hasQueryUpdates =\n this.#pendingQueryUpdates.size > 0 ||\n this.#pendingQueryPartialUpdates.size > 0;\n\n const desireFlush = this.#flushDesires(tx, lc);\n\n let queryFlushes: PendingQuery<Row[]>[] = [];\n if (hasQueryUpdates) {\n queryFlushes = this.#flushQueries(tx, lc);\n\n // Count both full updates and partial-only updates\n const partialOnlyCount = [\n ...this.#pendingQueryPartialUpdates.keys(),\n ].filter(key => !this.#pendingQueryUpdates.has(key)).length;\n\n stats.queries = this.#pendingQueryUpdates.size + partialOnlyCount;\n stats.statements +=\n (this.#pendingQueryUpdates.size > 0 ? 1 : 0) +\n (partialOnlyCount > 0 ? 1 : 0);\n\n if (desireFlush) {\n stats.desires = this.#pendingDesireUpdates.size;\n stats.statements++;\n }\n } else if (desireFlush) {\n stats.desires = this.#pendingDesireUpdates.size;\n stats.statements++;\n }\n\n const rowUpdates = this.#rowCache.executeRowUpdates(\n tx,\n cvr.version,\n this.#pendingRowRecordUpdates,\n 'allow-defer',\n lc,\n );\n stats.statements += rowUpdates.length;\n\n // Pipeline writes now that the version check has passed.\n const pipelined = [\n ...writeQueries,\n ...queryFlushes,\n ...(desireFlush ? [desireFlush] : []),\n ...rowUpdates,\n ];\n\n lc.debug?.(`returning ${pipelined.length} queries for pipelining`);\n\n // Explicitly await all pipelined queries. When the begin callback is async,\n // postgres.js does not call Promise.all() on the return value the way it does\n // for sync callbacks, so we must do it ourselves.\n return Promise.all(pipelined);\n },\n {mode: Mode.READ_COMMITTED},\n );\n\n lc.debug?.(`flush tx completed after ${Date.now() - start} ms`);\n\n // Calculate how many row update queries were in the pipeline.\n // Note: the version check was awaited separately and is not in the results array.\n const baseQueries =\n (this.#pendingInstanceWrite ? 1 : 0) +\n this.#writes.size +\n (this.#pendingQueryUpdates.size > 0 ? 1 : 0) +\n ([...this.#pendingQueryPartialUpdates.keys()].filter(\n key => !this.#pendingQueryUpdates.has(key),\n ).length > 0\n ? 1\n : 0) +\n (this.#pendingDesireUpdates.size > 0 ? 1 : 0);\n const rowUpdateCount = results.length - baseQueries;\n\n const rowsFlushed = rowUpdateCount > 0;\n if (!rowsFlushed) {\n stats.rowsDeferred = this.#pendingRowRecordUpdates.size;\n } else {\n stats.rows += this.#pendingRowRecordUpdates.size;\n }\n\n this.#rowCount = await this.#rowCache.apply(\n this.#pendingRowRecordUpdates,\n cvr.version,\n rowsFlushed,\n );\n recordRowsSynced(this.#rowCount);\n\n return stats;\n }\n\n get rowCount(): number {\n return this.#rowCount;\n }\n\n async flush(\n lc: LogContext,\n expectedCurrentVersion: CVRVersion,\n cvr: CVRSnapshot,\n lastConnectTime: number,\n ): Promise<CVRFlushStats | null> {\n const start = performance.now();\n lc = lc.withContext('cvrFlushID', flushCounter++);\n try {\n const stats = await this.#flush(\n lc,\n expectedCurrentVersion,\n cvr,\n lastConnectTime,\n );\n if (stats) {\n const elapsed = performance.now() - start;\n lc.info?.(\n `flushed cvr@${versionString(cvr.version)} ` +\n `${JSON.stringify(stats)} in (${elapsed} ms)`,\n );\n this.#rowCache.recordSyncFlushStats(stats, elapsed);\n }\n return stats;\n } catch (e) {\n // Clear cached state if an error (e.g. ConcurrentModificationException) is encountered.\n this.#rowCache.clear();\n throw e;\n } finally {\n this.#writes.clear();\n this.#pendingInstanceWrite = undefined;\n this.#pendingRowRecordUpdates.clear();\n this.#forceUpdates.clear();\n this.#pendingQueryUpdates.clear();\n this.#pendingDesireUpdates.clear();\n this.#pendingQueryPartialUpdates.clear();\n }\n }\n\n hasPendingUpdates(): boolean {\n return this.#rowCache.hasPendingUpdates();\n }\n\n /** Resolves when all pending updates are flushed. */\n flushed(lc: LogContext): Promise<void> {\n return this.#rowCache.flushed(lc);\n }\n\n async inspectQueries(\n lc: LogContext,\n ttlClock: TTLClock,\n clientID?: string,\n ): Promise<InspectQueryRow[]> {\n const db = this.#db;\n const clientGroupID = this.#id;\n\n const reader = new TransactionPool(lc, {mode: Mode.READONLY}).run(db);\n try {\n return await reader.processReadTask(\n tx => tx<InspectQueryRow[]>`\n SELECT DISTINCT ON (d.\"clientID\", d.\"queryHash\")\n d.\"clientID\",\n d.\"queryHash\" AS \"queryID\",\n COALESCE(d.\"ttlMs\", ${DEFAULT_TTL_MS}) AS \"ttl\",\n d.\"inactivatedAtMs\" AS \"inactivatedAt\",\n (SELECT COUNT(*)::INT FROM ${this.#cvr('rows')} r \n WHERE r.\"clientGroupID\" = d.\"clientGroupID\" \n AND r.\"refCounts\" ? d.\"queryHash\") AS \"rowCount\",\n q.\"clientAST\" AS \"ast\",\n (q.\"patchVersion\" IS NOT NULL) AS \"got\",\n COALESCE(d.\"deleted\", FALSE) AS \"deleted\",\n q.\"queryName\" AS \"name\",\n q.\"queryArgs\" AS \"args\"\n FROM ${this.#cvr('desires')} d\n LEFT JOIN ${this.#cvr('queries')} q\n ON q.\"clientGroupID\" = d.\"clientGroupID\"\n AND q.\"queryHash\" = d.\"queryHash\"\n WHERE d.\"clientGroupID\" = ${clientGroupID}\n ${clientID ? tx`AND d.\"clientID\" = ${clientID}` : tx``}\n AND NOT (\n d.\"inactivatedAtMs\" IS NOT NULL \n AND d.\"ttlMs\" IS NOT NULL \n AND (d.\"inactivatedAtMs\" + d.\"ttlMs\") <= ${ttlClockAsNumber(ttlClock)}\n )\n ORDER BY d.\"clientID\", d.\"queryHash\"`,\n );\n } finally {\n reader.setDone();\n }\n }\n}\n\n/**\n * This is similar to {@link CVRStore.#checkVersionAndOwnership} except\n * that it only checks the version and is suitable for snapshot reads\n * (i.e. by doing a plain `SELECT` rather than a `SELECT ... FOR UPDATE`).\n */\nexport async function checkVersion(\n tx: PostgresTransaction,\n schema: string,\n clientGroupID: string,\n expectedCurrentVersion: CVRVersion,\n): Promise<void> {\n const expected = versionString(expectedCurrentVersion);\n const result = await tx<Pick<InstancesRow, 'version'>[]>`\n SELECT version FROM ${tx(schema)}.instances \n WHERE \"clientGroupID\" = ${clientGroupID}`;\n const {version} =\n result.length > 0 ? result[0] : {version: EMPTY_CVR_VERSION.stateVersion};\n if (version !== expected) {\n throw new ConcurrentModificationException(expected, version);\n }\n}\n\nexport class ClientNotFoundError extends ProtocolErrorWithLevel {\n constructor(message: string) {\n super(\n {\n kind: ErrorKind.ClientNotFound,\n message,\n origin: ErrorOrigin.ZeroCache,\n },\n 'warn',\n );\n }\n}\n\nexport class ConcurrentModificationException extends ProtocolErrorWithLevel {\n readonly name = 'ConcurrentModificationException';\n\n constructor(expectedVersion: string, actualVersion: string) {\n super(\n {\n kind: ErrorKind.Rehome,\n message: `CVR has been concurrently modified. Expected ${expectedVersion}, got ${actualVersion}`,\n origin: ErrorOrigin.ZeroCache,\n },\n 'warn',\n );\n }\n}\n\nexport class OwnershipError extends ProtocolErrorWithLevel {\n readonly name = 'OwnershipError';\n\n constructor(\n owner: string | null,\n grantedAt: number | null,\n lastConnectTime: number,\n ) {\n super(\n {\n kind: ErrorKind.Rehome,\n message:\n `CVR ownership was transferred to ${owner} at ` +\n `${new Date(grantedAt ?? 0).toISOString()} ` +\n `(last connect time: ${new Date(lastConnectTime).toISOString()})`,\n maxBackoffMs: 0,\n origin: ErrorOrigin.ZeroCache,\n },\n 'info',\n );\n }\n}\n\nexport class InvalidClientSchemaError extends ProtocolErrorWithLevel {\n readonly name = 'InvalidClientSchemaError';\n\n constructor(cause: unknown) {\n super(\n {\n kind: ErrorKind.SchemaVersionNotSupported,\n message: `Could not parse clientSchema stored in CVR: ${String(cause)}`,\n origin: ErrorOrigin.ZeroCache,\n },\n 'warn',\n {cause},\n );\n }\n}\n\nexport class RowsVersionBehindError extends Error {\n readonly name = 'RowsVersionBehindError';\n readonly cvrVersion: string;\n readonly rowsVersion: string | null;\n\n constructor(cvrVersion: string, rowsVersion: string | null) {\n super(`rowsVersion (${rowsVersion}) is behind CVR ${cvrVersion}`);\n this.cvrVersion = cvrVersion;\n this.rowsVersion = rowsVersion;\n }\n}\n"],"mappings":"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"}
@@ -3,8 +3,8 @@ import { deepEqual } from "../../../../shared/src/json.js";
3
3
  import { must } from "../../../../shared/src/must.js";
4
4
  import { skipYields } from "../../../../zql/src/ivm/skip-yields.js";
5
5
  import { MeasurePushOperator } from "../../../../zql/src/query/measure-push-operator.js";
6
- import { buildPipeline } from "../../../../zql/src/builder/builder.js";
7
6
  import { makeSourceChangeAdd, makeSourceChangeEdit, makeSourceChangeRemove } from "../../../../zql/src/ivm/source.js";
7
+ import { buildPipeline } from "../../../../zql/src/builder/builder.js";
8
8
  import { Debug, runtimeDebugFlags } from "../../../../zql/src/builder/debug-delegate.js";
9
9
  import { TableSource } from "../../../../zqlite/src/table-source.js";
10
10
  import { ZERO_VERSION_COLUMN_NAME } from "../replicator/schema/constants.js";
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline-driver.js","names":["#tables","#pipelines","#rowSetSignatures","#lc","#snapshotter","#storage","#shardID","#logConfig","#config","#tableSpecs","#allTableNames","#costModels","#yieldThresholdMs","#advanceTime","#conflictRowsDeleted","#inspectorDelegate","#initAndResetCommon","#primaryKeys","#replicaVersion","#permissions","#getSource","#createStorage","#trackRowSetSignatures","#addQueryImpl","#ensureCostModelExistsIfEnabled","#advanceContext","#hydrateContext","#resolveScalarSubqueries","#streamer","#advance","#shouldAdvanceYieldMaybeAbortAdvance","#push","#shouldYield","#startAccumulating","#stopAccumulating","#changes","#streamChanges","#streamNodes"],"sources":["../../../../../../zero-cache/src/services/view-syncer/pipeline-driver.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert, unreachable} from '../../../../shared/src/asserts.ts';\nimport {deepEqual, type JSONValue} from '../../../../shared/src/json.ts';\nimport {must} from '../../../../shared/src/must.ts';\nimport type {AST, LiteralValue} from '../../../../zero-protocol/src/ast.ts';\nimport type {ClientSchema} from '../../../../zero-protocol/src/client-schema.ts';\nimport type {Row} from '../../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../../zero-protocol/src/primary-key.ts';\nimport {buildPipeline} from '../../../../zql/src/builder/builder.ts';\nimport {\n Debug,\n runtimeDebugFlags,\n} from '../../../../zql/src/builder/debug-delegate.ts';\nimport {ChangeIndex} from '../../../../zql/src/ivm/change-index.ts';\nimport {ChangeType} from '../../../../zql/src/ivm/change-type.ts';\nimport type {Change} from '../../../../zql/src/ivm/change.ts';\nimport type {Node} from '../../../../zql/src/ivm/data.ts';\nimport {\n skipYields,\n type Input,\n type Storage,\n} from '../../../../zql/src/ivm/operator.ts';\nimport type {SourceSchema} from '../../../../zql/src/ivm/schema.ts';\nimport {\n type Source,\n type SourceChange,\n type SourceInput,\n makeSourceChangeAdd,\n makeSourceChangeEdit,\n makeSourceChangeRemove,\n} from '../../../../zql/src/ivm/source.ts';\nimport type {ConnectionCostModel} from '../../../../zql/src/planner/planner-connection.ts';\nimport {MeasurePushOperator} from '../../../../zql/src/query/measure-push-operator.ts';\nimport type {ClientGroupStorage} from '../../../../zqlite/src/database-storage.ts';\nimport type {Database} from '../../../../zqlite/src/db.ts';\nimport {\n resolveSimpleScalarSubqueries,\n type CompanionSubquery,\n} from '../../../../zqlite/src/resolve-scalar-subqueries.ts';\nimport {createSQLiteCostModel} from '../../../../zqlite/src/sqlite-cost-model.ts';\nimport {TableSource} from '../../../../zqlite/src/table-source.ts';\nimport {\n reloadPermissionsIfChanged,\n type LoadedPermissions,\n} from '../../auth/load-permissions.ts';\nimport type {LogConfig, ZeroConfig} from '../../config/zero-config.ts';\nimport {computeZqlSpecs, mustGetTableSpec} from '../../db/lite-tables.ts';\nimport type {LiteAndZqlSpec, LiteTableSpec} from '../../db/specs.ts';\nimport {\n getOrCreateCounter,\n getOrCreateLatencyHistogram,\n} from '../../observability/metrics.ts';\nimport type {InspectorDelegate} from '../../server/inspector-delegate.ts';\nimport {type RowKey} from '../../types/row-key.ts';\nimport {type ShardID} from '../../types/shards.ts';\nimport {\n getSubscriptionState,\n ZERO_VERSION_COLUMN_NAME,\n} from '../replicator/schema/replication-state.ts';\nimport {checkClientSchema} from './client-schema.ts';\nimport {rowIDSignatureUnit} from './row-set-signature.ts';\nimport type {Snapshotter} from './snapshotter.ts';\nimport {ResetPipelinesSignal, type SnapshotDiff} from './snapshotter.ts';\n\ntype RowOp<Op extends Omit<ChangeType, ChangeType.CHILD>> = {\n readonly type: Op;\n readonly queryID: string;\n readonly table: string;\n readonly rowKey: Row;\n readonly row: Row;\n};\n\nexport type RowAdd = RowOp<ChangeType.ADD>;\n\nexport type RowRemove = RowOp<ChangeType.REMOVE>;\n\nexport type RowEdit = RowOp<ChangeType.EDIT>;\n\nexport type RowChange = RowAdd | RowRemove | RowEdit;\n\ntype CompanionPipeline = {\n readonly input: Input;\n readonly childField: string;\n readonly resolvedValue: LiteralValue | null | undefined;\n};\n\ntype Pipeline = {\n readonly input: Input;\n readonly hydrationTimeMs: number;\n readonly transformedAst: AST;\n readonly transformationHash: string;\n readonly companions: readonly CompanionPipeline[];\n};\n\ntype QueryInfo = {\n readonly transformedAst: AST;\n readonly transformationHash: string;\n};\n\ntype AdvanceContext = {\n readonly timer: Timer;\n readonly totalHydrationTimeMs: number;\n readonly numChanges: number;\n pos: number;\n};\n\ntype HydrateContext = {\n readonly timer: Timer;\n};\n\nexport type Timer = {\n elapsedLap: () => number;\n totalElapsed: () => number;\n};\n\n/**\n * No matter how fast hydration is, advancement is given at least this long to\n * complete before doing a pipeline reset.\n */\nconst MIN_ADVANCEMENT_TIME_LIMIT_MS = 50;\n\n/**\n * Manages the state of IVM pipelines for a given ViewSyncer (i.e. client group).\n */\nexport class PipelineDriver {\n readonly #tables = new Map<string, TableSource>();\n // Query id to pipeline\n readonly #pipelines = new Map<string, Pipeline>();\n /**\n * XOR signature of the set of rows currently attached to each active\n * query, maintained as RowChanges are yielded from {@link addQuery} and\n * {@link advance}. ADDs / REMOVEs XOR the row's unit in (XOR is\n * self-inverse, so one op serves both directions); EDITs are no-ops.\n * Hydration implicitly reseeds from `0n` because {@link addQuery} calls\n * {@link removeQuery} first, which deletes the entry.\n */\n readonly #rowSetSignatures = new Map<string, bigint>();\n\n readonly #lc: LogContext;\n readonly #snapshotter: Snapshotter;\n readonly #storage: ClientGroupStorage;\n readonly #shardID: ShardID;\n readonly #logConfig: LogConfig;\n readonly #config: ZeroConfig | undefined;\n readonly #tableSpecs = new Map<string, LiteAndZqlSpec>();\n readonly #allTableNames = new Set<string>();\n readonly #costModels: WeakMap<Database, ConnectionCostModel> | undefined;\n readonly #yieldThresholdMs: () => number;\n #streamer: Streamer | null = null;\n #hydrateContext: HydrateContext | null = null;\n #advanceContext: AdvanceContext | null = null;\n #replicaVersion: string | null = null;\n #primaryKeys: Map<string, PrimaryKey> | null = null;\n #permissions: LoadedPermissions | null = null;\n\n readonly #advanceTime = getOrCreateLatencyHistogram(\n 'sync',\n 'ivm.advance-time',\n 'Time to advance all queries for a given client group in response to a single change.',\n );\n\n readonly #conflictRowsDeleted = getOrCreateCounter(\n 'sync',\n 'ivm.conflict-rows-deleted',\n 'Number of rows deleted because they conflicted with added row',\n );\n\n readonly #inspectorDelegate: InspectorDelegate;\n\n constructor(\n lc: LogContext,\n logConfig: LogConfig,\n snapshotter: Snapshotter,\n shardID: ShardID,\n storage: ClientGroupStorage,\n clientGroupID: string,\n inspectorDelegate: InspectorDelegate,\n yieldThresholdMs: () => number,\n enablePlanner?: boolean,\n config?: ZeroConfig,\n ) {\n this.#lc = lc.withContext('clientGroupID', clientGroupID);\n this.#snapshotter = snapshotter;\n this.#storage = storage;\n this.#shardID = shardID;\n this.#logConfig = logConfig;\n this.#config = config;\n this.#inspectorDelegate = inspectorDelegate;\n this.#costModels = enablePlanner ? new WeakMap() : undefined;\n this.#yieldThresholdMs = yieldThresholdMs;\n }\n\n /**\n * Initializes the PipelineDriver to the current head of the database.\n * Queries can then be added (i.e. hydrated) with {@link addQuery()}.\n *\n * Must only be called once.\n */\n init(clientSchema: ClientSchema) {\n assert(!this.#snapshotter.initialized(), 'Already initialized');\n this.#snapshotter.init();\n this.#initAndResetCommon(clientSchema);\n }\n\n /**\n * @returns Whether the PipelineDriver has been initialized.\n */\n initialized(): boolean {\n return this.#snapshotter.initialized();\n }\n\n /**\n * Clears the current pipelines and TableSources, returning the PipelineDriver\n * to its initial state. This should be called in response to a schema change,\n * as TableSources need to be recomputed.\n */\n reset(clientSchema: ClientSchema) {\n for (const pipeline of this.#pipelines.values()) {\n pipeline.input.destroy();\n for (const companion of pipeline.companions) {\n companion.input.destroy();\n }\n }\n this.#pipelines.clear();\n this.#tables.clear();\n this.#allTableNames.clear();\n this.#rowSetSignatures.clear();\n this.#initAndResetCommon(clientSchema);\n }\n\n #initAndResetCommon(clientSchema: ClientSchema) {\n const {db} = this.#snapshotter.current();\n const fullTables = new Map<string, LiteTableSpec>();\n computeZqlSpecs(\n this.#lc,\n db.db,\n {includeBackfillingColumns: false},\n this.#tableSpecs,\n fullTables,\n );\n checkClientSchema(\n this.#shardID,\n clientSchema,\n this.#tableSpecs,\n fullTables,\n );\n this.#allTableNames.clear();\n for (const table of fullTables.keys()) {\n this.#allTableNames.add(table);\n }\n const primaryKeys = this.#primaryKeys ?? new Map<string, PrimaryKey>();\n this.#primaryKeys = primaryKeys;\n primaryKeys.clear();\n for (const [table, spec] of this.#tableSpecs.entries()) {\n primaryKeys.set(table, spec.tableSpec.primaryKey);\n }\n buildPrimaryKeys(clientSchema, primaryKeys);\n const {replicaVersion} = getSubscriptionState(db);\n this.#replicaVersion = replicaVersion;\n }\n\n /** @returns The replica version. The PipelineDriver must have been initialized. */\n get replicaVersion(): string {\n return must(this.#replicaVersion, 'Not yet initialized');\n }\n\n /**\n * Returns the current version of the database. This will reflect the\n * latest version change when calling {@link advance()} once the\n * iteration has begun.\n */\n currentVersion(): string {\n assert(this.initialized(), 'Not yet initialized');\n return this.#snapshotter.current().version;\n }\n\n /**\n * Returns the current upstream {app}.permissions, or `null` if none are defined.\n */\n currentPermissions(): LoadedPermissions | null {\n assert(this.initialized(), 'Not yet initialized');\n const res = reloadPermissionsIfChanged(\n this.#lc,\n this.#snapshotter.current().db,\n this.#shardID.appID,\n this.#permissions,\n this.#config,\n );\n if (res.changed) {\n this.#permissions = res.permissions;\n this.#lc.debug?.(\n 'Reloaded permissions',\n JSON.stringify(this.#permissions),\n );\n }\n return this.#permissions;\n }\n\n advanceWithoutDiff(): string {\n const {db, version} = this.#snapshotter.advanceWithoutDiff().curr;\n for (const table of this.#tables.values()) {\n table.setDB(db.db);\n }\n return version;\n }\n\n #ensureCostModelExistsIfEnabled(db: Database) {\n let existing = this.#costModels?.get(db);\n if (existing) {\n return existing;\n }\n if (this.#costModels) {\n const costModel = createSQLiteCostModel(db, this.#tableSpecs);\n this.#costModels.set(db, costModel);\n return costModel;\n }\n return undefined;\n }\n\n /**\n * Clears storage used for the pipelines. Call this when the\n * PipelineDriver will no longer be used.\n */\n destroy() {\n this.#storage.destroy();\n this.#snapshotter.destroy();\n }\n\n /** @return Map from query ID to PipelineInfo for all added queries. */\n queries(): ReadonlyMap<string, QueryInfo> {\n return this.#pipelines;\n }\n\n totalHydrationTimeMs(): number {\n let total = 0;\n for (const pipeline of this.#pipelines.values()) {\n total += pipeline.hydrationTimeMs;\n }\n return total;\n }\n\n #resolveScalarSubqueries(ast: AST): {\n ast: AST;\n companionRows: {table: string; row: Row}[];\n companions: CompanionSubquery[];\n companionInputs: Input[];\n } {\n const companionRows: {table: string; row: Row}[] = [];\n const companionInputs: Input[] = [];\n\n const executor = (\n subqueryAST: AST,\n childField: string,\n ): LiteralValue | null | undefined => {\n const input = buildPipeline(\n subqueryAST,\n {\n getSource: name => this.#getSource(name),\n createStorage: () => this.#createStorage(),\n decorateSourceInput: (input: SourceInput): Input => input,\n decorateInput: input => input,\n addEdge() {},\n decorateFilterInput: input => input,\n },\n 'scalar-subquery',\n );\n // Consume the full stream rather than using first() to avoid\n // triggering early return on Take's #initialFetch assertion.\n // The subquery AST already has limit: 1, so at most one row is produced.\n let node: Node | undefined;\n for (const n of skipYields(input.fetch({}))) {\n node ??= n;\n }\n if (!node) {\n // Keep the companion alive even with no results — it will\n // detect a future insert that creates the row.\n companionInputs.push(input);\n return undefined;\n }\n companionRows.push({table: subqueryAST.table, row: node.row as Row});\n companionInputs.push(input);\n return (node.row[childField] as LiteralValue) ?? null;\n };\n\n const {ast: resolved, companions} = resolveSimpleScalarSubqueries(\n ast,\n this.#tableSpecs,\n executor,\n );\n return {ast: resolved, companionRows, companions, companionInputs};\n }\n\n /**\n * Adds a pipeline for the query. The method will hydrate the query using the\n * driver's current snapshot of the database and return a stream of results.\n * Henceforth, updates to the query will be returned when the driver is\n * {@link advance}d. The query and its pipeline can be removed with\n * {@link removeQuery()}.\n *\n * If a query with the same queryID is already added, the existing pipeline\n * will be removed and destroyed before adding the new pipeline.\n *\n * @param timer The caller-controlled {@link Timer} used to determine the\n * final hydration time. (The caller may pause and resume the timer\n * when yielding the thread for time-slicing).\n * @return The rows from the initial hydration of the query.\n */\n addQuery(\n transformationHash: string,\n queryID: string,\n query: AST,\n timer: Timer,\n ): Iterable<RowChange | 'yield'> {\n return this.#trackRowSetSignatures(\n this.#addQueryImpl(transformationHash, queryID, query, timer),\n );\n }\n\n *#addQueryImpl(\n transformationHash: string,\n queryID: string,\n query: AST,\n timer: Timer,\n ): Iterable<RowChange | 'yield'> {\n assert(\n this.initialized(),\n 'Pipeline driver must be initialized before adding queries',\n );\n this.removeQuery(queryID);\n const debugDelegate = runtimeDebugFlags.trackRowsVended\n ? new Debug()\n : undefined;\n\n const costModel = this.#ensureCostModelExistsIfEnabled(\n this.#snapshotter.current().db.db,\n );\n\n assert(\n this.#advanceContext === null,\n 'Cannot hydrate while advance is in progress',\n );\n this.#hydrateContext = {\n timer,\n };\n try {\n const {\n ast: resolvedQuery,\n companionRows,\n companions: companionMeta,\n companionInputs,\n } = this.#resolveScalarSubqueries(query);\n\n const input = buildPipeline(\n resolvedQuery,\n {\n debug: debugDelegate,\n enableNotExists: true, // Server-side can handle NOT EXISTS\n getSource: name => this.#getSource(name),\n createStorage: () => this.#createStorage(),\n decorateSourceInput: (input: SourceInput, _queryID: string): Input =>\n new MeasurePushOperator(\n input,\n queryID,\n this.#inspectorDelegate,\n 'query-update-server',\n ),\n decorateInput: input => input,\n addEdge() {},\n decorateFilterInput: input => input,\n },\n queryID,\n costModel,\n );\n const schema = input.getSchema();\n input.setOutput({\n push: change => {\n const streamer = this.#streamer;\n assert(streamer, 'must #startAccumulating() before pushing changes');\n streamer.accumulate(queryID, schema, [change]);\n return [];\n },\n });\n\n yield* hydrateInternal(\n input,\n queryID,\n must(this.#primaryKeys),\n this.#tableSpecs,\n );\n\n for (const {table, row} of companionRows) {\n const primaryKey = mustGetPrimaryKey(this.#primaryKeys, table);\n yield {\n type: ChangeType.ADD,\n queryID,\n table,\n rowKey: getRowKey(primaryKey, row),\n row,\n } as RowChange;\n }\n\n const hydrationTimeMs = timer.totalElapsed();\n if (runtimeDebugFlags.trackRowCountsVended) {\n if (hydrationTimeMs > this.#logConfig.slowHydrateThreshold) {\n let totalRowsConsidered = 0;\n const lc = this.#lc\n .withContext('queryID', queryID)\n .withContext('hydrationTimeMs', hydrationTimeMs);\n for (const tableName of this.#tables.keys()) {\n const entries = Object.entries(\n debugDelegate?.getVendedRowCounts()[tableName] ?? {},\n );\n totalRowsConsidered += entries.reduce(\n (acc, entry) => acc + entry[1],\n 0,\n );\n lc.info?.(tableName + ' VENDED: ', entries);\n }\n lc.info?.(`Total rows considered: ${totalRowsConsidered}`);\n }\n }\n debugDelegate?.reset();\n\n // Set up live companion pipelines for reactive scalar subquery monitoring.\n const liveCompanions: CompanionPipeline[] = [];\n for (let i = 0; i < companionMeta.length; i++) {\n const meta = companionMeta[i];\n const companionInput = companionInputs[i];\n const companionSchema = companionInput.getSchema();\n const {childField, resolvedValue} = meta;\n companionInput.setOutput({\n push: (change: Change) => {\n let newValue: LiteralValue | null | undefined;\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n case ChangeType.EDIT:\n newValue =\n (change[ChangeIndex.NODE].row[childField] as LiteralValue) ??\n null;\n break;\n case ChangeType.REMOVE:\n newValue = undefined;\n break;\n case ChangeType.CHILD:\n return [];\n }\n if (!scalarValuesEqual(newValue, resolvedValue)) {\n throw new ResetPipelinesSignal(\n `Scalar subquery value changed for ${meta.ast.table}: ` +\n `${String(resolvedValue)} -> ${String(newValue)}`,\n 'scalar-subquery',\n );\n }\n const streamer = this.#streamer;\n assert(\n streamer,\n 'must #startAccumulating() before pushing changes',\n );\n streamer.accumulate(queryID, companionSchema, [change]);\n return [];\n },\n });\n liveCompanions.push({input: companionInput, childField, resolvedValue});\n }\n\n // Note: This hydrationTime is a wall-clock overestimate, as it does\n // not take time slicing into account. The view-syncer resets this\n // to a more precise processing-time measurement with setHydrationTime().\n this.#pipelines.set(queryID, {\n input,\n hydrationTimeMs,\n transformedAst: resolvedQuery,\n transformationHash,\n companions: liveCompanions,\n });\n } finally {\n this.#hydrateContext = null;\n }\n }\n\n /**\n * Removes the pipeline for the query. This is a no-op if the query\n * was not added.\n */\n removeQuery(queryID: string) {\n const pipeline = this.#pipelines.get(queryID);\n if (pipeline) {\n this.#pipelines.delete(queryID);\n pipeline.input.destroy();\n for (const companion of pipeline.companions) {\n companion.input.destroy();\n }\n }\n this.#rowSetSignatures.delete(queryID);\n }\n\n /**\n * Current XOR signature of the row-set attached to `queryID`, or\n * `undefined` if no pipeline for the query is currently active.\n * Maintained incrementally by {@link addQuery} and {@link advance}.\n */\n rowSetSignature(queryID: string): bigint | undefined {\n return this.#rowSetSignatures.get(queryID);\n }\n\n /**\n * Wraps an iterable of RowChanges, XORing each row's unit hash into the\n * query's signature (ADDs and REMOVEs share the same op; EDITs are no-ops).\n * Used to intercept the yield streams from {@link addQuery} and\n * {@link advance}.\n */\n *#trackRowSetSignatures(\n changes: Iterable<RowChange | 'yield'>,\n ): Iterable<RowChange | 'yield'> {\n for (const change of changes) {\n if (change !== 'yield' && change.type !== ChangeType.EDIT) {\n const cur = this.#rowSetSignatures.get(change.queryID) ?? 0n;\n const unit = rowIDSignatureUnit({\n schema: '',\n table: change.table,\n rowKey: change.rowKey as RowKey,\n });\n this.#rowSetSignatures.set(change.queryID, cur ^ unit);\n }\n yield change;\n }\n }\n\n /**\n * Returns the value of the row with the given primary key `pk`,\n * or `undefined` if there is no such row. The pipeline must have been\n * initialized.\n */\n getRow(table: string, pk: RowKey): Row | undefined {\n assert(this.initialized(), 'Not yet initialized');\n const source = must(this.#tables.get(table));\n return source.getRow(pk as Row);\n }\n\n /**\n * Advances to the new head of the database.\n *\n * @param timer The caller-controlled {@link Timer} that will be used to\n * measure the progress of the advancement and abort with a\n * {@link ResetPipelinesSignal} if it is estimated to take longer\n * than a hydration.\n * @return The resulting row changes for all added queries. Note that the\n * `changes` must be iterated over in their entirety in order to\n * advance the database snapshot.\n */\n advance(timer: Timer): {\n version: string;\n numChanges: number;\n changes: Iterable<RowChange | 'yield'>;\n } {\n assert(\n this.initialized(),\n 'Pipeline driver must be initialized before advancing',\n );\n const diff = this.#snapshotter.advance(\n this.#tableSpecs,\n this.#allTableNames,\n );\n const {prev, curr, changes} = diff;\n this.#lc.debug?.(\n `advance ${prev.version} => ${curr.version}: ${changes} changes`,\n );\n\n return {\n version: curr.version,\n numChanges: changes,\n changes: this.#trackRowSetSignatures(this.#advance(diff, timer, changes)),\n };\n }\n\n *#advance(\n diff: SnapshotDiff,\n timer: Timer,\n numChanges: number,\n ): Iterable<RowChange | 'yield'> {\n assert(\n this.#hydrateContext === null,\n 'Cannot advance while hydration is in progress',\n );\n const totalHydrationTimeMs = this.totalHydrationTimeMs();\n this.#advanceContext = {\n timer,\n totalHydrationTimeMs,\n numChanges,\n pos: 0,\n };\n this.#lc.info?.(\n `starting pipeline advancement of ${numChanges} changes with an ` +\n `advancement time limited based on total hydration time of ` +\n `${totalHydrationTimeMs} ms.`,\n );\n try {\n for (const {table, prevValues, nextValue} of diff) {\n // Advance progress is checked each time a row is fetched\n // from a TableSource during push processing, but some pushes\n // don't read any rows. Check progress here before processing\n // the next change.\n if (this.#shouldAdvanceYieldMaybeAbortAdvance()) {\n yield 'yield';\n }\n const start = timer.totalElapsed();\n\n let type;\n try {\n const tableSource = this.#tables.get(table);\n if (!tableSource) {\n // no pipelines read from this table, so no need to process the change\n continue;\n }\n const primaryKey = mustGetPrimaryKey(this.#primaryKeys, table);\n let editOldRow: Row | undefined = undefined;\n for (const prevValue of prevValues) {\n if (\n nextValue &&\n deepEqual(\n getRowKey(primaryKey, prevValue as Row) as JSONValue,\n getRowKey(primaryKey, nextValue as Row) as JSONValue,\n )\n ) {\n editOldRow = prevValue;\n } else {\n if (nextValue) {\n this.#conflictRowsDeleted.add(1);\n }\n yield* this.#push(\n tableSource,\n makeSourceChangeRemove(prevValue as Row),\n );\n }\n }\n if (nextValue) {\n if (editOldRow) {\n yield* this.#push(\n tableSource,\n makeSourceChangeEdit(nextValue as Row, editOldRow),\n );\n } else {\n yield* this.#push(\n tableSource,\n makeSourceChangeAdd(nextValue as Row),\n );\n }\n }\n } finally {\n this.#advanceContext.pos++;\n }\n\n const elapsed = timer.totalElapsed() - start;\n this.#advanceTime.recordMs(elapsed, {\n table,\n type,\n });\n }\n\n // Set the new snapshot on all TableSources.\n const {curr} = diff;\n for (const table of this.#tables.values()) {\n table.setDB(curr.db.db);\n }\n this.#ensureCostModelExistsIfEnabled(curr.db.db);\n this.#lc.debug?.(`Advanced to ${curr.version}`);\n } finally {\n this.#advanceContext = null;\n }\n }\n\n /** Implements `BuilderDelegate.getSource()` */\n #getSource(tableName: string): Source {\n let source = this.#tables.get(tableName);\n if (source) {\n return source;\n }\n\n const tableSpec = mustGetTableSpec(this.#tableSpecs, tableName);\n const primaryKey = mustGetPrimaryKey(this.#primaryKeys, tableName);\n\n const {db} = this.#snapshotter.current();\n source = new TableSource(\n this.#lc,\n this.#logConfig,\n db.db,\n tableName,\n tableSpec.zqlSpec,\n primaryKey,\n () => this.#shouldYield(),\n );\n this.#tables.set(tableName, source);\n this.#lc.debug?.(`created TableSource for ${tableName}`);\n return source;\n }\n\n #shouldYield(): boolean {\n if (this.#hydrateContext) {\n return this.#hydrateContext.timer.elapsedLap() > this.#yieldThresholdMs();\n }\n if (this.#advanceContext) {\n return this.#shouldAdvanceYieldMaybeAbortAdvance();\n }\n throw new Error('shouldYield called outside of hydration or advancement');\n }\n\n /**\n * Cancel the advancement processing, by throwing a ResetPipelinesSignal, if\n * it has taken longer than half the total hydration time to make it through\n * half of the advancement, or if processing time exceeds total hydration\n * time. This serves as both a circuit breaker for very large transactions,\n * as well as a bound on the amount of time the previous connection locks\n * the inactive WAL file (as the lock prevents WAL2 from switching to the\n * free WAL when the current one is over the size limit, which can make\n * the WAL grow continuously and compound slowness).\n * This is checked:\n * 1. before starting to process each change in an advancement is processed\n * 2. whenever a row is fetched from a TableSource during push processing\n */\n #shouldAdvanceYieldMaybeAbortAdvance(): boolean {\n const {\n pos,\n numChanges,\n timer: advanceTimer,\n totalHydrationTimeMs,\n } = must(this.#advanceContext);\n const elapsed = advanceTimer.totalElapsed();\n if (\n elapsed > MIN_ADVANCEMENT_TIME_LIMIT_MS &&\n (elapsed > totalHydrationTimeMs ||\n (elapsed > totalHydrationTimeMs / 2 && pos <= numChanges / 2))\n ) {\n throw new ResetPipelinesSignal(\n `Advancement exceeded timeout at ${pos} of ${numChanges} changes ` +\n `after ${elapsed} ms. Advancement time limited based on total ` +\n `hydration time of ${totalHydrationTimeMs} ms.`,\n 'advancement-timeout',\n );\n }\n return advanceTimer.elapsedLap() > this.#yieldThresholdMs();\n }\n\n /** Implements `BuilderDelegate.createStorage()` */\n #createStorage(): Storage {\n return this.#storage.createStorage();\n }\n\n *#push(\n source: TableSource,\n change: SourceChange,\n ): Iterable<RowChange | 'yield'> {\n this.#startAccumulating();\n try {\n for (const val of source.genPush(change)) {\n if (val === 'yield') {\n yield 'yield';\n }\n for (const changeOrYield of this.#stopAccumulating().stream()) {\n yield changeOrYield;\n }\n this.#startAccumulating();\n }\n } finally {\n if (this.#streamer !== null) {\n this.#stopAccumulating();\n }\n }\n }\n\n #startAccumulating() {\n assert(this.#streamer === null, 'Streamer already started');\n this.#streamer = new Streamer(must(this.#primaryKeys), this.#tableSpecs);\n }\n\n #stopAccumulating(): Streamer {\n const streamer = this.#streamer;\n assert(streamer, 'Streamer not started');\n this.#streamer = null;\n return streamer;\n }\n}\n\nclass Streamer {\n readonly #primaryKeys: Map<string, PrimaryKey>;\n readonly #tableSpecs: Map<string, LiteAndZqlSpec>;\n\n constructor(\n primaryKeys: Map<string, PrimaryKey>,\n tableSpecs: Map<string, LiteAndZqlSpec>,\n ) {\n this.#primaryKeys = primaryKeys;\n this.#tableSpecs = tableSpecs;\n }\n\n readonly #changes: [\n queryID: string,\n schema: SourceSchema,\n changes: Iterable<Change | 'yield'>,\n ][] = [];\n\n accumulate(\n queryID: string,\n schema: SourceSchema,\n changes: Iterable<Change | 'yield'>,\n ): this {\n this.#changes.push([queryID, schema, changes]);\n return this;\n }\n\n *stream(): Iterable<RowChange | 'yield'> {\n for (const [queryID, schema, changes] of this.#changes) {\n yield* this.#streamChanges(queryID, schema, changes);\n }\n }\n\n *#streamChanges(\n queryID: string,\n schema: SourceSchema,\n changes: Iterable<Change | 'yield'>,\n ): Iterable<RowChange | 'yield'> {\n // We do not sync rows gathered by the permissions\n // system to the client.\n if (schema.system === 'permissions') {\n return;\n }\n\n for (const change of changes) {\n if (change === 'yield') {\n yield change;\n continue;\n }\n const type = change[ChangeIndex.TYPE];\n switch (type) {\n case ChangeType.REMOVE:\n case ChangeType.ADD: {\n yield* this.#streamNodes(queryID, schema, type, () => [\n change[ChangeIndex.NODE],\n ]);\n break;\n }\n\n case ChangeType.CHILD: {\n const child = change[ChangeIndex.CHILD_DATA];\n const childSchema = must(\n schema.relationships[child.relationshipName],\n );\n\n yield* this.#streamChanges(queryID, childSchema, [child.change]);\n break;\n }\n case ChangeType.EDIT:\n yield* this.#streamNodes(queryID, schema, type, () => [\n {row: change[ChangeIndex.NODE].row, relationships: {}},\n ]);\n break;\n default:\n unreachable(change[ChangeIndex.TYPE]);\n }\n }\n }\n\n *#streamNodes(\n queryID: string,\n schema: SourceSchema,\n op: ChangeType.ADD | ChangeType.REMOVE | ChangeType.EDIT,\n nodes: () => Iterable<Node | 'yield'>,\n ): Iterable<RowChange | 'yield'> {\n const {tableName: table, system} = schema;\n\n const primaryKey = must(this.#primaryKeys.get(table));\n const spec = must(this.#tableSpecs.get(table)).tableSpec;\n\n // We do not sync rows gathered by the permissions\n // system to the client.\n if (system === 'permissions') {\n return;\n }\n\n for (const node of nodes()) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n const {relationships} = node;\n let {row} = node;\n const rowKey = getRowKey(primaryKey, row);\n if (op !== ChangeType.REMOVE) {\n const rowVersion = row[ZERO_VERSION_COLUMN_NAME];\n if (\n typeof rowVersion === 'string' &&\n rowVersion < (spec.minRowVersion ?? '00')\n ) {\n row = {...row, [ZERO_VERSION_COLUMN_NAME]: spec.minRowVersion};\n }\n }\n\n yield {\n type: op,\n queryID,\n table,\n rowKey,\n row: op === ChangeType.REMOVE ? undefined : row,\n } as RowChange;\n\n for (const [relationship, children] of Object.entries(relationships)) {\n const childSchema = must(schema.relationships[relationship]);\n yield* this.#streamNodes(queryID, childSchema, op, children);\n }\n }\n }\n}\n\nfunction* toAdds(nodes: Iterable<Node | 'yield'>): Iterable<Change | 'yield'> {\n for (const node of nodes) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n yield [ChangeType.ADD, node, null];\n }\n}\n\nfunction getRowKey(cols: PrimaryKey, row: Row): RowKey {\n return Object.fromEntries(cols.map(col => [col, must(row[col])]));\n}\n\n/**\n * Core hydration logic used by {@link PipelineDriver#addQuery}, extracted to a\n * function for reuse by the analyze-query RPC path so that analysis hydrates\n * queries the same way the view-syncer does in production.\n */\nexport function* hydrate(\n input: Input,\n hash: string,\n clientSchema: ClientSchema,\n tableSpecs: Map<string, LiteAndZqlSpec>,\n): Iterable<RowChange | 'yield'> {\n const res = input.fetch({});\n const streamer = new Streamer(\n buildPrimaryKeys(clientSchema),\n tableSpecs,\n ).accumulate(hash, input.getSchema(), toAdds(res));\n yield* streamer.stream();\n}\n\nexport function* hydrateInternal(\n input: Input,\n hash: string,\n primaryKeys: Map<string, PrimaryKey>,\n tableSpecs: Map<string, LiteAndZqlSpec>,\n): Iterable<RowChange | 'yield'> {\n const res = input.fetch({});\n const streamer = new Streamer(primaryKeys, tableSpecs).accumulate(\n hash,\n input.getSchema(),\n toAdds(res),\n );\n yield* streamer.stream();\n}\n\nfunction buildPrimaryKeys(\n clientSchema: ClientSchema,\n primaryKeys: Map<string, PrimaryKey> = new Map<string, PrimaryKey>(),\n) {\n for (const [tableName, {primaryKey}] of Object.entries(clientSchema.tables)) {\n primaryKeys.set(tableName, primaryKey as unknown as PrimaryKey);\n }\n return primaryKeys;\n}\n\nfunction mustGetPrimaryKey(\n primaryKeys: Map<string, PrimaryKey> | null,\n table: string,\n): PrimaryKey {\n const pKeys = must(primaryKeys, 'primaryKey map must be non-null');\n\n const rv = pKeys.get(table);\n assert(\n rv,\n () =>\n // oxlint-disable-next-line typescript/restrict-template-expressions e18e/prefer-array-to-sorted\n `table '${table}' is not one of: ${[...pKeys.keys()].sort()}. ` +\n `Check the spelling and ensure that the table has a primary key.`,\n );\n return rv;\n}\n\n/**\n * Compares two scalar subquery resolved values for equality.\n * Unlike `valuesEqual` in data.ts (which treats null != null for join\n * semantics), this uses identity semantics: undefined === undefined\n * (no row matched), null === null (row matched but field was NULL).\n */\nfunction scalarValuesEqual(\n a: LiteralValue | null | undefined,\n b: LiteralValue | null | undefined,\n): boolean {\n return a === b;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pipeline-driver.js","names":["#tables","#pipelines","#rowSetSignatures","#lc","#snapshotter","#storage","#shardID","#logConfig","#config","#tableSpecs","#allTableNames","#costModels","#yieldThresholdMs","#advanceTime","#conflictRowsDeleted","#inspectorDelegate","#initAndResetCommon","#primaryKeys","#replicaVersion","#permissions","#getSource","#createStorage","#trackRowSetSignatures","#addQueryImpl","#ensureCostModelExistsIfEnabled","#advanceContext","#hydrateContext","#resolveScalarSubqueries","#streamer","#advance","#shouldAdvanceYieldMaybeAbortAdvance","#push","#shouldYield","#startAccumulating","#stopAccumulating","#changes","#streamChanges","#streamNodes"],"sources":["../../../../../../zero-cache/src/services/view-syncer/pipeline-driver.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert, unreachable} from '../../../../shared/src/asserts.ts';\nimport {deepEqual, type JSONValue} from '../../../../shared/src/json.ts';\nimport {must} from '../../../../shared/src/must.ts';\nimport type {AST, LiteralValue} from '../../../../zero-protocol/src/ast.ts';\nimport type {ClientSchema} from '../../../../zero-protocol/src/client-schema.ts';\nimport type {Row} from '../../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../../zero-protocol/src/primary-key.ts';\nimport {buildPipeline} from '../../../../zql/src/builder/builder.ts';\nimport {\n Debug,\n runtimeDebugFlags,\n} from '../../../../zql/src/builder/debug-delegate.ts';\nimport {ChangeIndex} from '../../../../zql/src/ivm/change-index.ts';\nimport {ChangeType} from '../../../../zql/src/ivm/change-type.ts';\nimport type {Change} from '../../../../zql/src/ivm/change.ts';\nimport type {Node} from '../../../../zql/src/ivm/data.ts';\nimport {\n skipYields,\n type Input,\n type Storage,\n} from '../../../../zql/src/ivm/operator.ts';\nimport type {SourceSchema} from '../../../../zql/src/ivm/schema.ts';\nimport {\n type Source,\n type SourceChange,\n type SourceInput,\n makeSourceChangeAdd,\n makeSourceChangeEdit,\n makeSourceChangeRemove,\n} from '../../../../zql/src/ivm/source.ts';\nimport type {ConnectionCostModel} from '../../../../zql/src/planner/planner-connection.ts';\nimport {MeasurePushOperator} from '../../../../zql/src/query/measure-push-operator.ts';\nimport type {ClientGroupStorage} from '../../../../zqlite/src/database-storage.ts';\nimport type {Database} from '../../../../zqlite/src/db.ts';\nimport {\n resolveSimpleScalarSubqueries,\n type CompanionSubquery,\n} from '../../../../zqlite/src/resolve-scalar-subqueries.ts';\nimport {createSQLiteCostModel} from '../../../../zqlite/src/sqlite-cost-model.ts';\nimport {TableSource} from '../../../../zqlite/src/table-source.ts';\nimport {\n reloadPermissionsIfChanged,\n type LoadedPermissions,\n} from '../../auth/load-permissions.ts';\nimport type {LogConfig, ZeroConfig} from '../../config/zero-config.ts';\nimport {computeZqlSpecs, mustGetTableSpec} from '../../db/lite-tables.ts';\nimport type {LiteAndZqlSpec, LiteTableSpec} from '../../db/specs.ts';\nimport {\n getOrCreateCounter,\n getOrCreateLatencyHistogram,\n} from '../../observability/metrics.ts';\nimport type {InspectorDelegate} from '../../server/inspector-delegate.ts';\nimport {type RowKey} from '../../types/row-key.ts';\nimport {type ShardID} from '../../types/shards.ts';\nimport {\n getSubscriptionState,\n ZERO_VERSION_COLUMN_NAME,\n} from '../replicator/schema/replication-state.ts';\nimport {checkClientSchema} from './client-schema.ts';\nimport {rowIDSignatureUnit} from './row-set-signature.ts';\nimport type {Snapshotter} from './snapshotter.ts';\nimport {ResetPipelinesSignal, type SnapshotDiff} from './snapshotter.ts';\n\ntype RowOp<Op extends Omit<ChangeType, ChangeType.CHILD>> = {\n readonly type: Op;\n readonly queryID: string;\n readonly table: string;\n readonly rowKey: Row;\n readonly row: Row;\n};\n\nexport type RowAdd = RowOp<ChangeType.ADD>;\n\nexport type RowRemove = RowOp<ChangeType.REMOVE>;\n\nexport type RowEdit = RowOp<ChangeType.EDIT>;\n\nexport type RowChange = RowAdd | RowRemove | RowEdit;\n\ntype CompanionPipeline = {\n readonly input: Input;\n readonly childField: string;\n readonly resolvedValue: LiteralValue | null | undefined;\n};\n\ntype Pipeline = {\n readonly input: Input;\n readonly hydrationTimeMs: number;\n readonly transformedAst: AST;\n readonly transformationHash: string;\n readonly companions: readonly CompanionPipeline[];\n};\n\ntype QueryInfo = {\n readonly transformedAst: AST;\n readonly transformationHash: string;\n};\n\ntype AdvanceContext = {\n readonly timer: Timer;\n readonly totalHydrationTimeMs: number;\n readonly numChanges: number;\n pos: number;\n};\n\ntype HydrateContext = {\n readonly timer: Timer;\n};\n\nexport type Timer = {\n elapsedLap: () => number;\n totalElapsed: () => number;\n};\n\n/**\n * No matter how fast hydration is, advancement is given at least this long to\n * complete before doing a pipeline reset.\n */\nconst MIN_ADVANCEMENT_TIME_LIMIT_MS = 50;\n\n/**\n * Manages the state of IVM pipelines for a given ViewSyncer (i.e. client group).\n */\nexport class PipelineDriver {\n readonly #tables = new Map<string, TableSource>();\n // Query id to pipeline\n readonly #pipelines = new Map<string, Pipeline>();\n /**\n * XOR signature of the set of rows currently attached to each active\n * query, maintained as RowChanges are yielded from {@link addQuery} and\n * {@link advance}. ADDs / REMOVEs XOR the row's unit in (XOR is\n * self-inverse, so one op serves both directions); EDITs are no-ops.\n * Hydration implicitly reseeds from `0n` because {@link addQuery} calls\n * {@link removeQuery} first, which deletes the entry.\n */\n readonly #rowSetSignatures = new Map<string, bigint>();\n\n readonly #lc: LogContext;\n readonly #snapshotter: Snapshotter;\n readonly #storage: ClientGroupStorage;\n readonly #shardID: ShardID;\n readonly #logConfig: LogConfig;\n readonly #config: ZeroConfig | undefined;\n readonly #tableSpecs = new Map<string, LiteAndZqlSpec>();\n readonly #allTableNames = new Set<string>();\n readonly #costModels: WeakMap<Database, ConnectionCostModel> | undefined;\n readonly #yieldThresholdMs: () => number;\n #streamer: Streamer | null = null;\n #hydrateContext: HydrateContext | null = null;\n #advanceContext: AdvanceContext | null = null;\n #replicaVersion: string | null = null;\n #primaryKeys: Map<string, PrimaryKey> | null = null;\n #permissions: LoadedPermissions | null = null;\n\n readonly #advanceTime = getOrCreateLatencyHistogram(\n 'sync',\n 'ivm.advance-time',\n 'Time to advance all queries for a given client group in response to a single change.',\n );\n\n readonly #conflictRowsDeleted = getOrCreateCounter(\n 'sync',\n 'ivm.conflict-rows-deleted',\n 'Number of rows deleted because they conflicted with added row',\n );\n\n readonly #inspectorDelegate: InspectorDelegate;\n\n constructor(\n lc: LogContext,\n logConfig: LogConfig,\n snapshotter: Snapshotter,\n shardID: ShardID,\n storage: ClientGroupStorage,\n clientGroupID: string,\n inspectorDelegate: InspectorDelegate,\n yieldThresholdMs: () => number,\n enablePlanner?: boolean,\n config?: ZeroConfig,\n ) {\n this.#lc = lc.withContext('clientGroupID', clientGroupID);\n this.#snapshotter = snapshotter;\n this.#storage = storage;\n this.#shardID = shardID;\n this.#logConfig = logConfig;\n this.#config = config;\n this.#inspectorDelegate = inspectorDelegate;\n this.#costModels = enablePlanner ? new WeakMap() : undefined;\n this.#yieldThresholdMs = yieldThresholdMs;\n }\n\n /**\n * Initializes the PipelineDriver to the current head of the database.\n * Queries can then be added (i.e. hydrated) with {@link addQuery()}.\n *\n * Must only be called once.\n */\n init(clientSchema: ClientSchema) {\n assert(!this.#snapshotter.initialized(), 'Already initialized');\n this.#snapshotter.init();\n this.#initAndResetCommon(clientSchema);\n }\n\n /**\n * @returns Whether the PipelineDriver has been initialized.\n */\n initialized(): boolean {\n return this.#snapshotter.initialized();\n }\n\n /**\n * Clears the current pipelines and TableSources, returning the PipelineDriver\n * to its initial state. This should be called in response to a schema change,\n * as TableSources need to be recomputed.\n */\n reset(clientSchema: ClientSchema) {\n for (const pipeline of this.#pipelines.values()) {\n pipeline.input.destroy();\n for (const companion of pipeline.companions) {\n companion.input.destroy();\n }\n }\n this.#pipelines.clear();\n this.#tables.clear();\n this.#allTableNames.clear();\n this.#rowSetSignatures.clear();\n this.#initAndResetCommon(clientSchema);\n }\n\n #initAndResetCommon(clientSchema: ClientSchema) {\n const {db} = this.#snapshotter.current();\n const fullTables = new Map<string, LiteTableSpec>();\n computeZqlSpecs(\n this.#lc,\n db.db,\n {includeBackfillingColumns: false},\n this.#tableSpecs,\n fullTables,\n );\n checkClientSchema(\n this.#shardID,\n clientSchema,\n this.#tableSpecs,\n fullTables,\n );\n this.#allTableNames.clear();\n for (const table of fullTables.keys()) {\n this.#allTableNames.add(table);\n }\n const primaryKeys = this.#primaryKeys ?? new Map<string, PrimaryKey>();\n this.#primaryKeys = primaryKeys;\n primaryKeys.clear();\n for (const [table, spec] of this.#tableSpecs.entries()) {\n primaryKeys.set(table, spec.tableSpec.primaryKey);\n }\n buildPrimaryKeys(clientSchema, primaryKeys);\n const {replicaVersion} = getSubscriptionState(db);\n this.#replicaVersion = replicaVersion;\n }\n\n /** @returns The replica version. The PipelineDriver must have been initialized. */\n get replicaVersion(): string {\n return must(this.#replicaVersion, 'Not yet initialized');\n }\n\n /**\n * Returns the current version of the database. This will reflect the\n * latest version change when calling {@link advance()} once the\n * iteration has begun.\n */\n currentVersion(): string {\n assert(this.initialized(), 'Not yet initialized');\n return this.#snapshotter.current().version;\n }\n\n /**\n * Returns the current upstream {app}.permissions, or `null` if none are defined.\n */\n currentPermissions(): LoadedPermissions | null {\n assert(this.initialized(), 'Not yet initialized');\n const res = reloadPermissionsIfChanged(\n this.#lc,\n this.#snapshotter.current().db,\n this.#shardID.appID,\n this.#permissions,\n this.#config,\n );\n if (res.changed) {\n this.#permissions = res.permissions;\n this.#lc.debug?.(\n 'Reloaded permissions',\n JSON.stringify(this.#permissions),\n );\n }\n return this.#permissions;\n }\n\n advanceWithoutDiff(): string {\n const {db, version} = this.#snapshotter.advanceWithoutDiff().curr;\n for (const table of this.#tables.values()) {\n table.setDB(db.db);\n }\n return version;\n }\n\n #ensureCostModelExistsIfEnabled(db: Database) {\n let existing = this.#costModels?.get(db);\n if (existing) {\n return existing;\n }\n if (this.#costModels) {\n const costModel = createSQLiteCostModel(db, this.#tableSpecs);\n this.#costModels.set(db, costModel);\n return costModel;\n }\n return undefined;\n }\n\n /**\n * Clears storage used for the pipelines. Call this when the\n * PipelineDriver will no longer be used.\n */\n destroy() {\n this.#storage.destroy();\n this.#snapshotter.destroy();\n }\n\n /** @return Map from query ID to PipelineInfo for all added queries. */\n queries(): ReadonlyMap<string, QueryInfo> {\n return this.#pipelines;\n }\n\n totalHydrationTimeMs(): number {\n let total = 0;\n for (const pipeline of this.#pipelines.values()) {\n total += pipeline.hydrationTimeMs;\n }\n return total;\n }\n\n #resolveScalarSubqueries(ast: AST): {\n ast: AST;\n companionRows: {table: string; row: Row}[];\n companions: CompanionSubquery[];\n companionInputs: Input[];\n } {\n const companionRows: {table: string; row: Row}[] = [];\n const companionInputs: Input[] = [];\n\n const executor = (\n subqueryAST: AST,\n childField: string,\n ): LiteralValue | null | undefined => {\n const input = buildPipeline(\n subqueryAST,\n {\n getSource: name => this.#getSource(name),\n createStorage: () => this.#createStorage(),\n decorateSourceInput: (input: SourceInput): Input => input,\n decorateInput: input => input,\n addEdge() {},\n decorateFilterInput: input => input,\n },\n 'scalar-subquery',\n );\n // Consume the full stream rather than using first() to avoid\n // triggering early return on Take's #initialFetch assertion.\n // The subquery AST already has limit: 1, so at most one row is produced.\n let node: Node | undefined;\n for (const n of skipYields(input.fetch({}))) {\n node ??= n;\n }\n if (!node) {\n // Keep the companion alive even with no results — it will\n // detect a future insert that creates the row.\n companionInputs.push(input);\n return undefined;\n }\n companionRows.push({table: subqueryAST.table, row: node.row as Row});\n companionInputs.push(input);\n return (node.row[childField] as LiteralValue) ?? null;\n };\n\n const {ast: resolved, companions} = resolveSimpleScalarSubqueries(\n ast,\n this.#tableSpecs,\n executor,\n );\n return {ast: resolved, companionRows, companions, companionInputs};\n }\n\n /**\n * Adds a pipeline for the query. The method will hydrate the query using the\n * driver's current snapshot of the database and return a stream of results.\n * Henceforth, updates to the query will be returned when the driver is\n * {@link advance}d. The query and its pipeline can be removed with\n * {@link removeQuery()}.\n *\n * If a query with the same queryID is already added, the existing pipeline\n * will be removed and destroyed before adding the new pipeline.\n *\n * @param timer The caller-controlled {@link Timer} used to determine the\n * final hydration time. (The caller may pause and resume the timer\n * when yielding the thread for time-slicing).\n * @return The rows from the initial hydration of the query.\n */\n addQuery(\n transformationHash: string,\n queryID: string,\n query: AST,\n timer: Timer,\n ): Iterable<RowChange | 'yield'> {\n return this.#trackRowSetSignatures(\n this.#addQueryImpl(transformationHash, queryID, query, timer),\n );\n }\n\n *#addQueryImpl(\n transformationHash: string,\n queryID: string,\n query: AST,\n timer: Timer,\n ): Iterable<RowChange | 'yield'> {\n assert(\n this.initialized(),\n 'Pipeline driver must be initialized before adding queries',\n );\n this.removeQuery(queryID);\n const debugDelegate = runtimeDebugFlags.trackRowsVended\n ? new Debug()\n : undefined;\n\n const costModel = this.#ensureCostModelExistsIfEnabled(\n this.#snapshotter.current().db.db,\n );\n\n assert(\n this.#advanceContext === null,\n 'Cannot hydrate while advance is in progress',\n );\n this.#hydrateContext = {\n timer,\n };\n try {\n const {\n ast: resolvedQuery,\n companionRows,\n companions: companionMeta,\n companionInputs,\n } = this.#resolveScalarSubqueries(query);\n\n const input = buildPipeline(\n resolvedQuery,\n {\n debug: debugDelegate,\n enableNotExists: true, // Server-side can handle NOT EXISTS\n getSource: name => this.#getSource(name),\n createStorage: () => this.#createStorage(),\n decorateSourceInput: (input: SourceInput, _queryID: string): Input =>\n new MeasurePushOperator(\n input,\n queryID,\n this.#inspectorDelegate,\n 'query-update-server',\n ),\n decorateInput: input => input,\n addEdge() {},\n decorateFilterInput: input => input,\n },\n queryID,\n costModel,\n );\n const schema = input.getSchema();\n input.setOutput({\n push: change => {\n const streamer = this.#streamer;\n assert(streamer, 'must #startAccumulating() before pushing changes');\n streamer.accumulate(queryID, schema, [change]);\n return [];\n },\n });\n\n yield* hydrateInternal(\n input,\n queryID,\n must(this.#primaryKeys),\n this.#tableSpecs,\n );\n\n for (const {table, row} of companionRows) {\n const primaryKey = mustGetPrimaryKey(this.#primaryKeys, table);\n yield {\n type: ChangeType.ADD,\n queryID,\n table,\n rowKey: getRowKey(primaryKey, row),\n row,\n } as RowChange;\n }\n\n const hydrationTimeMs = timer.totalElapsed();\n if (runtimeDebugFlags.trackRowCountsVended) {\n if (hydrationTimeMs > this.#logConfig.slowHydrateThreshold) {\n let totalRowsConsidered = 0;\n const lc = this.#lc\n .withContext('queryID', queryID)\n .withContext('hydrationTimeMs', hydrationTimeMs);\n for (const tableName of this.#tables.keys()) {\n const entries = Object.entries(\n debugDelegate?.getVendedRowCounts()[tableName] ?? {},\n );\n totalRowsConsidered += entries.reduce(\n (acc, entry) => acc + entry[1],\n 0,\n );\n lc.info?.(tableName + ' VENDED: ', entries);\n }\n lc.info?.(`Total rows considered: ${totalRowsConsidered}`);\n }\n }\n debugDelegate?.reset();\n\n // Set up live companion pipelines for reactive scalar subquery monitoring.\n const liveCompanions: CompanionPipeline[] = [];\n for (let i = 0; i < companionMeta.length; i++) {\n const meta = companionMeta[i];\n const companionInput = companionInputs[i];\n const companionSchema = companionInput.getSchema();\n const {childField, resolvedValue} = meta;\n companionInput.setOutput({\n push: (change: Change) => {\n let newValue: LiteralValue | null | undefined;\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n case ChangeType.EDIT:\n newValue =\n (change[ChangeIndex.NODE].row[childField] as LiteralValue) ??\n null;\n break;\n case ChangeType.REMOVE:\n newValue = undefined;\n break;\n case ChangeType.CHILD:\n return [];\n }\n if (!scalarValuesEqual(newValue, resolvedValue)) {\n throw new ResetPipelinesSignal(\n `Scalar subquery value changed for ${meta.ast.table}: ` +\n `${String(resolvedValue)} -> ${String(newValue)}`,\n 'scalar-subquery',\n );\n }\n const streamer = this.#streamer;\n assert(\n streamer,\n 'must #startAccumulating() before pushing changes',\n );\n streamer.accumulate(queryID, companionSchema, [change]);\n return [];\n },\n });\n liveCompanions.push({input: companionInput, childField, resolvedValue});\n }\n\n // Note: This hydrationTime is a wall-clock overestimate, as it does\n // not take time slicing into account. The view-syncer resets this\n // to a more precise processing-time measurement with setHydrationTime().\n this.#pipelines.set(queryID, {\n input,\n hydrationTimeMs,\n transformedAst: resolvedQuery,\n transformationHash,\n companions: liveCompanions,\n });\n } finally {\n this.#hydrateContext = null;\n }\n }\n\n /**\n * Removes the pipeline for the query. This is a no-op if the query\n * was not added.\n */\n removeQuery(queryID: string) {\n const pipeline = this.#pipelines.get(queryID);\n if (pipeline) {\n this.#pipelines.delete(queryID);\n pipeline.input.destroy();\n for (const companion of pipeline.companions) {\n companion.input.destroy();\n }\n }\n this.#rowSetSignatures.delete(queryID);\n }\n\n /**\n * Current XOR signature of the row-set attached to `queryID`, or\n * `undefined` if no pipeline for the query is currently active.\n * Maintained incrementally by {@link addQuery} and {@link advance}.\n */\n rowSetSignature(queryID: string): bigint | undefined {\n return this.#rowSetSignatures.get(queryID);\n }\n\n /**\n * Wraps an iterable of RowChanges, XORing each row's unit hash into the\n * query's signature (ADDs and REMOVEs share the same op; EDITs are no-ops).\n * Used to intercept the yield streams from {@link addQuery} and\n * {@link advance}.\n */\n *#trackRowSetSignatures(\n changes: Iterable<RowChange | 'yield'>,\n ): Iterable<RowChange | 'yield'> {\n for (const change of changes) {\n if (change !== 'yield' && change.type !== ChangeType.EDIT) {\n const cur = this.#rowSetSignatures.get(change.queryID) ?? 0n;\n const unit = rowIDSignatureUnit({\n schema: '',\n table: change.table,\n rowKey: change.rowKey as RowKey,\n });\n this.#rowSetSignatures.set(change.queryID, cur ^ unit);\n }\n yield change;\n }\n }\n\n /**\n * Returns the value of the row with the given primary key `pk`,\n * or `undefined` if there is no such row. The pipeline must have been\n * initialized.\n */\n getRow(table: string, pk: RowKey): Row | undefined {\n assert(this.initialized(), 'Not yet initialized');\n const source = must(this.#tables.get(table));\n return source.getRow(pk as Row);\n }\n\n /**\n * Advances to the new head of the database.\n *\n * @param timer The caller-controlled {@link Timer} that will be used to\n * measure the progress of the advancement and abort with a\n * {@link ResetPipelinesSignal} if it is estimated to take longer\n * than a hydration.\n * @return The resulting row changes for all added queries. Note that the\n * `changes` must be iterated over in their entirety in order to\n * advance the database snapshot.\n */\n advance(timer: Timer): {\n version: string;\n numChanges: number;\n changes: Iterable<RowChange | 'yield'>;\n } {\n assert(\n this.initialized(),\n 'Pipeline driver must be initialized before advancing',\n );\n const diff = this.#snapshotter.advance(\n this.#tableSpecs,\n this.#allTableNames,\n );\n const {prev, curr, changes} = diff;\n this.#lc.debug?.(\n `advance ${prev.version} => ${curr.version}: ${changes} changes`,\n );\n\n return {\n version: curr.version,\n numChanges: changes,\n changes: this.#trackRowSetSignatures(this.#advance(diff, timer, changes)),\n };\n }\n\n *#advance(\n diff: SnapshotDiff,\n timer: Timer,\n numChanges: number,\n ): Iterable<RowChange | 'yield'> {\n assert(\n this.#hydrateContext === null,\n 'Cannot advance while hydration is in progress',\n );\n const totalHydrationTimeMs = this.totalHydrationTimeMs();\n this.#advanceContext = {\n timer,\n totalHydrationTimeMs,\n numChanges,\n pos: 0,\n };\n this.#lc.info?.(\n `starting pipeline advancement of ${numChanges} changes with an ` +\n `advancement time limited based on total hydration time of ` +\n `${totalHydrationTimeMs} ms.`,\n );\n try {\n for (const {table, prevValues, nextValue} of diff) {\n // Advance progress is checked each time a row is fetched\n // from a TableSource during push processing, but some pushes\n // don't read any rows. Check progress here before processing\n // the next change.\n if (this.#shouldAdvanceYieldMaybeAbortAdvance()) {\n yield 'yield';\n }\n const start = timer.totalElapsed();\n\n let type;\n try {\n const tableSource = this.#tables.get(table);\n if (!tableSource) {\n // no pipelines read from this table, so no need to process the change\n continue;\n }\n const primaryKey = mustGetPrimaryKey(this.#primaryKeys, table);\n let editOldRow: Row | undefined = undefined;\n for (const prevValue of prevValues) {\n if (\n nextValue &&\n deepEqual(\n getRowKey(primaryKey, prevValue as Row) as JSONValue,\n getRowKey(primaryKey, nextValue as Row) as JSONValue,\n )\n ) {\n editOldRow = prevValue;\n } else {\n if (nextValue) {\n this.#conflictRowsDeleted.add(1);\n }\n yield* this.#push(\n tableSource,\n makeSourceChangeRemove(prevValue as Row),\n );\n }\n }\n if (nextValue) {\n if (editOldRow) {\n yield* this.#push(\n tableSource,\n makeSourceChangeEdit(nextValue as Row, editOldRow),\n );\n } else {\n yield* this.#push(\n tableSource,\n makeSourceChangeAdd(nextValue as Row),\n );\n }\n }\n } finally {\n this.#advanceContext.pos++;\n }\n\n const elapsed = timer.totalElapsed() - start;\n this.#advanceTime.recordMs(elapsed, {\n table,\n type,\n });\n }\n\n // Set the new snapshot on all TableSources.\n const {curr} = diff;\n for (const table of this.#tables.values()) {\n table.setDB(curr.db.db);\n }\n this.#ensureCostModelExistsIfEnabled(curr.db.db);\n this.#lc.debug?.(`Advanced to ${curr.version}`);\n } finally {\n this.#advanceContext = null;\n }\n }\n\n /** Implements `BuilderDelegate.getSource()` */\n #getSource(tableName: string): Source {\n let source = this.#tables.get(tableName);\n if (source) {\n return source;\n }\n\n const tableSpec = mustGetTableSpec(this.#tableSpecs, tableName);\n const primaryKey = mustGetPrimaryKey(this.#primaryKeys, tableName);\n\n const {db} = this.#snapshotter.current();\n source = new TableSource(\n this.#lc,\n this.#logConfig,\n db.db,\n tableName,\n tableSpec.zqlSpec,\n primaryKey,\n () => this.#shouldYield(),\n );\n this.#tables.set(tableName, source);\n this.#lc.debug?.(`created TableSource for ${tableName}`);\n return source;\n }\n\n #shouldYield(): boolean {\n if (this.#hydrateContext) {\n return this.#hydrateContext.timer.elapsedLap() > this.#yieldThresholdMs();\n }\n if (this.#advanceContext) {\n return this.#shouldAdvanceYieldMaybeAbortAdvance();\n }\n throw new Error('shouldYield called outside of hydration or advancement');\n }\n\n /**\n * Cancel the advancement processing, by throwing a ResetPipelinesSignal, if\n * it has taken longer than half the total hydration time to make it through\n * half of the advancement, or if processing time exceeds total hydration\n * time. This serves as both a circuit breaker for very large transactions,\n * as well as a bound on the amount of time the previous connection locks\n * the inactive WAL file (as the lock prevents WAL2 from switching to the\n * free WAL when the current one is over the size limit, which can make\n * the WAL grow continuously and compound slowness).\n * This is checked:\n * 1. before starting to process each change in an advancement is processed\n * 2. whenever a row is fetched from a TableSource during push processing\n */\n #shouldAdvanceYieldMaybeAbortAdvance(): boolean {\n const {\n pos,\n numChanges,\n timer: advanceTimer,\n totalHydrationTimeMs,\n } = must(this.#advanceContext);\n const elapsed = advanceTimer.totalElapsed();\n if (\n elapsed > MIN_ADVANCEMENT_TIME_LIMIT_MS &&\n (elapsed > totalHydrationTimeMs ||\n (elapsed > totalHydrationTimeMs / 2 && pos <= numChanges / 2))\n ) {\n throw new ResetPipelinesSignal(\n `Advancement exceeded timeout at ${pos} of ${numChanges} changes ` +\n `after ${elapsed} ms. Advancement time limited based on total ` +\n `hydration time of ${totalHydrationTimeMs} ms.`,\n 'advancement-timeout',\n );\n }\n return advanceTimer.elapsedLap() > this.#yieldThresholdMs();\n }\n\n /** Implements `BuilderDelegate.createStorage()` */\n #createStorage(): Storage {\n return this.#storage.createStorage();\n }\n\n *#push(\n source: TableSource,\n change: SourceChange,\n ): Iterable<RowChange | 'yield'> {\n this.#startAccumulating();\n try {\n for (const val of source.genPush(change)) {\n if (val === 'yield') {\n yield 'yield';\n }\n for (const changeOrYield of this.#stopAccumulating().stream()) {\n yield changeOrYield;\n }\n this.#startAccumulating();\n }\n } finally {\n if (this.#streamer !== null) {\n this.#stopAccumulating();\n }\n }\n }\n\n #startAccumulating() {\n assert(this.#streamer === null, 'Streamer already started');\n this.#streamer = new Streamer(must(this.#primaryKeys), this.#tableSpecs);\n }\n\n #stopAccumulating(): Streamer {\n const streamer = this.#streamer;\n assert(streamer, 'Streamer not started');\n this.#streamer = null;\n return streamer;\n }\n}\n\nclass Streamer {\n readonly #primaryKeys: Map<string, PrimaryKey>;\n readonly #tableSpecs: Map<string, LiteAndZqlSpec>;\n\n constructor(\n primaryKeys: Map<string, PrimaryKey>,\n tableSpecs: Map<string, LiteAndZqlSpec>,\n ) {\n this.#primaryKeys = primaryKeys;\n this.#tableSpecs = tableSpecs;\n }\n\n readonly #changes: [\n queryID: string,\n schema: SourceSchema,\n changes: Iterable<Change | 'yield'>,\n ][] = [];\n\n accumulate(\n queryID: string,\n schema: SourceSchema,\n changes: Iterable<Change | 'yield'>,\n ): this {\n this.#changes.push([queryID, schema, changes]);\n return this;\n }\n\n *stream(): Iterable<RowChange | 'yield'> {\n for (const [queryID, schema, changes] of this.#changes) {\n yield* this.#streamChanges(queryID, schema, changes);\n }\n }\n\n *#streamChanges(\n queryID: string,\n schema: SourceSchema,\n changes: Iterable<Change | 'yield'>,\n ): Iterable<RowChange | 'yield'> {\n // We do not sync rows gathered by the permissions\n // system to the client.\n if (schema.system === 'permissions') {\n return;\n }\n\n for (const change of changes) {\n if (change === 'yield') {\n yield change;\n continue;\n }\n const type = change[ChangeIndex.TYPE];\n switch (type) {\n case ChangeType.REMOVE:\n case ChangeType.ADD: {\n yield* this.#streamNodes(queryID, schema, type, () => [\n change[ChangeIndex.NODE],\n ]);\n break;\n }\n\n case ChangeType.CHILD: {\n const child = change[ChangeIndex.CHILD_DATA];\n const childSchema = must(\n schema.relationships[child.relationshipName],\n );\n\n yield* this.#streamChanges(queryID, childSchema, [child.change]);\n break;\n }\n case ChangeType.EDIT:\n yield* this.#streamNodes(queryID, schema, type, () => [\n {row: change[ChangeIndex.NODE].row, relationships: {}},\n ]);\n break;\n default:\n unreachable(change[ChangeIndex.TYPE]);\n }\n }\n }\n\n *#streamNodes(\n queryID: string,\n schema: SourceSchema,\n op: ChangeType.ADD | ChangeType.REMOVE | ChangeType.EDIT,\n nodes: () => Iterable<Node | 'yield'>,\n ): Iterable<RowChange | 'yield'> {\n const {tableName: table, system} = schema;\n\n const primaryKey = must(this.#primaryKeys.get(table));\n const spec = must(this.#tableSpecs.get(table)).tableSpec;\n\n // We do not sync rows gathered by the permissions\n // system to the client.\n if (system === 'permissions') {\n return;\n }\n\n for (const node of nodes()) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n const {relationships} = node;\n let {row} = node;\n const rowKey = getRowKey(primaryKey, row);\n if (op !== ChangeType.REMOVE) {\n const rowVersion = row[ZERO_VERSION_COLUMN_NAME];\n if (\n typeof rowVersion === 'string' &&\n rowVersion < (spec.minRowVersion ?? '00')\n ) {\n row = {...row, [ZERO_VERSION_COLUMN_NAME]: spec.minRowVersion};\n }\n }\n\n yield {\n type: op,\n queryID,\n table,\n rowKey,\n row: op === ChangeType.REMOVE ? undefined : row,\n } as RowChange;\n\n for (const [relationship, children] of Object.entries(relationships)) {\n const childSchema = must(schema.relationships[relationship]);\n yield* this.#streamNodes(queryID, childSchema, op, children);\n }\n }\n }\n}\n\nfunction* toAdds(nodes: Iterable<Node | 'yield'>): Iterable<Change | 'yield'> {\n for (const node of nodes) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n yield [ChangeType.ADD, node, null];\n }\n}\n\nfunction getRowKey(cols: PrimaryKey, row: Row): RowKey {\n return Object.fromEntries(cols.map(col => [col, must(row[col])]));\n}\n\n/**\n * Core hydration logic used by {@link PipelineDriver#addQuery}, extracted to a\n * function for reuse by the analyze-query RPC path so that analysis hydrates\n * queries the same way the view-syncer does in production.\n */\nexport function* hydrate(\n input: Input,\n hash: string,\n clientSchema: ClientSchema,\n tableSpecs: Map<string, LiteAndZqlSpec>,\n): Iterable<RowChange | 'yield'> {\n const res = input.fetch({});\n const streamer = new Streamer(\n buildPrimaryKeys(clientSchema),\n tableSpecs,\n ).accumulate(hash, input.getSchema(), toAdds(res));\n yield* streamer.stream();\n}\n\nexport function* hydrateInternal(\n input: Input,\n hash: string,\n primaryKeys: Map<string, PrimaryKey>,\n tableSpecs: Map<string, LiteAndZqlSpec>,\n): Iterable<RowChange | 'yield'> {\n const res = input.fetch({});\n const streamer = new Streamer(primaryKeys, tableSpecs).accumulate(\n hash,\n input.getSchema(),\n toAdds(res),\n );\n yield* streamer.stream();\n}\n\nfunction buildPrimaryKeys(\n clientSchema: ClientSchema,\n primaryKeys: Map<string, PrimaryKey> = new Map<string, PrimaryKey>(),\n) {\n for (const [tableName, {primaryKey}] of Object.entries(clientSchema.tables)) {\n primaryKeys.set(tableName, primaryKey as unknown as PrimaryKey);\n }\n return primaryKeys;\n}\n\nfunction mustGetPrimaryKey(\n primaryKeys: Map<string, PrimaryKey> | null,\n table: string,\n): PrimaryKey {\n const pKeys = must(primaryKeys, 'primaryKey map must be non-null');\n\n const rv = pKeys.get(table);\n assert(\n rv,\n () =>\n // oxlint-disable-next-line e18e/prefer-array-to-sorted\n `table '${table}' is not one of: ${[...pKeys.keys()].sort()}. ` +\n `Check the spelling and ensure that the table has a primary key.`,\n );\n return rv;\n}\n\n/**\n * Compares two scalar subquery resolved values for equality.\n * Unlike `valuesEqual` in data.ts (which treats null != null for join\n * semantics), this uses identity semantics: undefined === undefined\n * (no row matched), null === null (row matched but field was NULL).\n */\nfunction scalarValuesEqual(\n a: LiteralValue | null | undefined,\n b: LiteralValue | null | undefined,\n): boolean {\n return a === b;\n}\n"],"mappings":"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"}
@@ -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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"}