Padrão Cache-aside

Hi guys 👋

Pessoal, neste post vamos falar um pouco sobre o padrão Cache-Aside e como estou utilizando essa estratégia no meu projeto.

O que é o padrão Cache-Aside?

Cache-Aside é uma estratégia de cache onde armazenamos o resultado de uma consulta que é custosa para a aplicação. Dessa forma, após o valor estar salvo em cache, não é necessário realizar novamente a consulta no banco de dados ou em uma API externa.

O resultado disso é:

. ⏱️ Menor tempo de resposta
. 💰 Redução do custo de processamento
. 📉 Menor carga sobre serviços externos e banco de dados

Posso usar cache em tudo?

Não exatamente.

Cache é uma excelente solução para melhorar a performance da aplicação, mas não é bala de prata. Por isso, deve ser utilizada com parcimônia e sabedoria, avaliando bem o contexto e os dados envolvidos.

Onde não devo usar cache?

O padrão Cache-Aside tem como objetivo manter em cache, por um determinado tempo o resultado de consultas ao banco de dados ou a serviços externos, evitando chamadas repetidas enquanto o valor ainda for válido.

Porém, não faz sentido usar cache quando precisamos garantir a integridade do dado em tempo real, como por exemplo:

.💰 Saldo de conta bancária
.📦 Estoque de produtos
.💲 Preço de um item
.📈 Cotação de criptomoedas

Esses valores podem mudar com muita frequência e precisam ser recuperados no exato momento da consulta.

Embora existam estratégias de invalidação de cache, se o custo de implementar e manter esse controle for maior do que o custo da consulta original (seja em complexidade ou processamento), na minha opinião não compensa armazenar esse tipo de dado em cache.

Cache-Aside na prática: como implementei no meu projeto

Depois de todos os disclaimers, vamos para a parte prática 😊

Vou mostrar como apliquei cache na minha aplicação Redesenhe e onde vejo necessidade de uso no sistema atualmente.

⚙️ Passos da implementação:

  1. Adicionei a dependência do Redis no pom.xml

     <dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-data-redis</artifactId>
    		<exclusions>
    			<exclusion>
    				<groupId>io.lettuce</groupId>
    				<artifactId>lettuce-core</artifactId>
    			</exclusion>
    		</exclusions>
    	</dependency>
    	<dependency>
    	    <groupId>redis.clients</groupId>
    		<artifactId>jedis</artifactId>
     </dependency>
  2. Criei um arquivo de configuração Responsável por criar os Beans necessários para o Redis

    @Configuration
    public class RedisConfig {
    
     @Value("${spring.redis.host}")
     private String host;
    
     @Value("${spring.redis.port}")
     private Integer port;
    
    
     @Bean
     public JedisConnectionFactory jedisConnectionFactory() {
         RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(host, port);
         return new JedisConnectionFactory(config);
     }
    
    
     @Bean
     public RedisCacheConfiguration cacheConfiguration(ObjectMapper mapper) {
         ObjectMapper mapperCopy = mapper.copy()
                 .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                 .activateDefaultTyping(
                         new ObjectMapper().getPolymorphicTypeValidator(),
                         ObjectMapper.DefaultTyping.EVERYTHING,
                         JsonTypeInfo.As.PROPERTY
                 );
         return RedisCacheConfiguration.defaultCacheConfig()
                 .entryTtl(Duration.ofMinutes(10))
                 .serializeValuesWith(
                         RedisSerializationContext.SerializationPair.fromSerializer(
                                 new GenericJackson2JsonRedisSerializer(mapperCopy)
                         )
                 );
     }
    }
  3. Defina as credenciais de conexao

    redis:
      ssl: false
      host: localhost
      port: 6379
      timeout: 10000
  4. Criei uma camada de persistência Para separar a lógica de armazenamento e recuperação do cache

    @Component
    public class RedisPersiste {
    
     @Autowired
     private StringRedisTemplate redisTemplate;
    
     public String obter(final String key) {
         return redisTemplate.opsForValue().get(key);
     }
    
     public void save(final String key, final String value, final Long ttl) {
         redisTemplate.opsForValue().set(key, value, ttl, TimeUnit.MINUTES);
     }
    
     public void delete(final String key) {
         redisTemplate.delete(key);
     }
    }
    
  5. Implementei a lógica de leitura e escrita no cache Quando o valor não existe no cache, busco na fonte original e salvo print cache print cache recupera print convert em obj

  6. Teste de performance (sem cache) Tempo de resposta da API quando não há registro em cache print resposta sem cache

  7. Teste de performance (com cache) Tempo de resposta da mesma API com o valor já em cache print resposta com cache

Quer se aprofundar mais?

Se você quiser entender melhor o padrão Cache-Aside, recomendo fortemente este vídeo incrível do Renato Augusto:

Cache-Aside

Pessoal, por hoje é isso! Espero que esse conteúdo tenha despertado seu interesse em aprender mais sobre cache e arquitetura de software.

Até o próximo post 👋

Carregando comentários...