It recently came to my attention that we have a bunch of post processing BPMs updating the database incorrectly. The BPMs do this:
Db.Validate();
Db.SaveChanges();
This intermittently results in an EpicorServerException
with the message
Error accessing the database: Transaction (Process ID 308) was deadlocked on lock | communication buffer resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
I don’t know if REST has anything to do with it, but I’ve never seen these errors in the UI, or a log entry that I’m sure was not from a REST call.
I started fixing this by changing one BPM. First I changed it to only call Db.Validate()
. This didn’t change the frequency of errors from that BPM, but changed the type of error to a System.Transactions.TransactionInDoubtException
with the message
Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
Then I changed it to what I think is the “right way”, using a transaction scope and LockHint
and calling Db.Validate()
and tx.Complete()
as shown in the Converted Code Programming Guide and Luke McAvoy’s blog post. I’m still getting intermittent errors from that BPM, but now it’s a System.Transactions.TransactionAbortedException
with the message
The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.
The weird thing is, neither the TransactionInDoubtException
nor the TransactionAbortedException
seem to result in any data loss. So I’m not even sure it’s my update that’s generating these errors. I imagine there’s some kind of enclosing transaction scope surrounding the execution of the BPM which may be failing.
So I wonder if I’m still getting errors because of interactions with BPMs that are updating the database the wrong way. Before I fix them, can someone confirm that this is the right way to do it?
using(var tx = IceDataContext.CreateDefaultTransactionScope())
{
var dbJob = Db.JobHead.With(LockHint.UpdLock).First(o => o.JobNum == ttJob.JobNum);
dbJob.ProdQty = someValue;
Db.Validate(dbJob);
tx.Complete();
}