The standard behavior (tested on Opportunities) is that Salesforce seems to handle dirty updates / save conflicts as expected:

  1. User A opens Opportunity X
  2. User B opens Opportunity X
  3. User A modifies and saves Opportunity X
  4. User B modifies and tries to save Opportunity X
  5. User B sees a conflict error and CANNOT override unseen changes from User A

When I try this on a custom object with a controller extension that has a custom doSave() method that calls standardController.save() the behavior is different and has dirty updates.

  1. User A opens Custom Object X
  2. User B opens Custom Object X
  3. User A (implicitly by a triggered Batch job) modifies and saves Custom Object X
  4. User B modifies and SUCCESSFULLY saves Custom Object X
  5. User B sees a has reverted all changes that were saved by User A

The problem is 5. User B has accidentally overriden all the changes made by User A just because he still has them in memory.

Do I need to handle / prevent this on my own in my controller extension? Or should I call update instead of standardController.save() in my custom save method?

  • 2
    Are you retrieving the SystemModstamp in your SOQL queries? I haven't researched how it works, but I'd assume it is needed to determine if there is a dirty write about to occur. Commented Apr 23, 2013 at 2:29
  • 3
    Hi Robert, I'd noted this question to come back to actually, can you post either in your answer or a Gist link of the code, I think I've got a handle on what might be happening, likely associated with the work being done somewhat out of process via your batch job. Having a peak at some of the code will help me confirm my hunch. Commented Apr 25, 2013 at 11:32
  • 1
    Andrew: Which part of the code would be interessting? Due to IP things I can't paste to much but the batch is set a record locked (just a boolean field) and other processes respect this field and abort their work). Commented Apr 25, 2013 at 13:01
  • The solution to this sounds like a design pattern waiting to happen... Commented Apr 26, 2013 at 12:13
  • @RobertSösemann I am not seeing dirty update on standard page. I tried on opportunity layout like, opening same record in two tabs and saving one after another.So was it enabled earlier and changed now? or am I doing something wrong? Commented Apr 12, 2016 at 13:59

2 Answers 2


Salesforce DOESN'T handle this in custom save actions and doing an update instead of controller.save() makes no difference.

One solution would be to re-read the record from the database in your controller extension save() method, before committing the record (save/update), and compare the re-read SystemModstamp with the SystemModstamp of the record in your viewstate.

You will need to use controller.addFields(new List{'SystemModstamp'}) in the controller extension constructor if your page does not display the SystemModstamp.

If the SystemModstamps are different you know that the record has been modified and you can decide what to do.

For example:

public with sharing class OpportunityController
    ApexPages.StandardController m_controller;

    public OpportunityController(ApexPages.StandardController controller)
        m_controller = controller;
        m_controller.addFields(new List<String>{'SystemModstamp'});

    public ApexPages.PageReference save()
        Opportunity record = (Opportunity) m_controller.getRecord();
        Opportunity dbRecord = [select SystemModstamp from Opportunity where Id=:record.Id];
            ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.FATAL, 'Someone else edited the record'));
            return null;
        return m_controller.save();
  • 3
    Wow. This sound like a solution. I will soon check it and give you credits ;) Commented Apr 26, 2013 at 8:44
  • 2
    I'd also use FOR UPDATE in the save method SOQL query to prevent any other process from updating it while you are checking the SystemModstamp for conflicts. Commented Apr 26, 2013 at 10:30
  • 1
    What is going to happen between comparison of SystemModstamp and updating the records? Daniel added a good hint, otherwise there would be still a gap for two possible updates on the same record. Commented Apr 26, 2013 at 15:00
  • 1
    Yes, locking the row would be an improvement. I have not been entirely happy with FOR UDATE in the past, but I am going to try that out with this example. However, of course the main danger is the relatively long period of time where data becomes stale in VF viewstate, rather than the much shorter period during the SystemModstamp check. Commented Apr 28, 2013 at 10:58
  • I think a trigger implementation would also be better, though I have an open case with Salesforce at the moment - I am unable to make the trigger implementation work at the moment Commented Apr 28, 2013 at 11:00

Also take a look at the Salesforce Streaming API, If I remember correctly, you could subscribe to a "topic", where that topic can be a specific SObject you define (in your case, you can also subscribe to a list of SObjects, or an SObjectType), if any changes are made to that SObject, you would receive a push notification.

You could use it to show it on your visualforce page to the user, where changes would appear on the top (Attention, a change has been made to the SObject you're trying to edit), you can still use Stephen's solution to block the save, but this way your user is aware that changes have been made, and could stop his editing and save some annoyance.

I haven't used it myself yet, only followed a webinar, but this could be a use-case for that API.

The Getting Started Guide explains some examples.

  • 1
    Nice idea, but not exactely an answer to my question... Commented Apr 26, 2013 at 13:02
  • 3
    There's a limit of topics you can subscribe to if I recall correctly. And it'd be still messy if I navigate to your page (subscription) then try to leave it without making changes (who will unsubscribe me)? I think Streaming API was meant for external systems (equivalent of a web hook) rather than internal apps.
    – eyescream
    Commented Apr 26, 2013 at 14:40

You must log in to answer this question.

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