SIGN IN SIGN UP

Never match responses on a connection whose write failed mid-flight (#2919) (#3205)

A write that dies after its message has been queued in _writtenAwaitingResponse
must either complete, or take the connection out of service. Otherwise the queue
is permanently one message out of step with the replies on the wire, and every
caller on that connection silently receives somebody else's answer.

Both recovery paths allocate before they reach RecordConnectionFailed:
message.Fail goes via ResultProcessor.ConnectionFail (StringBuilder plus
message.ToString()), and HandleWriteException builds a RedisConnectionException.
That code only ever runs *because* something already failed, so under memory
exhaustion it fails again and teardown is skipped. v3 adding the second teardown
attempt in HandleWriteException narrowed this from two faults to three; it did
not close it.

So make the decision to stop trusting the connection allocation-free, infallible,
and first:

- PhysicalConnection.PoisonWrite sets a volatile bool, nothing more
- both write-failure catches call it before anything that can allocate
- MatchNextResult refuses to match once poisoned, throwing a pre-allocated
  sentinel so the read loop runs the normal teardown and fails everything queued
- the write path refuses to queue onto a poisoned connection

Worst case this degrades to timeouts, which is the right trade against handing
back silently wrong data.

Adds a regression test, plus the internal seam it needs to simulate a teardown
that cannot allocate. On the previous code that test fails with GET k0 returning
v1 - the exact symptom reported in #2919.
M
Marc Gravell committed
e27e9e1e8c0c19c79bc892679cfaf6cf36c975a5
Parent: d37414d
Committed by GitHub <noreply@github.com> on 9/6/2026, 5:39:28 PM