← Volver al blog

Optimización de costos: Cache de Secrets y DynamoDB con Redis/Valkey

March 19, 2025

En aplicaciones que hacen un uso intensivo de Secrets Manager y DynamoDB, los costos operativos pueden dispararse rápidamente si no se aplica una estrategia de optimización adecuada. En este artículo, comparto cómo hemos implementado una solución de cache con Redis (y posteriormente Valkey) que nos ha permitido reducir los costos de estos servicios de 300$ a 45$ mensuales, mejorando además la latencia de nuestras aplicaciones.

El problema: Costos desproporcionados en API calls

Nuestra arquitectura consistía en diversos componentes que accedían frecuentemente a secrets y datos de configuración:

Componentes afectados:

¿El problema principal? Cada vez que un worker o la web necesitaba un secret o una configuración, hacía una petición directa a AWS Secrets Manager o DynamoDB. Esto significaba:

La solución: Capa de cache con Redis

La estrategia fue sencilla pero efectiva: interponer una capa de cache entre la aplicación y los servicios de AWS.

Arquitectura inicial: Redis in-cluster

La primera implementación utilizó Redis desplegado dentro del clúster Kubernetes:

Características:

Beneficios inmediatos:

  1. Reducción drástica de peticiones: La gran mayoría de consultas se servían desde cache
  2. Mejora de latencia: Las consultas locales eran significativamente más rápidas
  3. Coste menor: El coste de Redis dentro del clúster era insignificante comparado con el ahorro

Implementación: Patrón Cache-Aside

Para la implementación, utilizamos el patrón Cache-Aside (también conocido como Lazy Loading):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Pseudocódigo del patrón Cache-Aside
def get_secret(secret_name):
    # 1. Primero, intentamos obtener del cache
    cached_value = redis.get(f"secret:{secret_name}")
    if cached_value:
        return cached_value

    # 2. Si no existe en el cache, vamos a la fuente
    secret_value = secrets_manager.get_secret_value(secret_name)

    # 3. Guardamos en el cache con un TTL adecuado
    redis.setex(f"secret:{secret_name}", 3600, secret_value)

    return secret_value

Estrategia de TTL:

Migración a Valkey (AWS)

Una vez validamos que el approach de cache funcionaba correctamente, decidimos migrar a Valkey gestionado por AWS (ElastiCache).

¿Por qué Valkey?

Valkey es el proyecto open source que surgió después de la divergencia de Redis. AWS lo adoptó como opción por defecto para ElastiCache.

Ventajas de la migración:

  1. Gestión reducida: No hay que gestionar la infraestructura del cache
  2. Alta disponibilidad: Replicación automática y failover
  3. Escalabilidad: Fácil escalar verticalmente u horizontalmente
  4. Monitorización: Integración nativa con CloudWatch
  5. Seguridad: Encriptación en tránsito y reposo

Configuración implementada:

Resultados cuantificables

Ahorro en costes

Antes (sin cache):

Después (con Valkey):

Ahorro total: 255$/mes (85% de reducción)

Mejora en rendimiento

Más allá del ahorro económico, las mejoras de rendimiento han sido significativas:

Beneficios operativos

  1. Reducción de dependencias externas: La mayoría de peticiones no salen del clúster
  2. Mejor experiencia de usuario: Las APIs responden más rápido
  3. Menos limitaciones: Reducción significativa de throttling a Secrets Manager y DynamoDB
  4. Escalabilidad: El sistema puede gestionar más carga con los mismos recursos

Lecciones aprendidas

1. No hace falta cachear todo

Es importante analizar qué datos se benefician realmente del cache:

2. El tamaño importa

El tamaño de la instancia de Valkey debe ajustarse según:

En nuestro caso, un cache.t4g.small ha sido suficiente, pero hay que monitorizar.

3. Monitorización es clave

Hemos implementado alertas para:

4. Planificando el fallback

Siempre debemos tener un plan por si Valkey cae:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
def get_with_fallback(key, fetcher, ttl=3600):
    try:
        # Intentar obtener del cache
        cached = redis.get(key)
        if cached:
            return cached
    except RedisError:
        # Si Redis falla, vamos directamente a la fuente
        pass  # Fallback

    # Obtener de la fuente (AWS)
    value = fetcher()

    # Intentar cachear (opcional, puede fallar)
    try:
        redis.setex(key, ttl, value)
    except RedisError:
        pass  # No fallar la operación si cache falla

    return value

Esta estrategia garantiza que el sistema siga funcionando aunque el cache falle.

Conclusión

La implementación de una capa de cache con Redis/Valkey entre nuestra aplicación y los servicios de AWS (Secrets Manager y DynamoDB) ha sido una de las optimizaciones más impactantes que hemos realizado.

Resultados finales:

La clave del éxito ha sido:

  1. Analizar el patrón de uso antes de implementar
  2. Comenzar sencillo (Redis in-cluster) y después escalar (Valkey AWS)
  3. Implementar estrategias adecuadas de TTL e invalidación
  4. Monitorizar continuamente para optimizar
  5. Planificar el fallback para garantizar disponibilidad

Para cualquier aplicación que haga un uso intensivo de Secrets Manager, DynamoDB o cualquier servicio de AWS con costes por API call, el uso de un cache como Valkey es, casi seguro, una de las mejores inversiones que puedes hacer. El retorno es inmediato, tanto en costes como en rendimiento.

Si tu aplicación tiene una factura mensual elevada en estos servicios, te animo a probar esta estrategia. Los resultados, como hemos visto nosotros, pueden ser sorprendentes.