Repository navigation
Fix the honesty trio: fake granted-memory zero, dropped physical-reads column, caveat-free RCSI advice - #3550
Conversation
…he dropped physical-reads column, mirror the RCSI caveats #3529: get_memory_trend shipped a hardcoded total_granted_mb = 0.0 on both SKUs — an agent investigating memory-grant pressure read "granted was 0 all window" and ruled out the real culprit. The field is now an explicit null, the envelope carries a granted_note naming get_memory_grants as the grants series' source, and both tool descriptions stop promising granted memory. Pinned by payload tests (Lite real-DuckDB, Darling gated-live) and per-SKU description pins. #3530: Lite's QueryStore time-slice reader mapped TotalReads AND TotalLogicalReads to ordinal 4 (the logical aggregate — a deliberate alias, matching the QueryStats slicer) but never read ordinal 6, so the SELECT's total_physical_reads was computed and dropped. TotalPhysicalReads now maps ordinal 6, pinned by a fixture whose I/O columns carry pairwise-distinct values — equal values are exactly how the slip stayed invisible. #3531: the LCK_M_IS advice handed out the RCSI ALTER calling it "strictly better" with none of the caveats its LCK_M_S twin carried. Both twins now name the brief exclusive lock the ALTER takes, the tempdb version-store cost (phrased after FactRiskDisclosure's RCSI risk items), and the test-on-a-copy warning, pinned by a shared content test. Fixes #3529, fixes #3530, fixes #3531 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QMYFB4qv5tqGwMmdPsa4Zx
|
Reviewed the diff against CONTRIBUTING.md's C#/Two-Store-Parity conventions, focusing on correctness, Lite/Darling parity, security, and perf.
No correctness, security, or Lite/Darling parity issues found in the actual changed code. |
There was a problem hiding this comment.
LGTM — verified the memory-trend null/granted_note change serializes correctly (no DefaultIgnoreCondition on JsonOptions), confirmed the QueryStore ordinal-6 fix matches the SQL and that Darling's equivalent reader already had it right (no parity drift), and confirmed FactAdvice.cs is shared so the RCSI caveat fix covers both SKUs.
…always-0 parity placeholder Boundary extension approved by the wave lead: doc lines going stale because of the #3529 change belong in the same PR. Darling/README.md's tool-catalog line and DarlingTrendReader's MemoryTrendPoint doc comment now describe the shipped behavior — explicit null plus a granted_note naming get_memory_grants. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QMYFB4qv5tqGwMmdPsa4Zx
There was a problem hiding this comment.
LGTM — reviewed the memory-trend null/granted_note change (Lite + Darling wording is byte-identical), the QueryStore physical-reads ordinal fix (verified ordinal 6 in the SELECT is total_physical_reads, and confirmed Darling's equivalent slicer already read it correctly so no parity gap existed), and the LCK_M_S/LCK_M_IS advice-caveat parity (shared FactAdvice.cs, wording consistent with FactRiskDisclosure's existing tempdb version-store language). No correctness, parity, or security issues found; tests added are appropriately targeted (distinct fixture values per I/O column, both SKUs pinned).
…s and overlay both #3547, folded in per the wave lead. The grid's sorting handler mapped "AvgPhysicalReads" to the LOGICAL series under a physical-reads label — a forced workaround while the reader dropped the physical column, which the #3530 fix ended. The mapping now targets TotalPhysicalReads with the bucket.Value case to match, and the selected-row overlay follows: the timeline read carries avg_physical_io_reads through the same deduped CTE (no new dedup site), the point record gains PhysicalReads, and the overlay selector plots it — otherwise honest bars would have drawn under an elapsed-ms overlay, a new mismatch replacing the old label lie. The distinct-per-column test now covers the timeline's read path too, so a logical/physical swap on either the bars' or the overlay's source goes red. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QMYFB4qv5tqGwMmdPsa4Zx
|
Reviewed the diff in detail (Lite/Darling parity, the QueryStore ordinal fix, the RCSI advice caveats, and the memory-grant nulling). Everything in the diff itself checks out:
One thing worth a look, not in this diff's blast radius but directly adjacent to it: "TotalPhysicalReads" => ("TotalReads", "Total Physical Reads"),And its data source, Not asking to block on this since it's outside the file regions this PR touches and wasn't called out as one of the deliberately-deferred items (unlike #3548), but given the PR's whole premise is "don't silently drop the physical-reads column or mislabel the series," it seems worth a fast follow-up issue so it doesn't get lost. |
There was a problem hiding this comment.
LGTM — verified the ordinal-6 physical-reads mapping against the actual SELECT column order, traced the new PhysicalReads field through the SQL → record → grid overlay selector, confirmed Lite/Darling get_memory_trend descriptions and granted_note payloads are word-for-word identical (shared FactAdvice covers the RCSI caveat fix for both SKUs automatically), and confirmed JsonSerializerOptions has no null-ignore so the null-payload tests reflect real behavior. Flagged one non-blocking adjacent issue (ProcedureStatsGrid_Sorting has the same dropped-physical-reads bug, untouched by this PR) as a PR comment for a follow-up.
…rted PR #3567's claude-review flagged that ViewerServerTab.Queries.cs — the viewer's copy of Lite's sort-driven slicer metric mapping — still carried both bug classes this PR fixes in Lite. The procedures grid's physical sort mapped to the LOGICAL series with no physical value case, and the Query Store grid had both the "Avg"-over-totals labels AND the un-ported #3547 physical/logical swap. Both handlers now match Lite: TotalPhysReads mapping, value case, honest Total labels. No reader threading was needed anywhere — the shared slicer reader (ReadQueryStatsSlicerAsync) has always mapped ordinal 6's total_physical_reads, all three slicer SELECTs compute it, and the three item-timeline reads already carry physical_reads into ItemTimelinePoint, with ComputeOverlayPoints' TotalPhysReads arm sitting dormant — so only the handlers were lying. One genuine port gap beyond the switch arms: Lite's sorting handlers re-run SelectionChanged after a metric change so a selected row's overlay re-projects onto the new metric; the viewer's handlers never did, which would have drawn the newly-honest physical bars under a stale-metric overlay — the exact mismatch the #3550 commit warned about. All three handlers gain the re-trigger. Pins mirror Lite's: a source-text pin file over both fixed handlers plus the re-trigger (the omission was per handler, so pinned per handler), the slicer SQL shape theory now asserts the reads/writes/physical alias order the shared reader's ordinals depend on, and the live slicer + dedup tests gain distinct-per-column fixture values (Lite's 110/30/50 signature; 41x100/41x10 for the Query Store bucket and 40x100/40x10 for the timeline point) — equal fixture values are exactly how a swap stays invisible. Verified against a throwaway PG 18.4 + TimescaleDB cluster: 11/11 live, 48/48 ungated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QMYFB4qv5tqGwMmdPsa4Zx
… Store slice-total labels say Total (#3567) * Procedures slicer: physical sort plots the physical series, and slice totals stop calling themselves averages Fixes #3556 — the #3547 bug's verified siblings, found during that fix and kept out of its file boundary. The procedures grid's physical-reads sort mapped to the LOGICAL aggregate under a "Total Physical Reads" label, with no physical case in the value switch — while the slicer's SELECT computed total_physical_reads at ordinal 6 and the reader dropped it on the floor, the #3530 shape exactly. The reader now maps TotalPhysicalReads, and the handler mirrors the Query Store fix: metric TotalPhysReads, the value-switch case, honest label. The overlay side was already wired — ComputeProcOverlayPoints has carried a dormant TotalPhysReads -> DeltaPhysicalReads arm all along, and the handler re-triggers SelectionChanged on metric change, so bars and overlay go physical together with no further threading. The Query Store switch's two remaining dishonest labels — "Avg Reads" and "Avg Writes" over execution-weighted slice TOTALS — now say Total, matching the physical arm beside them and the query-stats grid's vocabulary. Pinned three ways: a distinct-per-column reader test (110/30/50 — equal fixture values are how the drop stayed invisible), and a source-text pin over both sorting handlers' switch arms in the AvailabilityGroupsGridSortTests style, since the handlers are private WPF event handlers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QMYFB4qv5tqGwMmdPsa4Zx * Darling viewer: port the #3547/#3556 grid fixes the review found unported PR #3567's claude-review flagged that ViewerServerTab.Queries.cs — the viewer's copy of Lite's sort-driven slicer metric mapping — still carried both bug classes this PR fixes in Lite. The procedures grid's physical sort mapped to the LOGICAL series with no physical value case, and the Query Store grid had both the "Avg"-over-totals labels AND the un-ported #3547 physical/logical swap. Both handlers now match Lite: TotalPhysReads mapping, value case, honest Total labels. No reader threading was needed anywhere — the shared slicer reader (ReadQueryStatsSlicerAsync) has always mapped ordinal 6's total_physical_reads, all three slicer SELECTs compute it, and the three item-timeline reads already carry physical_reads into ItemTimelinePoint, with ComputeOverlayPoints' TotalPhysReads arm sitting dormant — so only the handlers were lying. One genuine port gap beyond the switch arms: Lite's sorting handlers re-run SelectionChanged after a metric change so a selected row's overlay re-projects onto the new metric; the viewer's handlers never did, which would have drawn the newly-honest physical bars under a stale-metric overlay — the exact mismatch the #3550 commit warned about. All three handlers gain the re-trigger. Pins mirror Lite's: a source-text pin file over both fixed handlers plus the re-trigger (the omission was per handler, so pinned per handler), the slicer SQL shape theory now asserts the reads/writes/physical alias order the shared reader's ordinals depend on, and the live slicer + dedup tests gain distinct-per-column fixture values (Lite's 110/30/50 signature; 41x100/41x10 for the Query Store bucket and 40x100/40x10 for the timeline point) — equal fixture values are exactly how a swap stays invisible. Verified against a throwaway PG 18.4 + TimescaleDB cluster: 11/11 live, 48/48 ungated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QMYFB4qv5tqGwMmdPsa4Zx --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Three small verified honesty defects from the 2026-09 brains-review campaign, plus two lead-approved extensions folded in during review.
#3529 — get_memory_trend published a hardcoded
total_granted_mb = 0.0on both SKUsThe payload promised granted-memory data and shipped a literal zero; an agent investigating RESOURCE_SEMAPHORE could read "granted was 0 all window" and rule out memory grants at exactly the wrong moment.
total_granted_mbis now an explicitnullon both SKUs (identical shape), with an envelope-levelgranted_notenamingget_memory_grantsas the source of the grants series.get_memory_grants.#3530 — Lite QueryStore time-slice read TotalReads and TotalLogicalReads from the same ordinal
The SELECT computes
total_reads(logical) at ordinal 4,total_writesat 5, andtotal_physical_readsat 6. The reader mapped both read fields to 4 — a deliberate alias for the logical aggregate, matching the QueryStats slicer — but never read ordinal 6, so the physical column was computed and dropped.TotalPhysicalReadsnow maps ordinal 6.#3531 — LCK_M_IS advice handed out the RCSI ALTER with no caveats
The LCK_M_S twin closed with the brief-exclusive-lock timing note and the test-on-a-copy warning; LCK_M_IS recommended the identical ALTER calling RCSI "the strictly better mechanism" with neither. Both twins now carry the same three counter-objectives — the brief exclusive lock the ALTER takes, the tempdb version-store cost (phrased after FactRiskDisclosure's RCSI risk items, not duplicated from them), and the test-on-a-copy warning. Pinned by a shared advice-content test over both keys.
Extensions folded in per team-lead instruction
Both were initially left out under this PR's file boundary and filed as issues; the wave lead extended the boundary and directed they land here.
TotalPhysicalReads(label "Total Physical Reads", mirroring the QueryStats slicer), with the matchingbucket.Valuecase. The selected-row overlay follows: the timeline read carriesavg_physical_io_readsthrough the same deduped CTE (no new dedup site),QueryStoreItemTimelinePointgainsPhysicalReads, and the overlay selector plots it — bars-only would have traded the label lie for a bars-vs-overlay mismatch (elapsed overlay on physical bars). The distinct-per-column test covers the timeline read path too; the overlay's metric→field switch itself is WPF code-behind and not unit-testable, like its sibling arms.Darling/README.mdandDarlingTrendReader's doc comment now describe the shipped null +granted_notebehavior.Tests
_AgainstDevPostgrestest; CI's darling-pg job runs it)Fixes #3529, fixes #3530, fixes #3531, fixes #3547, fixes #3549
🤖 Generated with Claude Code
https://claude.ai/code/session_01QMYFB4qv5tqGwMmdPsa4Zx