Spring 集成 JDBC 锁定失败
Spring Integration JDBC lock failure
我不理解从 JdbcLockRegistry 获得的分布式锁的行为。
@Bean
public LockRepository lockRepository(DataSource datasource) {
return new DefaultLockRepository(datasource);
}
@Bean
public LockRegistry lockRegistry(LockRepository repository) {
return new JdbcLockRegistry(repository);
}
我的项目是 运行 PostgreSQL 并且 Spring 引导版本是 2.2.2
这是演示用例:
@GetMapping("/isolate")
public String isolate() throws InterruptedException {
Lock lock = registry.obtain("the-lock");
if (lock.tryLock(10, TimeUnit.SECONDS)) { // close
try {
Thread.sleep(30 * 1000L);
} finally {
lock.unlock(); // open
}
} else {
return "rejected";
}
return "acquired";
}
注意:该用例在使用 Hazelcast 分布式锁时有效。
观察到的行为是,通过在第一个实例上调用 API,第一个锁在数据库中正式注册。
然后,在 30 秒内,在另一个实例(其他端口)上请求第二个 on,它更新现有的 int_lock table 行(client_id 更改)而不是失败。因此,第一个端点在 30 秒后交付(无解锁失败),第二个端点在其自己的 30 秒时间后交付。没有互斥。
这些是单次采集的日志:
Trying to acquire lock...
Executing prepared SQL update
Executing prepared SQL statement [DELETE FROM INT_LOCK WHERE REGION=? AND LOCK_KEY=? AND CREATED_DATE<?]
Executing prepared SQL update
Executing prepared SQL statement [UPDATE INT_LOCK SET CREATED_DATE=? WHERE REGION=? AND LOCK_KEY=? AND CLIENT_ID=?]
Executing prepared SQL update
Executing prepared SQL statement [INSERT INTO INT_LOCK (REGION, LOCK_KEY, CLIENT_ID, CREATED_DATE) VALUES (?, ?, ?, ?)]
Processing...
Executing prepared SQL update
Executing prepared SQL statement [DELETE FROM INT_LOCK WHERE REGION=? AND LOCK_KEY=? AND CLIENT_ID=?]
获取过程从 DELETE 开始听起来很奇怪,但是...
我试图为 DefaultLockRepository 设置一个不变的客户端 ID,但没有改进。
有没有人知道如何解决这个问题?感谢您的帮助。
好的。碰巧存储库的 TTL 默认是 10s,就像我在那个特定用例中的超时一样。所以锁显然在超时期限之前就死了(DELETE)。
这是一个修复:
@Bean
public LockRepository lockRepository(DataSource datasource) {
DefaultLockRepository repository = new DefaultLockRepository(datasource);
repository.setTimeToLive(60 * 1000);
return repository;
}
为了维护锁,我尝试利用由 Lock#lock 调用的 DefaultLockRepository#acquire,它在插入新锁之前尝试更新(如前所述,在清理过期锁之后):
@GetMapping("/isolate")
public String isolate() throws InterruptedException {
Lock lock = registry.obtain("the-lock");
log.warn("Trying to acquire lock...");
if (lock.tryLock(10, TimeUnit.SECONDS)) { // close lock
try {
for (int i=0; i < 6; i++) { // very...
log.warn("Processing...");
Thread.sleep(5 * 1000L); // ... long task
lock.lock(); //DEBUG holding (lock update)
}
} finally {
if (!repository.isAcquired("the-lock")) {
throw new IllegalStateException("lock lost");
} else {
lock.unlock(); // open lock
}
}
} else {
return "rejected";
}
return "acquired";
}
但这并没有像预期的那样工作(注意:在这个测试中 ttl 是默认的 10s);
尽管我可以在 PostgreSQL 的控制台中看到锁定日期发生变化,但我总是在最后得到一个 lock lost IllegalStateException。
我不理解从 JdbcLockRegistry 获得的分布式锁的行为。
@Bean
public LockRepository lockRepository(DataSource datasource) {
return new DefaultLockRepository(datasource);
}
@Bean
public LockRegistry lockRegistry(LockRepository repository) {
return new JdbcLockRegistry(repository);
}
我的项目是 运行 PostgreSQL 并且 Spring 引导版本是 2.2.2 这是演示用例:
@GetMapping("/isolate")
public String isolate() throws InterruptedException {
Lock lock = registry.obtain("the-lock");
if (lock.tryLock(10, TimeUnit.SECONDS)) { // close
try {
Thread.sleep(30 * 1000L);
} finally {
lock.unlock(); // open
}
} else {
return "rejected";
}
return "acquired";
}
注意:该用例在使用 Hazelcast 分布式锁时有效。
观察到的行为是,通过在第一个实例上调用 API,第一个锁在数据库中正式注册。 然后,在 30 秒内,在另一个实例(其他端口)上请求第二个 on,它更新现有的 int_lock table 行(client_id 更改)而不是失败。因此,第一个端点在 30 秒后交付(无解锁失败),第二个端点在其自己的 30 秒时间后交付。没有互斥。
这些是单次采集的日志:
Trying to acquire lock...
Executing prepared SQL update
Executing prepared SQL statement [DELETE FROM INT_LOCK WHERE REGION=? AND LOCK_KEY=? AND CREATED_DATE<?]
Executing prepared SQL update
Executing prepared SQL statement [UPDATE INT_LOCK SET CREATED_DATE=? WHERE REGION=? AND LOCK_KEY=? AND CLIENT_ID=?]
Executing prepared SQL update
Executing prepared SQL statement [INSERT INTO INT_LOCK (REGION, LOCK_KEY, CLIENT_ID, CREATED_DATE) VALUES (?, ?, ?, ?)]
Processing...
Executing prepared SQL update
Executing prepared SQL statement [DELETE FROM INT_LOCK WHERE REGION=? AND LOCK_KEY=? AND CLIENT_ID=?]
获取过程从 DELETE 开始听起来很奇怪,但是... 我试图为 DefaultLockRepository 设置一个不变的客户端 ID,但没有改进。 有没有人知道如何解决这个问题?感谢您的帮助。
好的。碰巧存储库的 TTL 默认是 10s,就像我在那个特定用例中的超时一样。所以锁显然在超时期限之前就死了(DELETE)。 这是一个修复:
@Bean
public LockRepository lockRepository(DataSource datasource) {
DefaultLockRepository repository = new DefaultLockRepository(datasource);
repository.setTimeToLive(60 * 1000);
return repository;
}
为了维护锁,我尝试利用由 Lock#lock 调用的 DefaultLockRepository#acquire,它在插入新锁之前尝试更新(如前所述,在清理过期锁之后):
@GetMapping("/isolate")
public String isolate() throws InterruptedException {
Lock lock = registry.obtain("the-lock");
log.warn("Trying to acquire lock...");
if (lock.tryLock(10, TimeUnit.SECONDS)) { // close lock
try {
for (int i=0; i < 6; i++) { // very...
log.warn("Processing...");
Thread.sleep(5 * 1000L); // ... long task
lock.lock(); //DEBUG holding (lock update)
}
} finally {
if (!repository.isAcquired("the-lock")) {
throw new IllegalStateException("lock lost");
} else {
lock.unlock(); // open lock
}
}
} else {
return "rejected";
}
return "acquired";
}
但这并没有像预期的那样工作(注意:在这个测试中 ttl 是默认的 10s); 尽管我可以在 PostgreSQL 的控制台中看到锁定日期发生变化,但我总是在最后得到一个 lock lost IllegalStateException。