Understanding V4.2 advanced lock management
To prevent impairments to work object integrity, Process Commander provides a built-in work object locking facility.
In versions prior to the V4.2SP2 (SmartBuild) release, it was possible for a business process to be developed that would misuse this functionality, resulting in a potential data integrity exposure.
The V4.2SP2 release introduces more stringent validation at both design time and runtime that makes it more difficult to misuse the locking facility.
A key objective of the Version 4.2 SmartBuild release is to provide automatic best practice guidance during the development . As part of this guidance, enhancements have been made to the product that highlight circumstances where a design or implementation falls outside of the best practices. This article discusses the enhancements to locking that facilitate the management of work objects, and enforce best practice for transactions.
To prevent exposures to work object integrity, Process Commander provides a built-in work object locking facility. In releases before the V4.2SP2 release, it was possible to misuse this functionality, resulting in a potential data integrity exposure. V4.2SP2 introduces more stringent validation at design and runtime that makes it less easy to misuse the locking facility. This Advanced Lock Management feature catches the following two scenarios:
- Obj-Save or Obj-Delete calls which operate on an objects of a class that has locking enabled, but which do not hold a lock, will now fail.
- Applications that lack proper error handling for these Obj-Save and Obj-Delete calls will now have these errors flagged. Saving to the PegaRULES database will be prevented until the error is handled.
If errors were ignored (in previous releases) it was possible to continue with the business process, creating the possibility of saving contextually-inconsistent data. Advanced Locking keeps track of the errors and does not let a business process Commit any data that is potentially inconsistent as a result of the above condition.
Additional V4.2SP2 facilities provide greater visibility to lock processing:
- You can trace locks with the Tracer.
- A new activity makes it easier to work with locks.
- A new method allows more developer control over objects waiting to be saved.
These changes affect existing applications. It is strongly recommended that you thoroughly review and test existing applications before using them in a production environment with Version 4.2 SmartBuild Release, as Advanced Lock Management may detect issues in currently implemented business processes that should be addressed.
Upgrading existing applications
Beginning in Version 4.2 SmartBuild, Advanced Lock Management is enabled by default. This requires that an object lock be held by the calling process for all Obj-Save and Obj-Delete operations on any instances of classes which have the Allow Locking option selected.
At runtime, any Obj-Save or Obj-Delete step in an activity where a valid lock is not held on the instance results in a step-status of FAIL. As a result of this failure, the Commit for this transaction also fails. In addition, once such a failure has occurred, no further Commits may be executed for this user's session (Thread),.
Follow t he following process to upgrade your applications and correct/prevent these errors:
- Determine in which step of which activity the failure occurs.
- If failure is due to locking, then determine the best upgrade for this particular locking requirement.
- Upgrade the application, and test.
Determining the step where failure occurs
Users may see messages such as:
"...Commit error: A commit cannot be performed because a deferred save of an instance of class ... failed..."
as they try to submit a work object form.
In V4.2SP4, a new Tracer Event Type of Locking is available to assist in diagnosing lock events. Enable this Event Type to track and display lock acquisitions and releases in the Tracer window when tracing a process.
To enable this feature, on the Trace Options form, type
Locking in the Event Type field, and click Add. The Locking check box appears at the bottom of the Event Types to Trace section. Select the check box for this option.
On the Tracer display, Event Types of Acquired Lock and Release Lock appear. Review these messages to determine when locks are needed but not established, or when locks are released prematurely.
If locking is not enabled in the process being traced, the lack of locks and the subsequent errors are easy to discover:
As shown above, in the PegaSample.ShowCommitFailure activity, there is an Obj-Save in step 3 and a Commit in step 4, both of which show a FAIL status. This identifies the steps in the activity where locking is required.
In this example activity, no lock was acquired on the instance being opened (neither the Lock field nor the ReleaseOnCommit field were checked in the Obj-Open step), so an error will occur when the Obj-Save step (#3) is executed.
Determining the remedy
Once a Obj-Save/Obj-Delete failure and the Commit failure have occurred, both issues must be addressed. You must determine how best to design the locking functionality for the transactions, to prevent locking errors (the design-time locking strategy).
In addition, you must add error handling to the Obj-Save/Obj-Delete calls, to prevent future Commit failures while processing (the runtime locking strategy). These two strategies are described below.
Determining the locking needed - Design-time locking strategy
The purpose of locking items before making changes is to prevent data loss. Locking may be more important in some situations than in others. For example, history or log-file entries are rarely updated after being saved, so they may not have to be locked, as they are not transactional.
However, for work object processing, if two users have retrieved one work object from the database (without locking it), and both make changes, then the user who saves second will overwrite the changes made by the first user, possibly losing valuable data.
Thus, depending upon the type of processing being done, you may design different locking strategies:
- Always lock
- Sometimes lock
- Locking not needed
These different types of locking strategies require different implementations.
Case 1: Always Lock
If application users will enter work objects for which it is important that data is not overwritten, then locking must be enabled. For any activities that perform either an Obj-Open, Obj-Open-by-Handle, or an Obj-Delete, the Lock parameter must be checked. As a best practice, check the ReleaseOnCommit check box as well.
A new activity Work-.WorkLock locks a work object. Using WorkLock has advantages over Obj-Open or Obj-Open-by-Handle. Some work objects may be opened or viewed in read-only mode (not locked, but put on the clipboard read-only). If the user then decides to open the work object to change it, it is inefficient to return to the PegaRULES database to re-read this object. The WorkLock activity allows the system to simply take out a lock and open the object that is already on the clipboard.
In addition, this activity has full error handling and reporting - if another user has acquired a lock on the object after between the time it was opened in read-only mode and the time that the user required a lock, the system displays a "Locked by requestor XXX"S message.
In this example, the WorkLock parameters are set to show the Lock harness information. If a user tries to open a work object upon which another user has a lock, the Lock harness appears, with information about the user who holds the lock:
Case 2: Sometimes Lock
There may be cases where locks are sometimes needed, but not always. For example, a company may build an application where there are many tasks which are grouped under covers. Each cover may have multiple tasks, which can be processed simultaneously by different users. The task objects must be locked, to prevent data from being overwritten. However, if the cover is also locked when the task is locked, then only one user may work on a task from that cover at a time, which may not be efficient. If the cover is not being changed with every operation, then not locking the cover will allow multiple users to work on tasks for that cover simultaneously.
Some operations, however, will need to update the cover. For those operations, it is important that the cover also be locked, so that data is not overwritten. Thus, the cover needs to be locked on-the-fly only for certain operations.
The V4.2SP2 method Obj-Refresh-And-Lock handles this situation. At runtime, the, Obj-Refresh-And-Lock method queries the database to see if any requestor holds a lock on the requested object.
- If no one has the lock, then Obj-Refresh-And-Lock acquires the lock for and continues.
- If the page has changed (the timestamp in the database is different than the timestamp in the page on the clipboard), then the page on the clipboard is discarded (including any changes that may have been made), and the page in the database is locked and then put onto the clipboard. (It is no longer possible to make changes to a page [on a class that requires it] without a lock.)
- If someone else has acquired the lock for this item, the system displays an error message: "cannot obtain lock as Requestor xxxxx already has a lock."
Case 3: Locking Not Needed
For some classes, locking may not be needed. For example, History- instances are stored by using a timestamp from the server clock. Each instance is therefore stored with a unique key value; these entries are never modified after they are initially created, so there is no danger of different operators making conflicting changes to them. In this case, the locking may be turned off for this class (in the Class form).
Disabling advanced lock management
In unusual situations, you disable this feature. Using Advanced Lock Management is a best practice. However, you can may choose to run in backward compatibility mode and allow implementations that require relaxed transactional design principles.
By default, Advanced Lock Management is enabled in the pegarules.xml file in the
Database node by setting the
transactionalLockManagement entry to
advanced. Since this is default, the
transactionalLockManagement entry is not mentioned in the shipped
To disable Advanced Lock Management, you must add the transactionalLockManagement element to the database section in the
pegarules.xml file, and set the value to
<entry key="drivers" value="oracle.jdbc.OracleDriver;com.microsoft.jdbc.sqlserver.SQLServerDriver"/>
<entry key="storageVersion" value="5"/>
<entry key=" transactionalLockManagement" value="standard"/>
<entry key="name" value="pr4_base"/>
Adding error handling
In addition to using locking, it is a best practice to include preconditions and transitions to handle any possible errors. At design time, locking should be implemented so that all Obj-Save and Obj-Delete methods (for instances of classes requiring locking) will have locking enabled, preventing lock failure errors.
At runtime, other errors may occur, such as the lock required by a particular class instance already being taken by another user. To handle these situations, and prevent them from creating Commit failures, error handling must be added to activity processing.
This error handling is designed to handle runtime errors only, such as the locks being in use by another user; it will not handle situations where the lock was not taken out at all (a design error).
During processing, after an Obj-Save/Obj-Delete failure, there are three options:
- Give up on the transaction - Rollback.
- Retry the transaction.
- Ignore the transaction failure - Obj-Save-Cancel.
In addition to these options, the application must handle messages about failures from the system.
When a failure occurs in a deferred operation, it is important that the entire transaction be rolled back. As an example, one transaction may take money out of a customer's checking account and deposit it into their savings account. If the deposit into the savings account fails, the withdrawal from the checking account must be rolled back, so the money doesn't just disappear from the account.
Every deferred operation which updates or deletes instances from the database (such as Obj-Save or Obj-Delete) is put onto an internal deferred list, to be committed later (as opposed to the transactions which have the Write Now parameter checked, which are committed immediately). The Rollback method deletes the entire deferred list, thus rolling back that transaction. Most transactions use this method of handling a run-time error.
NOTE: The Rollback method also releases any locks which were specified as ReleaseOnCommit.
When using any of the harness rules, if an error occurs, the rollback occurs automatically.
Retry the transaction
If the system reports a lock error ("lock already held by another"), then you can retry the transaction, and allow processing to continue. For example, if the system reports that another user has the lock, the application can present a window to the user stating something like, "This transaction was unable to complete. Please try again," with a Submit button. If the lock were released by the other user before the Submit was entered, then this user can acquire the lock and complete the transaction.
Make sure that your application handles situations where the second submit was still unsuccessful, and determine how many tries are reasonable before failure and a Rollback.
Ignore failure - Obj-Save-Cancel
Important: This method is not a best practice.
Your activity can use the Obj-Save-Cancel activity method to remove one item from the deferred list. This method will remove the failed Obj-Save from the deferred list, allowing the Commit to succeed. This method could be used for objects where missing some data is not critical (such as log records or history records).
Process Commander offers several techniques available to handle error or failure messages,
In the following example, as part of the Rollback, the developer has determined that if the lock is not obtained, then the activity should stop. The possibility that the lock will not be obtained is handled in step 6, using Property-Set-Messages and the @getWorstMessage(tools) function. This function gets the processing status error message from the system, and then Property-Set-Messages puts that error into the specified Field . The work object form then presents this message to the user.
Next, the Transition is set to Exit the Activity.Resolving typical problems
When running activities directly (rather than through a work-object form), rollback may not occur (unless programmed into the activity). Thus, once an error occurs, no further Commits may be executed - including the Commit that may be needed to update the activity to fix the problem. Therefore, once the failing step in the activity is determined, you must log out and log back in, to clear the Commit error and be able to correct and resave the activity.
Lock released by intermediate Commit prior to Obj-Save
This issue (frequently associated with Utility activities) is usually the result of unintended commits that release a requestor's locks.
For example, as part of a work process, a second work object might be spun off from the main work object. As part of the spinoff process, the second work object may be committed to the database. The Commit process will commit all outstanding work to the database, so any data saved but not committed by the main process (which is still executing) might be unintentionally committed, and the lock released by the commit. Then when the Commit step is executed later in the main process, there is no lock available, and an error occurs.
This issue can be diagnosed by using the new Tracer Event Type of Locking.
There are two solutions for this problem:
- Remove the commit.
- Reacquire locks.
Remove the Commit
If the commit is not appropriate for the (subsidiary) process in question, simply remove the commit. Exercise caution to ensure that this commit is not from an Activity that is used in more than one place and has other callers that rely on the commit operation.
Flow processing typically handles its own commit operations, and it is rare that you need to augment this. Two new V4.2SP2 activities spin-off an additional work object:
These activities do not perform Commits. Call them for all spin-off processes, rather than the old activities Work-.Add and Work-.AddCovered, which do contain commits.
For instances other than work objects, when an intermediate commit is required for a particular object instance (this is rare), then reacquire locks by using either:
- the Obj-Refresh-And-Lock method
- The Work-WorkLock activity
Page transitions from New to Read from DB status
This issue describes a situation in which a new object instance is created and saved to the database, and subsequent saves require the instance to be locked. The first time the instance is saved, it is new and therefore does not require a lock (as it doesn't currently exist in the database - there is nothing to be overwritten). Once saved, however, the instance does exist in the database, and will therefore need a lock if subsequent saves are required.
The problem can occur when an Obj-Save for the newly-created object has Write Now checked, which causes a commit; the actual Commit statement then requires a lock. The Write Now option is often selected, but is not always necessary; simply uncheck the Write Now option if not needed, or alternatively, acquire the lock prior to the Obj-Save operation.