From f2e346bc187edc3616a6bd533dd65b8967ca3b0d Mon Sep 17 00:00:00 2001 From: dobi Date: Fri, 21 Apr 2017 10:00:33 +0900 Subject: [PATCH] fix #849 --- .../java/org/redisson/RedissonMapCache.java | 72 +++++++++++++------ .../org/redisson/RedissonMapCacheTest.java | 7 +- 2 files changed, 58 insertions(+), 21 deletions(-) diff --git a/redisson/src/main/java/org/redisson/RedissonMapCache.java b/redisson/src/main/java/org/redisson/RedissonMapCache.java index 546082ce8..1ba958070 100644 --- a/redisson/src/main/java/org/redisson/RedissonMapCache.java +++ b/redisson/src/main/java/org/redisson/RedissonMapCache.java @@ -81,6 +81,7 @@ public class RedissonMapCache extends RedissonMap implements RMapCac private static final RedisCommand EVAL_REMOVE = new RedisCommand("EVAL", 4, ValueType.MAP_KEY, ValueType.MAP_VALUE); private static final RedisCommand EVAL_REMOVE_VALUE = new RedisCommand("EVAL", new BooleanReplayConvertor(), 5, ValueType.MAP); private static final RedisCommand EVAL_PUT_TTL = new RedisCommand("EVAL", 9, ValueType.MAP, ValueType.MAP_VALUE); + private static final RedisCommand EVAL_PUT_TTL_IF_ABSENT = new RedisCommand("EVAL", 10, ValueType.MAP, ValueType.MAP_VALUE); private static final RedisCommand EVAL_FAST_PUT_TTL = new RedisCommand("EVAL", new BooleanReplayConvertor(), 9, ValueType.MAP, ValueType.MAP_VALUE); private static final RedisCommand EVAL_FAST_PUT_TTL_IF_ABSENT = new RedisCommand("EVAL", new BooleanReplayConvertor(), 10, ValueType.MAP, ValueType.MAP_VALUE); private static final RedisCommand EVAL_GET_TTL = new RedisCommand("EVAL", 7, ValueType.MAP_KEY, ValueType.MAP_VALUE); @@ -269,26 +270,57 @@ public class RedissonMapCache extends RedissonMap implements RMapCac maxIdleTimeout = System.currentTimeMillis() + maxIdleDelta; } - return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_PUT_TTL, - "if redis.call('hexists', KEYS[1], ARGV[4]) == 0 then " - + "if tonumber(ARGV[1]) > 0 then " - + "redis.call('zadd', KEYS[2], ARGV[1], ARGV[4]); " - + "end; " - + "if tonumber(ARGV[2]) > 0 then " - + "redis.call('zadd', KEYS[3], ARGV[2], ARGV[4]); " - + "end; " - + "local value = struct.pack('dLc0', ARGV[3], string.len(ARGV[5]), ARGV[5]); " - + "redis.call('hset', KEYS[1], ARGV[4], value); " - + "return nil; " - + "else " - + "local value = redis.call('hget', KEYS[1], ARGV[4]); " - + "if value == false then " - + "return nil; " - + "end;" - + "local t, val = struct.unpack('dLc0', value); " - + "return val; " - + "end", - Arrays.asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key)), ttlTimeout, maxIdleTimeout, maxIdleDelta, key, value); + return commandExecutor.evalWriteAsync(getName(key), codec, EVAL_PUT_TTL_IF_ABSENT, + "local insertable = false; " + + "local value = redis.call('hget', KEYS[1], ARGV[5]); " + + "if value == false then " + + "insertable = true; " + + "else " + + "if insertable == false then " + + "local t, val = struct.unpack('dLc0', value); " + + "local expireDate = 92233720368547758; " + + "local expireDateScore = redis.call('zscore', KEYS[2], ARGV[5]); " + + "if expireDateScore ~= false then " + + "expireDate = tonumber(expireDateScore) " + + "end; " + + "if t ~= 0 then " + + "local expireIdle = redis.call('zscore', KEYS[3], ARGV[5]); " + + "if expireIdle ~= false then " + + "expireDate = math.min(expireDate, tonumber(expireIdle)) " + + "end; " + + "end; " + + "if expireDate <= tonumber(ARGV[1]) then " + + "insertable = true; " + + "end; " + + "end; " + + "end; " + + + "if insertable == true then " + // ttl + + "if tonumber(ARGV[2]) > 0 then " + + "redis.call('zadd', KEYS[2], ARGV[2], ARGV[5]); " + + "else " + + "redis.call('zrem', KEYS[2], ARGV[5]); " + + "end; " + + // idle + + "if tonumber(ARGV[3]) > 0 then " + + "redis.call('zadd', KEYS[3], ARGV[3], ARGV[5]); " + + "else " + + "redis.call('zrem', KEYS[3], ARGV[5]); " + + "end; " + + // value + + "local val = struct.pack('dLc0', ARGV[4], string.len(ARGV[6]), ARGV[6]); " + + "redis.call('hset', KEYS[1], ARGV[5], val); " + + + "return nil;" + + "else " + + "local t, val = struct.unpack('dLc0', value); " + + "redis.call('zadd', KEYS[3], t + ARGV[1], ARGV[5]); " + + "return val;" + + "end; ", + Arrays.asList(getName(key), getTimeoutSetNameByKey(key), getIdleSetNameByKey(key)), System.currentTimeMillis(), ttlTimeout, maxIdleTimeout, maxIdleDelta, key, value); } @Override diff --git a/redisson/src/test/java/org/redisson/RedissonMapCacheTest.java b/redisson/src/test/java/org/redisson/RedissonMapCacheTest.java index 6a3c095db..828fbee24 100644 --- a/redisson/src/test/java/org/redisson/RedissonMapCacheTest.java +++ b/redisson/src/test/java/org/redisson/RedissonMapCacheTest.java @@ -557,10 +557,15 @@ public class RedissonMapCacheTest extends BaseTest { map.putIfAbsent(new SimpleKey("4"), new SimpleValue("4"), 1, TimeUnit.SECONDS); Assert.assertEquals(new SimpleValue("4"), map.get(new SimpleKey("4"))); - + Thread.sleep(1000); Assert.assertNull(map.get(new SimpleKey("4"))); + + // this should be passed + map.putIfAbsent(new SimpleKey("4"), new SimpleValue("4"), 1, TimeUnit.SECONDS); + Assert.assertEquals(new SimpleValue("4"), map.get(new SimpleKey("4"))); + SimpleKey key1 = new SimpleKey("2"); SimpleValue value1 = new SimpleValue("4");