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)
Maintainer thường phản hồi trong vòng 2 ngày
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 15/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Lĩnh vực
- databases, networking
Hướng nghiên cứu
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.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
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)
- Ngôn ngữ chính
- C#
- Star
- 1k
- Fork
- 352
- Merge trung bình
- 3 ngày 21 giờ
- Pull request đã merge (30 ngày)
- 112
Chuẩn bị môi trường
Khởi chạy dev container của dự án ngay trên trình duyệt, bằng tài khoản GitHub của bạn.
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của dotnet/SqlClient
-
Sync ExecuteReader on an STA thread deadlocks against a StateChange handler that marshals to the UI thread (OpenAsyncRetry path)Có thể làm lại được @mdaigle đã nhận 50 ngày trước và không có pull request nào đang mở. Đang mởExternal :link:
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 64/100
dotnet/SqlClient#4534 · 2 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 2 ngày
-
Tests | Additional test casesĐang mởArea\Tests
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
dotnet/SqlClient#4377 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 2 ngày
-
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)Có thể đã có người làm @poizan42 đã nhận 4 ngày trước. Đang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 25/100
dotnet/SqlClient#4994 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 2 ngày
-
A major performance regression from when you upgraded 5.2.2 to 7.1.1Có thể đã có người làm @cheenamalhotra đã nhận 4 ngày trước. Đang mởPerformance :chart_with_upwards_trend: Regression :boom:
dotnet/SqlClient#4985 · 3 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 2 ngày
-
SqlTransaction.Dispose() throws NullReferenceException from SqlInternalTransaction.Rollback() when it races the background close of a broken connectionCó thể đã có người làm @MaceWindu đã nhận 4 ngày trước. Đang mở
dotnet/SqlClient#4971 · 2 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 2 ngày
Tất cả issue của dotnet/SqlClient
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 74/100
juandresrodca/AdminForge#54 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
Maintainer thường phản hồi trong vòng 1 ngày
-
[辞書] C/C++のプリプロセッサ指令名を英字のままにするĐang mở要トリアージ 辞書
Độ khó 1/5 1-3 giờ Mức phù hợp với người mới 86/100
yksr-melt/Meltype#1625 · 1 reaction ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Docs typoĐang mở
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 78/100
MonoGame/docs.monogame.github.io#328 · 1 bình luận ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
space-wizards/RobustToolbox#7197 ·
Maintainer thường phản hồi trong vòng 1 ngày