-
Notifications
You must be signed in to change notification settings - Fork 324
LoginWithFailover missing parser state check causes transient errors to trigger failover instead of retry #4139
Description
Description
LoginWithFailover() in SqlConnectionInternal.cs is missing a parser state check that LoginNoFailover() already has. This causes transient errors (e.g. 40613, 42108, 42109) to be treated as network errors during failover scenarios, triggering failover alternation instead of throwing immediately so the outer ConnectRetryCount loop can handle retries.
Example error here: https://sqlclientdrivers.visualstudio.com/public/_build/results?buildId=146283&view=logs&j=4ad43083-dc29-517d-32db-fc6c44c7aa78&t=65e01f4b-b2d9-5e27-3295-390e31b6ce41
Root Cause
When the server sends a transient error (an explicit TDS error token), the TDS parser remains open (State != Closed). In LoginNoFailover(), there is a check:
if (_parser?.State is not TdsParserState.Closed ||
IsDoNotRetryConnectError(sqlex) ||
timeout.IsExpired)
{
throw;
}This correctly distinguishes login-phase errors (parser open) from network errors (parser closed). However, LoginWithFailover() is missing this check, so login-phase transient errors fall through and trigger the failover alternation logic.
The code comment in LoginWithFailover() even notes: "The logic in this method is paralleled by the logic in LoginNoFailover. Changes to either one should be examined to see if they need to be reflected in the other."
Impact
- With
ConnectRetryCount = 0and a user-provided failover partner, transient errors causeconnection.Open()to succeed (via failover) when it should fail - The unit test
TransientFault_WithUserProvidedPartner_RetryDisabled_ShouldFailfails because the connection succeeds instead of throwing
Fix
Add the same _parser?.State is not TdsParserState.Closed check to LoginWithFailover() catch block. This ensures:
- Login-phase errors (transient errors, explicit server rejections): throw immediately, handled by outer retry loop
- Network errors (parser closed): continue failover alternation as before
Metadata
Metadata
Assignees
Labels
Type
Projects
Status