Skip to content

Commit 3690721

Browse files
feat: shedlock over jdbc (#168)
1 parent 809ec33 commit 3690721

File tree

5 files changed

+165
-159
lines changed

5 files changed

+165
-159
lines changed

shedlock-ydb/pom.xml

+4-14
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>tech.ydb.dialects</groupId>
88
<artifactId>shedlock-ydb</artifactId>
9-
<version>0.2.0</version>
9+
<version>0.3.0</version>
1010

1111
<packaging>jar</packaging>
1212

@@ -38,8 +38,8 @@
3838

3939
<junit5.version>5.9.3</junit5.version>
4040
<log4j2.version>2.17.2</log4j2.version>
41-
<ydb.sdk.version>2.2.6</ydb.sdk.version>
42-
<ydb.jdbc.version>2.2.2</ydb.jdbc.version>
41+
<ydb.sdk.version>2.3.6</ydb.sdk.version>
42+
<ydb.jdbc.version>2.3.5</ydb.jdbc.version>
4343
<spring.boot.version>3.2.3</spring.boot.version>
4444
<shedlock-spring.version>5.15.0</shedlock-spring.version>
4545
</properties>
@@ -53,13 +53,6 @@
5353

5454
<dependencyManagement>
5555
<dependencies>
56-
<dependency>
57-
<groupId>tech.ydb</groupId>
58-
<artifactId>ydb-sdk-bom</artifactId>
59-
<version>${ydb.sdk.version}</version>
60-
<type>pom</type>
61-
<scope>import</scope>
62-
</dependency>
6356
<dependency>
6457
<groupId>org.springframework.boot</groupId>
6558
<artifactId>spring-boot-dependencies</artifactId>
@@ -71,10 +64,6 @@
7164
</dependencyManagement>
7265

7366
<dependencies>
74-
<dependency>
75-
<groupId>tech.ydb</groupId>
76-
<artifactId>ydb-sdk-coordination</artifactId>
77-
</dependency>
7867
<dependency>
7968
<groupId>net.javacrumbs.shedlock</groupId>
8069
<artifactId>shedlock-spring</artifactId>
@@ -101,6 +90,7 @@
10190
<dependency>
10291
<groupId>tech.ydb.test</groupId>
10392
<artifactId>ydb-junit5-support</artifactId>
93+
<version>${ydb.sdk.version}</version>
10494
<scope>test</scope>
10595
</dependency>
10696
<dependency>

shedlock-ydb/src/main/java/tech/ydb/lock/provider/YdbCoordinationServiceLockProvider.java

-127
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package tech.ydb.lock.provider;
2+
3+
import java.sql.SQLException;
4+
import java.util.Optional;
5+
import javax.sql.DataSource;
6+
import net.javacrumbs.shedlock.core.LockConfiguration;
7+
import net.javacrumbs.shedlock.core.LockProvider;
8+
import net.javacrumbs.shedlock.core.SimpleLock;
9+
import net.javacrumbs.shedlock.support.Utils;
10+
import org.slf4j.Logger;
11+
import org.slf4j.LoggerFactory;
12+
13+
/**
14+
* @author Kirill Kurdyukov
15+
*/
16+
public class YdbJDBCLockProvider implements LockProvider {
17+
private static final Logger LOGGER = LoggerFactory.getLogger(YdbJDBCLockProvider.class);
18+
private static final String LOCKED_BY = "Hostname=" + Utils.getHostname() + ", " +
19+
"Current PID=" + ProcessHandle.current().pid();
20+
21+
private final DataSource dataSource;
22+
23+
public YdbJDBCLockProvider(DataSource dataSource) {
24+
this.dataSource = dataSource;
25+
}
26+
27+
@Override
28+
public Optional<SimpleLock> lock(LockConfiguration lockConfiguration) {
29+
try (var connection = dataSource.getConnection()) {
30+
var autoCommit = connection.getAutoCommit();
31+
try {
32+
connection.setAutoCommit(false);
33+
34+
var selectPS = connection.prepareStatement("SELECT locked_by, lock_until FROM shedlock " +
35+
"WHERE name = ? AND lock_until > CurrentUtcTimestamp()");
36+
37+
selectPS.setString(1, lockConfiguration.getName());
38+
39+
try (var rs = selectPS.executeQuery()) {
40+
if (rs.next()) {
41+
LOGGER.debug("Instance[{}] acquire lock is failed. Leader is {}, lock_until = {}",
42+
LOCKED_BY, rs.getString(1), rs.getString(2));
43+
return Optional.empty();
44+
}
45+
}
46+
47+
var upsertPS = connection.prepareStatement("" +
48+
"UPSERT INTO shedlock(name, lock_until, locked_at, locked_by) " +
49+
"VALUES (?, Unwrap(CurrentUtcTimestamp() + ?), CurrentUtcTimestamp(), ?)"
50+
);
51+
52+
upsertPS.setObject(1, lockConfiguration.getName());
53+
upsertPS.setObject(2, lockConfiguration.getLockAtMostFor());
54+
upsertPS.setObject(3, LOCKED_BY);
55+
upsertPS.execute();
56+
57+
connection.commit();
58+
59+
LOGGER.debug("Instance[{}] is leader", LOCKED_BY);
60+
61+
return Optional.of(new YdbJDBCLock(lockConfiguration.getName(), dataSource));
62+
} finally {
63+
connection.setAutoCommit(autoCommit);
64+
}
65+
} catch (SQLException e) {
66+
LOGGER.debug("Instance[{}] acquire lock is failed", LOCKED_BY);
67+
68+
return Optional.empty();
69+
}
70+
}
71+
72+
private record YdbJDBCLock(String name, DataSource dataSource) implements SimpleLock {
73+
private static final int ATTEMPT_RELEASE_LOCK = 10;
74+
75+
@Override
76+
public void unlock() {
77+
for (int i = 0; i < ATTEMPT_RELEASE_LOCK; i++) {
78+
try {
79+
doUnlock();
80+
81+
return;
82+
} catch (SQLException e) {
83+
if (i == ATTEMPT_RELEASE_LOCK - 1) {
84+
throw new RuntimeException(e);
85+
}
86+
}
87+
}
88+
}
89+
90+
private void doUnlock() throws SQLException {
91+
try (var connection = dataSource.getConnection()) {
92+
var autoCommit = connection.getAutoCommit();
93+
94+
try {
95+
connection.setAutoCommit(true);
96+
var ps = connection.prepareStatement(
97+
"UPDATE shedlock SET lock_until = CurrentUtcTimestamp() WHERE name = ? and locked_by = ?");
98+
ps.setString(1, name);
99+
ps.setString(2, LOCKED_BY);
100+
ps.execute();
101+
} finally {
102+
connection.setAutoCommit(autoCommit);
103+
}
104+
} catch (SQLException e) {
105+
LOGGER.debug(String.format("Instance[{%s}] release lock is failed", LOCKED_BY), e);
106+
107+
throw e;
108+
}
109+
}
110+
}
111+
}
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
package tech.ydb.lock.provider;
22

3-
import java.sql.SQLException;
43
import javax.sql.DataSource;
4+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
55
import org.springframework.context.annotation.Bean;
66
import org.springframework.context.annotation.Configuration;
7-
import tech.ydb.jdbc.YdbConnection;
87

98
/**
109
* @author Kirill Kurdyukov
@@ -13,11 +12,8 @@
1312
@Configuration
1413
public class YdbLockProviderConfiguration {
1514
@Bean
16-
public YdbCoordinationServiceLockProvider ydbLockProvider(DataSource dataSource) throws SQLException {
17-
var provider = new YdbCoordinationServiceLockProvider(dataSource.getConnection().unwrap(YdbConnection.class));
18-
19-
provider.init();
20-
21-
return provider;
15+
@ConditionalOnBean(DataSource.class)
16+
public YdbJDBCLockProvider ydbLockProvider(DataSource dataSource) {
17+
return new YdbJDBCLockProvider(dataSource);
2218
}
2319
}

0 commit comments

Comments
 (0)