cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Lock IdentityIQ objects before modification (code sample)

Lock IdentityIQ objects before modification (code sample)

Question

When writing custom IIQ code, whether it be Rules, Workflow or Java classes, is it necessary to lock an object before modification?

 

Answer

Note: In version 5.1 and below, object locking was only supported for Identity objects. Certification locking has been introduced in 5.2.

 

If you are making modifications to objects, the recommendation is to always lock the object before modification and unlock the object after modification, to prevent concurrent modification errors. For example, multiple executing tasks may try to modify the same object at the same time. This can cause issues, such as IDX ordering problems.

 

Below is some explanation of various locking parameters as well as a code template that you can use that incorporates locking logic.

 

 

   /**
     * LockInfo.DEFAULT_LOCK_TIMEOUT
     * Set to 5, by default.
     * The default lock timeout in minutes. This can be overridden
     * in the system configuration via the following setting:
     * <entry key="persistentLockTimeout" value=""/>
     * The lock timeout will not come into play, assuming an object
     * lock is properly released.
     * Note: When a lock times out, the value in the corresponding
     * iiqlock database table column is NOT nulled out. The existing
     * value will be overwritten during the next lock attempt, assuming
     * the current lock is expired. So a value in this column is not
     * necessarily a good indicator that an object is locked. Use
     * the "iiq console" command "listLocks" to verify locked objects.
     */

    /**
     * ObjectUtil.DEFAULT_LOCK_TIMEOUT
     * Set to 60, by default.
     * Default number of seconds we wait attempting to obtain a
     * persistent lock before throwing ObjectAlreadyLockedException.
     */

    /**
     * LOCK_TYPE_PERSISTENT
     * A value for the LOCK_TYPE option that selects a persistent lock.
     *
     * A persistent lock is an application-level locking convention
     * that uses a special column in the object table to indicate
     * the lock status.  A persistent lock may span transactions.
     *
     * Persistent locks are appropriate if you need to lock
     * an object for a long period of time, or keep an object locked
     * across a transaction boundary.  
     *
     * This type of lock is most appropriate for editing sessions in
     * the UI.  Because it must modify the locked object it has more
     * overhead than a transaction lock which makes it less suitable
     * for background tasks that scan many objects.
    
     * The other thing to be careful of is that persistent locks require
     * a "lock name" that must be passed as the final argument. This is
     * part of the value that will be stored in the lock column and needs
     * to be unique. If you are certain that more than two instances of
     * a custom task won't be running at a time, and this task will not
     * launch multiple threads that need to do locking then you can hard
     * code a name. If you can't be certain then you will need to generate
     * a name, Util.uuid() works but the name will be relatively meaningless.
     */


    /**
     * LOCK_TYPE_TRANSACTION
     * A value for the LOCK_TYPE option that selects a transaction lock.
     *
     * A transaction lock is held only for the duration of the current
     * transaction, it corresponds to a Hibernate fetch with
     * LockMode.UPGRADE, which in turn corresponds to a SQL
     * select with the "for update" option.
     *
     * If another transaction has locked this object, the calling
     * thread will suspend until the other transaction completes
     * (or the request times out).
     *
     * This type of lock is appropriate for internal background tasks
     * that need exclusive access to an object for a short duration
     * and don't need the overhead of a persistent lock.
     */
    
    import sailpoint.api.ObjectUtil;
    import sailpoint.api.PersistenceManager;
    import sailpoint.object.Identity;
    import sailpoint.tools.Util;
    import sailpoint.tools.GeneralException;
    
    Identity ident = null;
    String identityName = "jdoe";
    
    try {
        ident = ObjectUtil.lockObject(context, Identity.class, null, identityName, PersistenceManager.LOCK_TYPE_PERSISTENT,
                                                                     Util.uuid(), ObjectUtil.DEFAULT_LOCK_TIMEOUT);

        if ( ident == null ) {
            return;
        }

        // Do something with the locked identity object here

    }
    catch (GeneralException ge) {
        System.out.println(ge.toString());
    }
    finally {
        if (ident != null) {
            if (ident.getLock() != null) {
                ident.setLock(null);
                context.saveObject(ident);
                context.commitTransaction();
            }
        }
    }

 

Labels (2)
Tags (3)
Comments

I did a quick search in our environment and I found several IdentityCreation rules and refreshRule that modify the identity object without locking them. These rules automatically receive the identity object as a parameter. I'm just wondering whether the identity is already being locked before it gets passed to the IdentityCreation and refreshRules?

Thanks

As far as I know, yes. At least in case of rules run during aggregation and refresh, that are meant to make modifications on an Identity. Such rules are in these cases part of a bigger process of refreshing/updating data on an identity object. It is more efficient to lock/unlock an identity just once during the refresh of an identity object. You should not take this for granted in rules that are not meant for identity modification. E.g. if you would abuse an exclusion rule or pre-delegation rule in a certification to modify an Identity, you should take care of the locking of an identity yourself.

- Menno

Hi Menno,

I have workflow where in one of steps we update identity object or link object under script. Do we need to acquire lock before making commit.

Could you please give us example of below rules.

You should not take this for granted in rules that are not meant for identity modification.

Thanks in advance.

Vijay

Hi vijay.sharma​,

Especially in a workflow, I would suggest to update identity or account attributes using provisioning features: create a provisioning plan to update the identity (application "IIQ") or account and use provisioning methods within workflow libraries to execute the provisioning plan. This is a much cleaner and safer way, without the explicit need to acquire a lock. Examples of how this is done can be found in the standard LCM workflows, like LCM Provisioning and LCM Create and Update. You can even use these workflows as subprocesses: you define one step to programmatically create a provisioning plan (see the JavaDocs for sailpoint.object.ProvisioningPlan, sailpoint.object.ProvisioningPlan.AccountRequest and sailpoint.object.ProvisioningPlan.AttributeRequest) and another to call LCM Provisioning with the plan, name of the identity and optionally some parameters to control the workflow behavior (policy checks, approvals, provisioning features, etc.).

- Menno

P.S. This way of working is taught in the Provisioning Training​, which I'd highly recommend.

In looking at object locking I found that PersistenceManager (extended by SailPointContext) includes a lockObject method with the following comment:

This method should be the entry point for all public locking calls. Other locking methods are there for backward compatibility and will delegate to this.

Not sure if it matters, but I'm guessing this should now be used in lieu of ObjectUtil (at least in 6.2 and beyond)...

The comment refers more to how it is implemented internally, it doesn't belong there.  All of the locking methods in PersistenceManager will always be supported, it's a matter of preference which ones you use.  The newer one that uses LockParameters was added to eliminate an ever growing list of method arguments to the locking methods and make them easier to pass around internally. 

The ObjectUtil methods all call the PersistenceManager methods with extra features added on top, such as waiting for a lock to be released.  I recommend that people use those instead since they hide some of the complexity.  Unfortunately we've grown a lot of those over time so selecting the right one is confusing.  The concept of "lockName" is actually no longer used as of 7.0 and should in practice always be a Util.uuid() pre-7.0, and we'll ignore it post 7.0.

There are three kinds of locks you need to worry about, identity locks, certification locks, and transaction locks.  Identity and Certification locks are "persistent" meaning that they will remain locked for a period of time (default 5 minutes) and span multiple database transactions.  Transaction locks are used for all other object types, these are less common, one use I've seen is to maintain a counter or some other piece of shared data in a custom Configuration object where multiple threads may need to update that object at the same time.

Since lockName is no longer relevant, and you don't usually need to mess with lock durations and timeouts, the following methods will meet most people's needs for identity locking.

    Identity ident = ObjectUtil.lockIdentity(context, "identity name");

    try {

              ...do stuff

    }

    finally {

        ObjectUtil.unlockIdentity(context, ident);

    }

For persistent locks it is important that you call unlockIdentity in a finally block to make sure the lock is released.

Locking Certification objects is less common, but you would use the same approach for those.

For transaction locks, this method is the simplest:

    Configuration config = ObjectUtil.transactionLock(context, Configuration.class, "my object name");

     ...do stuff

     if (thingsWentWell)

         context.commitTransaction();

     else

          context.rollbackTransaction();

A try/finally block isn't always required, you just need to commit the transaction to save the changes and release the row lock.   If you forget the transaction will be automatically rolled back when the connection is returned to the pool.

The dozens of other methods are all variants of this that give you more control over locking parameters.  Some of these are used by the system, but you don't need to worry about them unless you have a situation that requires them, which isn't common.

Also a clarification on when locking is necessary.

If you are implementing a rule that will be called during an aggregation or refresh task you do not need to lock the identity that is passed to you.  The tasks will handle the locking.  The only exception is if you need to modify a DIFFERENT identity than the one being refreshed.

If you are implementing a workflow step then you do need to handle locking.  As Menno mentions, the preferred way to do that is to use Provisioner and ProvisioningPlans which will do the locking for you. 

If you are implementing a custom Java TaskExecutor then you need to handle your own locking. 

Any rule or script outside of the refresh tasks need to handle locking but with the exception of workflow step rules, you really shouldn't be modifying identities in rules.  Sometimes we we write "fixer" rules that can be run from the console to clean up data corruption.  You don't have to lock in those IFF you know that you're the only user and there won't be any refresh tasks running at the same time.

Thanks for the clarification/additional information!

Hello All,

 

I'm getting this Concurrent.Modification.Exception while running "Perform Identity Request Maintenance Task",

In workflow i have written some code to remove  certain AccountRequests from the ProvisioningPlan, does locking requires here??

could you please suggest any best way to encounter this exception.

 

Thank you in advance

Chandra

Hello Chandra,

your are most probably referring to another problem, that is not related to object locking in IIQ.

We encountered the same ConcurrentModificationExceptions with partitioned (!) refresh tasks, which turned out to be a bug in IIQ (IIQTC-205). This is supposed to be fixed in 7.3p3, 8.0p1.

That may not necessarily apply to "Perform Identity Request Maintenance Task" also, but from the nature of that bug I would assume so.

Version history
Revision #:
4 of 4
Last update:
‎Jul 24, 2023 07:39 PM
Updated by:
 
Contributors