Skip to content

Commit 7ea7e63

Browse files
DavoudEshtehariDavoud Eshtehari
andauthored
Reset cached async state on disposing a command (#1128) (#1239)
Co-authored-by: Davoud Eshtehari <[email protected]>
1 parent cdafcfc commit 7ea7e63

File tree

5 files changed

+124
-19
lines changed

5 files changed

+124
-19
lines changed

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIStreams.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ public override async Task<int> ReadAsync(byte[] buffer, int offset, int count,
3333
{
3434
return await base.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
3535
}
36+
catch (System.Exception e)
37+
{
38+
SqlClientEventSource.Log.TrySNITraceEvent("SNISslStream.ReadAsync | ERR | Internal Exception occurred while reading data: {0}", args0: e?.Message);
39+
throw;
40+
}
3641
finally
3742
{
3843
_readAsyncSemaphore.Release();
@@ -47,6 +52,11 @@ public override async Task WriteAsync(byte[] buffer, int offset, int count, Canc
4752
{
4853
await base.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
4954
}
55+
catch (System.Exception e)
56+
{
57+
SqlClientEventSource.Log.TrySNITraceEvent("SNISslStream.ReadAsync | ERR | Internal Exception occurred while writing data: {0}", args0: e?.Message);
58+
throw;
59+
}
5060
finally
5161
{
5262
_writeAsyncSemaphore.Release();
@@ -76,6 +86,11 @@ public override async Task<int> ReadAsync(byte[] buffer, int offset, int count,
7686
{
7787
return await base.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
7888
}
89+
catch (System.Exception e)
90+
{
91+
SqlClientEventSource.Log.TrySNITraceEvent("SNINetworkStream.ReadAsync | ERR | Internal Exception occurred while reading data: {0}", args0: e?.Message);
92+
throw;
93+
}
7994
finally
8095
{
8196
_readAsyncSemaphore.Release();
@@ -90,6 +105,11 @@ public override async Task WriteAsync(byte[] buffer, int offset, int count, Canc
90105
{
91106
await base.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
92107
}
108+
catch (System.Exception e)
109+
{
110+
SqlClientEventSource.Log.TrySNITraceEvent("SNINetworkStream.ReadAsync | ERR | Internal Exception occurred while writing data: {0}", args0: e?.Message);
111+
throw;
112+
}
93113
finally
94114
{
95115
_writeAsyncSemaphore.Release();

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs

Lines changed: 69 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,8 @@ internal bool IsActiveConnectionValid(SqlConnection activeConnection)
235235

236236
internal void ResetAsyncState()
237237
{
238+
SqlClientEventSource.Log.TryTraceEvent("CachedAsyncState.ResetAsyncState | API | ObjectId {0}, Client Connection Id {1}, AsyncCommandInProgress={2}",
239+
_cachedAsyncConnection?.ObjectID, _cachedAsyncConnection?.ClientConnectionId, _cachedAsyncConnection?.AsyncCommandInProgress);
238240
_cachedAsyncCloseCount = -1;
239241
_cachedAsyncResult = null;
240242
if (_cachedAsyncConnection != null)
@@ -252,6 +254,7 @@ internal void SetActiveConnectionAndResult(TaskCompletionSource<object> completi
252254
{
253255
Debug.Assert(activeConnection != null, "Unexpected null connection argument on SetActiveConnectionAndResult!");
254256
TdsParser parser = activeConnection?.Parser;
257+
SqlClientEventSource.Log.TryTraceEvent("SqlCommand.SetActiveConnectionAndResult | API | ObjectId {0}, Client Connection Id {1}, MARS={2}", activeConnection?.ObjectID, activeConnection?.ClientConnectionId, parser?.MARSOn);
255258
if ((parser == null) || (parser.State == TdsParserState.Closed) || (parser.State == TdsParserState.Broken))
256259
{
257260
throw ADP.ClosedConnectionError();
@@ -972,8 +975,12 @@ override protected DbParameter CreateDbParameter()
972975
override protected void Dispose(bool disposing)
973976
{
974977
if (disposing)
975-
{ // release managed objects
978+
{
979+
// release managed objects
976980
_cachedMetaData = null;
981+
982+
// reset async cache information to allow a second async execute
983+
_cachedAsyncState?.ResetAsyncState();
977984
}
978985
// release unmanaged objects
979986
base.Dispose(disposing);
@@ -1213,14 +1220,23 @@ private void BeginExecuteNonQueryInternalReadStage(TaskCompletionSource<object>
12131220
cachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteNonQuery), _activeConnection);
12141221
_stateObj.ReadSni(completion);
12151222
}
1223+
// Cause of a possible unstable runtime situation on facing with `OutOfMemoryException` and `StackOverflowException` exceptions,
1224+
// trying to call further functions in the catch of either may fail that should be considered on debuging!
1225+
catch (System.OutOfMemoryException e)
1226+
{
1227+
_activeConnection.Abort(e);
1228+
throw;
1229+
}
1230+
catch (System.StackOverflowException e)
1231+
{
1232+
_activeConnection.Abort(e);
1233+
throw;
1234+
}
12161235
catch (Exception)
12171236
{
12181237
// Similarly, if an exception occurs put the stateObj back into the pool.
12191238
// and reset async cache information to allow a second async execute
1220-
if (null != _cachedAsyncState)
1221-
{
1222-
_cachedAsyncState.ResetAsyncState();
1223-
}
1239+
_cachedAsyncState?.ResetAsyncState();
12241240
ReliablePutStateObject();
12251241
throw;
12261242
}
@@ -1229,7 +1245,9 @@ private void BeginExecuteNonQueryInternalReadStage(TaskCompletionSource<object>
12291245
private void VerifyEndExecuteState(Task completionTask, string endMethod, bool fullCheckForColumnEncryption = false)
12301246
{
12311247
Debug.Assert(completionTask != null);
1232-
1248+
SqlClientEventSource.Log.TryTraceEvent("SqlCommand.VerifyEndExecuteState | API | ObjectId {0}, Client Connection Id {1}, MARS={2}, AsyncCommandInProgress={3}",
1249+
_activeConnection?.ObjectID, _activeConnection?.ClientConnectionId,
1250+
_activeConnection?.Parser?.MARSOn, _activeConnection?.AsyncCommandInProgress);
12331251
if (completionTask.IsCanceled)
12341252
{
12351253
if (_stateObj != null)
@@ -1336,10 +1354,7 @@ public int EndExecuteNonQueryAsync(IAsyncResult asyncResult)
13361354
if (asyncException != null)
13371355
{
13381356
// Leftover exception from the Begin...InternalReadStage
1339-
if (cachedAsyncState != null)
1340-
{
1341-
cachedAsyncState.ResetAsyncState();
1342-
}
1357+
cachedAsyncState?.ResetAsyncState();
13431358
ReliablePutStateObject();
13441359
throw asyncException.InnerException;
13451360
}
@@ -1402,6 +1417,9 @@ private int EndExecuteNonQueryInternal(IAsyncResult asyncResult)
14021417

14031418
private object InternalEndExecuteNonQuery(IAsyncResult asyncResult, bool isInternal, [CallerMemberName] string endMethod = "")
14041419
{
1420+
SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalEndExecuteNonQuery | INFO | ObjectId {0}, Client Connection Id {1}, MARS={2}, AsyncCommandInProgress={3}",
1421+
_activeConnection?.ObjectID, _activeConnection?.ClientConnectionId,
1422+
_activeConnection?.Parser?.MARSOn, _activeConnection?.AsyncCommandInProgress);
14051423
VerifyEndExecuteState((Task)asyncResult, endMethod);
14061424
WaitForAsyncResults(asyncResult, isInternal);
14071425

@@ -1486,6 +1504,8 @@ private object InternalEndExecuteNonQuery(IAsyncResult asyncResult, bool isInter
14861504

14871505
private Task InternalExecuteNonQuery(TaskCompletionSource<object> completion, bool sendToPipe, int timeout, out bool usedCache, bool asyncWrite = false, bool inRetry = false, [CallerMemberName] string methodName = "")
14881506
{
1507+
SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalExecuteNonQuery | INFO | ObjectId {0}, Client Connection Id {1}, AsyncCommandInProgress={2}",
1508+
_activeConnection?.ObjectID, _activeConnection?.ClientConnectionId, _activeConnection?.AsyncCommandInProgress);
14891509
bool isAsync = (null != completion);
14901510
usedCache = false;
14911511

@@ -1716,14 +1736,25 @@ private void BeginExecuteXmlReaderInternalReadStage(TaskCompletionSource<object>
17161736
_cachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteXmlReader), _activeConnection);
17171737
_stateObj.ReadSni(completion);
17181738
}
1739+
// Cause of a possible unstable runtime situation on facing with `OutOfMemoryException` and `StackOverflowException` exceptions,
1740+
// trying to call further functions in the catch of either may fail that should be considered on debuging!
1741+
catch (System.OutOfMemoryException e)
1742+
{
1743+
_activeConnection.Abort(e);
1744+
completion.TrySetException(e);
1745+
throw;
1746+
}
1747+
catch (System.StackOverflowException e)
1748+
{
1749+
_activeConnection.Abort(e);
1750+
completion.TrySetException(e);
1751+
throw;
1752+
}
17191753
catch (Exception e)
17201754
{
17211755
// Similarly, if an exception occurs put the stateObj back into the pool.
17221756
// and reset async cache information to allow a second async execute
1723-
if (null != _cachedAsyncState)
1724-
{
1725-
_cachedAsyncState.ResetAsyncState();
1726-
}
1757+
_cachedAsyncState?.ResetAsyncState();
17271758
ReliablePutStateObject();
17281759
completion.TrySetException(e);
17291760
}
@@ -1750,6 +1781,7 @@ private XmlReader EndExecuteXmlReaderAsync(IAsyncResult asyncResult)
17501781
Exception asyncException = ((Task)asyncResult).Exception;
17511782
if (asyncException != null)
17521783
{
1784+
cachedAsyncState?.ResetAsyncState();
17531785
ReliablePutStateObject();
17541786
throw asyncException.InnerException;
17551787
}
@@ -1944,6 +1976,7 @@ internal SqlDataReader EndExecuteReaderAsync(IAsyncResult asyncResult)
19441976
Exception asyncException = ((Task)asyncResult).Exception;
19451977
if (asyncException != null)
19461978
{
1979+
cachedAsyncState?.ResetAsyncState();
19471980
ReliablePutStateObject();
19481981
throw asyncException.InnerException;
19491982
}
@@ -1967,6 +2000,9 @@ internal SqlDataReader EndExecuteReaderAsync(IAsyncResult asyncResult)
19672000

19682001
private SqlDataReader EndExecuteReaderInternal(IAsyncResult asyncResult)
19692002
{
2003+
SqlClientEventSource.Log.TryTraceEvent("SqlCommand.EndExecuteReaderInternal | API | ObjectId {0}, Client Connection Id {1}, MARS={2}, AsyncCommandInProgress={3}",
2004+
_activeConnection?.ObjectID, _activeConnection?.ClientConnectionId,
2005+
_activeConnection?.Parser?.MARSOn, _activeConnection?.AsyncCommandInProgress);
19702006
SqlStatistics statistics = null;
19712007
bool success = false;
19722008
int? sqlExceptionNumber = null;
@@ -2337,28 +2373,43 @@ long firstAttemptStart
23372373
private void BeginExecuteReaderInternalReadStage(TaskCompletionSource<object> completion)
23382374
{
23392375
Debug.Assert(completion != null, "CompletionSource should not be null");
2376+
SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.BeginExecuteReaderInternalReadStage | INFO | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText);
23402377
// Read SNI does not have catches for async exceptions, handle here.
23412378
try
23422379
{
23432380
// must finish caching information before ReadSni which can activate the callback before returning
23442381
cachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteReader), _activeConnection);
23452382
_stateObj.ReadSni(completion);
23462383
}
2384+
// Cause of a possible unstable runtime situation on facing with `OutOfMemoryException` and `StackOverflowException` exceptions,
2385+
// trying to call further functions in the catch of either may fail that should be considered on debuging!
2386+
catch (System.OutOfMemoryException e)
2387+
{
2388+
_activeConnection.Abort(e);
2389+
completion.TrySetException(e);
2390+
throw;
2391+
}
2392+
catch (System.StackOverflowException e)
2393+
{
2394+
_activeConnection.Abort(e);
2395+
completion.TrySetException(e);
2396+
throw;
2397+
}
23472398
catch (Exception e)
23482399
{
23492400
// Similarly, if an exception occurs put the stateObj back into the pool.
23502401
// and reset async cache information to allow a second async execute
2351-
if (null != _cachedAsyncState)
2352-
{
2353-
_cachedAsyncState.ResetAsyncState();
2354-
}
2402+
_cachedAsyncState?.ResetAsyncState();
23552403
ReliablePutStateObject();
23562404
completion.TrySetException(e);
23572405
}
23582406
}
23592407

23602408
private SqlDataReader InternalEndExecuteReader(IAsyncResult asyncResult, bool isInternal, string endMethod)
23612409
{
2410+
SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalEndExecuteReader | INFO | ObjectId {0}, Client Connection Id {1}, MARS={2}, AsyncCommandInProgress={3}",
2411+
_activeConnection?.ObjectID, _activeConnection?.ClientConnectionId,
2412+
_activeConnection?.Parser?.MARSOn, _activeConnection?.AsyncCommandInProgress);
23622413
VerifyEndExecuteState((Task)asyncResult, endMethod);
23632414
WaitForAsyncResults(asyncResult, isInternal);
23642415

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2859,6 +2859,7 @@ public void ReadAsyncCallback(IntPtr key, PacketHandle packet, uint error)
28592859
// to the outstanding GCRoot until AppDomain.Unload.
28602860
// We live with the above for the time being due to the constraints of the current
28612861
// reliability infrastructure provided by the CLR.
2862+
SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObject.ReadAsyncCallback | Info | State Object Id {0}, received error {1} on idle connection", _objectID, (int)error);
28622863

28632864
TaskCompletionSource<object> source = _networkPacketTaskSource;
28642865
#if DEBUG

0 commit comments

Comments
 (0)