Skip to content

Commit 9e4e1e0

Browse files
committed
Add redis as a provider
1 parent 4e752cf commit 9e4e1e0

File tree

5 files changed

+253
-0
lines changed

5 files changed

+253
-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-redis</module>
2829
<module>shedlock-test-support</module>
2930
<module>shedlock-test-support-jdbc</module>
3031
<module>shedlock-provider-jdbc-internal</module>

shedlock-provider-redis/pom.xml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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-redis</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+
</dependencies>
34+
35+
</project>
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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.redis;
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.time.Instant;
26+
import java.util.Optional;
27+
28+
/**
29+
* Uses Redis as locking mechanism.
30+
*/
31+
public class RedisLockProvider implements LockProvider {
32+
33+
private static final String keyPrefix = "job-lock:";
34+
35+
private JedisPool jedisPool;
36+
37+
static String buildKey(String lockName) {
38+
return keyPrefix + lockName;
39+
}
40+
41+
public RedisLockProvider(JedisPool jedisPool) {
42+
this.jedisPool = jedisPool;
43+
}
44+
45+
@Override
46+
public Optional<SimpleLock> lock(LockConfiguration lockConfiguration) {
47+
long difference = getDifference(lockConfiguration);
48+
if (difference > 0) {
49+
String key = buildKey(lockConfiguration.getName());
50+
try (Jedis jedis = jedisPool.getResource()) {
51+
String rez = jedis.set(key, "value", "NX", "PX", difference);
52+
if (rez != null && "OK".equals(rez)) {
53+
return Optional.of(new RedisLock(key, jedisPool));
54+
}
55+
}
56+
}
57+
return Optional.empty();
58+
}
59+
60+
long getDifference(LockConfiguration lockConfiguration) {
61+
long now = Instant.now().toEpochMilli();
62+
long mostDiff = lockConfiguration.getLockAtMostUntil().toEpochMilli() - now;
63+
long leastDiff = lockConfiguration.getLockAtLeastUntil().toEpochMilli() - now;
64+
65+
long difference = -1;
66+
if (mostDiff > 0 && leastDiff > 0) {
67+
difference = Math.min(mostDiff, leastDiff);
68+
} else if (mostDiff > 0 && leastDiff <= 0) {
69+
difference = mostDiff;
70+
} else if (mostDiff <= 0 && leastDiff > 0) {
71+
difference = leastDiff;
72+
}
73+
return difference;
74+
}
75+
76+
private static final class RedisLock implements SimpleLock {
77+
private final String key;
78+
private final JedisPool jedisPool;
79+
80+
private RedisLock(String key, JedisPool jedisPool) {
81+
this.key= key;
82+
this.jedisPool = jedisPool;
83+
}
84+
85+
@Override
86+
public void unlock() {
87+
try (Jedis jedis = jedisPool.getResource()){
88+
jedis.del(key);
89+
} catch (Exception e) {
90+
throw new LockException("Can not remove node", e);
91+
}
92+
}
93+
}
94+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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.redis;
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.Assert;
23+
import org.junit.Before;
24+
import redis.clients.jedis.Jedis;
25+
import redis.clients.jedis.JedisPool;
26+
import redis.clients.jedis.JedisPoolConfig;
27+
28+
import java.time.Duration;
29+
import java.util.Optional;
30+
31+
import static java.lang.Thread.sleep;
32+
import static org.assertj.core.api.Assertions.assertThat;
33+
34+
public class RedisLockProviderIntegrationTest extends AbstractLockProviderIntegrationTest {
35+
36+
private static JedisPool jedisPool;
37+
private LockProvider lockProvider;
38+
39+
private final static int PORT = 6379;
40+
41+
@Before
42+
public void createLockProvider() {
43+
jedisPool = new JedisPool(new JedisPoolConfig(), "localhost");
44+
lockProvider = new RedisLockProvider(jedisPool);
45+
}
46+
47+
@Override
48+
protected LockProvider getLockProvider() {
49+
return lockProvider;
50+
}
51+
52+
@Override
53+
protected void assertUnlocked(String lockName) {
54+
try (Jedis jedis = jedisPool.getResource()) {
55+
Assert.assertNull(jedis.get(RedisLockProvider.buildKey(lockName)));
56+
}
57+
}
58+
59+
@Override
60+
protected void assertLocked(String lockName) {
61+
try (Jedis jedis = jedisPool.getResource()) {
62+
Assert.assertNotNull(jedis.get(RedisLockProvider.buildKey(lockName)));
63+
}
64+
}
65+
66+
@Override
67+
public void shouldTimeout() throws InterruptedException {
68+
LockConfiguration configWithShortTimeout = lockConfig(LOCK_NAME1, 2, Duration.ZERO);
69+
Optional<SimpleLock> lock1 = getLockProvider().lock(configWithShortTimeout);
70+
assertThat(lock1).isNotEmpty();
71+
72+
sleep(5);
73+
74+
assertUnlocked(configWithShortTimeout.getName());
75+
}
76+
77+
@Override
78+
public void shouldLockAtLeastFor() throws InterruptedException {
79+
LockConfiguration configWithGracePeriod = lockConfig(LOCK_NAME1, 0, LOCK_AT_LEAST_FOR);
80+
Optional<SimpleLock> lock1 = getLockProvider().lock(configWithGracePeriod);
81+
assertThat(lock1).isNotEmpty();
82+
83+
// can not acquire lock, grace period did not pass yet
84+
configWithGracePeriod = lockConfig(LOCK_NAME1, 0, LOCK_AT_LEAST_FOR);
85+
Optional<SimpleLock> lock2 = getLockProvider().lock(configWithGracePeriod);
86+
assertThat(lock2).isEmpty();
87+
88+
sleep(LOCK_AT_LEAST_FOR.toMillis());
89+
configWithGracePeriod = lockConfig(LOCK_NAME1, 0, LOCK_AT_LEAST_FOR);
90+
Optional<SimpleLock> lock3 = getLockProvider().lock(configWithGracePeriod);
91+
assertThat(lock3).isNotEmpty();
92+
lock3.get().unlock();
93+
}
94+
}
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)