Improvement - JCache performance optimization. #1722

pull/1792/head
Nikita Koksharov 6 years ago
parent 182e377b90
commit a89f1a7f25

@ -15,6 +15,9 @@
*/ */
package org.redisson.jcache; package org.redisson.jcache;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -82,6 +85,8 @@ import io.netty.util.internal.PlatformDependent;
*/ */
public class JCache<K, V> extends RedissonObject implements Cache<K, V> { public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
private final boolean v2 = System.getProperty("org.jsr107.tck.management.agentId") == null;
private final JCacheManager cacheManager; private final JCacheManager cacheManager;
private final JCacheConfiguration<K, V> config; private final JCacheConfiguration<K, V> config;
private final ConcurrentMap<CacheEntryListenerConfiguration<K, V>, Map<Integer, String>> listeners = private final ConcurrentMap<CacheEntryListenerConfiguration<K, V>, Map<Integer, String>> listeners =
@ -93,6 +98,13 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
private boolean closed; private boolean closed;
private boolean hasOwnRedisson; private boolean hasOwnRedisson;
private static final RLock DUMMY_LOCK = (RLock) Proxy.newProxyInstance(JCache.class.getClassLoader(), new Class[] {RLock.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
});
public JCache(JCacheManager cacheManager, Redisson redisson, String name, JCacheConfiguration<K, V> config, boolean hasOwnRedisson) { public JCache(JCacheManager cacheManager, Redisson redisson, String name, JCacheConfiguration<K, V> config, boolean hasOwnRedisson) {
super(redisson.getConfig().getCodec(), redisson.getCommandExecutor(), name); super(redisson.getConfig().getCodec(), redisson.getCommandExecutor(), name);
@ -176,7 +188,12 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
long startTime = currentNanoTime(); long startTime = currentNanoTime();
RLock lock = getLockedLock(key); RLock lock = getLockedLock(key);
try { try {
V value = getValueLocked(key); V value;
if (v2) {
value = getValue(key);
} else {
value = getValueLocked(key);
}
if (value == null) { if (value == null) {
cacheManager.getStatBean(this).addMisses(1); cacheManager.getStatBean(this).addMisses(1);
if (config.isReadThrough()) { if (config.isReadThrough()) {
@ -253,6 +270,29 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
private V getValue(K key) { private V getValue(K key) {
Long accessTimeout = getAccessTimeout(); Long accessTimeout = getAccessTimeout();
if (accessTimeout == -1) {
V value = evalRead(getName(), codec, RedisCommands.EVAL_MAP_VALUE,
"local value = redis.call('hget', KEYS[1], ARGV[3]); "
+ "if value == false then "
+ "return nil; "
+ "end; "
+ "local expireDate = 92233720368547758; "
+ "local expireDateScore = redis.call('zscore', KEYS[2], ARGV[3]); "
+ "if expireDateScore ~= false then "
+ "expireDate = tonumber(expireDateScore); "
+ "end; "
+ "if expireDate <= tonumber(ARGV[2]) then "
+ "return nil; "
+ "end; "
+ "return value; ",
Arrays.<Object>asList(getName(), getTimeoutSetName(), getRemovedChannelName()),
accessTimeout, System.currentTimeMillis(), encodeMapKey(key));
return value;
}
V value = evalWrite(getName(), codec, RedisCommands.EVAL_MAP_VALUE, V value = evalWrite(getName(), codec, RedisCommands.EVAL_MAP_VALUE,
"local value = redis.call('hget', KEYS[1], ARGV[3]); " "local value = redis.call('hget', KEYS[1], ARGV[3]); "
+ "if value == false then " + "if value == false then "
@ -301,7 +341,12 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
V load(K key) { V load(K key) {
RLock lock = getLockedLock(key); RLock lock = getLockedLock(key);
try { try {
V value = getValueLocked(key); V value;
if (v2) {
value = getValue(key);
} else {
value = getValueLocked(key);
}
if (value == null) { if (value == null) {
value = loadValue(key); value = loadValue(key);
} }
@ -320,7 +365,11 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
} }
if (value != null) { if (value != null) {
long startTime = currentNanoTime(); long startTime = currentNanoTime();
if (v2) {
putValue(key, value);
} else {
putValueLocked(key, value); putValueLocked(key, value);
}
cacheManager.getStatBean(this).addGetTime(currentNanoTime() - startTime); cacheManager.getStatBean(this).addGetTime(currentNanoTime() - startTime);
} }
return value; return value;
@ -518,14 +567,15 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
private boolean putIfAbsentValue(K key, Object value) { private boolean putIfAbsentValue(K key, Object value) {
Long creationTimeout = getCreationTimeout(); Long creationTimeout = getCreationTimeout();
if (creationTimeout == 0) {
return false;
}
return evalWrite(getName(), codec, RedisCommands.EVAL_BOOLEAN, return evalWrite(getName(), codec, RedisCommands.EVAL_BOOLEAN,
"if redis.call('hexists', KEYS[1], ARGV[2]) == 1 then " "if redis.call('hexists', KEYS[1], ARGV[2]) == 1 then "
+ "return 0; " + "return 0; "
+ "else " + "else "
+ "if ARGV[1] == '0' then " + "if ARGV[1] ~= '-1' then "
+ "return 0;"
+ "elseif ARGV[1] ~= '-1' then "
+ "redis.call('hset', KEYS[1], ARGV[2], ARGV[3]); " + "redis.call('hset', KEYS[1], ARGV[2], ARGV[3]); "
+ "redis.call('zadd', KEYS[2], ARGV[1], ARGV[2]); " + "redis.call('zadd', KEYS[2], ARGV[1], ARGV[2]); "
+ "local msg = struct.pack('Lc0Lc0', string.len(ARGV[2]), ARGV[2], string.len(ARGV[3]), ARGV[3]); " + "local msg = struct.pack('Lc0Lc0', string.len(ARGV[2]), ARGV[2], string.len(ARGV[3]), ARGV[3]); "
@ -548,10 +598,11 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
} }
Long creationTimeout = getCreationTimeout(); Long creationTimeout = getCreationTimeout();
if (creationTimeout == 0) {
return false;
}
return evalWrite(getName(), codec, RedisCommands.EVAL_BOOLEAN, return evalWrite(getName(), codec, RedisCommands.EVAL_BOOLEAN,
"if ARGV[1] == '0' then " "if ARGV[1] ~= '-1' then "
+ "return 0;"
+ "elseif ARGV[1] ~= '-1' then "
+ "redis.call('hset', KEYS[1], ARGV[2], ARGV[3]); " + "redis.call('hset', KEYS[1], ARGV[2], ARGV[3]); "
+ "redis.call('zadd', KEYS[2], ARGV[1], ARGV[2]); " + "redis.call('zadd', KEYS[2], ARGV[1], ARGV[2]); "
+ "local msg = struct.pack('Lc0Lc0', string.len(ARGV[2]), ARGV[2], string.len(ARGV[3]), ARGV[3]); " + "local msg = struct.pack('Lc0Lc0', string.len(ARGV[2]), ARGV[2], string.len(ARGV[3]), ARGV[3]); "
@ -590,16 +641,19 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
} }
long startTime = currentNanoTime(); long startTime = currentNanoTime();
if (!config.isReadThrough()) {
boolean exists = false; boolean exists = false;
for (K key : keys) { for (K key : keys) {
if (containsKey(key)) { if (containsKey(key)) {
exists = true; exists = true;
break;
} }
} }
if (!exists && !config.isReadThrough()) { if (!exists) {
cacheManager.getStatBean(this).addGetTime(currentNanoTime() - startTime); cacheManager.getStatBean(this).addGetTime(currentNanoTime() - startTime);
return Collections.emptyMap(); return Collections.emptyMap();
} }
}
Long accessTimeout = getAccessTimeout(); Long accessTimeout = getAccessTimeout();
@ -609,7 +663,37 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
args.add(System.currentTimeMillis()); args.add(System.currentTimeMillis());
encode(args, keys); encode(args, keys);
Map<K, V> res = evalWrite(getName(), codec, new RedisCommand<Map<Object, Object>>("EVAL", new MapGetAllDecoder(new ArrayList<Object>(keys), 0, true), ValueType.MAP_VALUE), Map<K, V> res;
if (accessTimeout == -1) {
res = evalRead(getName(), codec, new RedisCommand<Map<Object, Object>>("EVAL", new MapGetAllDecoder(new ArrayList<Object>(keys), 0, true), ValueType.MAP_VALUE),
"local expireHead = redis.call('zrange', KEYS[2], 0, 0, 'withscores');"
+ "local accessTimeout = ARGV[1]; "
+ "local currentTime = tonumber(ARGV[2]); "
+ "local hasExpire = #expireHead == 2 and tonumber(expireHead[2]) <= currentTime; "
+ "local map = redis.call('hmget', KEYS[1], unpack(ARGV, 3, #ARGV)); "
+ "local result = {};"
+ "for i, value in ipairs(map) do "
+ "if value ~= false then "
+ "local key = ARGV[i+2]; "
+ "if hasExpire then "
+ "local expireDate = 92233720368547758; "
+ "local expireDateScore = redis.call('zscore', KEYS[2], key); "
+ "if expireDateScore ~= false then "
+ "expireDate = tonumber(expireDateScore); "
+ "end; "
+ "if expireDate <= currentTime then "
+ "value = false; "
+ "end; "
+ "end; "
+ "end; "
+ "table.insert(result, value); "
+ "end; "
+ "return result;",
Arrays.<Object>asList(getName(), getTimeoutSetName(), getRemovedChannelName()), args.toArray());
} else {
res = evalWrite(getName(), codec, new RedisCommand<Map<Object, Object>>("EVAL", new MapGetAllDecoder(new ArrayList<Object>(keys), 0, true), ValueType.MAP_VALUE),
"local expireHead = redis.call('zrange', KEYS[2], 0, 0, 'withscores');" "local expireHead = redis.call('zrange', KEYS[2], 0, 0, 'withscores');"
+ "local accessTimeout = ARGV[1]; " + "local accessTimeout = ARGV[1]; "
+ "local currentTime = tonumber(ARGV[2]); " + "local currentTime = tonumber(ARGV[2]); "
@ -645,6 +729,8 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
+ "end; " + "end; "
+ "return result;", + "return result;",
Arrays.<Object>asList(getName(), getTimeoutSetName(), getRemovedChannelName()), args.toArray()); Arrays.<Object>asList(getName(), getTimeoutSetName(), getRemovedChannelName()), args.toArray());
}
Map<K, V> result = new HashMap<K, V>(); Map<K, V> result = new HashMap<K, V>();
for (Map.Entry<K, V> entry : res.entrySet()) { for (Map.Entry<K, V> entry : res.entrySet()) {
@ -674,7 +760,7 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
throw new NullPointerException(); throw new NullPointerException();
} }
return evalWrite(getName(), codec, RedisCommands.EVAL_BOOLEAN, return evalRead(getName(), codec, RedisCommands.EVAL_BOOLEAN,
"if redis.call('hexists', KEYS[1], ARGV[2]) == 0 then " "if redis.call('hexists', KEYS[1], ARGV[2]) == 0 then "
+ "return 0;" + "return 0;"
+ "end;" + "end;"
@ -729,9 +815,13 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
throw new CacheLoaderException(ex); throw new CacheLoaderException(ex);
} }
if (value != null) { if (value != null) {
if (v2) {
putValue(key, value);
} else {
putValueLocked(key, value); putValueLocked(key, value);
} }
} }
}
} finally { } finally {
lock.unlock(); lock.unlock();
} }
@ -751,6 +841,10 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
} }
private RLock getLockedLock(K key) { private RLock getLockedLock(K key) {
if (v2) {
return DUMMY_LOCK;
}
String lockName = getLockName(key); String lockName = getLockName(key);
RLock lock = redisson.getLock(lockName); RLock lock = redisson.getLock(lockName);
try { try {
@ -776,7 +870,12 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
if (config.isWriteThrough()) { if (config.isWriteThrough()) {
RLock lock = getLockedLock(key); RLock lock = getLockedLock(key);
try { try {
List<Object> result = getAndPutValueLocked(key, value); List<Object> result;
if (v2) {
result = getAndPutValue(key, value);
} else {
result = getAndPutValueLocked(key, value);
}
if (result.isEmpty()) { if (result.isEmpty()) {
cacheManager.getStatBean(this).addPuts(1); cacheManager.getStatBean(this).addPuts(1);
cacheManager.getStatBean(this).addPutTime(currentNanoTime() - startTime); cacheManager.getStatBean(this).addPutTime(currentNanoTime() - startTime);
@ -821,7 +920,12 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
} else { } else {
RLock lock = getLockedLock(key); RLock lock = getLockedLock(key);
try { try {
boolean result = putValueLocked(key, value); boolean result;
if (v2) {
result = putValue(key, value);
} else {
result = putValueLocked(key, value);
}
if (result) { if (result) {
cacheManager.getStatBean(this).addPuts(1); cacheManager.getStatBean(this).addPuts(1);
} }
@ -988,7 +1092,12 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
if (config.isWriteThrough()) { if (config.isWriteThrough()) {
RLock lock = getLockedLock(key); RLock lock = getLockedLock(key);
try { try {
List<Object> result = getAndPutValueLocked(key, value); List<Object> result;
if (v2) {
result = getAndPutValue(key, value);
} else {
result = getAndPutValueLocked(key, value);
}
if (result.isEmpty()) { if (result.isEmpty()) {
cacheManager.getStatBean(this).addPuts(1); cacheManager.getStatBean(this).addPuts(1);
cacheManager.getStatBean(this).addMisses(1); cacheManager.getStatBean(this).addMisses(1);
@ -1037,7 +1146,12 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
} else { } else {
RLock lock = getLockedLock(key); RLock lock = getLockedLock(key);
try { try {
List<Object> result = getAndPutValueLocked(key, value); List<Object> result;
if (v2) {
result = getAndPutValue(key, value);
} else {
result = getAndPutValueLocked(key, value);
}
return getAndPutResult(startTime, result); return getAndPutResult(startTime, result);
} finally { } finally {
lock.unlock(); lock.unlock();
@ -1190,7 +1304,12 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
if (config.isWriteThrough()) { if (config.isWriteThrough()) {
RLock lock = getLockedLock(key); RLock lock = getLockedLock(key);
try { try {
boolean result = putIfAbsentValueLocked(key, value); boolean result;
if (v2) {
result = putIfAbsentValue(key, value);
} else {
result = putIfAbsentValueLocked(key, value);
}
if (result) { if (result) {
cacheManager.getStatBean(this).addPuts(1); cacheManager.getStatBean(this).addPuts(1);
try { try {
@ -1211,7 +1330,12 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
} else { } else {
RLock lock = getLockedLock(key); RLock lock = getLockedLock(key);
try { try {
boolean result = putIfAbsentValueLocked(key, value); boolean result;
if (v2) {
result = putIfAbsentValue(key, value);
} else {
result = putIfAbsentValueLocked(key, value);
}
if (result) { if (result) {
cacheManager.getStatBean(this).addPuts(1); cacheManager.getStatBean(this).addPuts(1);
} }
@ -1271,8 +1395,7 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
if (config.isWriteThrough()) { if (config.isWriteThrough()) {
RLock lock = getLockedLock(key); RLock lock = getLockedLock(key);
try { try {
V oldValue = getValue(key); V oldValue = getAndRemoveValue(key);
boolean result = removeValue(key);
try { try {
cacheWriter.delete(key); cacheWriter.delete(key);
} catch (CacheWriterException e) { } catch (CacheWriterException e) {
@ -1286,11 +1409,11 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
} }
throw new CacheWriterException(e); throw new CacheWriterException(e);
} }
if (result) { if (oldValue != null) {
cacheManager.getStatBean(this).addRemovals(1); cacheManager.getStatBean(this).addRemovals(1);
} }
cacheManager.getStatBean(this).addRemoveTime(currentNanoTime() - startTime); cacheManager.getStatBean(this).addRemoveTime(currentNanoTime() - startTime);
return result; return oldValue != null;
} finally { } finally {
lock.unlock(); lock.unlock();
} }
@ -1412,7 +1535,11 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
if (config.isWriteThrough()) { if (config.isWriteThrough()) {
RLock lock = getLockedLock(key); RLock lock = getLockedLock(key);
try { try {
if (v2) {
result = removeValue(key, value);
} else {
result = removeValueLocked(key, value); result = removeValueLocked(key, value);
}
if (result) { if (result) {
try { try {
cacheWriter.delete(key); cacheWriter.delete(key);
@ -1438,7 +1565,11 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
} else { } else {
RLock lock = getLockedLock(key); RLock lock = getLockedLock(key);
try { try {
if (v2) {
result = removeValue(key, value);
} else {
result = removeValueLocked(key, value); result = removeValueLocked(key, value);
}
if (result) { if (result) {
cacheManager.getStatBean(this).addHits(1); cacheManager.getStatBean(this).addHits(1);
cacheManager.getStatBean(this).addRemovals(1); cacheManager.getStatBean(this).addRemovals(1);
@ -1707,7 +1838,12 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
if (config.isWriteThrough()) { if (config.isWriteThrough()) {
RLock lock = getLockedLock(key); RLock lock = getLockedLock(key);
try { try {
long result = replaceValueLocked(key, oldValue, newValue); long result;
if (v2) {
result = replaceValue(key, oldValue, newValue);
} else {
result = replaceValueLocked(key, oldValue, newValue);
}
if (result == 1) { if (result == 1) {
try { try {
cacheWriter.write(new JCacheEntry<K, V>(key, newValue)); cacheWriter.write(new JCacheEntry<K, V>(key, newValue));
@ -1739,7 +1875,12 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
} else { } else {
RLock lock = getLockedLock(key); RLock lock = getLockedLock(key);
try { try {
long result = replaceValueLocked(key, oldValue, newValue); long result;
if (v2) {
result = replaceValue(key, oldValue, newValue);
} else {
result = replaceValueLocked(key, oldValue, newValue);
}
if (result == 1) { if (result == 1) {
cacheManager.getStatBean(this).addHits(1); cacheManager.getStatBean(this).addHits(1);
cacheManager.getStatBean(this).addPuts(1); cacheManager.getStatBean(this).addPuts(1);
@ -1950,7 +2091,12 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
if (config.isWriteThrough()) { if (config.isWriteThrough()) {
RLock lock = getLockedLock(key); RLock lock = getLockedLock(key);
try { try {
boolean result = replaceValueLocked(key, value); boolean result;
if (v2) {
result = replaceValue(key, value);
} else {
result = replaceValueLocked(key, value);
}
if (result) { if (result) {
cacheManager.getStatBean(this).addHits(1); cacheManager.getStatBean(this).addHits(1);
cacheManager.getStatBean(this).addPuts(1); cacheManager.getStatBean(this).addPuts(1);
@ -1974,7 +2120,12 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
} else { } else {
RLock lock = getLockedLock(key); RLock lock = getLockedLock(key);
try { try {
boolean result = replaceValueLocked(key, value); boolean result;
if (v2) {
result = replaceValue(key, value);
} else {
result = replaceValueLocked(key, value);
}
if (result) { if (result) {
cacheManager.getStatBean(this).addHits(1); cacheManager.getStatBean(this).addHits(1);
cacheManager.getStatBean(this).addPuts(1); cacheManager.getStatBean(this).addPuts(1);
@ -2003,7 +2154,12 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
if (config.isWriteThrough()) { if (config.isWriteThrough()) {
RLock lock = getLockedLock(key); RLock lock = getLockedLock(key);
try { try {
V result = getAndReplaceValueLocked(key, value); V result;
if (v2) {
result = getAndReplaceValue(key, value);
} else {
result = getAndReplaceValueLocked(key, value);
}
if (result != null) { if (result != null) {
cacheManager.getStatBean(this).addHits(1); cacheManager.getStatBean(this).addHits(1);
cacheManager.getStatBean(this).addPuts(1); cacheManager.getStatBean(this).addPuts(1);
@ -2028,7 +2184,12 @@ public class JCache<K, V> extends RedissonObject implements Cache<K, V> {
} else { } else {
RLock lock = getLockedLock(key); RLock lock = getLockedLock(key);
try { try {
V result = getAndReplaceValueLocked(key, value); V result;
if (v2) {
result = getAndReplaceValue(key, value);
} else {
result = getAndReplaceValueLocked(key, value);
}
if (result != null) { if (result != null) {
cacheManager.getStatBean(this).addHits(1); cacheManager.getStatBean(this).addHits(1);
cacheManager.getStatBean(this).addPuts(1); cacheManager.getStatBean(this).addPuts(1);

Loading…
Cancel
Save