Concurrent access and race condition in Salesforce on insert operation

Assume you have some VisualForce page with complex logic which also has to create some custom object record if it does not exist for current user and current month or even for current day. Is there any possibility to prevent concurrent record insertion like race condition in Salesforce?

First thought that comes to mind is just to check if record does not exist just before record insertion so it should prevent from any duplication happening. However, in a real world situation it may not help if the same user opens the same page in two tabs or browsers and it happens that in both cases record does not exist just before insert\upsert operation, so a duplicate record is still created like it is shown on this picture

RaceConditionConcurrentInsert

Salesforce provides built-in keyword ‘for update’ if you want to make thread-safe update operation. If Salesforce locks records when update operation happen, so you may see error like “UNABLE_TO_LOCK_ROW, unable to obtain exclusive access to this record”. I have seen this error many times during tests execution, it happens when some unit test tries to touch some record which is locked by another unit test or user. However, this does not really help in insert or upsert operation when your goal is to prevent duplicate record insertion.

There is still a solution.

You need to create some new unique text field on the custom object you want to prevent from duplicated record insertion or you can mark as unique some of the existing text field which should be unique and would uniquely identify the record.

For my case I have created Unique Key field

1
2
3
4
5
6
7
8
9
10
11
12
13
<fields>
    <fullName>Unique_Key__c</fullName>
    <caseSensitive>false</caseSensitive>
    <description>Unique Key to prevent duplication of records on 
concurrent insert\race condition</description>
    <externalId>false</externalId>
    <label>Unique Key</label>
    <length>255</length>
    <required>false</required>
    <trackFeedHistory>false</trackFeedHistory>
    <trackHistory>false</trackHistory>
    <type>Text</type>
    <unique>true</unique>
</fields>

on both custom object definitions where I wanted to prevent duplicated records insertion.
Also I had to create trigger on both of my custom objects and introduce new method which would populate Unique Key field and would be called from before insert trigger

1
2
3
trigger MyCustomObjectTrigger on My_Custom_Object__c (before insert) {
    MyCustomObjectServices.populateUniqueKey( Trigger.new );
}
1
2
3
4
5
6
7
public static void populateUniqueKey(List<My_Custom_Object__c> objects){
    for ( My_Custom_Object__c o: objects) {
        Date day = o.My_Custom_Date_Field__c;
        
        o.Unique_Key__c = day.day() + '_' + day.month() + '_' + day.year() + '_' + ts.My_Custom_Lookup_To_User_Field__c;
    }
}

Both of My_Custom_Date_Field__c and My_Custom_Lookup_To_User_Field__c should be marked as required to be able to populate Unique Key field in before insert trigger.

Also I had to change implementation of my page controller extension so show appropriate error message for users who do not understand what is concurrent access and race condition

1
2
3
4
5
6
7
8
9
10
try {
     upsert myCustomObjectList;
} catch ( Dmlexception dmle ){
    String errorMessage = dmle.getDmlMessage( 0 );
    if ( StatusCode.DUPLICATE_VALUE == dmle.getDmlType( 0 ) ) {
        errorMessage = 'Looks like you are trying to perform this logic from multiple tabs or browsers simultaneously. Operation failed.';
    }
    ApexPages.addMessage( new ApexPages.Message( ApexPages.Severity.ERROR, errorMessage ) );
    return;
}

I hope you enjoyed reading this post and found something useful and learned something new.

You may click like to let me know that you did really enjoy 🙂

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *