Categorías
Desarrollo

Cacheo en Memoria con Spring Boot Cache

En este artículo comentaremos de forma muy breve qué es una caché y cómo Spring Boot puede ayudarnos a implementar de forma muy sencilla un mecanismo de caché básico residente en memoria.

¿Qué es una caché?

“Una caché es una capa de almacenamiento de datos de alta velocidad que almacena un subconjunto de datos, normalmente transitorios, de modo que las solicitudes futuras de dichos datos se atienden con mayor rapidez que si se debe acceder a los datos desde la ubicación de almacenamiento principal. El almacenamiento en caché permite reutilizar de forma eficaz los datos recuperados o procesados anteriormente.” (https://aws.amazon.com/es/caching/)

¿Cuándo utilizar una caché? 

Puede ser beneficioso para el rendimiento de nuestros sistemas implementar una caché en situaciones en las que detectemos que nuestro sistema se vuelve más lento debido a:

  • Accesos a datos muy frecuentes (tablas maestras, por ejemplo).
  • Cálculos complejos que se repiten.
  • Solicitudes a determinadas APIs.

Implementando una caché en Spring Boot

En este ejemplo usaremos Gradle como gestor de dependencias.

En primer lugar, debemos incluir la siguiente dependencia correspondiente en nuestro proyecto.

implementation group: 'org.springframework.boot', name: 'spring-boot-starter-cache'

Posteriormente, con la etiqueta @EnableCaching en la clase de inicio o principal del proyecto, habilitaremos nuestra aplicación para usar el almacenamiento en caché.

@SpringBootApplication
@EnableCaching
public class MyCacheableApplication {
   public static void main(String[] args) {
      SpringApplication.run(MyCacheableApplication.class, args);
   }
}
@Cacheable

Para activar el cacheo en un método, usaremos la etiqueta @Cacheable con el nombre de la caché donde se van a almacenar los resultados de las invocaciones a dicho método.

Vamos a simular un método que ocupa 5 segundos aproximadamente en cada ejecución, y que retorna un objeto de tipo Resource.

@Cacheable("resources") 
public Resource getResource(Integer resourceId) {    
    Resource resource = new Resource();    
    resource.setId(resourceId);    
    try {        
        Thread.sleep(Long.parseLong("5000"));    
    } catch (InterruptedException e) {        
        e.printStackTrace();    
    }    
    return resource; 
}

Podemos ver como para un valor de entrada determinado (54), la primera llamada dura 5 segundos pero las sucesivas son inmediatas, ya que se ha almacenado en caché el resultado del método.

2022-06-27 17:07:16.773  INFO… : INIT: getResource with ID: 54
2022-06-27 17:07:21.786  INFO… : END: getResource with ID: 54

2022-06-27 17:07:24.271  INFO… : INIT: getResource with ID: 54
2022-06-27 17:07:24.273  INFO… : END: getResource with ID: 54

2022-06-27 17:07:49.201  INFO… : INIT: getResource with ID: 54
2022-06-27 17:07:49.202  INFO… : END: getResource with ID: 54

Ejecutando con otro valor de entrada, la primera llamada vuelve a ocupar 5 seg pero las sucesivas vuelven a ser inmediatas.

2022-06-27 17:07:54.481  INFO… : INIT: getResource with ID: 55
2022-06-27 17:07:59.483  INFO… : END: getResource with ID: 55

2022-06-27 17:08:00.842  INFO… : INIT: getResource with ID: 55
2022-06-27 17:08:00.843  INFO… : END: getResource with ID: 55
@CacheEvict

Evidentemente, el espacio en memoria es limitado, y las cachés pueden crecer muy rápido. Utilizaremos la anotación @CacheEvict para eliminar la caché indicada (en este caso “resources”)

@CacheEvict("resources")
public Resource save(Resource resource) {
   //....
}
@CachePut 

Actualiza el contenido de la caché, de igual forma que @Cacheable, pero además invocará la llamada del método cada vez. El método siempre se ejecutará y el resultado será cacheado.

@CachePut("resources")
public Resource save(Resource resource) {
   //....
}

Este artículo práctico ha pretendido ser un ejemplo básico sobre el uso de la caché por defecto de Spring, que utiliza un ConcurrentHashMap en memoria.

Además, Spring Boot incluye soporte para otras implementaciones de proveedores de caché como Redis, EhCache, Caffeine, … (https://docs.spring.io/spring-boot/docs/current/reference/html/io.html#io.caching)