Skip to content

Commit df1820c

Browse files
committed
Now SCRIPT FLUSH, etc. does not makes consequent executions fail
1 parent dd080ae commit df1820c

File tree

5 files changed

+85
-35
lines changed

5 files changed

+85
-35
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ However, some latest Redis APIs are not implemented (for example, Redis Streams
77
This small library tries to implement some missing functions. The approach is very simple: every command is implemented as Lua script. BWT it would be obviously better to get native support (at least of stream commands) in Jedis.
88

99
### Release notes
10+
##### v 0.0.2
11+
- Now `SCRIPT FLUSH`, etc. does not makes consequent executions fail.
12+
1013
##### v 0.0.1
1114
- Implemented batch `XADD` command.
1215
- Implemented `XDEL` command.

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
group 'tech.javajefe'
2-
version '0.0.1'
2+
version '0.0.2'
33

44
apply plugin: 'groovy'
55
apply plugin: 'java'

src/main/java/tech/javajefe/redis/jedis/extensions/RedisExtensions.java

Lines changed: 50 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
import org.slf4j.Logger;
55
import org.slf4j.LoggerFactory;
66
import redis.clients.jedis.commands.ScriptingCommands;
7+
import redis.clients.jedis.exceptions.JedisNoScriptException;
78

89
import java.io.IOException;
910
import java.util.*;
11+
import java.util.concurrent.ConcurrentHashMap;
1012
import java.util.stream.Collectors;
1113

1214
/**
@@ -15,36 +17,60 @@
1517
public class RedisExtensions {
1618

1719
private static final Logger log = LoggerFactory.getLogger(RedisExtensions.class);
18-
private enum SCRIPTS {
1920

20-
executeArbitraryCommand("/lua/executeArbitraryCommand.lua"),
21-
batchXADD("/lua/batchXADD.lua");
22-
23-
private String path;
24-
25-
SCRIPTS(String path) {
26-
this.path = path;
27-
}
28-
29-
String getPath() {
30-
return this.path;
31-
}
32-
}
3321
private final Gson gson;
3422
private final ScriptingCommands client;
35-
private final Map<SCRIPTS, String> loadedScriptIds;
23+
private final Map<Scripts, String> loadedScriptIds;
3624

3725
public RedisExtensions(ScriptingCommands client) throws IOException {
3826
this.gson = new Gson();
3927
this.client = client;
40-
Map<SCRIPTS, String> scripts = new HashMap<>();
41-
for (SCRIPTS scriptMeta: SCRIPTS.values()) {
42-
String lua = Utils.loadResource(scriptMeta.getPath());
43-
String sha = client.scriptLoad(lua);
44-
log.debug("Loaded script {} with sha marker {}", sha);
45-
scripts.put(scriptMeta, sha);
28+
Map<Scripts, String> scripts = new ConcurrentHashMap<>();
29+
for (Scripts scriptMeta: Scripts.values()) {
30+
scripts.put(scriptMeta, loadScript(scriptMeta));
31+
}
32+
this.loadedScriptIds = scripts;
33+
}
34+
35+
public Object executeArbitraryCommand(String...params) {
36+
return executeArbitraryCommand(Arrays.asList(params));
37+
}
38+
39+
private Object executeArbitraryCommand(List<String> params) {
40+
return evalShaWithRetry(Scripts.executeArbitraryCommand, Collections.EMPTY_LIST, params);
41+
}
42+
43+
private String loadScript(Scripts scriptMeta) throws IOException {
44+
String lua = Utils.loadResource(scriptMeta.getPath());
45+
String sha = client.scriptLoad(lua);
46+
log.debug("Loaded script {} with sha marker {}", scriptMeta.getPath(), sha);
47+
return sha;
48+
}
49+
50+
private Object evalShaWithRetry(Scripts script, List<String> keys, List<String> args) {
51+
return evalShaWithRetry(script, keys, args, true);
52+
}
53+
54+
private Object evalShaWithRetry(Scripts script, List<String> keys, List<String> args, boolean firstAttempt) {
55+
Object result = null;
56+
String sha = loadedScriptIds.get(script);
57+
try {
58+
result = client.evalsha(sha, keys, args);
59+
} catch (JedisNoScriptException ex) {
60+
if (firstAttempt) {
61+
log.warn("No script {} with sha marker {}. Trying to reload.", script.getPath(), sha);
62+
try {
63+
String newSha = loadScript(script);
64+
loadedScriptIds.put(script, newSha);
65+
result = evalShaWithRetry(script, keys, args, false);
66+
} catch (IOException ioex) {
67+
throw ex;
68+
}
69+
} else {
70+
throw ex;
71+
}
4672
}
47-
this.loadedScriptIds = Collections.unmodifiableMap(scripts);
73+
return result;
4874
}
4975

5076
public List<StreamMessageId> batchXADD(String key, List<Map<String, String>> messages) {
@@ -57,24 +83,14 @@ public List<StreamMessageId> batchXADD(String key, List<Map<String, String>> mes
5783
if (messages.stream().flatMap(m -> m.values().stream()).anyMatch(v -> v == null)) {
5884
throw new IllegalArgumentException("null values are disallowed");
5985
}
60-
String sha = loadedScriptIds.get(SCRIPTS.batchXADD);
6186
List<String> argv = messages.stream()
6287
.map(m -> gson.toJson(m))
6388
.collect(Collectors.toList());
64-
List<String> ids = (List<String>) client.evalsha(sha, Collections.singletonList(key), argv);
89+
List<String> ids = (List<String>) evalShaWithRetry(Scripts.batchXADD,
90+
Collections.singletonList(key), argv);
6591
return ids.stream().map(StreamMessageId::from).collect(Collectors.toList());
6692
}
6793

68-
public Object executeArbitraryCommand(String...params) {
69-
String sha = loadedScriptIds.get(SCRIPTS.executeArbitraryCommand);
70-
return client.evalsha(sha, 0, params);
71-
}
72-
73-
public Object executeArbitraryCommand(List<String> params) {
74-
String sha = loadedScriptIds.get(SCRIPTS.executeArbitraryCommand);
75-
return client.evalsha(sha, Collections.EMPTY_LIST, params);
76-
}
77-
7894
public long XDEL(String key, List<StreamMessageId> ids) {
7995
// TODO
8096
List<String> params = ids.stream()
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package tech.javajefe.redis.jedis.extensions;
2+
3+
/**
4+
* Created by Alexander Bukarev on 07.03.2019.
5+
*/
6+
enum Scripts {
7+
8+
executeArbitraryCommand("/lua/executeArbitraryCommand.lua"),
9+
batchXADD("/lua/batchXADD.lua");
10+
11+
private String path;
12+
13+
Scripts(String path) {
14+
this.path = path;
15+
}
16+
17+
String getPath() {
18+
return this.path;
19+
}
20+
}

src/test/groovy/tech/javajefe/redis/jedis/extensions/RedisExtensionsTests.groovy

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,17 @@ class RedisExtensionsTests extends Specification {
3434
jedis.close()
3535
}
3636

37+
def "All commands stay operatable after FLUSHALL / FLUSHDB / SCRIPT FLUSH / restart / etc."() {
38+
when:
39+
redisExtensions.batchXADD(streamName, [[i: 1 as String]])
40+
jedis.scriptFlush()
41+
redisExtensions.batchXADD(streamName, [[i: 2 as String]])
42+
def streamSize = redisExtensions.XLEN(streamName)
43+
then:
44+
notThrown(Throwable)
45+
streamSize == 2
46+
}
47+
3748
def "Empty key is not allowed in Batch XADD"() {
3849
when:
3950
redisExtensions.batchXADD('', [[k: 'v']])

0 commit comments

Comments
 (0)