From d1fb17ce3021f8bac135aef3fd306d62c877c13c Mon Sep 17 00:00:00 2001 From: M Sazzadul Hoque Date: Sun, 3 Jun 2018 20:13:14 +0600 Subject: [PATCH] RESTORE with REPLACE option implementation (#1743) --- hbase-formatter.xml | 2 +- .../redis/clients/jedis/BinaryClient.java | 4 ++ .../java/redis/clients/jedis/BinaryJedis.java | 7 +++ .../clients/jedis/BinaryShardedJedis.java | 6 ++ src/main/java/redis/clients/jedis/Client.java | 7 +++ src/main/java/redis/clients/jedis/Jedis.java | 7 +++ .../redis/clients/jedis/PipelineBase.java | 16 ++++++ .../java/redis/clients/jedis/Protocol.java | 57 ++++++++++--------- .../redis/clients/jedis/ShardedJedis.java | 6 ++ .../jedis/commands/BinaryJedisCommands.java | 2 + .../jedis/commands/BinaryRedisPipeline.java | 6 ++ .../clients/jedis/commands/Commands.java | 6 ++ .../clients/jedis/commands/JedisCommands.java | 2 + .../clients/jedis/commands/RedisPipeline.java | 6 ++ .../commands/AllKindOfValuesCommandsTest.java | 39 ++++++++++++- 15 files changed, 144 insertions(+), 29 deletions(-) diff --git a/hbase-formatter.xml b/hbase-formatter.xml index 0a3f8d133d..cf59372fa5 100644 --- a/hbase-formatter.xml +++ b/hbase-formatter.xml @@ -255,7 +255,7 @@ - + diff --git a/src/main/java/redis/clients/jedis/BinaryClient.java b/src/main/java/redis/clients/jedis/BinaryClient.java index b35034ebc9..c15ebeecbc 100644 --- a/src/main/java/redis/clients/jedis/BinaryClient.java +++ b/src/main/java/redis/clients/jedis/BinaryClient.java @@ -997,6 +997,10 @@ public void restore(final byte[] key, final int ttl, final byte[] serializedValu sendCommand(RESTORE, key, toByteArray(ttl), serializedValue); } + public void restoreReplace(final byte[] key, final int ttl, final byte[] serializedValue) { + sendCommand(RESTORE, key, toByteArray(ttl), serializedValue, Keyword.REPLACE.raw); + } + public void pexpire(final byte[] key, final long milliseconds) { sendCommand(PEXPIRE, key, toByteArray(milliseconds)); } diff --git a/src/main/java/redis/clients/jedis/BinaryJedis.java b/src/main/java/redis/clients/jedis/BinaryJedis.java index 5c08aa07af..275cfd81d5 100644 --- a/src/main/java/redis/clients/jedis/BinaryJedis.java +++ b/src/main/java/redis/clients/jedis/BinaryJedis.java @@ -3441,6 +3441,13 @@ public String restore(final byte[] key, final int ttl, final byte[] serializedVa return client.getStatusCodeReply(); } + @Override + public String restoreReplace(final byte[] key, final int ttl, final byte[] serializedValue) { + checkIsInMultiOrPipeline(); + client.restoreReplace(key, ttl, serializedValue); + return client.getStatusCodeReply(); + } + /** * Set a timeout on the specified key. After the timeout the key will be automatically deleted by * the server. A key with an associated timeout is said to be volatile in Redis terminology. diff --git a/src/main/java/redis/clients/jedis/BinaryShardedJedis.java b/src/main/java/redis/clients/jedis/BinaryShardedJedis.java index 8224dbf71f..9d0015cb7b 100644 --- a/src/main/java/redis/clients/jedis/BinaryShardedJedis.java +++ b/src/main/java/redis/clients/jedis/BinaryShardedJedis.java @@ -96,6 +96,12 @@ public String restore(final byte[] key, final int ttl, final byte[] serializedVa return j.restore(key, ttl, serializedValue); } + @Override + public String restoreReplace(final byte[] key, final int ttl, final byte[] serializedValue) { + Jedis j = getShard(key); + return j.restoreReplace(key, ttl, serializedValue); + } + @Override public Long expire(final byte[] key, final int seconds) { Jedis j = getShard(key); diff --git a/src/main/java/redis/clients/jedis/Client.java b/src/main/java/redis/clients/jedis/Client.java index 6488c51b6d..39a2999fd9 100644 --- a/src/main/java/redis/clients/jedis/Client.java +++ b/src/main/java/redis/clients/jedis/Client.java @@ -839,14 +839,21 @@ public void sentinel(final String... args) { sentinel(SafeEncoder.encodeMany(args)); } + @Override public void dump(final String key) { dump(SafeEncoder.encode(key)); } + @Override public void restore(final String key, final int ttl, final byte[] serializedValue) { restore(SafeEncoder.encode(key), ttl, serializedValue); } + @Override + public void restoreReplace(final String key, final int ttl, final byte[] serializedValue) { + restoreReplace(SafeEncoder.encode(key), ttl, serializedValue); + } + public void pexpire(final String key, final long milliseconds) { pexpire(SafeEncoder.encode(key), milliseconds); } diff --git a/src/main/java/redis/clients/jedis/Jedis.java b/src/main/java/redis/clients/jedis/Jedis.java index 69d4620b0e..fd82b516b1 100644 --- a/src/main/java/redis/clients/jedis/Jedis.java +++ b/src/main/java/redis/clients/jedis/Jedis.java @@ -3110,6 +3110,13 @@ public String restore(final String key, final int ttl, final byte[] serializedVa return client.getStatusCodeReply(); } + @Override + public String restoreReplace(final String key, final int ttl, final byte[] serializedValue) { + checkIsInMultiOrPipeline(); + client.restoreReplace(key, ttl, serializedValue); + return client.getStatusCodeReply(); + } + @Override public Long pexpire(final String key, final long milliseconds) { checkIsInMultiOrPipeline(); diff --git a/src/main/java/redis/clients/jedis/PipelineBase.java b/src/main/java/redis/clients/jedis/PipelineBase.java index c2fbb92e28..34ca5fde60 100644 --- a/src/main/java/redis/clients/jedis/PipelineBase.java +++ b/src/main/java/redis/clients/jedis/PipelineBase.java @@ -1389,11 +1389,13 @@ public Response bitcount(final byte[] key, final long start, final long en return getResponse(BuilderFactory.LONG); } + @Override public Response dump(final String key) { getClient(key).dump(key); return getResponse(BuilderFactory.BYTE_ARRAY); } + @Override public Response dump(final byte[] key) { getClient(key).dump(key); return getResponse(BuilderFactory.BYTE_ARRAY); @@ -1473,16 +1475,30 @@ public Response pttl(final byte[] key) { return getResponse(BuilderFactory.LONG); } + @Override public Response restore(final String key, final int ttl, final byte[] serializedValue) { getClient(key).restore(key, ttl, serializedValue); return getResponse(BuilderFactory.STRING); } + @Override public Response restore(final byte[] key, final int ttl, final byte[] serializedValue) { getClient(key).restore(key, ttl, serializedValue); return getResponse(BuilderFactory.STRING); } + @Override + public Response restoreReplace(final String key, final int ttl, final byte[] serializedValue) { + getClient(key).restoreReplace(key, ttl, serializedValue); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response restoreReplace(final byte[] key, final int ttl, final byte[] serializedValue) { + getClient(key).restoreReplace(key, ttl, serializedValue); + return getResponse(BuilderFactory.STRING); + } + public Response incrByFloat(final String key, final double increment) { getClient(key).incrByFloat(key, increment); return getResponse(BuilderFactory.DOUBLE); diff --git a/src/main/java/redis/clients/jedis/Protocol.java b/src/main/java/redis/clients/jedis/Protocol.java index d009573739..684fe461e7 100644 --- a/src/main/java/redis/clients/jedis/Protocol.java +++ b/src/main/java/redis/clients/jedis/Protocol.java @@ -19,11 +19,11 @@ public final class Protocol { - private static final String ASK_RESPONSE = "ASK"; - private static final String MOVED_RESPONSE = "MOVED"; - private static final String CLUSTERDOWN_RESPONSE = "CLUSTERDOWN"; - private static final String BUSY_RESPONSE = "BUSY"; - private static final String NOSCRIPT_RESPONSE = "NOSCRIPT"; + private static final String ASK_PREFIX = "ASK "; + private static final String MOVED_PREFIX = "MOVED "; + private static final String CLUSTERDOWN_PREFIX = "CLUSTERDOWN "; + private static final String BUSY_PREFIX = "BUSY "; + private static final String NOSCRIPT_PREFIX = "NOSCRIPT "; public static final String DEFAULT_HOST = "localhost"; public static final int DEFAULT_PORT = 6379; @@ -113,19 +113,19 @@ private static void processError(final RedisInputStream is) { String message = is.readLine(); // TODO: I'm not sure if this is the best way to do this. // Maybe Read only first 5 bytes instead? - if (message.startsWith(MOVED_RESPONSE)) { + if (message.startsWith(MOVED_PREFIX)) { String[] movedInfo = parseTargetHostAndSlot(message); throw new JedisMovedDataException(message, new HostAndPort(movedInfo[1], Integer.parseInt(movedInfo[2])), Integer.parseInt(movedInfo[0])); - } else if (message.startsWith(ASK_RESPONSE)) { + } else if (message.startsWith(ASK_PREFIX)) { String[] askInfo = parseTargetHostAndSlot(message); throw new JedisAskDataException(message, new HostAndPort(askInfo[1], Integer.parseInt(askInfo[2])), Integer.parseInt(askInfo[0])); - } else if (message.startsWith(CLUSTERDOWN_RESPONSE)) { + } else if (message.startsWith(CLUSTERDOWN_PREFIX)) { throw new JedisClusterException(message); - } else if (message.startsWith(BUSY_RESPONSE)) { + } else if (message.startsWith(BUSY_PREFIX)) { throw new JedisBusyException(message); - } else if (message.startsWith(NOSCRIPT_RESPONSE) ) { + } else if (message.startsWith(NOSCRIPT_PREFIX) ) { throw new JedisNoScriptException(message); } throw new JedisDataException(message); @@ -242,21 +242,22 @@ public static final byte[] toByteArray(final double value) { } public static enum Command implements ProtocolCommand { - PING, SET, GET, QUIT, EXISTS, DEL, UNLINK, TYPE, FLUSHDB, KEYS, RANDOMKEY, RENAME, RENAMENX, RENAMEX, - DBSIZE, EXPIRE, EXPIREAT, TTL, SELECT, MOVE, FLUSHALL, GETSET, MGET, SETNX, SETEX, MSET, MSETNX, - DECRBY, DECR, INCRBY, INCR, APPEND, SUBSTR, HSET, HGET, HSETNX, HMSET, HMGET, HINCRBY, HEXISTS, - HDEL, HLEN, HKEYS, HVALS, HGETALL, RPUSH, LPUSH, LLEN, LRANGE, LTRIM, LINDEX, LSET, LREM, LPOP, RPOP, - RPOPLPUSH, SADD, SMEMBERS, SREM, SPOP, SMOVE, SCARD, SISMEMBER, SINTER, SINTERSTORE, SUNION, - SUNIONSTORE, SDIFF, SDIFFSTORE, SRANDMEMBER, ZADD, ZRANGE, ZREM, ZINCRBY, ZRANK, ZREVRANK, - ZREVRANGE, ZCARD, ZSCORE, MULTI, DISCARD, EXEC, WATCH, UNWATCH, SORT, BLPOP, BRPOP, AUTH, - SUBSCRIBE, PUBLISH, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBSUB, ZCOUNT, ZRANGEBYSCORE, - ZREVRANGEBYSCORE, ZREMRANGEBYRANK, ZREMRANGEBYSCORE, ZUNIONSTORE, ZINTERSTORE, ZLEXCOUNT, - ZRANGEBYLEX, ZREVRANGEBYLEX, ZREMRANGEBYLEX, SAVE, BGSAVE, BGREWRITEAOF, LASTSAVE, SHUTDOWN, - INFO, MONITOR, SLAVEOF, CONFIG, STRLEN, SYNC, LPUSHX, PERSIST, RPUSHX, ECHO, LINSERT, DEBUG, BRPOPLPUSH, - SETBIT, GETBIT, BITPOS, SETRANGE, GETRANGE, EVAL, EVALSHA, SCRIPT, SLOWLOG, OBJECT, BITCOUNT, BITOP, - SENTINEL, DUMP, RESTORE, PEXPIRE, PEXPIREAT, PTTL, INCRBYFLOAT, PSETEX, CLIENT, TIME, MIGRATE, HINCRBYFLOAT, - SCAN, HSCAN, SSCAN, ZSCAN, WAIT, CLUSTER, ASKING, PFADD, PFCOUNT, PFMERGE, READONLY, GEOADD, GEODIST, - GEOHASH, GEOPOS, GEORADIUS, GEORADIUSBYMEMBER, MODULE, BITFIELD, HSTRLEN, TOUCH, SWAPDB; + PING, SET, GET, QUIT, EXISTS, DEL, UNLINK, TYPE, FLUSHDB, KEYS, RANDOMKEY, RENAME, RENAMENX, + RENAMEX, DBSIZE, EXPIRE, EXPIREAT, TTL, SELECT, MOVE, FLUSHALL, GETSET, MGET, SETNX, SETEX, + MSET, MSETNX, DECRBY, DECR, INCRBY, INCR, APPEND, SUBSTR, HSET, HGET, HSETNX, HMSET, HMGET, + HINCRBY, HEXISTS, HDEL, HLEN, HKEYS, HVALS, HGETALL, RPUSH, LPUSH, LLEN, LRANGE, LTRIM, LINDEX, + LSET, LREM, LPOP, RPOP, RPOPLPUSH, SADD, SMEMBERS, SREM, SPOP, SMOVE, SCARD, SISMEMBER, SINTER, + SINTERSTORE, SUNION, SUNIONSTORE, SDIFF, SDIFFSTORE, SRANDMEMBER, ZADD, ZRANGE, ZREM, ZINCRBY, + ZRANK, ZREVRANK, ZREVRANGE, ZCARD, ZSCORE, MULTI, DISCARD, EXEC, WATCH, UNWATCH, SORT, BLPOP, + BRPOP, AUTH, SUBSCRIBE, PUBLISH, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBSUB, ZCOUNT, + ZRANGEBYSCORE, ZREVRANGEBYSCORE, ZREMRANGEBYRANK, ZREMRANGEBYSCORE, ZUNIONSTORE, ZINTERSTORE, + ZLEXCOUNT, ZRANGEBYLEX, ZREVRANGEBYLEX, ZREMRANGEBYLEX, SAVE, BGSAVE, BGREWRITEAOF, LASTSAVE, + SHUTDOWN, INFO, MONITOR, SLAVEOF, CONFIG, STRLEN, SYNC, LPUSHX, PERSIST, RPUSHX, ECHO, LINSERT, + DEBUG, BRPOPLPUSH, SETBIT, GETBIT, BITPOS, SETRANGE, GETRANGE, EVAL, EVALSHA, SCRIPT, SLOWLOG, + OBJECT, BITCOUNT, BITOP, SENTINEL, DUMP, RESTORE, PEXPIRE, PEXPIREAT, PTTL, INCRBYFLOAT, + PSETEX, CLIENT, TIME, MIGRATE, HINCRBYFLOAT, SCAN, HSCAN, SSCAN, ZSCAN, WAIT, CLUSTER, ASKING, + PFADD, PFCOUNT, PFMERGE, READONLY, GEOADD, GEODIST, GEOHASH, GEOPOS, GEORADIUS, + GEORADIUSBYMEMBER, MODULE, BITFIELD, HSTRLEN, TOUCH, SWAPDB; private final byte[] raw; @@ -271,7 +272,11 @@ public byte[] getRaw() { } public static enum Keyword { - AGGREGATE, ALPHA, ASC, BY, DESC, GET, LIMIT, MESSAGE, NO, NOSORT, PMESSAGE, PSUBSCRIBE, PUNSUBSCRIBE, OK, ONE, QUEUED, SET, STORE, SUBSCRIBE, UNSUBSCRIBE, WEIGHTS, WITHSCORES, RESETSTAT, REWRITE, RESET, FLUSH, EXISTS, LOAD, KILL, LEN, REFCOUNT, ENCODING, IDLETIME, GETNAME, SETNAME, LIST, MATCH, COUNT, PING, PONG, UNLOAD; + AGGREGATE, ALPHA, ASC, BY, DESC, GET, LIMIT, MESSAGE, NO, NOSORT, PMESSAGE, PSUBSCRIBE, + PUNSUBSCRIBE, OK, ONE, QUEUED, SET, STORE, SUBSCRIBE, UNSUBSCRIBE, WEIGHTS, WITHSCORES, + RESETSTAT, REWRITE, RESET, FLUSH, EXISTS, LOAD, KILL, LEN, REFCOUNT, ENCODING, IDLETIME, + GETNAME, SETNAME, LIST, MATCH, COUNT, PING, PONG, UNLOAD, REPLACE; + public final byte[] raw; Keyword() { diff --git a/src/main/java/redis/clients/jedis/ShardedJedis.java b/src/main/java/redis/clients/jedis/ShardedJedis.java index b80eb589e4..4b09992116 100644 --- a/src/main/java/redis/clients/jedis/ShardedJedis.java +++ b/src/main/java/redis/clients/jedis/ShardedJedis.java @@ -82,6 +82,12 @@ public String restore(final String key, final int ttl, final byte[] serializedVa return j.restore(key, ttl, serializedValue); } + @Override + public String restoreReplace(final String key, final int ttl, final byte[] serializedValue) { + Jedis j = getShard(key); + return j.restoreReplace(key, ttl, serializedValue); + } + @Override public Long expire(final String key, final int seconds) { Jedis j = getShard(key); diff --git a/src/main/java/redis/clients/jedis/commands/BinaryJedisCommands.java b/src/main/java/redis/clients/jedis/commands/BinaryJedisCommands.java index 34160b70d7..85129de72f 100644 --- a/src/main/java/redis/clients/jedis/commands/BinaryJedisCommands.java +++ b/src/main/java/redis/clients/jedis/commands/BinaryJedisCommands.java @@ -31,6 +31,8 @@ public interface BinaryJedisCommands { String restore(byte[] key, int ttl, byte[] serializedValue); + String restoreReplace(byte[] key, int ttl, byte[] serializedValue); + Long expire(byte[] key, int seconds); Long pexpire(byte[] key, long milliseconds); diff --git a/src/main/java/redis/clients/jedis/commands/BinaryRedisPipeline.java b/src/main/java/redis/clients/jedis/commands/BinaryRedisPipeline.java index 4391ae1c87..4f24d0362e 100644 --- a/src/main/java/redis/clients/jedis/commands/BinaryRedisPipeline.java +++ b/src/main/java/redis/clients/jedis/commands/BinaryRedisPipeline.java @@ -247,6 +247,12 @@ Response> zrevrangeByLex(byte[] key, byte[] max, byte[] min, Response pfcount(byte[] key); + Response dump(byte[] key); + + Response restore(byte[] key, int ttl, byte[] serializedValue); + + Response restoreReplace(byte[] key, int ttl, byte[] serializedValue); + // Geo Commands Response geoadd(byte[] key, double longitude, double latitude, byte[] member); diff --git a/src/main/java/redis/clients/jedis/commands/Commands.java b/src/main/java/redis/clients/jedis/commands/Commands.java index 658f23523c..7e69e7bab4 100644 --- a/src/main/java/redis/clients/jedis/commands/Commands.java +++ b/src/main/java/redis/clients/jedis/commands/Commands.java @@ -307,6 +307,12 @@ void zrevrangeByScoreWithScores(String key, String max, String min, void bitop(BitOP op, String destKey, String... srcKeys); + void dump(String key); + + void restore(String key, int ttl, byte[] serializedValue); + + void restoreReplace(String key, int ttl, byte[] serializedValue); + void scan(String cursor, ScanParams params); void hscan(String key, String cursor, ScanParams params); diff --git a/src/main/java/redis/clients/jedis/commands/JedisCommands.java b/src/main/java/redis/clients/jedis/commands/JedisCommands.java index 80467386f5..387e9e5eb7 100644 --- a/src/main/java/redis/clients/jedis/commands/JedisCommands.java +++ b/src/main/java/redis/clients/jedis/commands/JedisCommands.java @@ -30,6 +30,8 @@ public interface JedisCommands { String restore(String key, int ttl, byte[] serializedValue); + String restoreReplace(String key, int ttl, byte[] serializedValue); + Long expire(String key, int seconds); Long pexpire(String key, long milliseconds); diff --git a/src/main/java/redis/clients/jedis/commands/RedisPipeline.java b/src/main/java/redis/clients/jedis/commands/RedisPipeline.java index 9859c718d8..59da41a1fd 100644 --- a/src/main/java/redis/clients/jedis/commands/RedisPipeline.java +++ b/src/main/java/redis/clients/jedis/commands/RedisPipeline.java @@ -243,6 +243,12 @@ Response> zrevrangeByLex(String key, String max, String min, Response hstrlen(String key, String field); + Response dump(String key); + + Response restore(String key, int ttl, byte[] serializedValue); + + Response restoreReplace(String key, int ttl, byte[] serializedValue); + // Geo Commands Response geoadd(String key, double longitude, double latitude, String member); diff --git a/src/test/java/redis/clients/jedis/tests/commands/AllKindOfValuesCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/AllKindOfValuesCommandsTest.java index 942e35da3c..d19f4e9e22 100644 --- a/src/test/java/redis/clients/jedis/tests/commands/AllKindOfValuesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/tests/commands/AllKindOfValuesCommandsTest.java @@ -6,20 +6,25 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; import static redis.clients.jedis.ScanParams.SCAN_POINTER_START; import static redis.clients.jedis.ScanParams.SCAN_POINTER_START_BINARY; import static redis.clients.jedis.params.SetParams.setParams; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; import org.junit.Test; +import redis.clients.jedis.Jedis; import redis.clients.jedis.Protocol.Keyword; import redis.clients.jedis.ScanParams; import redis.clients.jedis.ScanResult; import redis.clients.jedis.util.SafeEncoder; +import redis.clients.jedis.exceptions.JedisDataException; public class AllKindOfValuesCommandsTest extends JedisCommandTestBase { final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; @@ -609,10 +614,40 @@ public void echo() { @Test public void dumpAndRestore() { - jedis.set("foo1", "bar1"); + jedis.set("foo1", "bar"); byte[] sv = jedis.dump("foo1"); jedis.restore("foo2", 0, sv); - assertTrue(jedis.exists("foo2")); + assertEquals("bar", jedis.get("foo2")); + } + + @Test + public void restoreReplace() { + // take a separate instance + Jedis jedis2 = new Jedis(hnp.getHost(), 6380, 500); + jedis2.auth("foobared"); + jedis2.flushAll(); + + jedis2.set("foo", "bar"); + + Map map = new HashMap(); + map.put("a", "A"); + map.put("b", "B"); + + jedis.hset("from", map); + byte[] serialized = jedis.dump("from"); + + try { + jedis2.restore("foo", 0, serialized); + fail("Simple restore on a existing key should fail"); + } catch(JedisDataException e) { + // should be here + } + assertEquals("bar", jedis2.get("foo")); + + jedis2.restoreReplace("foo", 0, serialized); + assertEquals(map, jedis2.hgetAll("foo")); + + jedis2.close(); } @Test