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。