In my scenario, I have a method called GetEntries that invokes another method called Get within a transaction scope. Get method contains three LINQ queries. The first query retrieves a single collection, the second query also retrieves a single collection, and the third query performs a group join using both collections.
Pseudo code
internal static class AuditTest
{
public static IEnumerable<Audit> Get(
DataContext dataContext,
Int32 id,
Int32[] recordIds)
{
var collection1 =
dataContext
.Table1
.Where(d => d.MemberId == id && recordIds.Contains(d.RecordId))
.Select(
record =>
new
{
......
})
.ToArray();
var collection2 =
dataContext
.Table2
.Where(r => r.MemberId == id && recordIds.Contains(r.RecordId))
.Select(
record =>
new
{
......
})
.ToArray();
return
collection1
.GroupJoin(
collection2,
record1 => record1.RecordId,
record2 => record2.RecordId,
(record1, record1WithRecord2) =>
new Audit
{
.......
});
}
public static IEnumerable<Entry> GetEntries(
DataContext dataContext,
Int32 Id,
Int32[] Ids)
{
var option = new TransactionOptions()
{
IsolationLevel = IsolationLevel.ReadCommitted,
Timeout = TransactionManager.MaximumTimeout
};
using (var transactionScope = new TransactionScope(
TransactionScopeOption.Suppress, option))
{
return
Get(dataContext, Id, Ids)
.Select(
m =>
new Entry
{
Guid = m.Guid,
Id = m.Id,
});
}
}
}
The Get
method is called inside a TransactionScope
public static IEnumerable<Entry> GetEntries(
DataContext dataContext,
Int32 Id,
Int32[] Ids)
{
var option = new TransactionOptions()
{
IsolationLevel = IsolationLevel.ReadCommitted,
Timeout = TransactionManager.MaximumTimeout
};
using (var transactionScope = new TransactionScope(
TransactionScopeOption.Suppress, option))
{
return
Get(dataContext, Id, Ids)
.Select(
m =>
new Entry
{
Guid = m.Guid,
Id = m.Id,
});
}
}
}
Upon investigation, it appears that the first query executed with the expected isolation level, which is "ReadCommitted." However, the second query unexpectedly executed with the "Serializable (4)" isolation level. This change in isolation level caused a timeout exception, as reported in the blocked-process-report.
To determine why the isolation level changed, I'm unable to pinpoint the exact cause at the moment. However, this issue has occurred sporadically in our production server.
Why it happens and how to reproduce it in a local machine? How to fix it?
Exception:
SqlException "Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding."
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at System.Data.SqlClient.SqlDataReader.TryConsumeMetaData()
at System.Data.SqlClient.SqlDataReader.get_MetaData()
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption, Boolean shouldCacheForAlwaysEncrypted)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
at System.Data.Linq.SqlClient.SqlProvider.Execute(Expression query, QueryInfo queryInfo, IObjectReaderFactory factory, Object[] parentArgs, Object[] userArgs, ICompiledSubQuery[] subQueries, Object lastResult)
at System.Data.Linq.SqlClient.SqlProvider.ExecuteAll(Expression query, QueryInfo[] queryInfos, IObjectReaderFactory factory, Object[] userArguments, ICompiledSubQuery[] subQueries)
at System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq.Provider.IProvider.Execute(Expression query)
at System.Data.Linq.DataQuery`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator()
at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
.......
Blocked process report:
<blocked-process-report>
<blocked-process>
<process taskpriority="0" logused="0" waitresource="KEY: 11:72057594321895424 (67f1cd66ae3e)" waittime="7078" ownerId="26395737" transactionname="SELECT" lasttranstarted="2023-02-02T09:40:11.237" lockMode="RangeS-S" schedulerid="1" kpid="6632" status="suspended" spid="77" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2023-02-02T09:40:11.237" lastbatchcompleted="2023-02-02T09:40:11.240" lastattention="1900-01-01T00:00:00.240" clientapp=".Net SqlClient Data Provider" isolationlevel="serializable (4)" xactid="26395737" currentdb="11" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame line="1" stmtstart="50" stmtend="2520"/>
<frame line="1"/>
</executionStack>
<inputbuf>{{Query 1}}</inputbuf>
</process>
</blocked-process>
<blocking-process>
<process status="sleeping" spid="74" sbid="0" ecid="0" priority="0" trancount="1" lastbatchstarted="2023-02-02T09:40:11.210" lastbatchcompleted="2023-02-02T09:40:11.210" lastattention="1900-01-01T00:00:00.210" clientapp=".Net SqlClient Data Provider" isolationlevel="read committed (2)" xactid="26390106" currentdb="11" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack/>
<inputbuf>{{Query 2}}</inputbuf>
</process>
</blocking-process>