.env.
Lição 0: do zero ao primeiro comando (15 min)
Client-side caching¶
Milhares de GET iguais por minuto para um valor que muda raramente: client-side caching guarda a resposta na memória da própria aplicação e deixa o servidor avisar quando ela mudar. O Redis faz o tracking das chaves que cada conexão leu e manda uma invalidação quando alguém escreve nelas. Leitura em microssegundos, zero viagens de rede, nenhum TTL no chute.
O que o lab faz¶
- Criar
quest:config:motde lê-la 1000 vezes com um client com cache local (1 miss, 999 hits) - Comparar com 200
GETsem cache e medir a diferença - Mudar a mensagem por um segundo client e ver a invalidação chegar
- Jedis: RESP3 +
CacheConfig+getCache().getStats(); Lettuce:ClientSideCaching+CacheFrontend
Faça agora¶
./quest run 301-02 jedis
./quest run 301-02 lettuce # opcional: mesmo lab, outro client
./quest verify 301-02
O código¶
DefaultJedisClientConfig config = DefaultJedisClientConfig.builder()
.resp3() // invalidations arrive as RESP3 push messages
.build();
CacheConfig cacheConfig = CacheConfig.builder().maxSize(1000).build();
try (RedisClient cached = RedisClient.builder()
.hostAndPort(new HostAndPort(host, port))
.clientConfig(config)
.cacheConfig(cacheConfig)
.build()) {
for (int i = 0; i < 1000; i++) cached.get(motd); // 1 miss, then the cache answers
CacheStats stats = cached.getCache().getStats();
stats.getHitCount(); // 999
stats.getMissCount(); // 1
plain.set(motd, "Aviso do mestre do jogo: ..."); // another client writes the key
cached.get(motd); // invalidated: goes to the server, returns the new value
stats.getInvalidationCount(); // 1
}
StatefulRedisConnection<String, String> connection = client.connect(); // RESP3 is the default
Map<String, String> map = new ConcurrentHashMap<>();
try (CacheFrontend<String, String> frontend = ClientSideCaching.enable(
CacheAccessor.forMap(map), connection, TrackingArgs.Builder.enabled())) { // CLIENT TRACKING ON
for (int i = 0; i < 1000; i++) frontend.get(motd); // one GET, then the map answers
plain.set(motd, "Aviso do mestre do jogo: ..."); // another connection writes the key
frontend.get(motd); // the push evicted the entry: GET again
}
No lab, um CacheAccessor próprio (dez linhas em volta do mesmo Map) conta hits, misses e evicts para mostrar os números no console.
No Redis Insight¶
Abra o Profiler antes de rodar. Com cache, as 1000 leituras aparecem como um único GET quest:config:motd; as 200 sem cache aparecem uma a uma. No Workbench, rode CLIENT LIST durante o lab: a conexão com cache tem resp=3 e a flag t (tracking ligado). No Browser, quest:config:motd mostra a mensagem nova depois que o mestre do jogo a trocou, e quest:csc:clients diz qual client rodou.
Por dentro
| Comando | O que faz |
|---|---|
HELLO 3 |
Negocia RESP3 no handshake; os dois clients fazem isso sozinhos |
CLIENT TRACKING ON |
Pede ao servidor para lembrar as chaves lidas nesta conexão (o Jedis manda ao abrir cada conexão com cacheConfig) |
GET quest:config:motd |
Só o primeiro vai ao servidor; a resposta fica no cache local |
SET quest:config:motd ... |
Feito por outro client: dispara a mensagem de invalidação para quem leu a chave |
invalidate (push) |
Mensagem RESP3 do servidor; o client apaga a entrada e a próxima leitura volta ao Redis |
CLIENT INFO |
Mostra resp=3, a prova de que a conexão aceita mensagens push |
Em produção
- Redis Cloud e Redis Software: client-side caching exige banco na versão 7.4 ou superior e RESP3. O modo de duas conexões (
REDIRECT) não é suportado nesses produtos, e o Jedis não implementaBCAST,OPTINnemOPTOUT: use o modo padrão, como no lab. - O exemplo usa
ClientSideCachingna versão Lettuce 7.7.0 fixada no projeto. Ao atualizar o client, confira o status e os requisitos dessa API na documentação da versão adotada; tracking e invalidação continuam sendo os conceitos a entender. - Cache é para o que muda pouco e lê muito. Contadores e rankings geram uma enxurrada de invalidações: sirva esses por uma conexão sem cache. Dimensione
maxSizepelo tamanho médio dos valores (MEMORY USAGE) e lembre que qualquer desconexão do client zera o cache.
Desafio
Mude maxSize para 1 e leia duas chaves alternadas (quest:config:motd e quest:world:motd, da lição 100-02): veja getEvictCount() subir e os hits caírem. Depois chame cached.getCache().deleteByRedisKey(motd) e confirme que a leitura seguinte vira miss sem nenhuma escrita no servidor.