|
@@ -2,22 +2,22 @@ package org.cobbzilla.wizard.cache.redis; |
|
|
|
|
|
|
|
|
import lombok.Getter; |
|
|
import lombok.Getter; |
|
|
import lombok.NoArgsConstructor; |
|
|
import lombok.NoArgsConstructor; |
|
|
|
|
|
import lombok.NonNull; |
|
|
import lombok.Setter; |
|
|
import lombok.Setter; |
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
import org.cobbzilla.util.collection.SingletonSet; |
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
import org.springframework.stereotype.Service; |
|
|
import org.springframework.stereotype.Service; |
|
|
import redis.clients.jedis.Jedis; |
|
|
import redis.clients.jedis.Jedis; |
|
|
import redis.clients.jedis.params.SetParams; |
|
|
import redis.clients.jedis.params.SetParams; |
|
|
|
|
|
|
|
|
|
|
|
import javax.annotation.Nullable; |
|
|
import java.util.*; |
|
|
import java.util.*; |
|
|
import java.util.concurrent.ConcurrentHashMap; |
|
|
import java.util.concurrent.ConcurrentHashMap; |
|
|
import java.util.concurrent.atomic.AtomicReference; |
|
|
import java.util.concurrent.atomic.AtomicReference; |
|
|
|
|
|
import java.util.function.Supplier; |
|
|
import java.util.stream.Collectors; |
|
|
import java.util.stream.Collectors; |
|
|
|
|
|
|
|
|
import static net.sf.cglib.core.CollectionUtils.transform; |
|
|
|
|
|
import static org.cobbzilla.util.daemon.ZillaRuntime.*; |
|
|
import static org.cobbzilla.util.daemon.ZillaRuntime.*; |
|
|
import static org.cobbzilla.util.daemon.ZillaRuntime.now; |
|
|
|
|
|
import static org.cobbzilla.util.json.JsonUtil.fromJsonOrDie; |
|
|
import static org.cobbzilla.util.json.JsonUtil.fromJsonOrDie; |
|
|
import static org.cobbzilla.util.json.JsonUtil.toJsonOrDie; |
|
|
import static org.cobbzilla.util.json.JsonUtil.toJsonOrDie; |
|
|
import static org.cobbzilla.util.security.CryptoUtil.string_decrypt; |
|
|
import static org.cobbzilla.util.security.CryptoUtil.string_decrypt; |
|
@@ -27,7 +27,7 @@ import static org.cobbzilla.util.system.Sleep.sleep; |
|
|
@Service @NoArgsConstructor @Slf4j |
|
|
@Service @NoArgsConstructor @Slf4j |
|
|
public class RedisService { |
|
|
public class RedisService { |
|
|
|
|
|
|
|
|
public static final int MAX_RETRIES = 5; |
|
|
|
|
|
|
|
|
public static final byte MAX_RETRIES = 5; |
|
|
|
|
|
|
|
|
public static final String NX = "NX"; |
|
|
public static final String NX = "NX"; |
|
|
public static final String XX = "XX"; |
|
|
public static final String XX = "XX"; |
|
@@ -40,10 +40,12 @@ public class RedisService { |
|
|
@Autowired @Getter @Setter private HasRedisConfiguration configuration; |
|
|
@Autowired @Getter @Setter private HasRedisConfiguration configuration; |
|
|
|
|
|
|
|
|
@Getter @Setter private String key; |
|
|
@Getter @Setter private String key; |
|
|
protected boolean hasKey () { return !empty(getKey()); } |
|
|
|
|
|
|
|
|
protected boolean hasKey() { return !empty(getKey()); } |
|
|
|
|
|
|
|
|
private final AtomicReference<Jedis> redis = new AtomicReference<>(); |
|
|
private final AtomicReference<Jedis> redis = new AtomicReference<>(); |
|
|
private Jedis newJedis() { return new Jedis(configuration.getRedis().getHost(), configuration.getRedis().getPort()); } |
|
|
|
|
|
|
|
|
private Jedis newJedis() { |
|
|
|
|
|
return new Jedis(configuration.getRedis().getHost(), configuration.getRedis().getPort()); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
@Getter @Setter private String prefix = null; |
|
|
@Getter @Setter private String prefix = null; |
|
|
|
|
|
|
|
@@ -55,7 +57,7 @@ public class RedisService { |
|
|
this(configuration.getRedis(), prefix, key); |
|
|
this(configuration.getRedis(), prefix, key); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public RedisService(RedisConfiguration configuration, String prefix, String key) { |
|
|
|
|
|
|
|
|
public RedisService(final RedisConfiguration configuration, String prefix, String key) { |
|
|
this.configuration = () -> configuration; |
|
|
this.configuration = () -> configuration; |
|
|
this.prefix = prefix; |
|
|
this.prefix = prefix; |
|
|
this.key = key; |
|
|
this.key = key; |
|
@@ -63,7 +65,9 @@ public class RedisService { |
|
|
|
|
|
|
|
|
private Map<String, RedisService> prefixServiceCache = new ConcurrentHashMap<>(); |
|
|
private Map<String, RedisService> prefixServiceCache = new ConcurrentHashMap<>(); |
|
|
|
|
|
|
|
|
public RedisService prefixNamespace(String prefix) { return prefixNamespace(prefix, configuration.getRedis().getKey()); } |
|
|
|
|
|
|
|
|
public RedisService prefixNamespace(String prefix) { |
|
|
|
|
|
return prefixNamespace(prefix, configuration.getRedis().getKey()); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
public RedisService prefixNamespace(String prefix, String key) { |
|
|
public RedisService prefixNamespace(String prefix, String key) { |
|
|
RedisService r = prefixServiceCache.get(prefix); |
|
|
RedisService r = prefixServiceCache.get(prefix); |
|
@@ -74,10 +78,9 @@ public class RedisService { |
|
|
prefixServiceCache.put(prefix, r); |
|
|
prefixServiceCache.put(prefix, r); |
|
|
} |
|
|
} |
|
|
return r; |
|
|
return r; |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public void reconnect () { |
|
|
|
|
|
|
|
|
public void reconnect() { |
|
|
if (log.isDebugEnabled()) log.debug("marking redis for reconnection..."); |
|
|
if (log.isDebugEnabled()) log.debug("marking redis for reconnection..."); |
|
|
synchronized (redis) { |
|
|
synchronized (redis) { |
|
|
if (redis.get() != null) { |
|
|
if (redis.get() != null) { |
|
@@ -89,7 +92,7 @@ public class RedisService { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private Jedis getRedis () { |
|
|
|
|
|
|
|
|
private Jedis getRedis() { |
|
|
synchronized (redis) { |
|
|
synchronized (redis) { |
|
|
if (redis.get() == null) { |
|
|
if (redis.get() == null) { |
|
|
if (log.isDebugEnabled()) log.debug("connecting to redis..."); |
|
|
if (log.isDebugEnabled()) log.debug("connecting to redis..."); |
|
@@ -99,19 +102,21 @@ public class RedisService { |
|
|
return redis.get(); |
|
|
return redis.get(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public <V> RedisMap<V> map (String prefix) { return map(prefix, null); } |
|
|
|
|
|
public <V> RedisMap<V> map (String prefix, Long duration) { return new RedisMap<>(prefix, duration, this); } |
|
|
|
|
|
|
|
|
public <V> RedisMap<V> map(String prefix) { return map(prefix, null); } |
|
|
|
|
|
public <V> RedisMap<V> map(String prefix, Long duration) { return new RedisMap<>(prefix, duration, this); } |
|
|
|
|
|
|
|
|
public boolean exists(String key) { return __exists(key, 0, MAX_RETRIES); } |
|
|
|
|
|
|
|
|
public boolean exists(@NonNull final String key) { |
|
|
|
|
|
return retry(() -> getRedis().exists(prefix(key)), "exists"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
public boolean anyExists(Collection<String> keys) { |
|
|
|
|
|
|
|
|
public boolean anyExists(@NonNull final Collection<String> keys) { |
|
|
for (String k : keys) if (exists(k)) return true; |
|
|
for (String k : keys) if (exists(k)) return true; |
|
|
return false; |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public boolean allExist(Collection<String> keys) { |
|
|
|
|
|
for (String k : keys) if (!exists(k)) return false; |
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
|
public boolean allExist(@NonNull final Collection<String> keys) { |
|
|
|
|
|
final var prefixedKeysArray = keys.stream().map(k -> prefix(k)).toArray(String[]::new); |
|
|
|
|
|
return keys.size() == retry(() -> getRedis().exists(prefixedKeysArray), "exists"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public <T> T getObject(String key, Class<T> clazz) { |
|
|
public <T> T getObject(String key, Class<T> clazz) { |
|
@@ -119,124 +124,197 @@ public class RedisService { |
|
|
return empty(json) ? null : fromJsonOrDie(json, clazz); |
|
|
return empty(json) ? null : fromJsonOrDie(json, clazz); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public String get(String key) { return decrypt(__get(key, 0, MAX_RETRIES)); } |
|
|
|
|
|
public String get_withPrefix(String prefixedKey) { return decrypt(__get(prefixedKey, 0, MAX_RETRIES, false)); } |
|
|
|
|
|
|
|
|
@Nullable public String get(@NonNull final String key) { |
|
|
|
|
|
return decrypt(retry(() -> getRedis().get(prefix(key)), "get")); |
|
|
|
|
|
} |
|
|
|
|
|
@Nullable public String get_withPrefix(@NonNull final String prefixedKey) { |
|
|
|
|
|
return decrypt(retry(() -> getRedis().get(prefixedKey), "get")); |
|
|
|
|
|
} |
|
|
|
|
|
@Nullable public String get_plaintext(@NonNull final String key) { |
|
|
|
|
|
return retry(() -> getRedis().get(prefix(key)), "get"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
public String get_plaintext(String key) { return __get(key, 0, MAX_RETRIES); } |
|
|
|
|
|
|
|
|
|
|
|
public void set(String key, String value, String nxxx, String expx, long time) { |
|
|
|
|
|
__set(key, value, nxxx, expx, time, 0, MAX_RETRIES); |
|
|
|
|
|
|
|
|
public void set(@NonNull final String key, @NonNull final String value) { |
|
|
|
|
|
retry(() -> getRedis().set(prefix(key), encrypt(value)), "set"); |
|
|
} |
|
|
} |
|
|
|
|
|
public <T> void setObject(@NonNull final String key, @NonNull final T thing) { set(key, toJsonOrDie(thing)); } |
|
|
|
|
|
|
|
|
public void set(String key, String value, String expx, long time) { |
|
|
|
|
|
__set(key, value, XX, expx, time, 0, MAX_RETRIES); |
|
|
|
|
|
__set(key, value, NX, expx, time, 0, MAX_RETRIES); |
|
|
|
|
|
|
|
|
public void set(@NonNull final String key, @NonNull final String value, @NonNull final String nxxx, |
|
|
|
|
|
@NonNull final String expx, final long time) { |
|
|
|
|
|
retry(() -> getRedis().set(prefix(key), encrypt(value), buildSetParams(nxxx, expx, time)), "set"); |
|
|
|
|
|
} |
|
|
|
|
|
public void set(@NonNull final String key, @NonNull final String value, @NonNull final String expx, |
|
|
|
|
|
final long time) { |
|
|
|
|
|
set(key, value, XX, expx, time); |
|
|
|
|
|
set(key, value, NX, expx, time); |
|
|
|
|
|
} |
|
|
|
|
|
public void setAll(@NonNull final Collection<String> keys, @NonNull final String value, @NonNull final String expx, |
|
|
|
|
|
final long time) { |
|
|
|
|
|
for (String k : keys) set(k, value, expx, time); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public void set(String key, String value) { __set(key, value, 0, MAX_RETRIES); } |
|
|
|
|
|
|
|
|
public void set_plaintext(@NonNull final String key, @NonNull final String value) { |
|
|
|
|
|
retry(() -> getRedis().set(prefix(key), value), "set_plaintext"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
public void set_plaintext(String key, String value, String nxxx, String expx, long time) { |
|
|
|
|
|
__set_plaintext(key, value, nxxx, expx, time, 0, MAX_RETRIES); |
|
|
|
|
|
|
|
|
public void set_plaintext(@NonNull final String key, @NonNull final String value, @NonNull final String nxxx, |
|
|
|
|
|
@NonNull final String expx, final long time) { |
|
|
|
|
|
retry(() -> getRedis().set(prefix(key), value, buildSetParams(nxxx, expx, time)), "set_plaintext"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public void set_plaintext(String key, String value, String expx, long time) { |
|
|
|
|
|
__set_plaintext(key, value, XX, expx, time, 0, MAX_RETRIES); |
|
|
|
|
|
__set_plaintext(key, value, NX, expx, time, 0, MAX_RETRIES); |
|
|
|
|
|
|
|
|
public void set_plaintext(@NonNull final String key, @NonNull final String value, @NonNull final String expx, |
|
|
|
|
|
final long time) { |
|
|
|
|
|
set_plaintext(key, value, XX, expx, time); |
|
|
|
|
|
set_plaintext(key, value, NX, expx, time); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public void set_plaintext(String key, String value) { __set_plaintext(key, value, 0, MAX_RETRIES); } |
|
|
|
|
|
|
|
|
public Long touch(@NonNull final String key) { return retry(() -> getRedis().touch(prefix(key)), "touch"); } |
|
|
|
|
|
|
|
|
public void setAll(Collection<String> keys, String value, String expx, long time) { |
|
|
|
|
|
for (String k : keys) set(k, value, expx, time); |
|
|
|
|
|
|
|
|
public Long expire(@NonNull final String key, final int ttlSeconds) { |
|
|
|
|
|
return retry(() -> getRedis().expire(prefix(key), ttlSeconds), "expire"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public <T> void setObject(String key, T thing) { __set(key, toJsonOrDie(thing), 0, MAX_RETRIES); } |
|
|
|
|
|
|
|
|
|
|
|
public Long touch(String key) { return __touch(key, 0, MAX_RETRIES); } |
|
|
|
|
|
public Long expire(String key, long ttl) { return __expire(key, (int) ttl, 0, MAX_RETRIES); } |
|
|
|
|
|
public Long pexpire(String key, long ttl) { return __pexpire(key, (int) ttl, 0, MAX_RETRIES); } |
|
|
|
|
|
|
|
|
public Long pexpire(@NonNull final String key, final long ttlMillis) { |
|
|
|
|
|
return retry(() -> getRedis().pexpire(prefix(key), ttlMillis), "pexpire"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
public List<String> list(String key) { return lrange(key, 0, -1); } |
|
|
|
|
|
public Long llen(String key) { return __llen(key, 0, MAX_RETRIES); } |
|
|
|
|
|
public List<String> lrange(String key, int start, int end) { return __lrange(key, start, end, 0, MAX_RETRIES); } |
|
|
|
|
|
public void lpush(String key, String value) { __lpush(key, value, 0, MAX_RETRIES); } |
|
|
|
|
|
public String lpop(String data) { return decrypt(__lpop(data, 0, MAX_RETRIES)); } |
|
|
|
|
|
public void rpush(String key, String value) { __rpush(key, value, 0, MAX_RETRIES); } |
|
|
|
|
|
public String rpop(String data) { return decrypt(__rpop(data, 0, MAX_RETRIES)); } |
|
|
|
|
|
|
|
|
public List<String> list(@NonNull final String key) { return lrange(key, 0, -1); } |
|
|
|
|
|
public List<String> lrange(@NonNull final String key, final int start, final int end) { |
|
|
|
|
|
final var list = retry(() -> getRedis().lrange(prefix(key), start, end), "lrange"); |
|
|
|
|
|
return hasKey() ? list.stream().map(this::decrypt).collect(Collectors.toList()) : list; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
public void hset(String key, String field, String value) { __hset(key, field, value, 0, MAX_RETRIES); } |
|
|
|
|
|
public String hget(String key, String field) { return decrypt(__hget(key, field, 0, MAX_RETRIES)); } |
|
|
|
|
|
public Map<String, String> hgetall(String key) { return decrypt(__hgetall(key, 0, MAX_RETRIES)); } |
|
|
|
|
|
|
|
|
public Long llen(@NonNull final String key) { |
|
|
|
|
|
return retry(() -> getRedis().llen(prefix(key)), "llen"); |
|
|
|
|
|
} |
|
|
|
|
|
public void lpush(@NonNull final String key, @NonNull final String value) { |
|
|
|
|
|
retry(() -> getRedis().lpush(prefix(key), encrypt(value)), "lpush"); |
|
|
|
|
|
} |
|
|
|
|
|
public String lpop(@NonNull final String key) { |
|
|
|
|
|
return decrypt(retry(() -> getRedis().lpop(prefix(key)), "lpop")); |
|
|
|
|
|
} |
|
|
|
|
|
public void rpush(@NonNull final String key, @NonNull final String value) { |
|
|
|
|
|
retry(() -> getRedis().rpush(prefix(key), encrypt(value)), "rpush"); |
|
|
|
|
|
} |
|
|
|
|
|
public String rpop(@NonNull final String key) { |
|
|
|
|
|
return decrypt(retry(() -> getRedis().rpop(prefix(key)), "rpop")); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
private Map<String, String> decrypt(Map<String, String> map) { |
|
|
|
|
|
|
|
|
public void hset(@NonNull final String key, @NonNull final String field, @NonNull final String value) { |
|
|
|
|
|
retry(() -> getRedis().hset(prefix(key), encrypt(field), encrypt(value)), "hget"); |
|
|
|
|
|
} |
|
|
|
|
|
public String hget(@NonNull final String key, @NonNull final String field) { |
|
|
|
|
|
if (empty(field)) return die("__hget(" + key + "/): field was empty"); |
|
|
|
|
|
return decrypt(retry(() -> getRedis().hget(prefix(key), encrypt(field)), "hget")); |
|
|
|
|
|
} |
|
|
|
|
|
public Map<String, String> hgetall(@NonNull final String key) { |
|
|
|
|
|
final var map = retry(() -> getRedis().hgetAll(prefix(key)), "hgetall"); |
|
|
if (!hasKey()) return map; |
|
|
if (!hasKey()) return map; |
|
|
final Map<String, String> decrypted = new HashMap<>(); |
|
|
|
|
|
for (Map.Entry<String, String> entry : map.entrySet()) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final var decrypted = new HashMap<String, String>(map.size()); |
|
|
|
|
|
for (final var entry : map.entrySet()) { |
|
|
decrypted.put(decrypt(entry.getKey()), decrypt(entry.getValue())); |
|
|
decrypted.put(decrypt(entry.getKey()), decrypt(entry.getValue())); |
|
|
} |
|
|
} |
|
|
return decrypted; |
|
|
return decrypted; |
|
|
} |
|
|
} |
|
|
|
|
|
public Long hdel(@NonNull final String key, @NonNull final String field) { |
|
|
|
|
|
return retry(() -> getRedis().hdel(prefix(key), encrypt(field)), "hdel"); |
|
|
|
|
|
} |
|
|
|
|
|
public Set<String> hkeys(@NonNull final String key) { |
|
|
|
|
|
return retry(() -> getRedis().hkeys(prefix(key)), "hget"); |
|
|
|
|
|
} |
|
|
|
|
|
public Long hlen(@NonNull final String key) { |
|
|
|
|
|
return retry(() -> getRedis().hlen(prefix(key)), "hlen"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
public Long hdel(String key, String field) { return __hdel(key, field, 0, MAX_RETRIES); } |
|
|
|
|
|
public Set<String> hkeys(String key) { return __hkeys(key, 0, MAX_RETRIES); } |
|
|
|
|
|
public Long hlen(String key) { return __hlen(key, 0, MAX_RETRIES); } |
|
|
|
|
|
|
|
|
|
|
|
public Long del(String key) { return __del(key, 0, MAX_RETRIES); } |
|
|
|
|
|
public Long del_withPrefix(String prefixedKey) { return __del(prefixedKey, 0, MAX_RETRIES, false); } |
|
|
|
|
|
|
|
|
|
|
|
public Long del_matching(String keyMatch) { |
|
|
|
|
|
Long count = 0L; |
|
|
|
|
|
for (String key : keys(keyMatch)) { |
|
|
|
|
|
|
|
|
public Long del(@NonNull final String key) { |
|
|
|
|
|
return retry(() -> getRedis().del(prefix(key)), "del"); |
|
|
|
|
|
} |
|
|
|
|
|
public Long del_withPrefix(@NonNull final String prefixedKey) { |
|
|
|
|
|
return retry(() -> getRedis().del(prefixedKey), "del"); |
|
|
|
|
|
} |
|
|
|
|
|
public Long del_matching(@NonNull final String keyMatch) { |
|
|
|
|
|
var count = 0L; |
|
|
|
|
|
for (final var key : keys(keyMatch)) { |
|
|
count += del_withPrefix(key); |
|
|
count += del_withPrefix(key); |
|
|
} |
|
|
} |
|
|
return count; |
|
|
return count; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public Long sadd(String key, String value) { return sadd(key, new String[]{value}); } |
|
|
|
|
|
public Long sadd(String key, String[] values) { return __sadd(key, values, 0, MAX_RETRIES); } |
|
|
|
|
|
|
|
|
|
|
|
public Long sadd_plaintext(String key, String value) { return sadd_plaintext(key, new String[]{value}); } |
|
|
|
|
|
public Long sadd_plaintext(String key, String[] values) { return __sadd(key, values, 0, MAX_RETRIES, false); } |
|
|
|
|
|
|
|
|
|
|
|
public Long srem(String key, String value) { return srem(key, new String[]{value}); } |
|
|
|
|
|
public Long srem(String key, String[] values) { return __srem(key, values, 0, MAX_RETRIES); } |
|
|
|
|
|
|
|
|
|
|
|
public Set<String> smembers(String key) { return __smembers(key, 0, MAX_RETRIES); } |
|
|
|
|
|
public boolean sismember(String key, String value) { return __sismember(key, value, 0, MAX_RETRIES); } |
|
|
|
|
|
|
|
|
|
|
|
public List<String> srandmembers(String key, int count) { return __srandmember(key, count, 0, MAX_RETRIES); } |
|
|
|
|
|
public String srandmember(String key) { |
|
|
|
|
|
final List<String> rand = srandmembers(key, 1); |
|
|
|
|
|
|
|
|
public Long sadd(@NonNull final String key, final String value) { return sadd(key, new String[]{value}); } |
|
|
|
|
|
public Long sadd(@NonNull final String key, final String[] values) { |
|
|
|
|
|
return retry(() -> getRedis().sadd(prefix(key), encrypt(values)), "sadd"); |
|
|
|
|
|
} |
|
|
|
|
|
public Long sadd_plaintext(@NonNull final String key, final String value) { |
|
|
|
|
|
return sadd_plaintext(key, new String[]{value}); |
|
|
|
|
|
} |
|
|
|
|
|
public Long sadd_plaintext(@NonNull final String key, final String[] values) { |
|
|
|
|
|
return retry(() -> getRedis().sadd(prefix(key), values), "sadd"); |
|
|
|
|
|
} |
|
|
|
|
|
public Long srem(@NonNull final String key, final String value) { return srem(key, new String[]{value}); } |
|
|
|
|
|
public Long srem(@NonNull final String key, final String[] values) { |
|
|
|
|
|
return retry(() -> getRedis().srem(prefix(key), encrypt(values)), "srem"); |
|
|
|
|
|
} |
|
|
|
|
|
public Set<String> smembers(@NonNull final String key) { |
|
|
|
|
|
return retry(() -> decrypt(getRedis().smembers(prefix(key))), "smembers"); |
|
|
|
|
|
} |
|
|
|
|
|
public boolean sismember(@NonNull final String key, @NonNull final String value) { |
|
|
|
|
|
return retry(() -> getRedis().sismember(prefix(key), encrypt(value)), "sismember"); |
|
|
|
|
|
} |
|
|
|
|
|
public List<String> srandmembers(@NonNull final String key, final int count) { |
|
|
|
|
|
return retry(() -> decrypt(getRedis().srandmember(prefix(key), count)), "srandmember"); |
|
|
|
|
|
} |
|
|
|
|
|
public String srandmember(@NonNull final String key) { |
|
|
|
|
|
final var rand = srandmembers(key, 1); |
|
|
return empty(rand) ? null : rand.get(0); |
|
|
return empty(rand) ? null : rand.get(0); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public String spop(String key) { |
|
|
|
|
|
final Set<String> popped = spop(key, 1); |
|
|
|
|
|
return empty(popped) ? null : popped.iterator().next(); |
|
|
|
|
|
|
|
|
@Nullable public Set<String> spop(@NonNull final String key, final long count) { |
|
|
|
|
|
return retry(() -> decrypt(getRedis().spop(prefix(key), count)), "spop"); |
|
|
|
|
|
} |
|
|
|
|
|
@Nullable public String spop(@NonNull final String key) { |
|
|
|
|
|
final var member = spop(key, 1); |
|
|
|
|
|
return empty(member) ? null : member.iterator().next(); |
|
|
|
|
|
} |
|
|
|
|
|
public long scard(@NonNull final String key) { |
|
|
|
|
|
return retry(() -> getRedis().scard(prefix(key)), "scard"); |
|
|
|
|
|
} |
|
|
|
|
|
public Set<String> sunion(@NonNull final Collection<String> keys) { |
|
|
|
|
|
return decrypt(sunion_plaintext(keys)); |
|
|
|
|
|
} |
|
|
|
|
|
public Set<String> sunion_plaintext(@NonNull final Collection<String> keys) { |
|
|
|
|
|
String[] prefixedKeys = keys.stream().map(this::prefix).toArray(String[]::new); |
|
|
|
|
|
return retry(() -> getRedis().sunion(prefixedKeys), "sunion"); |
|
|
|
|
|
} |
|
|
|
|
|
public Long sunionstore((@NonNull final String destKey, (@NonNull final Collection<String> keys) { |
|
|
|
|
|
String[] prefixedKeys = keys.stream().map(this::prefix).toArray(String[]::new); |
|
|
|
|
|
return retry(() -> getRedis().sunionstore(prefix(destKey), prefixedKeys), "sunionstore"); |
|
|
} |
|
|
} |
|
|
public Set<String> spop(String key, long count) { return __spop(key, count, 0, MAX_RETRIES); } |
|
|
|
|
|
|
|
|
|
|
|
public long scard(String key) { return __scard(key, 0, MAX_RETRIES); } |
|
|
|
|
|
|
|
|
|
|
|
public Set<String> sunion(Collection<String> keys) { return __sunion(keys, 0, MAX_RETRIES); } |
|
|
|
|
|
public Set<String> sunion_plaintext(Collection<String> keys) { return __sunion(keys, 0, MAX_RETRIES, false); } |
|
|
|
|
|
|
|
|
|
|
|
public Long sunionstore(String destKey, Collection<String> keys) { return __sunionstore(destKey, keys, 0, MAX_RETRIES); } |
|
|
|
|
|
|
|
|
|
|
|
public Long incr(String key) { return __incrBy(key, 1, 0, MAX_RETRIES); } |
|
|
|
|
|
public Long counterValue(String key) { |
|
|
|
|
|
final String value = get_plaintext(key); |
|
|
|
|
|
|
|
|
@Nullable public Long counterValue(@NonNull final String key) { |
|
|
|
|
|
final var value = get_plaintext(key); |
|
|
return value == null ? null : Long.parseLong(value); |
|
|
return value == null ? null : Long.parseLong(value); |
|
|
} |
|
|
} |
|
|
|
|
|
public Long incr(@NonNull final String key) { |
|
|
|
|
|
return retry(() -> getRedis().incrBy(prefix(key), 1), "incrBy"); |
|
|
|
|
|
} |
|
|
|
|
|
public Long incrBy(@NonNull final String key, final long value) { |
|
|
|
|
|
return retry(() -> getRedis().incrBy(prefix(key), value), "incrBy"); |
|
|
|
|
|
} |
|
|
|
|
|
public Long decr(@NonNull final String key) { |
|
|
|
|
|
return retry(() -> getRedis().decrBy(prefix(key), 1), "decrBy"); |
|
|
|
|
|
} |
|
|
|
|
|
public Long decrBy(@NonNull final String key, final long value) { |
|
|
|
|
|
return retry(() -> getRedis().decrBy(prefix(key), value), "decrBy"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
public Long incrBy(String key, long value) { return __incrBy(key, value, 0, MAX_RETRIES); } |
|
|
|
|
|
public Long decr(String key) { return __decrBy(key, 1, 0, MAX_RETRIES); } |
|
|
|
|
|
|
|
|
|
|
|
public Long decrBy(String key, long value) { return __decrBy(key, value, 0, MAX_RETRIES); } |
|
|
|
|
|
|
|
|
|
|
|
public Collection<String> keys(String key) { return __keys(key, 0, MAX_RETRIES); } |
|
|
|
|
|
|
|
|
public Collection<String> keys(@NonNull final String key) { |
|
|
|
|
|
return retry(() -> getRedis().keys(prefix(key)), "keys"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
public String rename(String key, String newKey) { return __rename(key, newKey, 0, MAX_RETRIES); } |
|
|
|
|
|
|
|
|
public String rename(@NonNull final String key, @NonNull final String newKey) { |
|
|
|
|
|
retry(() -> getRedis().rename(prefix(key), newKey), "rename"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
public static final String LOCK_SUFFIX = "._lock"; |
|
|
public static final String LOCK_SUFFIX = "._lock"; |
|
|
|
|
|
|
|
@@ -280,47 +358,27 @@ public class RedisService { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public String loadScript(String script) { return __loadScript(script, 0, MAX_RETRIES); } |
|
|
|
|
|
|
|
|
|
|
|
private String __loadScript(String script, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().scriptLoad(script); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__loadScript"); |
|
|
|
|
|
return __loadScript(script, attempt+1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
public String loadScript(final String script) { |
|
|
|
|
|
return retry(() -> getRedis().scriptLoad(script), "loadScript"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Object eval(String scriptsha, List<String> keys, List<String> args) { |
|
|
|
|
|
return __eval(scriptsha, prefix(keys), args, 0, MAX_RETRIES); |
|
|
|
|
|
|
|
|
public Object eval(final String scriptSHA, final List<String> keys, final List<String> args) { |
|
|
|
|
|
return retry(() -> getRedis().evalsha(scriptSHA, prefix(keys), args), "eval"); |
|
|
} |
|
|
} |
|
|
|
|
|
public String prefix(String key) { return empty(prefix) ? key : prefix + "." + key; } |
|
|
|
|
|
|
|
|
private Object __eval(String scriptsha, List<String> keys, List<String> args, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().evalsha(scriptsha, keys, args); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__eval"); |
|
|
|
|
|
return __eval(scriptsha, keys, args, attempt+1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
public List<String> prefix(Collection<String> keys) { |
|
|
|
|
|
return keys.parallelStream().map(this::prefix).collect(Collectors.toList()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public String prefix (String key) { return empty(prefix) ? key : prefix + "." + key; } |
|
|
|
|
|
public List<String> prefix(Collection<String> keys) { return transform(keys, o -> prefix(o.toString())); } |
|
|
|
|
|
|
|
|
|
|
|
// override these for full control |
|
|
// override these for full control |
|
|
|
|
|
|
|
|
protected String encrypt(String data) { |
|
|
protected String encrypt(String data) { |
|
|
if (!hasKey()) return data; |
|
|
if (!hasKey()) return data; |
|
|
return string_encrypt(data, getKey()); |
|
|
return string_encrypt(data, getKey()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
protected String[] encrypt(String[] data) { |
|
|
|
|
|
|
|
|
protected String[] encrypt(final String[] data) { |
|
|
if (!hasKey() || empty(data)) return data; |
|
|
if (!hasKey() || empty(data)) return data; |
|
|
final String[] encrypted = new String[data.length]; |
|
|
final String[] encrypted = new String[data.length]; |
|
|
for (int i=0; i<data.length; i++) { |
|
|
for (int i=0; i<data.length; i++) { |
|
@@ -334,7 +392,6 @@ public class RedisService { |
|
|
if (data == null) return null; |
|
|
if (data == null) return null; |
|
|
return string_decrypt(data, getKey()); |
|
|
return string_decrypt(data, getKey()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
protected <T extends Collection<String>> T decrypt(T data) { |
|
|
protected <T extends Collection<String>> T decrypt(T data) { |
|
|
if (!hasKey() || empty(data)) return data; |
|
|
if (!hasKey() || empty(data)) return data; |
|
|
final T decrypted = (T) new ArrayList<String>(); |
|
|
final T decrypted = (T) new ArrayList<String>(); |
|
@@ -343,6 +400,7 @@ public class RedisService { |
|
|
} |
|
|
} |
|
|
return decrypted; |
|
|
return decrypted; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
protected String[] decrypt(String[] data) { |
|
|
protected String[] decrypt(String[] data) { |
|
|
if (!hasKey() || empty(data)) return data; |
|
|
if (!hasKey() || empty(data)) return data; |
|
|
final String[] decrypted = new String[data.length]; |
|
|
final String[] decrypted = new String[data.length]; |
|
@@ -352,40 +410,24 @@ public class RedisService { |
|
|
return decrypted; |
|
|
return decrypted; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private void resetForRetry(int attempt, String reason) { |
|
|
|
|
|
reconnect(); |
|
|
|
|
|
sleep(attempt * 10, reason); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private String __get(String key, int attempt, int maxRetries) { |
|
|
|
|
|
return __get(key, attempt, maxRetries, true); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private String __get(String key, int attempt, int maxRetries, boolean applyPrefix) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().get(applyPrefix ? prefix(key) : key); |
|
|
|
|
|
|
|
|
@Nullable private <T> T retry(@NonNull final Supplier<T> function, @NonNull final String logSuffix) { |
|
|
|
|
|
for (var attempt = 1; attempt <= MAX_RETRIES; attempt++) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return function.get(); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt >= MAX_RETRIES) throw e; // here's the final exit from the loop (no need to reconnect) |
|
|
|
|
|
reconnect(); |
|
|
|
|
|
sleep(attempt * 10, "retrying RedisService " + logSuffix); |
|
|
} |
|
|
} |
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__get"); |
|
|
|
|
|
return __get(key, attempt+1, maxRetries); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private boolean __exists(String key, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().exists(prefix(key)); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__exists"); |
|
|
|
|
|
return __exists(key, attempt+1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// Should not happen, but just in case: |
|
|
|
|
|
throw new RuntimeException("retry: Something went wrong while working with redis"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private SetParams getSetParams(String nxxx, String expx, long time) { |
|
|
|
|
|
|
|
|
private SetParams buildSetParams(String nxxx, String expx, long time) { |
|
|
SetParams setParams = new SetParams(); |
|
|
SetParams setParams = new SetParams(); |
|
|
switch (nxxx) { |
|
|
switch (nxxx) { |
|
|
case NX: setParams.nx(); break; |
|
|
case NX: setParams.nx(); break; |
|
@@ -398,430 +440,6 @@ public class RedisService { |
|
|
return setParams; |
|
|
return setParams; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private String __set(String key, String value, String nxxx, String expx, long time, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().set(prefix(key), encrypt(value), getSetParams(nxxx, expx, time)); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__set"); |
|
|
|
|
|
return __set(key, value, nxxx, expx, time, attempt + 1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private String __set(String key, String value, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().set(prefix(key), encrypt(value)); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__set"); |
|
|
|
|
|
return __set(key, value, attempt+1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private String __set_plaintext(String key, String value, String nxxx, String expx, long time, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().set(prefix(key), value, getSetParams(nxxx, expx, time)); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__set_plaintext"); |
|
|
|
|
|
return __set_plaintext(key, value, nxxx, expx, time, attempt + 1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private String __set_plaintext(String key, String value, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().set(prefix(key), value); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__set_plaintext"); |
|
|
|
|
|
return __set_plaintext(key, value, attempt+1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Long __touch(String key, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().touch(prefix(key)); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__touch"); |
|
|
|
|
|
return __touch(key, attempt+1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Long __expire(String key, int ttl, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().expire(prefix(key), ttl); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__expire"); |
|
|
|
|
|
return __expire(key, ttl, attempt+1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Long __pexpire(String key, long ttl, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().pexpire(prefix(key), ttl); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__pexpire"); |
|
|
|
|
|
return __pexpire(key, ttl, attempt+1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Long __lpush(String key, String value, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().lpush(prefix(key), encrypt(value)); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__lpush"); |
|
|
|
|
|
return __lpush(key, value, attempt + 1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private String __lpop(String key, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().lpop(prefix(key)); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__lpop"); |
|
|
|
|
|
return __lpop(key, attempt+1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Long __rpush(String key, String value, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().rpush(prefix(key), encrypt(value)); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__rpush"); |
|
|
|
|
|
return __rpush(key, value, attempt + 1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private String __rpop(String data, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().rpop(data); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__rpop"); |
|
|
|
|
|
return __rpop(data, attempt+1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private String __hget(String key, String field, int attempt, int maxRetries) { |
|
|
|
|
|
if (empty(field)) return die("__hget("+key+"/): field was empty"); |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().hget(prefix(key), encrypt(field)); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__hget"); |
|
|
|
|
|
return __hget(key, field, attempt + 1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Map<String, String> __hgetall(String key, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().hgetAll(prefix(key)); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__hgetall"); |
|
|
|
|
|
return __hgetall(key, attempt + 1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Long __hset(String key, String field, String value, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().hset(prefix(key), encrypt(field), encrypt(value)); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__hget"); |
|
|
|
|
|
return __hset(key, field, value, attempt + 1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Long __hdel(String key, String field, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().hdel(prefix(key), encrypt(field)); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__hdel"); |
|
|
|
|
|
return __hdel(key, field, attempt + 1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Set<String> __hkeys(String key, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().hkeys(prefix(key)); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__hget"); |
|
|
|
|
|
return __hkeys(key, attempt + 1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Long __hlen(String key, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().hlen(prefix(key)); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__hlen"); |
|
|
|
|
|
return __hlen(key, attempt + 1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Long __del(String key, int attempt, int maxRetries) { |
|
|
|
|
|
return __del(key, attempt, maxRetries, true); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Long __del(String key, int attempt, int maxRetries, boolean applyPrefix) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().del(applyPrefix ? prefix(key) : key); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__del"); |
|
|
|
|
|
return __del(key, attempt+1, maxRetries, applyPrefix); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Long __sadd(String key, String[] members, int attempt, int maxRetries) { |
|
|
|
|
|
return __sadd(key, members, attempt, maxRetries, true); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Long __sadd(String key, String[] members, int attempt, int maxRetries, boolean crypt) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().sadd(prefix(key), crypt ? encrypt(members) : members); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__sadd"); |
|
|
|
|
|
return __sadd(key, members, attempt+1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Long __srem(String key, String[] members, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().srem(prefix(key), encrypt(members)); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__srem"); |
|
|
|
|
|
return __srem(key, members, attempt+1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Set<String> __smembers(String key, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return decrypt(getRedis().smembers(prefix(key))); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__smembers"); |
|
|
|
|
|
return __smembers(key, attempt+1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private boolean __sismember(String key, String value, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().sismember(prefix(key), encrypt(value)); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__sismember"); |
|
|
|
|
|
return __sismember(key, value, attempt+1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private List<String> __srandmember(String key, int count, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return decrypt(getRedis().srandmember(prefix(key), count)); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__srandmember"); |
|
|
|
|
|
return __srandmember(key, count, attempt+1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Set<String> __spop(String key, long count, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return decrypt(count == 1 ? new SingletonSet<>(getRedis().spop(prefix(key))) : getRedis().spop(prefix(key), count)); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__spop"); |
|
|
|
|
|
return __spop(key, count, attempt+1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Long __scard(String key, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().scard(prefix(key)); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__scard"); |
|
|
|
|
|
return __scard(key, attempt+1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Set<String> __sunion(Collection<String> keys, int attempt, int maxRetries) { |
|
|
|
|
|
return __sunion(keys, attempt, maxRetries, true); |
|
|
|
|
|
} |
|
|
|
|
|
private Set<String> __sunion(Collection<String> keys, int attempt, int maxRetries, boolean crypt) { |
|
|
|
|
|
try { |
|
|
|
|
|
String[] prefixedKeys = keys.stream().map(this::prefix).toArray(String[]::new); |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
final Set<String> values = getRedis().sunion(prefixedKeys); |
|
|
|
|
|
return crypt ? values.stream().map(this::decrypt).collect(Collectors.toSet()) : values; |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__sunion"); |
|
|
|
|
|
return __sunion(keys, attempt+1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Long __sunionstore(String destKey, Collection<String> keys, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
String[] prefixedKeys = keys.stream().map(this::prefix).toArray(String[]::new); |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().sunionstore(prefix(destKey), prefixedKeys); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__sunionstore"); |
|
|
|
|
|
return __sunionstore(destKey, keys, attempt+1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Long __incrBy(String key, long value, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().incrBy(prefix(key), value); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__incrBy"); |
|
|
|
|
|
return __incrBy(key, value, attempt + 1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Long __decrBy(String key, long value, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().decrBy(prefix(key), value); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__decrBy"); |
|
|
|
|
|
return __decrBy(key, value, attempt+1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Long __llen(String key, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
return getRedis().llen(prefix(key)); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__llen"); |
|
|
|
|
|
return __llen(key, attempt + 1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private List<String> __lrange(String key, int start, int end, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
final List<String> range; |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
range = getRedis().lrange(prefix(key), start, end); |
|
|
|
|
|
} |
|
|
|
|
|
final List<String> list = new ArrayList<>(range.size()); |
|
|
|
|
|
for (String item : range) list.add(decrypt(item)); |
|
|
|
|
|
|
|
|
|
|
|
return list; |
|
|
|
|
|
|
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__lrange"); |
|
|
|
|
|
return __lrange(key, start, end, attempt + 1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Collection<String> __keys(String key, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
final Set<String> keys; |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
keys = getRedis().keys(prefix(key)); |
|
|
|
|
|
} |
|
|
|
|
|
return keys; |
|
|
|
|
|
|
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__keys"); |
|
|
|
|
|
return __keys(key, attempt + 1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private String __rename(String key, String newKey, int attempt, int maxRetries) { |
|
|
|
|
|
try { |
|
|
|
|
|
final String rval; |
|
|
|
|
|
synchronized (redis) { |
|
|
|
|
|
rval = getRedis().rename(prefix(key), newKey); |
|
|
|
|
|
} |
|
|
|
|
|
return rval; |
|
|
|
|
|
|
|
|
|
|
|
} catch (RuntimeException e) { |
|
|
|
|
|
if (attempt > maxRetries) throw e; |
|
|
|
|
|
resetForRetry(attempt, "retrying RedisService.__rename"); |
|
|
|
|
|
return __rename(key, newKey, attempt + 1, maxRetries); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void flush() { del_matching(ALL_KEYS); } |
|
|
public void flush() { del_matching(ALL_KEYS); } |
|
|
|
|
|
|
|
|
} |
|
|
} |