Skip to content

Commit ba2dbfe

Browse files
Reset cached async state on disposing a command (#1128)
1 parent 4e72e43 commit ba2dbfe

File tree

6 files changed

+149
-33
lines changed

6 files changed

+149
-33
lines changed

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

+20
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ public override async Task<int> ReadAsync(byte[] buffer, int offset, int count,
2121
{
2222
return await base.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
2323
}
24+
catch (System.Exception e)
25+
{
26+
SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNISslStream), EventType.ERR, "Internal Exception occurred while reading data: {0}", args0: e?.Message);
27+
throw;
28+
}
2429
finally
2530
{
2631
_readAsyncSemaphore.Release();
@@ -34,6 +39,11 @@ public override async Task WriteAsync(byte[] buffer, int offset, int count, Canc
3439
{
3540
await base.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
3641
}
42+
catch (System.Exception e)
43+
{
44+
SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNISslStream), EventType.ERR, "Internal Exception occurred while reading data: {0}", args0: e?.Message);
45+
throw;
46+
}
3747
finally
3848
{
3949
_writeAsyncSemaphore.Release();
@@ -50,6 +60,11 @@ public override async Task<int> ReadAsync(byte[] buffer, int offset, int count,
5060
{
5161
return await base.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
5262
}
63+
catch (System.Exception e)
64+
{
65+
SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNISslStream), EventType.ERR, "Internal Exception occurred while reading data: {0}", args0: e?.Message);
66+
throw;
67+
}
5368
finally
5469
{
5570
_readAsyncSemaphore.Release();
@@ -64,6 +79,11 @@ public override async Task WriteAsync(byte[] buffer, int offset, int count, Canc
6479
{
6580
await base.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
6681
}
82+
catch (System.Exception e)
83+
{
84+
SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNISslStream), EventType.ERR, "Internal Exception occurred while reading data: {0}", args0: e?.Message);
85+
throw;
86+
}
6787
finally
6888
{
6989
_writeAsyncSemaphore.Release();

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

+20
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ public override async ValueTask<int> ReadAsync(Memory<byte> buffer, Cancellation
2222
{
2323
return await base.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
2424
}
25+
catch (Exception e)
26+
{
27+
SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNISslStream), EventType.ERR, "Internal Exception occurred while reading data: {0}", args0: e?.Message);
28+
throw;
29+
}
2530
finally
2631
{
2732
_readAsyncSemaphore.Release();
@@ -40,6 +45,11 @@ public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, Cancella
4045
{
4146
await base.WriteAsync(buffer, cancellationToken).ConfigureAwait(false);
4247
}
48+
catch (Exception e)
49+
{
50+
SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNISslStream), EventType.ERR, "Internal Exception occurred while reading data: {0}", args0: e?.Message);
51+
throw;
52+
}
4353
finally
4454
{
4555
_writeAsyncSemaphore.Release();
@@ -61,6 +71,11 @@ public override async ValueTask<int> ReadAsync(Memory<byte> buffer, Cancellation
6171
{
6272
return await base.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
6373
}
74+
catch (Exception e)
75+
{
76+
SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNISslStream), EventType.ERR, "Internal Exception occurred while reading data: {0}", args0: e?.Message);
77+
throw;
78+
}
6479
finally
6580
{
6681
_readAsyncSemaphore.Release();
@@ -80,6 +95,11 @@ public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, Cancella
8095
{
8196
await base.WriteAsync(buffer, cancellationToken).ConfigureAwait(false);
8297
}
98+
catch (Exception e)
99+
{
100+
SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNISslStream), EventType.ERR, "Internal Exception occurred while reading data: {0}", args0: e?.Message);
101+
throw;
102+
}
83103
finally
84104
{
85105
_writeAsyncSemaphore.Release();

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

+69-18
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,8 @@ internal bool IsActiveConnectionValid(SqlConnection activeConnection)
253253

254254
internal void ResetAsyncState()
255255
{
256+
SqlClientEventSource.Log.TryTraceEvent("CachedAsyncState.ResetAsyncState | API | ObjectId {0}, Client Connection Id {1}, AsyncCommandInProgress={2}",
257+
_cachedAsyncConnection?.ObjectID, _cachedAsyncConnection?.ClientConnectionId, _cachedAsyncConnection?.AsyncCommandInProgress);
256258
_cachedAsyncCloseCount = -1;
257259
_cachedAsyncResult = null;
258260
if (_cachedAsyncConnection != null)
@@ -270,6 +272,7 @@ internal void SetActiveConnectionAndResult(TaskCompletionSource<object> completi
270272
{
271273
Debug.Assert(activeConnection != null, "Unexpected null connection argument on SetActiveConnectionAndResult!");
272274
TdsParser parser = activeConnection?.Parser;
275+
SqlClientEventSource.Log.TryTraceEvent("SqlCommand.SetActiveConnectionAndResult | API | ObjectId {0}, Client Connection Id {1}, MARS={2}", activeConnection?.ObjectID, activeConnection?.ClientConnectionId, parser?.MARSOn);
273276
if ((parser == null) || (parser.State == TdsParserState.Closed) || (parser.State == TdsParserState.Broken))
274277
{
275278
throw ADP.ClosedConnectionError();
@@ -1014,8 +1017,12 @@ protected override DbParameter CreateDbParameter()
10141017
protected override void Dispose(bool disposing)
10151018
{
10161019
if (disposing)
1017-
{ // release managed objects
1020+
{
1021+
// release managed objects
10181022
_cachedMetaData = null;
1023+
1024+
// reset async cache information to allow a second async execute
1025+
_cachedAsyncState?.ResetAsyncState();
10191026
}
10201027
// release unmanaged objects
10211028
base.Dispose(disposing);
@@ -1274,14 +1281,23 @@ private void BeginExecuteNonQueryInternalReadStage(TaskCompletionSource<object>
12741281
cachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteNonQuery), _activeConnection);
12751282
_stateObj.ReadSni(completion);
12761283
}
1284+
// Cause of a possible unstable runtime situation on facing with `OutOfMemoryException` and `StackOverflowException` exceptions,
1285+
// trying to call further functions in the catch of either may fail that should be considered on debuging!
1286+
catch (System.OutOfMemoryException e)
1287+
{
1288+
_activeConnection.Abort(e);
1289+
throw;
1290+
}
1291+
catch (System.StackOverflowException e)
1292+
{
1293+
_activeConnection.Abort(e);
1294+
throw;
1295+
}
12771296
catch (Exception)
12781297
{
12791298
// Similarly, if an exception occurs put the stateObj back into the pool.
12801299
// and reset async cache information to allow a second async execute
1281-
if (null != _cachedAsyncState)
1282-
{
1283-
_cachedAsyncState.ResetAsyncState();
1284-
}
1300+
_cachedAsyncState?.ResetAsyncState();
12851301
ReliablePutStateObject();
12861302
throw;
12871303
}
@@ -1290,7 +1306,9 @@ private void BeginExecuteNonQueryInternalReadStage(TaskCompletionSource<object>
12901306
private void VerifyEndExecuteState(Task completionTask, string endMethod, bool fullCheckForColumnEncryption = false)
12911307
{
12921308
Debug.Assert(completionTask != null);
1293-
1309+
SqlClientEventSource.Log.TryTraceEvent("SqlCommand.VerifyEndExecuteState | API | ObjectId {0}, Client Connection Id {1}, MARS={2}, AsyncCommandInProgress={3}",
1310+
_activeConnection?.ObjectID, _activeConnection?.ClientConnectionId,
1311+
_activeConnection?.Parser?.MARSOn, _activeConnection?.AsyncCommandInProgress);
12941312
if (completionTask.IsCanceled)
12951313
{
12961314
if (_stateObj != null)
@@ -1397,10 +1415,7 @@ public int EndExecuteNonQueryAsync(IAsyncResult asyncResult)
13971415
if (asyncException != null)
13981416
{
13991417
// Leftover exception from the Begin...InternalReadStage
1400-
if (cachedAsyncState != null)
1401-
{
1402-
cachedAsyncState.ResetAsyncState();
1403-
}
1418+
cachedAsyncState?.ResetAsyncState();
14041419
ReliablePutStateObject();
14051420
throw asyncException.InnerException;
14061421
}
@@ -1463,6 +1478,9 @@ private int EndExecuteNonQueryInternal(IAsyncResult asyncResult)
14631478

14641479
private object InternalEndExecuteNonQuery(IAsyncResult asyncResult, bool isInternal, [CallerMemberName] string endMethod = "")
14651480
{
1481+
SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalEndExecuteNonQuery | INFO | ObjectId {0}, Client Connection Id {1}, MARS={2}, AsyncCommandInProgress={3}",
1482+
_activeConnection?.ObjectID, _activeConnection?.ClientConnectionId,
1483+
_activeConnection?.Parser?.MARSOn, _activeConnection?.AsyncCommandInProgress);
14661484
VerifyEndExecuteState((Task)asyncResult, endMethod);
14671485
WaitForAsyncResults(asyncResult, isInternal);
14681486

@@ -1547,6 +1565,8 @@ private object InternalEndExecuteNonQuery(IAsyncResult asyncResult, bool isInter
15471565

15481566
private Task InternalExecuteNonQuery(TaskCompletionSource<object> completion, bool sendToPipe, int timeout, out bool usedCache, bool asyncWrite = false, bool inRetry = false, [CallerMemberName] string methodName = "")
15491567
{
1568+
SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalExecuteNonQuery | INFO | ObjectId {0}, Client Connection Id {1}, AsyncCommandInProgress={2}",
1569+
_activeConnection?.ObjectID, _activeConnection?.ClientConnectionId, _activeConnection?.AsyncCommandInProgress);
15501570
bool isAsync = (null != completion);
15511571
usedCache = false;
15521572

@@ -1783,14 +1803,25 @@ private void BeginExecuteXmlReaderInternalReadStage(TaskCompletionSource<object>
17831803
_cachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteXmlReader), _activeConnection);
17841804
_stateObj.ReadSni(completion);
17851805
}
1806+
// Cause of a possible unstable runtime situation on facing with `OutOfMemoryException` and `StackOverflowException` exceptions,
1807+
// trying to call further functions in the catch of either may fail that should be considered on debuging!
1808+
catch (System.OutOfMemoryException e)
1809+
{
1810+
_activeConnection.Abort(e);
1811+
completion.TrySetException(e);
1812+
throw;
1813+
}
1814+
catch (System.StackOverflowException e)
1815+
{
1816+
_activeConnection.Abort(e);
1817+
completion.TrySetException(e);
1818+
throw;
1819+
}
17861820
catch (Exception e)
17871821
{
17881822
// Similarly, if an exception occurs put the stateObj back into the pool.
17891823
// and reset async cache information to allow a second async execute
1790-
if (null != _cachedAsyncState)
1791-
{
1792-
_cachedAsyncState.ResetAsyncState();
1793-
}
1824+
_cachedAsyncState?.ResetAsyncState();
17941825
ReliablePutStateObject();
17951826
completion.TrySetException(e);
17961827
}
@@ -1817,6 +1848,7 @@ private XmlReader EndExecuteXmlReaderAsync(IAsyncResult asyncResult)
18171848
Exception asyncException = ((Task)asyncResult).Exception;
18181849
if (asyncException != null)
18191850
{
1851+
cachedAsyncState?.ResetAsyncState();
18201852
ReliablePutStateObject();
18211853
throw asyncException.InnerException;
18221854
}
@@ -2014,6 +2046,7 @@ internal SqlDataReader EndExecuteReaderAsync(IAsyncResult asyncResult)
20142046
Exception asyncException = ((Task)asyncResult).Exception;
20152047
if (asyncException != null)
20162048
{
2049+
cachedAsyncState?.ResetAsyncState();
20172050
ReliablePutStateObject();
20182051
throw asyncException.InnerException;
20192052
}
@@ -2037,6 +2070,9 @@ internal SqlDataReader EndExecuteReaderAsync(IAsyncResult asyncResult)
20372070

20382071
private SqlDataReader EndExecuteReaderInternal(IAsyncResult asyncResult)
20392072
{
2073+
SqlClientEventSource.Log.TryTraceEvent("SqlCommand.EndExecuteReaderInternal | API | ObjectId {0}, Client Connection Id {1}, MARS={2}, AsyncCommandInProgress={3}",
2074+
_activeConnection?.ObjectID, _activeConnection?.ClientConnectionId,
2075+
_activeConnection?.Parser?.MARSOn, _activeConnection?.AsyncCommandInProgress);
20402076
SqlStatistics statistics = null;
20412077
bool success = false;
20422078
int? sqlExceptionNumber = null;
@@ -2407,28 +2443,43 @@ long firstAttemptStart
24072443
private void BeginExecuteReaderInternalReadStage(TaskCompletionSource<object> completion)
24082444
{
24092445
Debug.Assert(completion != null, "CompletionSource should not be null");
2446+
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);
24102447
// Read SNI does not have catches for async exceptions, handle here.
24112448
try
24122449
{
24132450
// must finish caching information before ReadSni which can activate the callback before returning
24142451
cachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteReader), _activeConnection);
24152452
_stateObj.ReadSni(completion);
24162453
}
2454+
// Cause of a possible unstable runtime situation on facing with `OutOfMemoryException` and `StackOverflowException` exceptions,
2455+
// trying to call further functions in the catch of either may fail that should be considered on debuging!
2456+
catch (System.OutOfMemoryException e)
2457+
{
2458+
_activeConnection.Abort(e);
2459+
completion.TrySetException(e);
2460+
throw;
2461+
}
2462+
catch (System.StackOverflowException e)
2463+
{
2464+
_activeConnection.Abort(e);
2465+
completion.TrySetException(e);
2466+
throw;
2467+
}
24172468
catch (Exception e)
24182469
{
24192470
// Similarly, if an exception occurs put the stateObj back into the pool.
24202471
// and reset async cache information to allow a second async execute
2421-
if (null != _cachedAsyncState)
2422-
{
2423-
_cachedAsyncState.ResetAsyncState();
2424-
}
2472+
_cachedAsyncState?.ResetAsyncState();
24252473
ReliablePutStateObject();
24262474
completion.TrySetException(e);
24272475
}
24282476
}
24292477

24302478
private SqlDataReader InternalEndExecuteReader(IAsyncResult asyncResult, bool isInternal, string endMethod)
24312479
{
2480+
SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalEndExecuteReader | INFO | ObjectId {0}, Client Connection Id {1}, MARS={2}, AsyncCommandInProgress={3}",
2481+
_activeConnection?.ObjectID, _activeConnection?.ClientConnectionId,
2482+
_activeConnection?.Parser?.MARSOn, _activeConnection?.AsyncCommandInProgress);
24322483
VerifyEndExecuteState((Task)asyncResult, endMethod);
24332484
WaitForAsyncResults(asyncResult, isInternal);
24342485

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

+1
Original file line numberDiff line numberDiff line change
@@ -2872,6 +2872,7 @@ public void ReadAsyncCallback(IntPtr key, PacketHandle packet, uint error)
28722872
// to the outstanding GCRoot until AppDomain.Unload.
28732873
// We live with the above for the time being due to the constraints of the current
28742874
// reliability infrastructure provided by the CLR.
2875+
SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObject.ReadAsyncCallback | Info | State Object Id {0}, received error {1} on idle connection", _objectID, (int)error);
28752876

28762877
TaskCompletionSource<object> source = _networkPacketTaskSource;
28772878
#if DEBUG

0 commit comments

Comments
 (0)