feat: cost schema the production snapshot doesn't cover via the synthesizer - #184
Merged
Merged
Conversation
…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
force-pushed
the
feat-sync-production-stats
branch
from
July 27, 2026 15:37
6b8495c to
5e672e8
Compare
Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Query Doctor Analysis
3 queries analyzed
0 regressed · 0 improved · 0 new · 0 removed
2 pre-existing issues
SELECT "guests"."id", "guests"."session_id", "guests"."username", "guests"."avatar_path", "guests"."color", "guests"."side", "guests"."audio_recording_path", "guests"."audio_recording_public", "gue...
indexassets(event_id, inserted_at desc)
cost 31,003,449 → 1,498 (100% reduction)SELECT * FROM guest_ip_addresses WHERE ip_address = '127.0.0.1';
indexguest_ip_addresses(ip_address)
cost 154,402 → 8 (100% reduction)
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.
What
Before,
QueryOptimizer.setStatisticsbuiltStatisticswithout 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— passschemaas the fifthStatisticsargument (the onlynew Statisticsin this repo), and add asyntheticTablesgetter mirroringcomputedStats/ownMetadata.reporter.ts/runner.ts— plumb the synthesized-table set ontoReportContext.modeledTables.package.json—@query-doctor/coreto^0.16.0, the minor that adds thecurrentSchemaconstructor argument and thesyntheticTablesgetter.The push path is unaffected: it sends
ownMetadata(remote.ts:392), and synthesized numbers live only incomputedStats. 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
0.16.0.npm installto sync the lockfile (it still carries^0.15.0from main), CI goes green.Tests
query-optimizer-synthesis.test.tscovers the wiring: thatsetStatisticsforwards the schema, and thatsyntheticTablesreports tables the snapshot doesn't cover. It runs against a testcontainer and goes green once core publishes.