Best Practices from Oracle Development's A‑Team

Engagement Cloud - avoiding concurrent updates

Tim Bennett
CX Solution Architect



If your users are encountering the above message in Engagement Cloud, then this article might be of interest!

The error message is self explanatory - it happens when a user tries to save a record that has been updated by another user since they retrieved it. There are a few scenarios that can cause this, including:

  • User A retrieves a record and starts to make changes, user B retrieves the same record, changes it and saves it, User A tries to save the record and gets the error

  • User A retrieves a record, makes a change and presses Save but does not leave the page, the save event triggers an integration or background process that modifies the record, user A makes another change, tries to save and gets the error.

The first scenario sometimes points to a flawed business process - why would 2 users be responsible for editing the same record at the same time? This is often seen when users are pulling "work" from a queue and is usually solved by sorting out the assignment rules.

The second scenario, where a background process updates the record, is much more common, for example - an outbound integration is triggered by a user action and the response from the integration updates the record while the user is still editing. A typical "fix" is to delay the outbound call for a period of time in the hope that the user will have finished their editing and "gone away" by the time the record is updated by the integration. This might work most of the time, but in environments where users "hold" records for long periods of time, it is somewhat haphazard. Also, there are many use cases where the user must wait for the response from the background process before they can continue working with the record (e.g. a confirmation from a back office system).

This article sets out an approach to preventing concurrent updates that are caused by integrations or other background processes over which you have control.

The remainder of this article uses the term "background process" to refer to any operation that updates a record that a user is interacting with, whether that be an integration or internal operation.  


High Level Approach

The solution flow is: 

  • User modifies and saves a record - in the Before Update event a "lock" flag is set

  • The object's dynamic layouts are evaluated and a non-editable "Record Locked" page is shown

  • An object Workflow triggers a process that will update the record

  • The process completes, updating the record as necessary and resetting the lock flag (hence this technique relies on having control over the background process)

  • The user presses a button that refreshes the data, this causes the dynamic page condition to be re-evaluated and the user is returned to the standard page


Configuration Overview

In addition to the workflow artifacts required for the core process flow, the configuration needs a number of new artifacts in Engagement Cloud:

  1. Custom field for the Lock flag

  2. Before Update trigger to set the Lock flag

  3. Action button to refresh the user interface

  4. OTBI report to be used in a mashup on the "Lock page" and which shows a status message to the user

  5. Dynamic page that will be displayed when Lock is true

Note - the configuration of the object workflow and subsequent update of the Lock flag by the background process is not covered.


Detailed Configuration

The configuration steps below use Sales Lead as the example object. The scenario is that a change to either of 2 fields (LOB or Region) will trigger an object workflow that executes a Groovy action. The Groovy action calls a REST service that updates the Lead, thereby creating a potential concurrent update situation.

1. Custom field for the Lock flag

Add a custom field to Sales Lead called Locked.

2. Before Update trigger

In the before Update Trigger, if the conditions for the background process have been met, set the Locked flag to true:

Note - the same conditions must be set on the object workflow that initiates the background process.

3. Action button to refresh the user interface

Create an Action button called Refresh:

Note the script is a single method - revertRowAndContainees()

This method refreshes the user interface with the current saved data - the effect of this is that if another process has modified the data since it was retrieved, the user interface will be refreshed with the latest data and any dynamic page expressions will be re-evaluated.

4. OTBI Report (or other web content) to show the user an appropriate  message

The purpose of the OTBI report is to give the user information about why the record is locked and when they can expect to be able to edit it again. Build a report that shows whatever information best suits your scenario. It is possible to create a report that automatically interacts with the Refresh button (using JS), but in this example a simple Static Text is used.


5. Dynamic page that will be displayed when Lock is true

Create a Dynamic Page page with Expression Locked_c == "Y"

Add the "Refresh" Action button to the page, remove all editable controls, and add the OTBI report using a mashup:

Design time view of Mashup definition:


Run time view of RecordLocked layout:


Run time Experience

Users will be able to make changes and save records as normal. If they make a change that will result in a background update being made then as soon as they save the record they will only see the "Locked" page. The Locked page will continue to display for as long as the locked flag is set to "Y" (the background process is responsible for setting it to "N"). If the user presses the Refresh button and the process has completed they will be able to resume editing the record, if the process has not completed the page will remain on the "Lock" page.

By using a Refresh button, the user does not have to exit the record, hence this technique is useful for integrations that are expected to complete relatively quickly and the user is likely to make further changes before leaving the record.


Additional Notes

The background process must update the Locked flag on successful completion. The code snippet below shows an example of how this might be done:


try {
      //call REST service
      def resp = leadService.POST("", param)
      //unlock if no technical error. You will probably want to check resp.statusCode too 
      newView('Lead').findByKey(key(LeadId),1).first().setAttribute("Locked_c", "N")  

    } catch(Exception e){
      println("Exception: " + e.getMessage())



Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha