v11.3.2
-
A request issued while the client is switching servers no longer hangs until
RequestTimeout(#177). Every path that retires a connection -ChangeServer, the ping-triggered fast reconnect,Disconnect,DisconnectAndWaitAsync, and the path taken when anOnConnectedhandler fails - rejected the pending requests first and cleared the socket reference afterwards. The rejection resumes the consumer, and a consumer that issues its next request from there - the second value of a page load, read from the response handler of the first - found the retired socket still installed, passed the connectivity check on it, and was written into it after the sweep that would have rejected it. Nothing completed it: the sweep had run, and a failed send is report-only. Forty seconds later it timed out, with the connection healthy for thirty-nine of them.- the fix clears the socket reference before the sweep. Moving it ahead of the first
await, as the issue proposed, is not enough:RequestManagerbuilds its completion sources withoutRunContinuationsAsynchronously, so on a thread pool the consumer's continuation runs inline, inside the sweep itself, before any await. A fifth retirement path the issue did not list is covered too ImmediateFailnow refuses such a request at once withNotConnectedException;WaitForConnectioncarries it over to the new connection. A request whose send fails because the connection went away between the check and the send is rejected rather than left pending forRequestTimeout- for consumers: retry logic that recognised this failure by the
TimeoutExceptionit used to produce now seesNotConnectedException(orOperationCanceledException, for a request that was in flight when the switch began) immediately. Classify by type rather than by message - the regression test hands the sweep a continuation that runs synchronously and asserts which socket the follow-up saw
- the fix clears the socket reference before the sweep. Moving it ahead of the first
-
ChangeServer, the fast reconnect andDisconnectno longer stall a single-threaded host for two seconds. Stopping the stream-message processor blocked the calling thread on its reader task, with a two-second cap. On Blazor WebAssembly the reader's continuation needs the very thread that was blocked, so the cap was always reached: every server switch froze the UI for two seconds, and a wake-from-background reconnect is such a switch. The stop is awaited now, after the request sweep, and the reader is gone in milliseconds. Measured on the WebAssembly test client: 2000 ms to 5-390 ms per switch. -
A ping-triggered reconnect no longer waits three seconds for itself.
RetireCurrentSessionAndReconnectAsyncruns inside the ping check that calls it, and waited for the ping to finish before retiring the session - its own, whose flag could not clear until it returned. Every reconnect the health check started paid the fullWaitForPingToFinishAsynctimeout before announcing that the session had ended. The wait now recognises the ping it runs in.RestoringConnectiontoOnSessionEndedon the stand: 6 s to 20 ms. -
A reconnect the loop finished is not reconnected a second time. When the fast reconnect's own attempt failed at the socket, the failure callback started the reconnect loop on the same cancellation source; the loop connected first,
OnceOpenretired the source, and the fast reconnect's wait came back cancelled. Its catch read that as a failure: it reportedRestoringConnectionon a client that was connected and started a second loop, whose first attempt retired the live socket and opened another. Consumers saw twoOnConnectedper recovery, with a spuriousRestoringConnectionbetween them, and restored their subscriptions twice. A connected client is now recognised as settled, and the loop no longer retires a socket that is open when its turn comes.- pinned by a test that takes the server down at
RestoringConnection, so the sequence falls to the loop, brings a replacement up on the same port and requires exactly one connection afterwards
- pinned by a test that takes the server down at