Skip to content

Commit 56bfe4b

Browse files
authored
Merge pull request lukas-krecan#18 from clamey/master
Added Jedis as provider
2 parents 1b57812 + 7ad021f commit 56bfe4b

File tree

5 files changed

+300
-0
lines changed

5 files changed

+300
-0
lines changed

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
<module>shedlock-spring</module>
2626
<module>shedlock-springboot-test</module>
2727
<module>shedlock-provider-mongo</module>
28+
<module>shedlock-provider-jedis</module>
2829
<module>shedlock-test-support</module>
2930
<module>shedlock-test-support-jdbc</module>
3031
<module>shedlock-provider-jdbc-internal</module>

shedlock-provider-jedis/pom.xml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<parent>
4+
<artifactId>shedlock-parent</artifactId>
5+
<groupId>net.javacrumbs.shedlock</groupId>
6+
<version>0.10.1-SNAPSHOT</version>
7+
</parent>
8+
<modelVersion>4.0.0</modelVersion>
9+
10+
<artifactId>shedlock-provider-jedis</artifactId>
11+
<version>0.10.1-SNAPSHOT</version>
12+
13+
<dependencies>
14+
<dependency>
15+
<groupId>net.javacrumbs.shedlock</groupId>
16+
<artifactId>shedlock-core</artifactId>
17+
<version>${project.version}</version>
18+
</dependency>
19+
20+
<dependency>
21+
<groupId>redis.clients</groupId>
22+
<artifactId>jedis</artifactId>
23+
<version>2.9.0</version>
24+
</dependency>
25+
26+
<dependency>
27+
<groupId>net.javacrumbs.shedlock</groupId>
28+
<artifactId>shedlock-test-support</artifactId>
29+
<version>${project.version}</version>
30+
<scope>test</scope>
31+
</dependency>
32+
33+
34+
<dependency>
35+
<groupId>com.github.kstyrc</groupId>
36+
<artifactId>embedded-redis</artifactId>
37+
<version>0.6</version>
38+
<scope>test</scope>
39+
</dependency>
40+
</dependencies>
41+
42+
</project>
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/**
2+
* Copyright 2009-2017 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package net.javacrumbs.shedlock.provider.jedis;
17+
18+
import net.javacrumbs.shedlock.core.LockConfiguration;
19+
import net.javacrumbs.shedlock.core.LockProvider;
20+
import net.javacrumbs.shedlock.core.SimpleLock;
21+
import net.javacrumbs.shedlock.support.LockException;
22+
import redis.clients.jedis.Jedis;
23+
import redis.clients.jedis.JedisPool;
24+
25+
import java.net.InetAddress;
26+
import java.net.UnknownHostException;
27+
import java.time.Duration;
28+
import java.time.Instant;
29+
import java.time.temporal.ChronoUnit;
30+
import java.util.Optional;
31+
32+
/**
33+
* Uses Redis's `SET resource-name anystring NX PX max-lock-ms-time` as locking mechanism.
34+
*
35+
* See https://redis.io/commands/set
36+
*
37+
*/
38+
public class JedisLockProvider implements LockProvider {
39+
40+
private static final String KEY_PREFIX = "job-lock";
41+
private static final String ENV_DEFAULT = "default";
42+
43+
private static final long AT_MOST_HOURS_DEFAULT = 1;
44+
45+
// Redis Flags
46+
private static final String SET_IF_NOT_EXIST = "NX";
47+
private static final String SET_EXPIRE_TIME_IN_MS = "PX";
48+
49+
private JedisPool jedisPool;
50+
private String environment;
51+
private Duration atMostHoursDefault;
52+
53+
public JedisLockProvider(JedisPool jedisPool) {
54+
this(jedisPool, ENV_DEFAULT, AT_MOST_HOURS_DEFAULT);
55+
}
56+
57+
public JedisLockProvider(JedisPool jedisPool, String environment) {
58+
this(jedisPool, environment, AT_MOST_HOURS_DEFAULT);
59+
}
60+
61+
public JedisLockProvider(JedisPool jedisPool, String environment, long atMostHoursDefault) {
62+
this.jedisPool = jedisPool;
63+
this.environment = environment;
64+
this.atMostHoursDefault = Duration.of(atMostHoursDefault, ChronoUnit.HOURS);
65+
}
66+
67+
@Override
68+
public Optional<SimpleLock> lock(LockConfiguration lockConfiguration) {
69+
Instant now = Instant.now();
70+
71+
// Get 'furthest out' configured expire time for lock TTL
72+
long expireTime = Math.max(
73+
Duration.between(now, lockConfiguration.getLockAtMostUntil()).toMillis(),
74+
Duration.between(now, lockConfiguration.getLockAtLeastUntil()).toMillis());
75+
76+
String key = buildKey(lockConfiguration.getName(), this.environment);
77+
78+
try (Jedis jedis = jedisPool.getResource()) {
79+
String rez = jedis.set(key,
80+
buildValue(),
81+
SET_IF_NOT_EXIST,
82+
SET_EXPIRE_TIME_IN_MS,
83+
expireTime < 0 ? atMostHoursDefault.toMillis() : expireTime); // And if not set, use default
84+
85+
if (rez != null && "OK".equals(rez)) {
86+
return Optional.of(new RedisLock(key, jedisPool, lockConfiguration));
87+
}
88+
}
89+
return Optional.empty();
90+
}
91+
92+
private static final class RedisLock implements SimpleLock {
93+
private final String key;
94+
private final JedisPool jedisPool;
95+
private final LockConfiguration lockConfiguration;
96+
97+
private RedisLock(String key, JedisPool jedisPool, LockConfiguration lockConfiguration) {
98+
this.key = key;
99+
this.jedisPool = jedisPool;
100+
this.lockConfiguration = lockConfiguration;
101+
}
102+
103+
@Override
104+
public void unlock() {
105+
Instant now = Instant.now();
106+
Instant atLeastUntil = lockConfiguration.getLockAtLeastUntil();
107+
108+
if (now.equals(atLeastUntil) || now.isAfter(atLeastUntil)) {
109+
try (Jedis jedis = jedisPool.getResource()) {
110+
jedis.del(key);
111+
} catch (Exception e) {
112+
throw new LockException("Can not remove node", e);
113+
}
114+
}
115+
}
116+
}
117+
118+
protected static String getHostname() {
119+
try {
120+
return InetAddress.getLocalHost().getHostName();
121+
} catch (UnknownHostException e) {
122+
return "unknown host";
123+
}
124+
}
125+
126+
public static String buildKey(String lockName, String env) {
127+
return String.format("%s:%s:%s", KEY_PREFIX, env, lockName);
128+
}
129+
130+
static String buildValue() {
131+
return String.format("ADDED:%s@%s", Instant.now().toString(),getHostname());
132+
}
133+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**
2+
* Copyright 2009-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package net.javacrumbs.shedlock.provider.jedis;
17+
18+
import net.javacrumbs.shedlock.core.LockConfiguration;
19+
import net.javacrumbs.shedlock.core.LockProvider;
20+
import net.javacrumbs.shedlock.core.SimpleLock;
21+
import net.javacrumbs.shedlock.test.support.AbstractLockProviderIntegrationTest;
22+
import org.junit.AfterClass;
23+
import org.junit.Assert;
24+
import org.junit.Before;
25+
import org.junit.BeforeClass;
26+
import redis.clients.jedis.Jedis;
27+
import redis.clients.jedis.JedisPool;
28+
import redis.embedded.RedisServer;
29+
30+
import java.io.IOException;
31+
import java.time.Duration;
32+
import java.util.Optional;
33+
34+
import static java.lang.Thread.sleep;
35+
import static org.assertj.core.api.Assertions.assertThat;
36+
37+
public class JedisLockProviderIntegrationTest extends AbstractLockProviderIntegrationTest {
38+
39+
private static JedisPool jedisPool;
40+
private static RedisServer redisServer;
41+
private LockProvider lockProvider;
42+
43+
private final static int PORT = 6380;
44+
private final static String HOST = "localhost";
45+
private final static String ENV = "test";
46+
47+
@BeforeClass
48+
public static void startRedis() throws IOException {
49+
redisServer = new RedisServer(PORT);
50+
redisServer.start();
51+
}
52+
53+
@AfterClass
54+
public static void stopRedis() {
55+
redisServer.stop();
56+
}
57+
58+
@Before
59+
public void createLockProvider() {
60+
jedisPool = new JedisPool(HOST, PORT);
61+
lockProvider = new JedisLockProvider(jedisPool, ENV);
62+
}
63+
64+
@Override
65+
protected LockProvider getLockProvider() {
66+
return lockProvider;
67+
}
68+
69+
@Override
70+
protected void assertUnlocked(String lockName) {
71+
try (Jedis jedis = jedisPool.getResource()) {
72+
Assert.assertNull(jedis.get(JedisLockProvider.buildKey(lockName, ENV)));
73+
}
74+
}
75+
76+
@Override
77+
protected void assertLocked(String lockName) {
78+
try (Jedis jedis = jedisPool.getResource()) {
79+
Assert.assertNotNull(jedis.get(JedisLockProvider.buildKey(lockName, ENV)));
80+
}
81+
}
82+
83+
@Override
84+
public void shouldTimeout() throws InterruptedException {
85+
LockConfiguration configWithShortTimeout = lockConfig(LOCK_NAME1, 2, Duration.ZERO);
86+
Optional<SimpleLock> lock1 = getLockProvider().lock(configWithShortTimeout);
87+
assertThat(lock1).isNotEmpty();
88+
89+
sleep(5);
90+
91+
// Get new config with updated timeout
92+
configWithShortTimeout = lockConfig(LOCK_NAME1, 2, Duration.ZERO);
93+
assertUnlocked(configWithShortTimeout.getName());
94+
}
95+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!--
2+
3+
Copyright 2009-2017 the original author or authors.
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
17+
-->
18+
<configuration>
19+
20+
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
21+
<encoder>
22+
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
23+
</encoder>
24+
</appender>
25+
26+
<root level="INFO">
27+
<appender-ref ref="STDOUT"/>
28+
</root>
29+
</configuration>

0 commit comments

Comments
 (0)