feat(remote): seed the drift baseline from the stored snapshot on connect - #197
Merged
Conversation
…nect Closes #3670. Drift is measured against the last dump this process pushed, so an analyzer that has never pushed has no baseline and never drifts. That is every project whose snapshot was seeded by hand. Query Doctor's own analyzer has been connected for 55 days without pushing statistics once, on a snapshot taken by a manual psql dump on 2026-06-18, leaving six tables uncovered. On connect, adopt the snapshot the server already holds as the baseline. The first schema poll then compares the live schema against it and re-dumps if it has fallen behind. ServerApi.getProductionStats already exists and is already called on the CI path, so no contract change is needed. Seeding never pushes. The numbers came from the server, and echoing them back would republish a stale snapshot as though it were a fresh dump. It is also ignored once a baseline exists, so a reconnect can't discard a fresher local one in favour of the server's older copy. 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: "019fa54e-fce8-76a2-bbd6-ca3c06df5b0c" }) · 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.
Closes Query-Doctor/Site#3670.
Goal
Make a stale Production Statistics snapshot heal itself, with no user action.
#195 added drift detection, but it measures against the last dump this process pushed. An analyzer that has never pushed has no baseline and never drifts. That is every project whose snapshot was seeded by hand.
Query Doctor's own project is the case: an analyzer connected for 55 days, heartbeating, that has pushed statistics zero times. Its snapshot was taken by a manual psql dump on 2026-06-18 and six tables added since are uncovered.
What
Before, drift armed only after this process dumped from source at least once.
After, the analyzer adopts the server's stored snapshot as its baseline on connect. The first schema poll then compares the live schema against it and re-dumps if it has fallen behind.
How
Remote.seedStatsBaselinetakes a snapshot and records it as the drift baseline.ApiConnection.connectWithReconnectcallsgetProductionStats()after connecting and passes the result in. That RPC already exists and is already called on the CI path, so there is no contract change.Two guards, both tested:
The fetch is fire-and-forget. If it fails, drift stays inert until a local dump sets a baseline, which is the behaviour before this change.
Tests
seed-stats-baseline.test.ts, four cases: a seeded baseline makes a new table read as Shape Drift; an empty snapshot leaves drift inert; a second seed does not overwrite the first; seeding emits nostatsApplied. No container needed, since seeding only writes to memory.