I have 2 Batch classes working on different child objects of a common master object. Batch Batches are started parallel to speed up this long running cleansing operation.

It depends on the underlying data structure which batch finishes first. But when both are finished the master object needs to be marked as cleaned.

Do do this both batches add and remove put their job id to a text field in the master object. If it is empty the master record is clean.

My problem now is that both batches sometimes try to write to the master at the same time and I receive a UNABLE_TO_LOCK_ROW exception.

I thought I could simply retry the update operation as long as it fails and my Limits are not consumed. Would that work and be a feasable solution or are there better ways?

public void modify() {
    CustomObject__c record = query();


    try {
        update record;
    catch(DMLException ex) {
        if(ex.getMessage().contains('UNABLE_TO_LOCK_ROW')) {
  • Just to confirm, are you using the new parallel processing features in SF?
    – crmprogdev
    Jul 2, 2014 at 14:12
  • @crmprogdev: What feature is this?! Never heard of it. Is this a pilot? Jul 2, 2014 at 14:15
  • Its actually primarily related to Bulk Data Loads. See General Guidelines for Data Loads for more info.
    – crmprogdev
    Jul 2, 2014 at 14:36

3 Answers 3


If you use the FOR UPDATE keyword in your query, the database will wait up to about 5 seconds before declaring that it cannot acquire the lock (this should normally be plenty of time). This event can be captured, and you can attempt to retry, assuming you have any SOQL queries/rows remaining. I've found that, in practice, it's rarely necessary to retry a query, however, assuming your transaction time is small (as well it should be).

  • 1
    Very cool. Nobody ever wrote about this 5 sec delay. Jul 2, 2014 at 14:23
  • Actually, there was a question on this that I answered. See salesforce.stackexchange.com/questions/22636/…
    – sfdcfox
    Jul 2, 2014 at 15:00
  • After I read you answer to the referenced question I am a bit confused. After your initial answer I added an FOR UPDATE to the SOQL queries in each batch and it seems to work. But...from reading the old thread it seems to be not a good solution if all "threads" that want to write do a FOR UPDATE. Jul 2, 2014 at 18:57

Another solution might be to create a cleaned checkbox field on the children. After cleaning, the batch job will set that checkbox on the child.

Then create a rollup summary field on the parent that counts all children with cleaned to false.

Then you can filter on all master records that have > 0 uncleaned children?

  • Except that you still need to acquire a record lock to set that field. Would that really change anything?
    – Adrian Larson
    Feb 22, 2017 at 15:07
  • 1
    No you don't, because quote "I have 2 Batch classes working on different child objects of a common master object." which I suppose means that the Batches will not have conflicts on the child records? Feb 23, 2017 at 9:17

I spoke with SF about this issue. Apex detects deadlocks via timeout. Retry by repeating the DML may be a viable technique. The important thing to understand is what you are effectively doing is simply increase the wait before timeout.

As others have pointed out, FOR UPDATE may be a good technique to avoid deadlock in some situations so you may wish to employ that technique first.

Regarding the way you coded it, you have two issues, one major and one minor. The major issue is that you will retry forever. You should probably limit the retry to a few tries. If it takes longer than two or three attempts, something may be very wrong. The minor issue is that you are using recursion so the stack is getting deeper and deeper with each attempt. You can fix both issues by putting the DML in a few loop and break from the for loop on success.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .