I have configured a retry strategy with Entity Framework (.NET framework) which works OK most of the time. I've never seen it not working for updates/write. But I do occasionally get issues that do not get intercepted.
In particular, I have a case where a deadlock is encountered during reading data. Then the retry mechanism doesn't work, as is visible from this stack trace excerpt:
Exception: System.Data.Entity.Core.EntityCommandExecutionException: An error occurred while reading from the store provider's data reader. See the inner exception for details. ---> System.Data.SqlClient.SqlException: Transaction (Process ID 56) was deadlocked on lock | communication buffer resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.SqlInternalConnection.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.TryHasMoreRows(Boolean& moreRows)
at System.Data.SqlClient.SqlDataReader.TryReadInternal(Boolean setTimeout, Boolean& more)
at System.Data.SqlClient.SqlDataReader.Read()
at System.Data.Entity.Core.Common.Internal.Materialization.Shaper`1.StoreRead()
--- End of inner exception stack trace ---
at System.Data.Entity.Core.Common.Internal.Materialization.Shaper`1.HandleReaderException(Exception e)
at System.Data.Entity.Core.Common.Internal.Materialization.Shaper`1.StoreRead()
at System.Data.Entity.Core.Common.Internal.Materialization.Shaper`1.SimpleEnumerator.MoveNext()
at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()
Here is an equivalent snipped of the code that causes it. It's the iterator in foreach that hits the deadlock, not the SaveChanges() method:
using (var context = new MyContest())
{
var query = context.messages.Where(m => m.unique_id == uniqueId);
foreach (var message in query) // <- The exception is hit here.
{
message.processed = 1;
message.update_date = DateTime.Now;
}
context.SaveChanges();
}
The context is bound to the retry strategy (and some logging interceptor) by a config class attribute:
[DbConfigurationType(typeof(MyDbConfiguration))]
public partial class MyContext
Lastly, the configuration contains the retry strategy, and some logging:
public CommonDbConfiguration()
{
DbInterception.Add(new CommonDbLogInterceptor(s =>
{
if (string.IsNullOrWhiteSpace(s)) return;
// some logging
}
));
SetExecutionStrategy("System.Data.SqlClient", () => new CommonExecutionStrategy());
}
I expect that the retry and logging interceptions would kick in and would be visible similarly to that update failure which got retried successfully:
at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.Reader(DbCommand command, DbCommandInterceptionContext interceptionContext)
at System.Data.Entity.Core.Mapping.Update.Internal.DynamicUpdateCommand.Execute(Dictionary`2 identifierValues, List`1 generatedValues)
at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.Update()
at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesToStore(SaveOptions options, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction)
at System.Data.Entity.Infrastructure.DbExecutionStrategy.Execute[TResult](Func`1 operation)
at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesInternal(SaveOptions options, Boolean executeInExistingTransaction)
at System.Data.Entity.Internal.InternalContext.SaveChanges()
Is there a way to remedy this without me having to call the execution strategy or some other retry mechanism explicitly?