Another way to implement “controlled” database lock

The problem:

we are performing a lot of changes in transaction and want to prevent transaction from being aborted due to VERSION MISMATCH errors, so we need to put row-level lock on objects to be modified, but we do not want to wait for a long time because our transaction locks objects too (see some explanation in: Pessimistic locking, Pessimistic locking. Advanced approach.)

The solution:

Actually the solution described in Pessimistic locking. Advanced approach. is awesome except the fact that described functionality wasn’t ported to D7, but EXEC_SELECT_SQL RPC comes to the rescue:

public static final String LOCK_SQL = "SELECT r_object_id FROM {0} WHERE "
        + "r_object_id=''{1}'' FOR UPDATE WAIT {2,number,#}";

public static boolean lockWithTimeout(IDfPersistentObject object,
        int timeout) throws DfException {
    IDfSession session = object.getSession();
    if (!session.isTransactionActive()) {
        throw new DfException(
                "Transaction is not active, unable to lock object "
                        + object.getObjectId().getId());
    }
    if (object.isNew()) {
        throw new DfException("Unable to lock object "
                + object.getObjectId().getId() + ", object is new");
    }
    String SQL = getLockSql(object, timeout);
    IDfCollection collection = null;
    boolean found = false;
    try {
        collection = ApplyCommand.execSelectSQL(session, SQL);
        if (collection.next()) {
            found = object.getObjectId().equals(
                    collection.getId(DfDocbaseConstants.R_OBJECT_ID));
        } else {
            // here is not clear why collection.next() does not throw
            // exception, doing it manually
            DfException exception = ((ISession) session)
                    .getExceptionsForAllMessages(0);
            if (exception != null) {
                throw exception;
            }
        }
    } catch (DfException ex) {
        handleLockException(ex);
        return false;
    } finally {
        if (collection != null
                && collection.getState() != IDfCollection.DF_CLOSED_STATE) {
            collection.close();
        }
    }
    if (!found) {
        throw new DfException("Unable to lock object "
                + object.getObjectId().getId() + ", not found in database");
    }
    object.fetch(null);
    return true;
}

private static void handleLockException(DfException ex) throws DfException {
    if (!"DM_SESSION_E_CURSOR_ERROR".equalsIgnoreCase(ex.getMessageId())) {
        throw ex;
    }
    String message = ex.getMessage();
    if (message == null) {
        throw ex;
    }
    if (!message.contains("ORA-30006")) {
        throw ex;
    }
}

private static String getLockSql(IDfPersistentObject object, int timeout)
    throws DfException {
    return MessageFormat.format(LOCK_SQL, getSuperType(object) + "_s",
            object.getObjectId().getId(), timeout);
}

private static String getSuperType(IDfPersistentObject object)
    throws DfException {
    IDfType type = object.getType();
    while (type.getSuperType() != null) {
        type = type.getSuperType();
    }
    return type.getName();
}

3 thoughts on “Another way to implement “controlled” database lock

  1. Pingback: Dealing with workflow methods. Part II | Documentum in a (nuts)HELL
  2. Pingback: Diagnostics challenge. Part I | Documentum in a (nuts)HELL
  3. Pingback: When documentum had started dying | Documentum in a (nuts)HELL

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s