Hey guys.
We are executing stored procedure that sometimes is taking as parameter a data table with a little bit of data (in the prepared repro case it is arround 0.5mb). When we try to do that we sometimes get following exception:
System.InvalidOperationException: Invalid operation. The connection is closed.
at Microsoft.Data.SqlClient.TdsParserStateObject.WritePacket(Byte flushMode, Boolean canAccumulate)
at Microsoft.Data.SqlClient.TdsParserStateObject.WriteBytes(ReadOnlySpan`1 b, Int32 len, Int32 offsetBuffer, Boolean canAccumulate, TaskCompletionSource`1 completion, Byte[] array)
at Microsoft.Data.SqlClient.TdsParserStateObject.WriteByteArray(Byte[] b, Int32 len, Int32 offsetBuffer, Boolean canAccumulate, TaskCompletionSource`1 completion)
at Microsoft.Data.SqlClient.TdsParser.WriteString(String s, Int32 length, Int32 offset, TdsParserStateObject stateObj, Boolean canAccumulate)
at Microsoft.Data.SqlClient.TdsValueSetter.SetString(String value, Int32 offset, Int32 length)
at Microsoft.Data.SqlClient.TdsRecordBufferSetter.SetString(SmiEventSink sink, Int32 ordinal, String value, Int32 offset, Int32 length)
at Microsoft.Data.SqlClient.Server.ValueUtilsSmi.SetString_Unchecked(SmiEventSink_Default sink, ITypedSettersV3 setters, Int32 ordinal, String value, Int32 offset, Int32 length)
at Microsoft.Data.SqlClient.Server.ValueUtilsSmi.SetString_LengthChecked(SmiEventSink_Default sink, ITypedSettersV3 setters, Int32 ordinal, SmiMetaData metaData, String value, Int32 offset)
at Microsoft.Data.SqlClient.Server.ValueUtilsSmi.SetCompatibleValue(SmiEventSink_Default sink, ITypedSettersV3 setters, Int32 ordinal, SmiMetaData metaData, Object value, ExtendedClrTypeCode typeCode, Int32 offset)
at Microsoft.Data.SqlClient.Server.ValueUtilsSmi.SetCompatibleValueV200(SmiEventSink_Default sink, SmiTypedGetterSetter setters, Int32 ordinal, SmiMetaData metaData, Object value, ExtendedClrTypeCode typeCode, Int32 offset, Int32 length, ParameterPeekAheadValue peekAhead)
at Microsoft.Data.SqlClient.Server.ValueUtilsSmi.SetDataTable_Unchecked(SmiEventSink_Default sink, SmiTypedGetterSetter setters, Int32 ordinal, SmiMetaData metaData, DataTable value)
at Microsoft.Data.SqlClient.Server.ValueUtilsSmi.SetCompatibleValueV200(SmiEventSink_Default sink, SmiTypedGetterSetter setters, Int32 ordinal, SmiMetaData metaData, Object value, ExtendedClrTypeCode typeCode, Int32 offset, Int32 length, ParameterPeekAheadValue peekAhead)
at Microsoft.Data.SqlClient.TdsParser.WriteSmiParameter(SqlParameter param, Int32 paramIndex, Boolean sendDefault, TdsParserStateObject stateObj, Boolean advancedTraceIsOn)
at Microsoft.Data.SqlClient.TdsParser.TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, Int32 timeout, Boolean inSchema, SqlNotificationRequest notificationRequest, TdsParserStateObject stateObj, Boolean isCommandProc, Boolean sync, TaskCompletionSource`1 completion, Int32 startRpc, Int32 startParam)
at Microsoft.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean isAsync, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest)
at Microsoft.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry, String method)
at Microsoft.Data.SqlClient.SqlCommand.BeginExecuteReaderInternal(CommandBehavior behavior, AsyncCallback callback, Object stateObject, Int32 timeout, Boolean inRetry, Boolean asyncWrite)
at Microsoft.Data.SqlClient.SqlCommand.BeginExecuteReaderAsync(CommandBehavior behavior, AsyncCallback callback, Object stateObject)
at Microsoft.Data.SqlClient.SqlCommand.<>c__DisplayClass173_0.<ExecuteReaderAsync>b__0(CommandBehavior commandBehavior, AsyncCallback callback, Object stateObject)
at System.Threading.Tasks.TaskFactory`1.FromAsyncImpl[TArg1](Func`4 beginMethod, Func`2 endFunction, Action`1 endAction, TArg1 arg1, Object state, TaskCreationOptions creationOptions)
at System.Threading.Tasks.TaskFactory`1.FromAsync[TArg1](Func`4 beginMethod, Func`2 endMethod, TArg1 arg1, Object state)
at Microsoft.Data.SqlClient.SqlCommand.ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
Microsoft.Data.SqlClient.SqlException (0x80131904): A transport-level error has occurred when sending the request to the server. (provider: TCP Provider, error: 35 - An internal exception was caught)
---> System.NotSupportedException: The WriteAsync method cannot be called when another write operation is pending.
at System.Net.Security.SslStream.WriteAsyncInternal[TIOAdapter](TIOAdapter writeAdapter, ReadOnlyMemory`1 buffer)
at Microsoft.Data.SqlClient.SNI.SNIPacket.WriteToStreamAsync(Stream stream, SNIAsyncCallback callback, SNIProviders provider)
ClientConnectionId:fc17a791-7e6e-4ba9-aeed-5d086c8206c4
Execute without error like in non-managed SNI implementation or sync version.
Describe the bug
Hey guys.
We are executing stored procedure that sometimes is taking as parameter a data table with a little bit of data (in the prepared repro case it is arround 0.5mb). When we try to do that we sometimes get following exception:
When I digged into the code I saw that connection is being closed here:
SqlClient/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs
Line 1802 in d14c0b1
And the exception received in that method is:
That led me to following methods:
SqlClient/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs
Line 707 in d14c0b1
SqlClient/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIPacket.cs
Line 304 in d14c0b1
So
SendAsyncreturns justuintand inside that method we have following call ofWriteToStreamAsyncwhich is aasync void:SqlClient/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs
Line 712 in d14c0b1
Since it's a
async voidit means it's fire and forget and there might be another call ofWriteToStreamAsyncbefore previous one completes which lead to writing into the same stream by multiple threads concurrently at the same time that is causing theNotSupportedException(there is also a lock inSendAsyncwhich I don't know what is a purpose of it but it does not defend against that case).If I use sync version of
ExecuteReaderor non-managed SNI on Windows it works fine. I also tried to play withpacketSizeproperty in connection string to increase/decrease but it caused the exception anyway.To reproduce
Here is a solution project with reproduction.
Expected behavior
Execute without error like in non-managed SNI implementation or sync version.
Further technical details
Microsoft.Data.SqlClient version: 1.1.3/2.0.1
.NET target: Net Core 3.1/.net5
SQL Server version: Azure Sql Server
Operating system: Windows(with managed SNI)/ Alpine docker container