Idiomaesp
← Volver a proyectos

Patrón Cache-Aside con Redis y Fingerprinting para aliviar MongoDB

enTelefónicaTelefónica Logo

Cómo eliminamos lecturas innecesarias de MongoDB en un sistema reactivo de alto rendimiento, utilizando Redis y fingerprinting.

2023⏱️ 4min de lectura
KotlinProject ReactorRedisMongoDB

El problema

En Telefónica, utilizamos Azure IoT Hub en un proyecto de telemetría para recibir millones de eventos diarios. El sistema estaba bien diseñado, utilizando Project Reactor con Kotlin, lo que le permitía procesar el tráfico entrante de manera eficiente y asíncrona.

Arquitectura Actual: Alta carga de lectura en MongoDB
MongoDB se convirtió en el principal cuello de botella de la infraestructura debido al enorme volumen de operaciones de lectura.

La aplicación comprobaba si ciertos valores de un evento entrante diferían de los que ya estaban guardados en la base de datos. Si eran diferentes, los actualizaba y activaba otros procesos y microservicios; si no, el evento se descartaba.

Aunque funcionalmente el flujo era correcto, operativamente significaba que cualquier evento recibido terminaba sobrecargando MongoDB. MongoDB se convirtió en uno de los componentes más utilizados del sistema.

Teníamos que aplicar algún tipo de caché.

Restricciones

Una caché tradicional no era lo correcto para este caso de uso.

Los documentos de MongoDB contenían significativamente más información de la necesaria para decidir si se debía activar una actualización. Almacenar el documento completo era innecesario y probablemente habría elevado tanto el coste de Redis que ni siquiera se habría considerado como solución. La única opción era guardar solo los valores que la lógica de negocio definía como “dignos de actualización”. Pero podíamos hacerlo mejor.

La solución: Crear un fingerprint de los valores relevantes

Siguiendo la estrategia trazada por el equipo de arquitectura, colaboré con un ingeniero senior para implementar el mecanismo de fingerprinting en pair programming.

Como ya hemos dicho, este era un sistema reactivo de alto rendimiento, por lo que la velocidad era la clave. Por eso recurrimos al fingerprinting.

En lugar de guardar los valores que podrían activar una actualización, guardamos un hash compuesto por esos valores.

De esta forma, el flujo de trabajo pasó a ser:

  1. Recibir un evento.
  2. Generar el hash a partir de los campos relevantes.
  3. Comparar el hash generado con el valor almacenado en Redis.
  4. Si el hash difiere, actualizamos.
  5. Si el hash coincide, omitimos por completo la interacción con la base de datos.

De este modo, Redis se convirtió en una capa de detección de cambios por encima de nuestro MongoDB, reduciendo las costosas lecturas que antes sobrecargaban nuestra base de datos.

Para el hashing, usamos xxHash debido a su velocidad y baja tasa de colisiones.

Arquitectura Solucionada: Carga de lectura optimizada

El resultado

En MongoDB se redujo drásticamente el número de lecturas, mejorando su velocidad al reducir la carga.

En cuanto a Redis, dado que solo se almacenaban valores hash compactos, el tamaño en memoria era una pequeña fracción de lo que hubiese requerido, si hubiesemos guardado el documento completo.

El fingerprinting demostró ser rápido y eficiente, y tras los cálculos pertinentes, el riesgo teórico de colisiones de hash se consideró insignificante para este caso de uso, quedando compensado por el rendimiento ganado.

Al final, nuestra solución fue muy eficiente: el tamaño de Redis estaba contenido, los tiempos de lectura mejoraron enormemente en comparación con la implementación anterior y se consiguió reducir los recursos que gastaba MongoDB.

Conclusiones y crecimiento personal

  • Conocimiento de aquitectura: Este proyecto me enseñó a buscar oportunidades de optimización en cualquier flujo de trabajo con BBDD, no solo en los índices.
  • Inmersión técnica: Fue una oportunidad para mejorar mi conocimiento de Kotlin y sumergirme de lleno en la programación reactiva.
  • Trabajo en equipo: Siempre disfruto del pair programming, y trabajar rodeado de personas con tal capacidad técnica y visión, adquirida en más de una década de experiencia, fue genial. Recuerdo este proyecto con mucho orgullo gracias al ese increíble equipo que me ayudó a crecer como profesional.