Skip to content

feat: cost schema the production snapshot doesn't cover via the synthesizer - #184

Merged
veksen merged 3 commits into
mainfrom
feat-sync-production-stats
Jul 27, 2026
Merged

veksen merged 3 commits into
mainfrom
feat-sync-production-stats

Conversation

@veksen

@veksen veksen commented Jul 17, 2026 •

Copy link
Copy Markdown
Member

Goal

The analyzer side of Production Statistics (Site#3587, ADR 0007). Makes the core synthesizer actually run on the connected and CI costing paths, so a table the production snapshot has never seen is costed with an extrapolated row count instead of the flat 10M default.

Blocked on @query-doctor/core@0.16.0. It publishes when Site#3587 merges to staging. Until then npm install can't resolve the range and CI is red — expected, and the only thing left blocking this branch.

What

Before, QueryOptimizer.setStatistics built Statistics without the current schema, so a table outside the snapshot's coverage fell to the default and an unindexed scan over it looked free. After, the schema is passed in, so those tables are sized by the synthesizer — foreign-key graph, then shape class, then database median — with column stats inferred from type and constraints.

Both the connected and CI paths already thread the schema through onSuccessfulSync → optimizer.start, so one wiring change covers both.

How

  • query-optimizer.ts — pass schema as the fifth Statistics argument (the only new Statistics in this repo), and add a syntheticTables getter mirroring computedStats / ownMetadata.
  • reporter.ts / runner.ts — plumb the synthesized-table set onto ReportContext.modeledTables.
  • package.json — @query-doctor/core to ^0.16.0, the minor that adds the currentSchema constructor argument and the syntheticTables getter.

The push path is unaffected: it sends ownMetadata (remote.ts:392), and synthesized numbers live only in computedStats. A modeled row count can't reach the stored snapshot.

Deferred to a follow-up

Rendering the "modeled, not verified" banner in the PR comment — template, buildViewModel, snapshot regen. Left until core publishes so the template and snapshot tests are validated rather than written blind. That banner is the half of Site#3420 a developer actually sees, so this branch does not close it on its own.

Sequencing

  1. Site#3587 → staging → core publishes 0.16.0.
  2. Here: npm install to sync the lockfile (it still carries ^0.15.0 from main), CI goes green.
  3. Then the comment-render follow-up.

Tests

query-optimizer-synthesis.test.ts covers the wiring: that setStatistics forwards the schema, and that syntheticTables reports tables the snapshot doesn't cover. It runs against a testcontainer and goes green once core publishes.

veksen and others added 2 commits July 27, 2026 12:36
…esizer

Pass the current schema into Statistics in QueryOptimizer.setStatistics, so a
table the exported production snapshot doesn't cover — added on this branch, or
since the snapshot was captured — is sized by core's synthesizer (foreign-key
graph, shape class, column inference) instead of the flat default. Both the
connected and CI paths already thread the schema through
onSuccessfulSync -> optimizer.start, so this one change covers both.

Expose the synthesized-table set (QueryOptimizer.syntheticTables) and plumb it
onto ReportContext.modeledTables for the run report. Rendering it in the PR
comment (a 'modeled, not verified' banner) is the follow-up, done once core
publishes so the template and snapshot tests can be validated.

Requires @query-doctor/core >= 0.13.0 (the currentSchema parameter and
syntheticTables); the dep is bumped but the lockfile can't reconcile until that
version is published. CI will fail on install until then — expected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Integration test (testcontainers, mirroring query-optimizer.test.ts): a table
absent from the exported snapshot but present in the schema — foreign-keyed to a
covered table sized at 1M — is reported in syntheticTables and costed from the
snapshot, not its single branch row. Proves setStatistics forwards the schema so
core's synthesizer engages.

Can't run until @query-doctor/core@0.13.0 publishes and deps install; staged to
validate when the branch is closed out post-publish, alongside the deferred
comment-render.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@veksen
veksen force-pushed the feat-sync-production-stats branch from 6b8495c to 5e672e8 Compare July 27, 2026 15:37
Co-Authored-By: Claude <noreply@anthropic.com>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Query Doctor Analysis

3 queries analyzed

0 regressed · 0 improved · 0 new · 0 removed

2 pre-existing issues

Using assumed statistics (10000000 rows/table). For better results, sync production stats.

More detail → get_ci_run({ runId: "019fa469-27b6-769f-8505-d50e1bd3fdec" }) · view run · docs

@veksen
veksen merged commit 3937f25 into main Jul 27, 2026
6 checks passed
@veksen
veksen deleted the feat-sync-production-stats branch July 27, 2026 20:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant