lettuce
lettuce copied to clipboard
Client-side caching clarifications
Hello
I see that Lettuce support client side caching https://github.com/lettuce-io/lettuce-core/issues/1281 - it is great, thanks for your effort to implement it :)
As I couldn't find more examples than in mentioned above ticket I have two questions:
- what is client side cache behaviour on Redis connection drop? I would like to keep high consistency so preferred would be to flush cache on connection drop. Is it possible to configure in such way (if it is not default behaviour)?
- is it possible to set any TTL for client side cache entries? I know that protocol should propagate invalidation signals, anyway I would like to invalidate cache entires after some long TTL (few hours). Is it sth that I can configure or it is sth that I would need implement externally?
Thanks for answers in advance!
Regards
hi Sebarys,
I got the same requirement to set TTL for client cached items. And I found a way to do that as below:
-
Write your own cache accessor by implements interface "io.lettuce.core.support.caching.CacheAccessor".
-
Use Guava or Caffeine cache to implements the methods "public V get(K key)"....., you need to initial Guava or Caffeine cache in contructor like: public CaffeineCacheMap() { this.cache = Caffeine.newBuilder(). expireAfterWrite(clientSideCacheExpireTimeMinutes, TimeUnit.MINUTES). maximumSize(clientSideCacheMaxNumber). build(); }
@Override
public V get(K key) { debugLogger.log(CLASS_NAME,"get(key)", "get cache with key [" + key + "]"); return cache.getIfPresent(key); }
@Override public void put(K key, V value) { debugLogger.log(CLASS_NAME, "put(key,value)", "put cache with key [" + key + "] and value [******]"); cache.put(key,value); }
@Override public void evict(K key) { debugLogger.log(CLASS_NAME,"evict(key)", "remove cache for key [" + key + "]"); cache.invalidate(key); }
-
put your own CacheAccessor as parameter when trying to enable client-caching for Luttuce StatefulRedisConnection. like: lettuceClient = (LettuceClient) GlobalRedisClientFactory.getClient(); StatefulRedisConnection<String, String> redisConnection = lettuceClient.unwrap(); frontend = ClientSideCaching.enable(cacheMap, redisConnection, TrackingArgs.Builder.enabled()); the "cacheMap" is an instance of your own implemented CacheAccessor.
But still, we still need to find a way to clean caches when "reconnection" happen since reconnection will make redis caching tracing not working anymore.
You can register a connection listener with the Redis Client to react to connect/disconnect events and issue commands after connecting.
Thanks @dkrcharlie and @mp911de ! So current implementation works only to first connection issues with Redis?
Yep.
Hi @sebarys
How have you dealt with the issue that the current implementation only works for the first connection?
You can register a connection listener with the Redis Client to react to connect/disconnect events and issue commands after connecting.
Hi, i got a problem here. when i try to enable tracking in the event listener, the command always get timeout... here is my code:
@Slf4j
@Component
@RequiredArgsConstructor
public class CacheTrackingPlugin {
private final Cache<String, String> cache; // Caffeine cache
private final LettuceConnectionFactory connectionFactory;
@PostConstruct
public void run() {
AbstractRedisClient absClient = connectionFactory.getNativeClient();
Assert.isInstanceOf(RedisClient.class, absClient);
RedisClient client = (RedisClient) absClient;
Assert.notNull(client, "client should not be null");
// add connection event listener
client.addListener(new RedisConnectionStateListener() {
@Override
@SuppressWarnings(value = {"unchecked", "rawtypes"})
public void onRedisConnected(RedisChannelHandler<?, ?> connection, SocketAddress socketAddress) {
log.info("========onRedisConnected========");
cache.invalidateAll();
StatefulRedisConnection conn = (StatefulRedisConnection) connection;
// enable tracking manually -> always timeout !!!
conn.sync().clientTracking(TrackingArgs.Builder.enabled());
log.info("========Enable Tracking========");
// add listener, to invalidate cache objects
conn.addListener(message -> {
if (message.getType().equals("invalidate")) {
List<Object> content = message.getContent(StringCodec.UTF8::decodeKey);
List<String> keys = (List<String>) content.get(1);
log.info("invalidated keys: {}", keys);
keys.forEach(cache::invalidate);
}
});
}
@Override
public void onRedisDisconnected(RedisChannelHandler<?, ?> connection) { }
@Override
public void onRedisExceptionCaught(RedisChannelHandler<?, ?> connection, Throwable cause) { }
});
// connect to redis
client.connect();
}
}
did i miss someting ? :(