JDBC Lock Registry

Version 4.3 introduced the JdbcLockRegistry. Certain components (for example, aggregator and resequencer) use a lock obtained from a LockRegistry instance to ensure that only one thread manipulates a group at a time. The DefaultLockRegistry performs this function within a single component. You can now configure an external lock registry on these components. When used with a shared MessageGroupStore, you can use the JdbcLockRegistry to provide this functionality across multiple application instances, such that only one instance can manipulate the group at a time.

When a lock is released by a local thread, another local thread can generally acquire the lock immediately. If a lock is released by a thread that uses a different registry instance, it can take up to 100ms to acquire the lock.

The JdbcLockRegistry is based on the LockRepository abstraction, which has a DefaultLockRepository implementation. The database schema scripts are located in the org.springframework.integration.jdbc package, which is divided for the particular RDBMS vendors. For example, the following listing shows the H2 DDL for the lock table:

CREATE TABLE INT_LOCK  (
    LOCK_KEY CHAR(36),
    REGION VARCHAR(100),
    CLIENT_ID CHAR(36),
    CREATED_DATE TIMESTAMP NOT NULL,
    constraint INT_LOCK_PK primary key (LOCK_KEY, REGION)
);

The INT_ can be changed according to the target database design requirements. Therefore, you must use prefix property on the DefaultLockRepository bean definition.

Sometimes, one application has moved to such a state that it cannot release the distributed lock and remove the particular record in the database. For this purpose, such deadlocks can be expired by the other application on the next locking invocation. The timeToLive (TTL) option on the DefaultLockRepository is provided for this purpose. You may also want to specify CLIENT_ID for the locks stored for a given DefaultLockRepository instance. If so, you can specify the id to be associated with the DefaultLockRepository as a constructor parameter.

Starting with version 5.1.8, the JdbcLockRegistry can be configured with the idleBetweenTries - a Duration to sleep between lock record insert/update executions. By default, it is 100 milliseconds and in some environments non-leaders pollute connections with data source too often.

Starting with version 5.4, the RenewableLockRegistry interface has been introduced and added to JdbcLockRegistry. The renewLock() method must be called during locked process in case of the locked process would be longer than time to live of the lock. So the time to live can be highly reduce and deployments can retake a lost lock quickly.

The lock renewal can be done only if the lock is held by the current thread.

Starting with version 5.5.6, the JdbcLockRegistry is support automatically clean up cache for JdbcLock in JdbcLockRegistry.locks via JdbcLockRegistry.setCacheCapacity(). See its JavaDocs for more information.

Starting with version 6.0, the DefaultLockRepository can be supplied with a PlatformTransactionManager instead of relying on the primary bean from the application context.

Starting with version 6.1, the DefaultLockRepository can be configured for custom insert, update and renew queries. For this purpose the respective setters and getters are exposed. For example, an insert query for PostgreSQL hint can be configured like this:

lockRepository.setInsertQuery(lockRepository.getInsertQuery() + " ON CONFLICT DO NOTHING");

Starting with version 6.4, the LockRepository.delete() method return the result of removing ownership of a distributed lock. And the JdbcLockRegistry.JdbcLock.unlock() method throws ConcurrentModificationException if the ownership of the lock is expired.