feat: add retrieval_type column to retrieval_checks clickhouse table#485
feat: add retrieval_type column to retrieval_checks clickhouse table#485iand wants to merge 5 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a retrieval_type dimension to ClickHouse retrieval_checks rows and introduces a simple ClickHouse schema migration mechanism (tracked via a schema_migrations table) so future schema evolution can be versioned and applied on startup.
Changes:
- Writes
retrieval_type: "deal"for deal-led retrieval checks when inserting into ClickHouse. - Replaces the prior “run DDL on startup” approach with versioned ClickHouse migrations tracked in a
schema_migrationstable. - Adds a ClickHouse migration to add the
retrieval_typecolumn toretrieval_checks.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| apps/backend/src/retrieval/retrieval.service.ts | Adds retrieval_type: "deal" to ClickHouse inserts for retrieval checks. |
| apps/backend/src/clickhouse/clickhouse.service.ts | Implements migration application + rollback tracking via schema_migrations. |
| apps/backend/src/clickhouse/clickhouse.migrations.ts | Defines versioned migrations (initial schema + add retrieval_type column). |
Comments suppressed due to low confidence (1)
apps/backend/src/clickhouse/clickhouse.migrations.ts:101
- Migration
add_retrieval_type_to_retrieval_checksadds a non-nullretrieval_typecolumn without a DEFAULT or backfill. Existing rows will get the type default (empty string), so historical data won’t be labeled asdealand queries filtering onretrieval_type='deal'will miss old records. Consider adding the column withDEFAULT 'deal'(and optionally backfilling existing rows) since all existing writes appear to be deal-led retrieval checks.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
| this.logger.log({ event: "clickhouse_migrated", database }); | ||
| await this.client.command({ | ||
| query: `ALTER TABLE ${this.database}.schema_migrations DELETE WHERE version = ${version}`, |
There was a problem hiding this comment.
These points are both reasonable and I'll change.
| const parsedUrl = new URL(this.config.url); | ||
| const database = parsedUrl.pathname.replace(/^\//, ""); | ||
| this.database = parsedUrl.pathname.replace(/^\//, ""); | ||
| try { | ||
| await this.migrate(database); | ||
| await this.migrate(this.database); |
There was a problem hiding this comment.
Again, this is valid and tightens the code against deployment errors
| await this.client.command({ | ||
| query: `CREATE TABLE IF NOT EXISTS ${database}.schema_migrations | ||
| ( | ||
| version UInt32, |
There was a problem hiding this comment.
Adding some stricter validation on the CLICKHOUSE_URL
| for (const sql of m.up) { | ||
| await this.client.command({ query: sql }); | ||
| } | ||
| await this.client.insert({ | ||
| table: `${database}.schema_migrations`, | ||
| values: [{ version: m.version, name: m.name }], | ||
| format: "JSONEachRow", | ||
| }); | ||
| this.logger.log({ event: "migration_applied", version: m.version, name: m.name }); | ||
| count++; | ||
| } | ||
|
|
||
| this.logger.log({ event: "clickhouse_migrated", database, appliedCount: count }); | ||
| } | ||
|
|
There was a problem hiding this comment.
This is another valid point (though the code copilot suggests uses raw interpolation which it complained about in an earlier comment!). It does make the migration code slightly more complex but it will be more robust when we run in multiple locations
There was a problem hiding this comment.
This suggested code it not great. It loops through migrations and if it can't obtain a lock for migration N it continues and attempts to get a lock for N+1, which may depend on the migration N. Plus whatever process held the lock for migration N now can't obtain the next one it needs.
There was a problem hiding this comment.
Pull request overview
Adds a retrieval_type dimension to ClickHouse retrieval_checks and introduces a simple ClickHouse schema migration system (tracked via a schema_migrations table) so future schema changes can be applied incrementally and safely.
Changes:
- Adds
retrieval_type: "deal"to the deal-led retrieval check row inserted intoretrieval_checks. - Refactors ClickHouse startup DDL into versioned migrations with a
schema_migrationstracking table and a startup migration lock. - Adds a migration to
ALTER TABLE ... ADD COLUMN retrieval_typeonretrieval_checks.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| apps/backend/src/retrieval/retrieval.service.ts | Adds retrieval_type to the ClickHouse row emitted for retrieval checks. |
| apps/backend/src/clickhouse/clickhouse.service.ts | Implements migration tracking + locking, validates DB name parsed from CLICKHOUSE_URL, and runs migrations on startup. |
| apps/backend/src/clickhouse/clickhouse.migrations.ts | Defines versioned migrations: initial table DDL plus retrieval_type column addition. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const migrations = getMigrations(database); | ||
| let count = 0; | ||
| let schemaVersion = applied.size > 0 ? Math.max(...applied) : 0; | ||
| for (const m of migrations) { | ||
| if (applied.has(m.version)) continue; | ||
| for (const sql of m.up) { | ||
| await this.client.command({ query: sql }); |
There was a problem hiding this comment.
Sorting the migrations is cheap and seems sensible to avoid errors down the road
| await this.client.command({ | ||
| query: `CREATE TABLE IF NOT EXISTS ${database}.schema_migrations | ||
| ( | ||
| version UInt32, | ||
| name String, | ||
| applied_at DateTime64(3, 'UTC') DEFAULT now64() | ||
| ) | ||
| ENGINE = MergeTree() | ||
| ORDER BY version`, | ||
| }); |
There was a problem hiding this comment.
This doesn't seem worth the effort. The database is a required precondition for dealbot when DEALBASE_URL is set, and the first migration error will cause dealbot to refuse to start if the database is not accessible on startup
There was a problem hiding this comment.
as agreed previously, since probelab is creating the clickhouse DB, i'm good with your reasoning here.
| this.logger.error({ event: "migration_locked", message: "Another instance is running migrations" }); | ||
| throw new Error("Migration lock is held by another instance"); |
There was a problem hiding this comment.
This is already noted in a comment. This situation only happens if dealbot crashes while holding the lock. Adding it to the log could be helpful though.
There was a problem hiding this comment.
the copilot suggested code hardcodes the name of the lock table
There was a problem hiding this comment.
Pull request overview
Adds a retrieval_type dimension to ClickHouse retrieval_checks and introduces a versioned ClickHouse schema migration mechanism (tracked in schema_migrations) to evolve the ClickHouse schema safely over time.
Changes:
- Populate a new
retrieval_typefield ("deal") when writingretrieval_checksrows. - Replace startup “create-if-not-exists” DDL execution with a versioned migration runner +
schema_migrationstracking and a migration lock table. - Add migration v2 to add/drop the
retrieval_typecolumn onretrieval_checks.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| apps/backend/src/retrieval/retrieval.service.ts | Adds retrieval_type: "deal" to ClickHouse inserts for retrieval checks. |
| apps/backend/src/clickhouse/clickhouse.service.ts | Implements ClickHouse migration runner, schema version tracking table, and migration lock; updates logging and URL DB parsing. |
| apps/backend/src/clickhouse/clickhouse.migrations.ts | Defines versioned migrations including initial schema and the new retrieval_type column migration. |
Comments suppressed due to low confidence (1)
apps/backend/src/clickhouse/clickhouse.migrations.ts:12
- Repository docs still refer to
apps/backend/src/clickhouse/clickhouse.schema.tsas the source of truth for ClickHouse DDL, but this PR has the DDL inclickhouse.migrations.tsand thatclickhouse.schema.tspath appears to be gone. Please update the docs references (e.g.docs/checks/events-and-metrics.md) to avoid broken links/confusion about where the authoritative schema lives.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
| this.logger.log({ event: "clickhouse_migrated", database }); | ||
| await this.client.command({ | ||
| query: `DELETE FROM ${this.database}.schema_migrations WHERE version = {version:UInt32}`, |
There was a problem hiding this comment.
DELETE FROM in clickhouse marks rows as deleted immediately via a deletion mask so this isn't needed
| async migrateDown(version: number): Promise<void> { | ||
| if (!this.client) throw new Error("ClickHouse not connected"); | ||
| const migrations = getMigrations(this.database); | ||
| const migration = migrations.find((m) => m.version === version); | ||
| if (!migration) throw new Error(`Migration version ${version} not found`); | ||
|
|
||
| for (const sql of migration.down) { | ||
| await this.client.command({ query: sql }); | ||
| } | ||
| this.logger.log({ event: "clickhouse_migrated", database }); | ||
| await this.client.command({ | ||
| query: `DELETE FROM ${this.database}.schema_migrations WHERE version = {version:UInt32}`, | ||
| query_params: { version }, | ||
| }); | ||
| this.logger.log({ event: "migration_rolled_back", version, name: migration.name }); | ||
| } |
There was a problem hiding this comment.
Pull request overview
Adds a retrieval_type dimension to ClickHouse retrieval_checks records and introduces a lightweight ClickHouse schema migration mechanism (tracked via a schema_migrations table) so schema changes can be applied incrementally on startup.
Changes:
- Add
retrieval_typefield to ClickHouse writes for deal-led retrieval checks. - Introduce ClickHouse schema versioning via
schema_migrations+ a migration lock table. - Convert ClickHouse DDL bootstrapping into versioned
up/downmigrations, including a migration to addretrieval_type.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| apps/backend/src/retrieval/retrieval.service.ts | Writes retrieval_type: "deal" into retrieval_checks rows. |
| apps/backend/src/clickhouse/clickhouse.service.ts | Adds migration runner with schema tracking, locking, and migrateDown(). |
| apps/backend/src/clickhouse/clickhouse.migrations.ts | Defines versioned migrations (initial schema + add retrieval_type). |
Comments suppressed due to low confidence (1)
apps/backend/src/clickhouse/clickhouse.migrations.ts:101
- Migration v2 adds
retrieval_typewithout a DEFAULT/backfill. Existing rows inretrieval_checks(written before this column existed) will read back as the type’s default value (empty string), which makes historical data inconsistent with the new semantics where all deal-led retrievals aredeal. Consider adding a DEFAULT (e.g.,'deal') in theADD COLUMNso old rows automatically present asdeal, or add an explicit backfill mutation in the migration if you need a different default strategy.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| async migrateDown(version: number): Promise<void> { | ||
| if (!this.client) throw new Error("ClickHouse not connected"); | ||
| const migrations = getMigrations(this.database); | ||
| const migration = migrations.find((m) => m.version === version); | ||
| if (!migration) throw new Error(`Migration version ${version} not found`); | ||
|
|
||
| const lockAcquired = await this.tryAcquireMigrationLock(this.database); | ||
| if (!lockAcquired) { | ||
| const lockTable = this.migrationLockTable(this.database); | ||
| throw new Error(`Could not acquire migration lock on ${lockTable}. Another migration operation is in progress.`); | ||
| } |
There was a problem hiding this comment.
I don't think this is worth the effort
|
It seems that this new column is not needed since #487 uses its own extended retrieval table. It leaves the Given that, I will close this PR. We can always resurrect it if we want to add the migration capability separately. |
* chore: add Copilot PR review instructions Adds .github/copilot-instructions.md to guide GitHub Copilot's PR review behavior toward high-signal feedback and away from CI-duplicate noise. Process: - Reviewed Copilot's review-platform constraints (4000-char base-branch read, Comment-only review, no merge gating, no external link following) plus Google/Microsoft/OWASP/NIST review literature. - Analyzed 319 Copilot inline comments across the last 150 dealbot PRs to identify which areas Copilot reviews well (job-state consistency, test/fixture-contract drift, multi-network behavior, quoted SQL identifiers, redaction) versus where it overreaches (generic-SQL assumptions on ClickHouse code in PRs #438 and #485, low-priority frontend optimization comments). - Iterated through rounds of adversarial review (self-review against the evidence, then a second-opinion review by Codex) to tighten wording, fit the 4000-byte budget, and encode dealbot-specific invariants. Encoded: - Repository context: monorepo layout, Postgres = source of truth, ClickHouse cluster/schema/migrations owned by an external team (dealbot reviews payload correctness and operational impact, not schema/retention/ops design). - Core invariants: at most one job per SP per check type per network; jobs fail only on execution failure, not on negative check results; scheduling/cleanup/filtering/queue execution stay consistent across the same SP set and network. - Blocker/Important priorities aligned to observed high-value comment themes. - Do-Not-Comment list to suppress CI-duplicate noise (Biome, build, typecheck, test, Docker already enforced in CI). Final size: 3890/4000 bytes. * chore: address Copilot feedback on review instructions - Clarify ClickHouse ownership: DDL (clickhouse.schema.ts) and event payloads are owned in-repo and in-scope for review; only cluster ops/retention/infra tuning are externally owned. Earlier wording could have suppressed legitimate schema review (Copilot caught this). - Add Prometheus as a source of truth alongside Postgres, and discourage adding new persisted DB state without need. - Align Comment Format header with the Blocker/Important priority scheme instead of a free-form `Severity:` label. - Drop the generic performance Important bullet; not backed by the PR-comment evidence and frees bytes for the above. Final size: 3950/4000 bytes.
Adds a
retrieval_typecolumn to the clickhouseretrieval_checkstable as suggested in #438 (comment)This column is populated with the value
dealby the current deal-led retrieval check. Other retrieval checks can add other values.Used this change to incorporate a simple schema migration system for clickhouse similar to the one used for postgres. This adds another table
schema_migrationsto track which migrations have been applied and what version the schema is at.