July 25, 2023
Maximo allows concurrent record updating without locking, such that two users may update the same record at the same time. When this occurs the second user to save the record will receive the following error:
BMXAA8229W - Record {OBJECTNAME} : {RECORDID} has been updated by another user.Your changes have not been saved. Refresh the record and try again.
While this is a legitimate error when two users are interacting through the web interface, it can also be caused by programming errors where the same record is updated through two separate relationships, triggering the concurrency error.
This often occurs in systems with a large number of automation scripts acting on the same object, where separate scripts may update the same record through different relationships. Using script libraries, with a single script delegating to the various library scripts helps reduce the likelihood of this sort of error by ensuring that scripts are executed in a deterministic order, avoiding intermittent errors that can occur based on execution order. It also ensures a level of developer visibility to all the scripts that are interacting on the same object. We covered this in our post about javascript libraries found here.
However even with careful planning, programming errors can occur and tracking down which relationships are being used and which script is triggering the error can be extremely difficult and time consuming. In this post we will review a script that automates the troubleshooting process and provides a clear listing of each Mbo involved in a transaction, duplicate relationships used and values updated. It utilizes the ability to trigger an automation script on error to set up a diagnostic listener and then captures the error details when the error occurs again.
Yasutaka Nishimura has written and excellent post detailing the causes of this error and his troubleshooting approach provided the inspiration for this script.
https://www.linkedin.com/pulse/explain-record-has-been-updated-another-user-how-debug-nishimura/
This script relies on the ability to trigger automation scripts based on specific errors, which we covered here. To briefly review, Record has been updated by another user
error is defined with the message group system
and message key rowupdateexinfo
and therefore a script named MXERR.SYSTEM.ROWUPDATEEXINFO
will be triggered whenever the error occurs. Unfortunately, the error script context does not provide information about the Maximo Business Object (Mbo) that triggered the error. Therefore, we use the initial error to register a diagnostic listener that captures all Mbo transactions and then uses that cache of transactions to determine where conflicts are occurring.
To begin you can download the diagnostic script that is available on our GitHub site: https://github.com/sharptree/autoscript-library/blob/main/record-updated-diagnostics/mxerr.system.rowupdateexinfo.js.
To deploy the script, create a new Automation Script named MXERR.SYSTEM.ROWUPDATEEXINFO
with Javascript type and paste the script source you downloaded. Alternatively, simply use our VS Code extension to deploy the script directly from VS Code to Maximo.
Note: The diagnostic script can be quite memory intensive, it should be used for troubleshooting purposes only and should be removed once troubleshooting is completed.
Using the script simply requires that a user trigger the error, which will display the following error, note the added details indicating a diagnostic listener has been added.
As indicated by the error message once the diagnostic listener has been added, the error must be triggered again to capture the details. Performing the transaction that triggers the error again will print the diagnostic information to the log and deregister the diagnostic listener to avoid potential memory issues. By default the diagnostic details are written to the Maximo system log, however there is a variable named email
at the top of the script that can be set that will cause the script to email the diagnostic results to the email address as well as the log.
Note that the second message indicates that the conflict details have been written to the log, confirming that the diagnostic script has run.
In the following example output, the WORKORDER
object was updated through the CHILDTASK
, $MYMBO
and WOACTIVITY
relationships with the DESCRIPTION
attribute updated through each of these relationships. With this information a developer can search automation scripts and customizations to locate where these relationships are used and where the modified values may be set.
The modifiedAttributes
property often include values that are updated every time a Mbo is updated such as the CHANGEBY
and CHANGEDATE
, which can usually be ignored. However, as shown below the updated DESCRIPTION
attribute is different between each entry and this can be very helpful in determining where the conflicting change is being made.
[{"objectName": "WORKORDER","objectDetails1": {"recordId": "WORKORDER : Site=BEDFORD Work Order=1006-70","relationship": "CHILDTASK","parent": "WORKORDER","child": "WORKORDER","modifiedAttributes": ["DESCRIPTION = Update 2","CHANGEBY = MAXADMIN","CHANGEDATE = 7/17/23 10:38 PM","ESTLABCOST = 0.00","JPASSETS = Y","WOGROUP = 1006","JPINCLUDECLASSLESS = Y"]},"objectDetails2": {"recordId": "WORKORDER : Site=BEDFORD Work Order=1006-70","relationship": "$MYMBO","parent": "WORKORDER","child": "WORKORDER","modifiedAttributes": ["DESCRIPTION = Update 3","CHANGEBY = MAXADMIN","CHANGEDATE = 7/17/23 10:38 PM","ESTLABCOST = 0.00","JPASSETS = Y","WOGROUP = 1006","JPINCLUDECLASSLESS = Y"]},"objectDetails3": {"recordId": "WOACTIVITY : Site=BEDFORD Activity=1006-10","relationship": "WOACTIVITY","parent": "WORKORDER","child": "WOACTIVITY","modifiedAttributes": ["DESCRIPTION = Update 1","CHANGEBY = MAXADMIN","CHANGEDATE = 7/17/23 10:38 PM","ESTLABCOST = 0.00","JPASSETS = Y","WOGROUP = 1006","JPINCLUDECLASSLESS = Y"]}}]
In this post we briefly explored how Record updated by another user
errors occur and then examined a script to automate diagnosing such errors. With this script deployed, when the error occurs a diagnostic listener is automatically added to the system and will capture the details necessary to identify the programming error that caused the error.
It is beyond the scope of this post, but you may notice that we are implementing the Java interface psdi.server.event.EventListener
as a custom Java class within the automation script. As shown in this script, it is possible to implement custom Java interfaces and even extend existing classes within an automation script. We will cover this in detail in a future post.
If you have any questions, comments or have suggestions for topics you would like to see us cover, please reach out to us at [email protected]