Thursday, July 2, 2009

Transaction (Process ID XX) was deadlocked on lock resources with another process and has been chosen as the deadlock victim

Asynchronous Plugin Error: "System.Web.Services.Protocols.SoapException: Server was unable to process request. ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Data.SqlClient.SqlException: Transaction (Process ID XX) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction."

This error will be reported using the CRM Trace Diag Tool. To simplify the analysis of the trace log, you can use Stunware's CRM Trace Log Viewer.

This error signifies that there are two plugins or two instances of a plugin trying to write to the same entity. It seems that the Microsoft Dynamics CRM Asynchronous Service does not handle threading and deadlocks too well internally.

As I understand, the CRM Asynchronous Service often collects a load of jobs and executes them all together. The problem is that one plugin may lock the entire SQL table while writing to one record. During this process's very short writing time, the service also executes a bunch of other plugins. So we're going to experience a deadlock in the event that another plugin needs to write to the same entity.

How do we get around it? Use the Lock call to synchronise all asynchronous plugins. This will ensure that only one update is sent to the database at a time.

public class SalesOrderDetailPreCreate : SalesOrderDetail, IPlugin
{
static Object thisLock = new Object();

public void Execute(IPluginExecutionContext context)
{
lock (thisLock)
{
//
// Do your stuff and entity update here
//...
}
}
}

As far as I understand, it is a good idea to use this code on any asynchronous plugin as in a multi user environment, there is fair chance of a clash at some time.