GH-3010 Add idleBetweenTries for JdbcLockRegistry

Fixes https://github.com/spring-projects/spring-integration/issues/3010

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

**Cherry-pick to 5.1.x**
This commit is contained in:
Artem Bilan
2019-08-19 16:07:48 -04:00
committed by Gary Russell
parent f0807fe704
commit f600361237
2 changed files with 30 additions and 10 deletions

View File

@@ -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;
@@ -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) {
@@ -87,14 +101,17 @@ public class JdbcLockRegistry implements ExpirableLockRegistry {
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;
}
@@ -108,7 +125,7 @@ public class JdbcLockRegistry implements ExpirableLockRegistry {
while (true) {
try {
while (!doLock()) {
Thread.sleep(100); //NOSONAR
Thread.sleep(this.idleBetweenTries.toMillis());
}
break;
}
@@ -116,11 +133,11 @@ public class JdbcLockRegistry implements ExpirableLockRegistry {
// 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();
@@ -139,7 +156,7 @@ public class JdbcLockRegistry implements ExpirableLockRegistry {
while (true) {
try {
while (!doLock()) {
Thread.sleep(100); //NOSONAR
Thread.sleep(this.idleBetweenTries.toMillis());
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
@@ -183,7 +200,7 @@ public class JdbcLockRegistry implements ExpirableLockRegistry {
while (true) {
try {
while (!(acquired = doLock()) && System.currentTimeMillis() < expire) { //NOSONAR
Thread.sleep(100); //NOSONAR
Thread.sleep(this.idleBetweenTries.toMillis());
}
if (!acquired) {
this.delegate.unlock();

View File

@@ -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