Skip to content

Commit 07ff7df

Browse files
committed
Fixes #636
1 parent 9cbdfd6 commit 07ff7df

3 files changed

Lines changed: 41 additions & 29 deletions

File tree

server/src/main/java/invite/cron/AbstractNodeLeader.java

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.sql.Connection;
99
import java.sql.PreparedStatement;
1010
import java.sql.ResultSet;
11+
import java.sql.SQLException;
1112

1213
public abstract class AbstractNodeLeader {
1314

@@ -32,6 +33,8 @@ public void perform(String name, Executable executable) {
3233

3334
if (!lockAcquired) {
3435
LOG.info(String.format("Another node is running %s, skipping this one", name));
36+
//Might be that there is a lock not cleaned up due to VM crash
37+
this.cleanupStaleLocks(conn, 60);
3538
return;
3639
}
3740

@@ -55,30 +58,44 @@ public void perform(String name, Executable executable) {
5558
conn.close();
5659
} catch (Exception ignored) {
5760
//Can't do anything about this
61+
LOG.warn(String.format("Failed to close lock %s", name));
5862
}
5963
}
6064
}
6165
}
6266

6367
protected boolean tryGetLock(Connection conn, String name) throws Exception {
64-
try (PreparedStatement ps = conn.prepareStatement("SELECT GET_LOCK(?, ?)")) {
65-
ps.setString(1, name);
66-
//Use timeout of 0 to have an immediate result
67-
ps.setInt(2, 0);
68-
try (ResultSet rs = ps.executeQuery()) {
69-
if (rs.next()) {
70-
int result = rs.getInt(1);
71-
return !rs.wasNull() && result == 1;
72-
}
68+
try {
69+
conn.setAutoCommit(false);
70+
try (PreparedStatement ps = conn.prepareStatement(
71+
"INSERT INTO distributed_locks (lock_name, acquired_at) VALUES (?, NOW())")) {
72+
ps.setString(1, name);
73+
ps.executeUpdate();
74+
conn.commit();
75+
return true;
76+
} catch (SQLException e) {
77+
conn.rollback();
78+
// Duplicate key or other constraint violation means lock is held
7379
return false;
7480
}
81+
} finally {
82+
conn.setAutoCommit(true);
7583
}
7684
}
7785

7886
private void releaseLock(Connection conn, String name) throws Exception {
79-
try (PreparedStatement ps = conn.prepareStatement("SELECT RELEASE_LOCK(?)")) {
87+
try (PreparedStatement ps = conn.prepareStatement(
88+
"DELETE FROM distributed_locks WHERE lock_name = ?")) {
8089
ps.setString(1, name);
81-
ps.executeQuery(); // ignore result
90+
ps.executeUpdate();
91+
}
92+
}
93+
94+
private void cleanupStaleLocks(Connection conn, int timeoutMinutes) throws Exception {
95+
try (PreparedStatement ps = conn.prepareStatement(
96+
"DELETE FROM distributed_locks WHERE acquired_at < NOW() - INTERVAL ? MINUTE")) {
97+
ps.setInt(1, timeoutMinutes);
98+
ps.executeUpdate();
8299
}
83100
}
84101

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
CREATE TABLE distributed_locks (
2+
lock_name VARCHAR(255) PRIMARY KEY,
3+
acquired_at TIMESTAMP
4+
) ENGINE=InnoDB
5+
DEFAULT CHARSET = utf8mb4;

server/src/test/java/invite/cron/ResourceCleanerTest.java

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
import javax.sql.DataSource;
1111
import java.sql.Connection;
12-
import java.sql.PreparedStatement;
1312
import java.time.Instant;
1413
import java.time.Period;
1514
import java.util.List;
@@ -73,24 +72,15 @@ void cleanUserRoles() throws JsonProcessingException {
7372
@SneakyThrows
7473
@Test
7574
void lockAlreadyAcquired() {
76-
Connection conn = null;
77-
try {
78-
conn = dataSource.getConnection();
79-
subject.tryGetLock(conn, LOCK_NAME);
80-
81-
long beforeUsers = userRepository.count();
82-
markUser(GUEST_SUB);
83-
84-
subject.clean();
85-
//Nothing happened
86-
assertEquals(beforeUsers, userRepository.count());
87-
} finally {
88-
try (PreparedStatement ps = conn.prepareStatement("SELECT RELEASE_LOCK(?)")) {
89-
ps.setString(1, LOCK_NAME);
90-
ps.executeQuery(); // ignore result
91-
}
92-
}
75+
Connection conn = dataSource.getConnection();
76+
subject.tryGetLock(conn, LOCK_NAME);
9377

78+
long beforeUsers = userRepository.count();
79+
markUser(GUEST_SUB);
80+
81+
subject.clean();
82+
//Nothing happened
83+
assertEquals(beforeUsers, userRepository.count());
9484
}
9585

9686
private void markUser(String sub) {

0 commit comments

Comments
 (0)