Repository navigation
Capture the target session id after the open round trip, and never write 0 (Fixes #2884) - #2886
Conversation
…ite 0 (Fixes #2884) Every ABANDONED row V109 wrote carried target_session_id = 0 while successful runs recorded real ids - and the abandon path is the one place the id is load-bearing, existing to join a stalled run to waiting_tasks / dmv_blocking_snapshot / query_snapshots. Two defects compounded. The capture sat before ExecuteReaderAsync, and SqlConnection.ServerProcessId is not reliably populated until the connection has round-tripped a command, so a connection that had not completed that exchange reported the provider's not-populated 0. And the write side guarded every other forensic figure (>= 0 on rows, bytes, last-read) but passed the session id through bare, so the 0 landed in the column as though it were data. The capture now happens after the open round trip: a drain-stall abandon (open ~100-200ms, budget fires minutes into the read) records its real id - the case the column was built for - while an abandon inside ExecuteReaderAsync leaves it NULL, the declared NOT-RECORDED convention. TryReadTargetSessionId normalizes non-positive values to null at the source; the writer grows the sibling-shaped spid > 0 guard because DrainForensics is a public record any caller can construct. Pinned three ways, each proven red against the unfixed source: a source-order pin that the capture sits after the ExecuteReaderAsync it depends on (the property is unmockable without a real SqlConnection, and hoisting the capture back above the open reintroduces the bug while compiling clean), the helper's normalization, and the writer's guard. The helper's behavior is additionally proven on the built assembly: a never-opened SqlConnection maps to null on the fixed helper and leaked 0 on the reverted one. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MX6HyjsuDCs15qGB2rh4Gy
|
Reviewed. This is a clean, narrowly-scoped fix and I did not find correctness, parity, security, or style issues. Correctness
Lite/Darling parity Tests Security / SQL No inline comments filed — nothing rose to that bar. |
Fixes #2884.
The defect
Every
ABANDONEDrow V109 wrote carriedtarget_session_id = 0— a value no real session has — while successful runs recorded real ids. The abandon path is the one place the id is load-bearing: it exists to join a stalled run towaiting_tasks/dmv_blocking_snapshot/query_snapshots.Two defects compounded:
ExecuteReaderAsync.SqlConnection.ServerProcessIdis not reliably populated until the connection has round-tripped a command, so a connection that had not yet completed that exchange reported the provider's not-populated 0.>= 0on rows/bytes/last-read, rejecting the in-memory-1sentinel) but passed the session id through bare, so the 0 landed in the column as though it were data.The fix
ExecuteReaderAsyncleaves it NULL: the declared NOT-RECORDED convention, and one of the reachable-NULL cases the V109 write-side comment already documents. No new NULL-semantics claim is introduced.TryReadTargetSessionIdnormalizes non-positive values to null at the source — 0 is the provider's "not available" state for bothSqlConnection.ServerProcessIdandNpgsqlConnection.ProcessID, never a measurement.spid > 0) beside the existing>= 0count guards, becauseDrainForensicsis a public record any caller can construct with a 0 the capture path would never produce.No schema change, no
StorageVersionbump — the column exists; this is a write-path fix. No log line changed.Verification
TheSessionIdIsCapturedAfterTheOpenRoundTrip— source-order: the capture must sit after theExecuteReaderAsyncit depends on, exactly once. Ordering is pinned on source because it is a source property: the property is unmockable without a realSqlConnection, and a refactor hoisting the capture back above the open reintroduces Abandon-path forensics record target_session_id = 0 instead of the real SPID (or NULL) #2884 while compiling clean.return raw > 0 ? raw : null;).TheUnmeasuredSentinelIsNeverWrittenAsALiteralguard set.net10.0): a never-openedSqlConnection— the same not-populated state the production rows captured — maps to null on the fixed helper and leaked 0 on the reverted one; the printed value changing across rebuilds also proves each rebuild took.EnableWindowsTargeting; no new warnings in touched files; CHANGELOG staged as 2 insertions / 0 deletions with a plaingit add.🤖 Generated with Claude Code
https://claude.ai/code/session_01MX6HyjsuDCs15qGB2rh4Gy