Skip to content

Commit

Permalink
GH-3010 Add idleBetweenTries for JdbcLockRegistry
Browse files Browse the repository at this point in the history
Fixes #3010

Sometimes `100` milliseconds interval is too often to try to obtain a
lock with `UPDATE/INSERT` queries

**Cherry-pick to 5.1.x**
  • Loading branch information
artembilan authored and garyrussell committed Aug 20, 2019
1 parent 4457345 commit c9e0cc0
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.springframework.integration.jdbc.lock;

import java.time.Duration;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
Expand Down Expand Up @@ -55,15 +56,28 @@ public class JdbcLockRegistry implements ExpirableLockRegistry {

private final LockRepository client;

private Duration idleBetweenTries = Duration.ofMillis(100);

public JdbcLockRegistry(LockRepository client) {
this.client = client;
}

/**
* Specify a @link Duration} to sleep between lock record insert/update attempts.
* Defaults to 100 milliseconds.
* @param idleBetweenTries the {@link Duration} to sleep between insert/update attempts.
* @since 5.1.8
*/
public void setIdleBetweenTries(Duration idleBetweenTries) {
Assert.notNull(idleBetweenTries, "'idleBetweenTries' must not be null");
this.idleBetweenTries = idleBetweenTries;
}

@Override
public Lock obtain(Object lockKey) {
Assert.isInstanceOf(String.class, lockKey);
String path = pathFor((String) lockKey);
return this.locks.computeIfAbsent(path, p -> new JdbcLock(this.client, p));
return this.locks.computeIfAbsent(path, (key) -> new JdbcLock(this.client, this.idleBetweenTries, key));
}

private String pathFor(String input) {
Expand All @@ -87,14 +101,17 @@ private static final class JdbcLock implements Lock {

private final LockRepository mutex;

private final Duration idleBetweenTries;

private final String path;

private volatile long lastUsed = System.currentTimeMillis();

private final ReentrantLock delegate = new ReentrantLock();

JdbcLock(LockRepository client, String path) {
JdbcLock(LockRepository client, Duration idleBetweenTries, String path) {
this.mutex = client;
this.idleBetweenTries = idleBetweenTries;
this.path = path;
}

Expand All @@ -108,19 +125,19 @@ public void lock() {
while (true) {
try {
while (!doLock()) {
Thread.sleep(100); //NOSONAR
Thread.sleep(this.idleBetweenTries.toMillis());
}
break;
}
catch (TransientDataAccessException e) {
// try again
}
catch (InterruptedException e) {
/*
* This method must be uninterruptible so catch and ignore
* interrupts and only break out of the while loop when
* we get the lock.
*/
/*
* This method must be uninterruptible so catch and ignore
* interrupts and only break out of the while loop when
* we get the lock.
*/
}
catch (Exception e) {
this.delegate.unlock();
Expand All @@ -139,7 +156,7 @@ public void lockInterruptibly() throws InterruptedException {
while (true) {
try {
while (!doLock()) {
Thread.sleep(100); //NOSONAR
Thread.sleep(this.idleBetweenTries.toMillis());
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
Expand Down Expand Up @@ -183,7 +200,7 @@ public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
while (true) {
try {
while (!(acquired = doLock()) && System.currentTimeMillis() < expire) { //NOSONAR
Thread.sleep(100); //NOSONAR
Thread.sleep(this.idleBetweenTries.toMillis());
}
if (!acquired) {
this.delegate.unlock();
Expand Down
3 changes: 3 additions & 0 deletions src/reference/asciidoc/jdbc.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,9 @@ The `timeToLive` (TTL) option on the `DefaultLockRepository` is provided for thi
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.

[[jdbc-metadata-store]]
=== JDBC Metadata Store

Expand Down

0 comments on commit c9e0cc0

Please sign in to comment.