Deadlocks in Entity Framework
Diego Vega wrote a very informative post that summarizes what you can do when you see deadlock errors in Entity Framework applications. They errors look like:
“Transaction (Process ID 54) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.”
Even if Diego says that he did not hear about these issues often, I actually saw them quite often in two high-traffic EF applications where I was involved.
Most of the errors are get during reads, so the most likely cause is the one that Diego describes about having readers block readers, and handling the isolation levels in the way Diego suggest would solve it.
In those two websites apps I mentioned, there is a single ObjectContext instance that is shared in per request and it’s used for all the read-only operations, e.g., retrieving the content that will be displayed on a single page. This context has all the entities set to MergeOption.NoTracking in all the entities, to avoid having the EF keep a copy of those instances in the ObjectContext.
It’s quite risky to use the TransactionScope approach together with that, because you cannot put it inside a using statement. You need to create it at the beginning of the request and finish it when at the end, which implies that any method that is transaction-aware will use it, not just the ones for the Read-Only object context.
The solution that kind of worked was to set the isolation level to read uncommitted explicitly in the context by executing a ‘store command’:
context.ExecuteStoreCommand("SET TRANSACTION ISOLATION LEVELREAD UNCOMMITTED;");
Of course, this can only be used if it makes sense in the context of your application. If it’s a banking app with high consistency needs where you cannot show data that is still not committed, it will not be a good idea. But for websites where most of the action is read-only and you don’t have those consistency requirements it seemed to work quite well.
In one of the applications we were using NServiceBus, and those errors appeared when processing a message. In that case, the NServiceBus infrastructure just did what the error message suggests, ‘Rerun the transaction’ .
This post also provides information that can be useful when tracking deadlocks.