Skip to content

Commit 072b349

Browse files
authoredSep 14, 2021
Reset cached async state on disposing a command (#1128) (#1238)
1 parent e6cb9e1 commit 072b349

File tree

5 files changed

+129
-33
lines changed

5 files changed

+129
-33
lines changed
 

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

+20
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(nameof(SNISslStream), EventType.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(nameof(SNISslStream), EventType.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(nameof(SNINetworkStream), EventType.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(nameof(SNINetworkStream), EventType.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

+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();
@@ -1011,8 +1014,12 @@ protected override DbParameter CreateDbParameter()
10111014
protected override void Dispose(bool disposing)
10121015
{
10131016
if (disposing)
1014-
{ // release managed objects
1017+
{
1018+
// release managed objects
10151019
_cachedMetaData = null;
1020+
1021+
// reset async cache information to allow a second async execute
1022+
_cachedAsyncState?.ResetAsyncState();
10161023
}
10171024
// release unmanaged objects
10181025
base.Dispose(disposing);
@@ -1271,14 +1278,23 @@ private void BeginExecuteNonQueryInternalReadStage(TaskCompletionSource<object>
12711278
cachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteNonQuery), _activeConnection);
12721279
_stateObj.ReadSni(completion);
12731280
}
1281+
// Cause of a possible unstable runtime situation on facing with `OutOfMemoryException` and `StackOverflowException` exceptions,
1282+
// trying to call further functions in the catch of either may fail that should be considered on debuging!
1283+
catch (System.OutOfMemoryException e)
1284+
{
1285+
_activeConnection.Abort(e);
1286+
throw;
1287+
}
1288+
catch (System.StackOverflowException e)
1289+
{
1290+
_activeConnection.Abort(e);
1291+
throw;
1292+
}
12741293
catch (Exception)
12751294
{
12761295
// Similarly, if an exception occurs put the stateObj back into the pool.
12771296
// and reset async cache information to allow a second async execute
1278-
if (null != _cachedAsyncState)
1279-
{
1280-
_cachedAsyncState.ResetAsyncState();
1281-
}
1297+
_cachedAsyncState?.ResetAsyncState();
12821298
ReliablePutStateObject();
12831299
throw;
12841300
}
@@ -1287,7 +1303,9 @@ private void BeginExecuteNonQueryInternalReadStage(TaskCompletionSource<object>
12871303
private void VerifyEndExecuteState(Task completionTask, string endMethod, bool fullCheckForColumnEncryption = false)
12881304
{
12891305
Debug.Assert(completionTask != null);
1290-
1306+
SqlClientEventSource.Log.TryTraceEvent("SqlCommand.VerifyEndExecuteState | API | ObjectId {0}, Client Connection Id {1}, MARS={2}, AsyncCommandInProgress={3}",
1307+
_activeConnection?.ObjectID, _activeConnection?.ClientConnectionId,
1308+
_activeConnection?.Parser?.MARSOn, _activeConnection?.AsyncCommandInProgress);
12911309
if (completionTask.IsCanceled)
12921310
{
12931311
if (_stateObj != null)
@@ -1394,10 +1412,7 @@ public int EndExecuteNonQueryAsync(IAsyncResult asyncResult)
13941412
if (asyncException != null)
13951413
{
13961414
// Leftover exception from the Begin...InternalReadStage
1397-
if (cachedAsyncState != null)
1398-
{
1399-
cachedAsyncState.ResetAsyncState();
1400-
}
1415+
cachedAsyncState?.ResetAsyncState();
14011416
ReliablePutStateObject();
14021417
throw asyncException.InnerException;
14031418
}
@@ -1460,6 +1475,9 @@ private int EndExecuteNonQueryInternal(IAsyncResult asyncResult)
14601475

14611476
private object InternalEndExecuteNonQuery(IAsyncResult asyncResult, bool isInternal, [CallerMemberName] string endMethod = "")
14621477
{
1478+
SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalEndExecuteNonQuery | INFO | ObjectId {0}, Client Connection Id {1}, MARS={2}, AsyncCommandInProgress={3}",
1479+
_activeConnection?.ObjectID, _activeConnection?.ClientConnectionId,
1480+
_activeConnection?.Parser?.MARSOn, _activeConnection?.AsyncCommandInProgress);
14631481
VerifyEndExecuteState((Task)asyncResult, endMethod);
14641482
WaitForAsyncResults(asyncResult, isInternal);
14651483

@@ -1544,6 +1562,8 @@ private object InternalEndExecuteNonQuery(IAsyncResult asyncResult, bool isInter
15441562

15451563
private Task InternalExecuteNonQuery(TaskCompletionSource<object> completion, bool sendToPipe, int timeout, out bool usedCache, bool asyncWrite = false, bool inRetry = false, [CallerMemberName] string methodName = "")
15461564
{
1565+
SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalExecuteNonQuery | INFO | ObjectId {0}, Client Connection Id {1}, AsyncCommandInProgress={2}",
1566+
_activeConnection?.ObjectID, _activeConnection?.ClientConnectionId, _activeConnection?.AsyncCommandInProgress);
15471567
bool isAsync = (null != completion);
15481568
usedCache = false;
15491569

@@ -1780,14 +1800,25 @@ private void BeginExecuteXmlReaderInternalReadStage(TaskCompletionSource<object>
17801800
_cachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteXmlReader), _activeConnection);
17811801
_stateObj.ReadSni(completion);
17821802
}
1803+
// Cause of a possible unstable runtime situation on facing with `OutOfMemoryException` and `StackOverflowException` exceptions,
1804+
// trying to call further functions in the catch of either may fail that should be considered on debuging!
1805+
catch (System.OutOfMemoryException e)
1806+
{
1807+
_activeConnection.Abort(e);
1808+
completion.TrySetException(e);
1809+
throw;
1810+
}
1811+
catch (System.StackOverflowException e)
1812+
{
1813+
_activeConnection.Abort(e);
1814+
completion.TrySetException(e);
1815+
throw;
1816+
}
17831817
catch (Exception e)
17841818
{
17851819
// Similarly, if an exception occurs put the stateObj back into the pool.
17861820
// and reset async cache information to allow a second async execute
1787-
if (null != _cachedAsyncState)
1788-
{
1789-
_cachedAsyncState.ResetAsyncState();
1790-
}
1821+
_cachedAsyncState?.ResetAsyncState();
17911822
ReliablePutStateObject();
17921823
completion.TrySetException(e);
17931824
}
@@ -1814,6 +1845,7 @@ private XmlReader EndExecuteXmlReaderAsync(IAsyncResult asyncResult)
18141845
Exception asyncException = ((Task)asyncResult).Exception;
18151846
if (asyncException != null)
18161847
{
1848+
cachedAsyncState?.ResetAsyncState();
18171849
ReliablePutStateObject();
18181850
throw asyncException.InnerException;
18191851
}
@@ -2011,6 +2043,7 @@ internal SqlDataReader EndExecuteReaderAsync(IAsyncResult asyncResult)
20112043
Exception asyncException = ((Task)asyncResult).Exception;
20122044
if (asyncException != null)
20132045
{
2046+
cachedAsyncState?.ResetAsyncState();
20142047
ReliablePutStateObject();
20152048
throw asyncException.InnerException;
20162049
}
@@ -2034,6 +2067,9 @@ internal SqlDataReader EndExecuteReaderAsync(IAsyncResult asyncResult)
20342067

20352068
private SqlDataReader EndExecuteReaderInternal(IAsyncResult asyncResult)
20362069
{
2070+
SqlClientEventSource.Log.TryTraceEvent("SqlCommand.EndExecuteReaderInternal | API | ObjectId {0}, Client Connection Id {1}, MARS={2}, AsyncCommandInProgress={3}",
2071+
_activeConnection?.ObjectID, _activeConnection?.ClientConnectionId,
2072+
_activeConnection?.Parser?.MARSOn, _activeConnection?.AsyncCommandInProgress);
20372073
SqlStatistics statistics = null;
20382074
bool success = false;
20392075
int? sqlExceptionNumber = null;
@@ -2404,28 +2440,43 @@ long firstAttemptStart
24042440
private void BeginExecuteReaderInternalReadStage(TaskCompletionSource<object> completion)
24052441
{
24062442
Debug.Assert(completion != null, "CompletionSource should not be null");
2443+
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);
24072444
// Read SNI does not have catches for async exceptions, handle here.
24082445
try
24092446
{
24102447
// must finish caching information before ReadSni which can activate the callback before returning
24112448
cachedAsyncState.SetActiveConnectionAndResult(completion, nameof(EndExecuteReader), _activeConnection);
24122449
_stateObj.ReadSni(completion);
24132450
}
2451+
// Cause of a possible unstable runtime situation on facing with `OutOfMemoryException` and `StackOverflowException` exceptions,
2452+
// trying to call further functions in the catch of either may fail that should be considered on debuging!
2453+
catch (System.OutOfMemoryException e)
2454+
{
2455+
_activeConnection.Abort(e);
2456+
completion.TrySetException(e);
2457+
throw;
2458+
}
2459+
catch (System.StackOverflowException e)
2460+
{
2461+
_activeConnection.Abort(e);
2462+
completion.TrySetException(e);
2463+
throw;
2464+
}
24142465
catch (Exception e)
24152466
{
24162467
// Similarly, if an exception occurs put the stateObj back into the pool.
24172468
// and reset async cache information to allow a second async execute
2418-
if (null != _cachedAsyncState)
2419-
{
2420-
_cachedAsyncState.ResetAsyncState();
2421-
}
2469+
_cachedAsyncState?.ResetAsyncState();
24222470
ReliablePutStateObject();
24232471
completion.TrySetException(e);
24242472
}
24252473
}
24262474

24272475
private SqlDataReader InternalEndExecuteReader(IAsyncResult asyncResult, bool isInternal, string endMethod)
24282476
{
2477+
SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalEndExecuteReader | INFO | ObjectId {0}, Client Connection Id {1}, MARS={2}, AsyncCommandInProgress={3}",
2478+
_activeConnection?.ObjectID, _activeConnection?.ClientConnectionId,
2479+
_activeConnection?.Parser?.MARSOn, _activeConnection?.AsyncCommandInProgress);
24292480
VerifyEndExecuteState((Task)asyncResult, endMethod);
24302481
WaitForAsyncResults(asyncResult, isInternal);
24312482

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

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

28682869
TaskCompletionSource<object> source = _networkPacketTaskSource;
28692870
#if DEBUG

0 commit comments

Comments
 (0)