Suspected: async MARS command can lose its own fatal error, and sync MARS Read waits for CommandTimeout, after another session tears down the connection (managed SNI)
I maintainer di solito rispondono entro 2 giorni
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 15/100
- Tipo di issue
- Bug
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Attiva
- Ambito
- databases, networking
Direzione di ricerca
Start with the race described in TdsParser.TryRun (Parser/TdsParser.cs, around the early return on Broken/Closed) and SqlConnection.OnError (SqlConnection.cs, around the Close call on breakConnection). The managed SNI MARS receive path under ManagedSni/SniMarsConnection*.cs is where a pending sync Read would need a wake-up on teardown. The repro is the ManualTest MarsReceiveTeardownTest.cs, which is flaky and needs sysadmin and a non-Azure server. Done means both suspected behaviors are confirmed or ruled out with a dedicated repro.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Describe the bug
These are two suspected (not confirmed) driver behaviors, found while investigating the flaky ManualTest MarsReceiveTeardownTest.FatalError_WhileMarsReaderStreams_DoesNotFaultReceivePump (added in #4680). They were observed with managed SNI and MARS enabled, when one MARS session raises a severity-20 error while another session is streaming a large result.
Suspected bug 1: an async MARS command can complete successfully even though its own result contained a fatal error.
RAISERROR('...', 20, 1) WITH LOG on MARS session 2 makes the server send the error token and then close the TCP connection. Often, the streaming reader on session 1 sees the transport close first:
- It raises a fatal (class 20)
SqlException. SqlConnection.OnErrorbreaks the parser and callsClose()(SqlConnection.cs~2668).- The async terminate command's End step then runs on a continuation.
TdsParser.TryRun(Parser/TdsParser.cs~2403) returnsDoneimmediately when the parser state isBrokenorClosed.
Step 4 means the command's own error token is never parsed, so ExecuteNonQueryAsync() completes without an exception. In other interleavings, the same command fails with InvalidOperationException: The connection is closed, either from the stored Begin-stage exception or from _activeConnection.Parser → GetOpenTdsConnection. The caller of the failing command never observes the sev-20 error it caused. The sync path does not show this, because the sync path parses the error token on the calling thread.
Suspected bug 2: on managed SNI, a sync MARS reader.Read() waits for CommandTimeout after teardown.
In the sync variants of the same test, the streaming session's reader.Read() sometimes takes about 60 s (the test's CommandTimeout = 60) to fail after the physical connection has been torn down. It does not fail promptly. This suggests a missing wake-up for pending sync MARS receives when the connection breaks.
Exceptions observed in the async CI failures (these are the "connection closed" variants of bug 1):
Exception message: System.InvalidOperationException : The connection is closed.
Stack trace:
at Microsoft.Data.SqlClient.SqlCommand.EndExecuteNonQueryAsync(IAsyncResult asyncResult) (line ~380, rethrow of asyncException.InnerException)
-- or --
at Microsoft.Data.SqlClient.SqlConnection.GetOpenTdsConnection()
at Microsoft.Data.SqlClient.SqlConnection.get_Parser()
at Microsoft.Data.SqlClient.SqlCommand.InternalEndExecuteNonQuery(...) (line ~445)
The silent-success variant of bug 1 surfaced in the test as:
Assert.Throws() Failure: No exception was thrown. Expected: typeof(SqlException).
To reproduce
Use the ManualTest src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/MarsReceiveTeardownTest.cs as it was before the test fix. It needs sysadmin, a non-Azure SQL Server, and Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows=true.
Simplified flow:
var cs = new SqlConnectionStringBuilder(baseConnectionString)
{
MultipleActiveResultSets = true,
CommandTimeout = 60
}.ConnectionString;
using var connection = new SqlConnection(cs);
await connection.OpenAsync();
// Session 1: stream a large result.
using var stream = new SqlCommand(
"SELECT TOP (200000) a.object_id, b.name, REPLICATE('x', 200) AS pad " +
"FROM sys.all_objects a CROSS JOIN sys.all_columns b", connection);
var reader = await stream.ExecuteReaderAsync();
var consumer = Task.Run(async () => { while (await reader.ReadAsync()) { } });
// Session 2: fatal error that terminates the physical connection.
using var terminate = new SqlCommand(
"RAISERROR('teardown', 20, 1) WITH LOG", connection);
await terminate.ExecuteNonQueryAsync(); // expected SqlException (Class 20);
// sometimes: no exception, or IOE "connection is closed"
Repro notes:
- CI (public ADO, CI-SqlClient): the test failed in builds 188518, 188399, 188372, 188312, 188092 and 187614 (twice). All failures were
async: Truevariants. Two failures werepooling False, delay 29with "No exception was thrown". The rest weredelay 1with "The connection is closed". - Local loopback (SQL Server 2022 16.0.1210.3, net10.0, managed SNI): 0 failures in 56 runs. The race does not reproduce on its own locally.
- Local, forced race: saturating the thread pool just before the async terminate command (
ProcessorCount * 4work items, each sleeping 300 ms) reproduced the "connection is closed" failure in 2 of 3 iterations. - Bug 2: the 60 s sync durations show up in ordinary local runs as test iterations taking about 2 min instead of about 1 min (the
async Falsecases).
Expected behavior
- A command whose own batch raised a severity-20 error should surface that error as a
SqlException(Class >= 20). If that is not possible, it should at least fail. It should never complete successfully because another MARS session observed the teardown first. - A pending sync
Read()on a MARS session should fail promptly when the physical connection is broken. It should not wait forCommandTimeout.
Further technical details
Microsoft.Data.SqlClient version: main (8.1.0-dev, after #4680 / 266a32e92)
.NET target: .NET 10.0 (managed SNI on Windows; CI job net10_0_AnyCPU_ManagedSNI_1)
SQL Server version: SQL Server 2022 (16.0.1210.3) locally; SQL Server 2025 named instance in CI (Win25_Sql25_Named_Instance)
Operating system: Windows 11 locally; Windows Server 2025 in CI
Additional context
- Both items are suspected. Neither has been confirmed with a dedicated repro outside the test.
- The test is being made robust to this race in a separate test-only PR. After that change, the test accepts either session reporting the fatal error, so it no longer detects suspected bug 1.
- Relevant code:
TdsParser.TryRunearly return onBroken/ClosedSqlConnection.OnErrorclose onbreakConnectionSqlCommand.NonQuery.csEndExecuteNonQueryAsync/InternalEndExecuteNonQuery- managed SNI MARS receive path (
ManagedSni/SniMarsConnection*.cs)
- Lingua principale
- C#
- Stelle
- 1k
- Fork
- 352
- Merge medio
- 4g 8h
- PR unite (30g)
- 104
Preparare l'ambiente
Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di dotnet/SqlClient
-
Sync ExecuteReader on an STA thread deadlocks against a StateChange handler that marshals to the UI thread (OpenAsyncRetry path)Forse di nuovo libera @mdaigle l’ha presa 49 giorni fa e non c’è nessuna pull request aperta. ApertaExternal :link:
Difficoltà 2/5 1-3 ore Idoneità per principianti 64/100
dotnet/SqlClient#4534 · 2 commenti · 1 assegnatario ·
I maintainer di solito rispondono entro 2 giorni
-
Area\Tests
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
dotnet/SqlClient#4377 · 2 commenti ·
I maintainer di solito rispondono entro 2 giorni
-
Server error tokens are lost when SQL Server aborts the connection before the driver has posted its read (bare transport error instead of the server's error)Forse già presa @poizan42 l’ha presa 2 giorni fa. Aperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 25/100
dotnet/SqlClient#4994 · 1 commento ·
I maintainer di solito rispondono entro 2 giorni
-
A major performance regression from when you upgraded 5.2.2 to 7.1.1Forse già presa @cheenamalhotra l’ha presa 2 giorni fa. ApertaPerformance :chart_with_upwards_trend: Regression :boom:
dotnet/SqlClient#4985 · 3 commenti · 1 assegnatario ·
I maintainer di solito rispondono entro 2 giorni
-
SqlTransaction.Dispose() throws NullReferenceException from SqlInternalTransaction.Rollback() when it races the background close of a broken connectionForse già presa @MaceWindu l’ha presa 2 giorni fa. Aperta
dotnet/SqlClient#4971 · 2 commenti · 1 assegnatario ·
I maintainer di solito rispondono entro 2 giorni
Tutte le issue di dotnet/SqlClient
Issue simili
-
needs-triage
Difficoltà 1/5 1-3 ore Idoneità per principianti 76/100
Azure/azure-sdk-for-net#64658 · 1 commento · 1 reazione ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
dotnet/aspnetcore#70370 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
NethermindEth/nethermind#15684 ·
I maintainer di solito rispondono entro 1 giorno
-
area/ai-assistant squad/flyt
Difficoltà 2/5 1-3 ore Idoneità per principianti 83/100
Altinn/altinn-studio#21798 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 64/100
SubtitleEdit/subtitleedit#16699 ·
I maintainer di solito rispondono entro 1 giorno