LettuceRedisOps.java
package com.rbctcsworld.ecommerce.auth;
import io.lettuce.core.ClientOptions;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.ScriptOutputType;
import io.lettuce.core.SocketOptions;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
import java.time.Duration;
/**
* RedisOps on the Lettuce client. The connection is opened on first use (the backend starts even when Redis
* is not up yet) and Lettuce reconnects by itself afterwards. Short timeouts: a slow Redis must not make
* every login slow - the store falls back to memory instead.
*/
class LettuceRedisOps implements RedisOps {
/** One round trip, no race between "count" and "lock": Redis runs a script atomically. */
static final String RECORD_FAILURE = """
local n = redis.call('INCR', KEYS[1])
if n == 1 then redis.call('PEXPIRE', KEYS[1], ARGV[1]) end
if n >= tonumber(ARGV[2]) then
redis.call('SET', KEYS[2], '1', 'PX', ARGV[3])
redis.call('DEL', KEYS[1])
end
return n
""";
private static final Duration TIMEOUT = Duration.ofMillis(500);
private final RedisClient client;
private StatefulRedisConnection<String, String> connection;
LettuceRedisOps(String url) {
RedisURI uri = RedisURI.create(url);
uri.setTimeout(TIMEOUT);
client = RedisClient.create(uri);
client.setOptions(ClientOptions.builder()
.socketOptions(SocketOptions.builder().connectTimeout(TIMEOUT).build())
.build());
}
private synchronized RedisCommands<String, String> redis() {
if (connection == null || !connection.isOpen()) {
connection = client.connect();
}
return connection.sync();
}
@Override
public long recordFailure(String failKey, String lockKey, long windowMillis, int maxFailures, long lockMillis) {
Long n = redis().eval(RECORD_FAILURE, ScriptOutputType.INTEGER, new String[] {failKey, lockKey},
String.valueOf(windowMillis), String.valueOf(maxFailures), String.valueOf(lockMillis));
return n == null ? 0 : n;
}
@Override
public long ttlMillis(String key) {
Long ttl = redis().pttl(key);
return ttl == null ? -2 : ttl;
}
@Override
public long count(String key) {
String v = redis().get(key);
return v == null ? 0 : Long.parseLong(v);
}
@Override
public void delete(String... keys) {
redis().del(keys);
}
@Override
public synchronized void close() {
if (connection != null) connection.close();
client.shutdown();
}
}