Repository navigation
Give every fact-collector read an explicit command deadline (Fixes #2810) - #2869
Merged
Merged
Conversation
) All 31 commands in PgFactCollector ran with no CommandTimeout, inheriting Npgsql's undocumented 30s default. Nobody chose 30s. Measured on the production store after retention was restored (#2809) and with #2827's dedup rewrite in place, PlanRegressionSql still crosses 30s on a COLD run - 34.7s and 32.4s on the two largest servers, against 10.6-21.2s for the same servers warm. It fails on the cold/large combination only, which is why it looked intermittent and why the two prior changes missed: touched the ceiling it was failing against. 60s is bounded on both sides. Below: it must clear the 34.7s cold worst case. Above: the whole analysis pass gets 120s (DarlingWorker.s_analysisTimeout) and thirty collect methods share it, so a deadline near that figure would let one stalled read consume the pass and cost the server its other twenty-nine facts - strictly worse than the failure being fixed. Applied to all 31 sites, not just the one that exposed it: same pass budget, same missing value, and pinning one while leaving thirty is the enumerated-list mistake this repo has paid for twice (#2344, PgStatementText). Pinned structurally, proven red three ways: reverting one site, restoring the 30s default, and raising it to the full 120s pass each fail a different assertion. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MX6HyjsuDCs15qGB2rh4Gy
erikdarlingdata
force-pushed
the
fix/2810-plan-regression-timeout
branch
from
September 4, 2026 00:52
7fdb883 to
d0b295d
Compare
|
Reviewed. This is a clean, well-scoped fix — no blocking issues found. Correctness
Lite/Darling parity
Test coverage / CI
Scope
No T-SQL touched, no security-relevant input handling changed (only a client-side timeout property), no performance regression — if anything this fixes one (missing facts on every cold/large pass). |
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.
Fixes #2810.
The diagnosis, which is not what the issue expected
The issue concluded "raising the timeout is NOT the fix", and at the time it was right: the query could not finish in 300 s because
query_store_statsheld 19 days at a 4-day policy. It also said what to do about that — "fix retention first, then re-measure this query before deciding whether it needs any change at all."Retention was fixed (#2809, ~87 GB reclaimed, 19 chunks down to 5). I re-measured. The answer changed.
Measured on the production store, now
Running the shipped query string (extracted from source, not retyped) against the six busiest servers, two passes:
Two servers cross 30 s cold; a third sits at 28.2 s, inside the noise. Every one of them is comfortably under it warm.
That is the entire defect.
PgFactCollectorsets noCommandTimeouton any of its 31 commands, so all of them inherit Npgsql's undocumented 30 s default. The plan-regression read needs ~35 s cold on the largest servers and ~11-21 s warm, so it fails on the cold/large combination only — which is why it presents as intermittent, and why a hand-run measurement makes it look fine (the second run is warm).It also explains why two changes that looked like they should have fixed it did not. #2827 made the query 2.4x faster and #2826 made the failure visible; neither touched the ceiling it was failing against. The comment #2827 left behind even records
38.9-40.9send-to-end at the time — above 30 s then too.The value, and why the upper bound is the one that matters
60 s. Bounded on both sides, and the second bound is easy to miss:
DarlingWorker.s_analysisTimeoutgives the whole analysis pass 120 s, and thirty collect methods share it. A per-command deadline at or near that figure would let one stalled read consume the entire pass and cost the server its other twenty-nine facts. That converts "one fact is missing" into "every fact is missing" — strictly worse than the failure being fixed.60 s clears the measured worst case by 1.7x and still leaves half the pass for everything else. When a store eventually outgrows it, the read starts warning (#2826) rather than failing silently, which is the signal to look at the scanned window rather than at the ceiling.
Applied to all 31 sites, not just the one that broke
They share one pass budget and had one missing value. Fixing the reported site and leaving thirty identical ones is the enumerated-list mistake this repo has already paid for twice (#2344,
PgStatementText, where a broken sibling arm survived a green suite because the test named the arm it was written for). A generous ceiling is inert on a query that returns in milliseconds, so uniformity costs nothing and removes the trap.Pins, proven red first
Darling.Testsisnet10.0-windows, so the assertions were exercised on macOS through a throwawaynet10.0harness against the real built assembly and the real sources.Three counterfactuals, each failing a different assertion — a pin that can only fail one way is not proving what it claims:
missing==0FAIL,covered>=31FAILvalue>35FAILvalue<=60FAILScope
No query change, no schedule change, no retention change, no migration rung. Instrumentation ceiling only.
Out of scope but worth recording:
PgBaselineProviderhas the same missing-timeout shape, and itsio_latencybaseline is currently timing out on the use2 box for what is very likely the same reason. The issue counts 160 such sites across 35 files; this PR closes the 31 inPgFactCollector.🤖 Generated with Claude Code
https://claude.ai/code/session_01MX6HyjsuDCs15qGB2rh4Gy