hace 10 años
En el vertiginoso mundo del desarrollo de aplicaciones modernas, la gestión eficiente de datos es fundamental. DynamoDB, la base de datos NoSQL totalmente administrada de AWS, se destaca por su escalabilidad y rendimiento excepcionales. Ofrece una latencia de milisegundos de un solo dígito y gestión transparente, liberando a los desarrolladores de las complejidades de la infraestructura. Pero, ¿cómo podemos ir más allá del modelo tradicional de base de datos centralizada y aprovechar el poder de los datos en movimiento? La respuesta reside en el procesamiento de flujos de datos, y en el contexto de DynamoDB, en los eventos de DynamoDB.

¿Qué son los Streams de DynamoDB?
Un Stream de DynamoDB es una secuencia cronológica de eventos que registra todas las modificaciones realizadas en una tabla de DynamoDB, prácticamente en tiempo real. Piensa en ello como un registro detallado de cada cambio: inserciones, actualizaciones y eliminaciones. Cada evento en el stream recibe un número de secuencia único, garantizando el orden de los cambios. Estos eventos se agrupan en fragmentos o shards, que se gestionan automáticamente para escalar según la carga de la tabla.

Imagina un Stream de DynamoDB como una cola de mensajes directa integrada con tu tabla, similar a Kinesis o Kafka, pero específicamente para los eventos de tu tabla DynamoDB. A diferencia del streaming tradicional, donde los datos pueden ser objetos de negocio completos, en un Stream de DynamoDB te limitas a los eventos generados por la tabla específica que lo configura. Es importante destacar que los Streams de DynamoDB se almacenan durante 24 horas, sin posibilidad de extender este periodo, y escalan automáticamente los shards utilizando la funcionalidad de autoescalado de particiones incorporada.
Primeros Pasos con los Streams de DynamoDB
La configuración de los Streams de DynamoDB en una tabla se realiza a través de StreamSpecification. Aquí defines si el stream está habilitado o no (StreamEnabled) y, crucialmente, qué tipo de información quieres incluir en los eventos del stream (StreamViewType). Las opciones para StreamViewType son:
- KEYS_ONLY: Solo incluye las claves primarias (clave de partición y clave de ordenamiento) del elemento modificado.
- NEW_IMAGE: Incluye la imagen completa del elemento después de que se hayan aplicado todos los cambios.
- OLD_IMAGE: Incluye la imagen completa del elemento antes de que se aplicaran los cambios.
- NEW_AND_OLD_IMAGES: Incluye ambas imágenes, la del elemento antes y después de los cambios.
Es importante tener en cuenta que al habilitar o deshabilitar Streams en una tabla DynamoDB, se crea un nuevo stream con un descriptor único. Este descriptor será necesario para cualquier modificación posterior de la configuración del stream.
Existen diversas formas de consumir un Stream de DynamoDB, siendo la más común el uso de funciones Lambda. Lambda se configura como un disparador (trigger) que se activa automáticamente al detectar nuevos eventos en el stream de DynamoDB, poniendo los datos del evento a disposición de la función Lambda como parámetros.
Casos de Uso de Streams de DynamoDB
Veamos algunos ejemplos prácticos de cómo podemos utilizar los Streams de DynamoDB en una aplicación de gestión de tareas, donde los usuarios crean listas de tareas. La base de datos backend es DynamoDB y tenemos una tabla llamada `todoListTask` para almacenar las tareas, con `ListId` como clave de partición y `TaskId` como clave de ordenamiento.
1. Replicación Cross-Region para Acceso Rápido Global
Si tienes usuarios distribuidos geográficamente, por ejemplo, en Estados Unidos y Europa, y tu base de datos está alojada en una región de EE.UU., los usuarios europeos experimentarán latencia. Una solución común es crear réplicas de la tabla en regiones más cercanas a los usuarios europeos. Sin embargo, esto introduce desafíos de sincronización entre las réplicas.

Aunque podrías usar Streams de DynamoDB con Lambda para replicar datos a otras regiones, esta solución personalizada puede ser compleja de mantener. Una alternativa más robusta es usar las tablas globales de DynamoDB. Las tablas globales son una solución gestionada por DynamoDB para la replicación multi-maestro y cross-region, ideal para aplicaciones con usuarios globales. DynamoDB se encarga de la sincronización en segundo plano.
Las tablas globales admiten lecturas consistentes solo dentro de la misma región. Las operaciones consistentes y fuertes deben ocurrir en la misma réplica regional. En caso de conflictos entre réplicas, se aplica la política de "último escritor gana". La configuración de tablas globales se realiza al crear la tabla o, posteriormente, si la tabla réplica está vacía. Algunos puntos clave para configurar tablas globales incluyen: una sola réplica por región, Streams habilitados con `NEW_AND_OLD_IMAGES`, y nombres y claves de tabla idénticos en todas las regiones.
2. Agregación de Datos en Otra Tabla
Imagina que necesitas mantener un recuento de tareas por lista en otra tabla, `todoTaskCount`, para fines de caché o visualización rápida en la aplicación. Esta tabla debe actualizarse cada vez que se añade una nueva tarea a una lista.
Con Streams de DynamoDB y Lambda, puedes automatizar este proceso. Primero, creas la tabla `todoTaskCount`. Luego, habilitas Streams en la tabla `todoListTask` con `NEW_IMAGES` (solo necesitas los nuevos valores). Finalmente, creas una función Lambda que se active con los eventos del stream. Esta función Lambda leerá los eventos de inserción de nuevas tareas en `todoListTask` y actualizará el recuento correspondiente en la tabla `todoTaskCount`. De esta manera, la tabla de recuentos se mantiene sincronizada automáticamente sin sobrecargar la aplicación principal.
3. Indexado de Tareas para Optimización de Búsquedas
Si tu aplicación crece y los usuarios solicitan funcionalidad de búsqueda de tareas, DynamoDB no es la solución ideal para búsquedas complejas. Una alternativa es utilizar un servicio de búsqueda gestionado como Amazon Elasticsearch Service (ahora OpenSearch Service).
Una solución inicial podría ser indexar las tareas en Elasticsearch directamente al crearlas. Sin embargo, esto puede aumentar la latencia de cada solicitud de creación de tarea. Con Streams de DynamoDB, puedes separar el proceso de indexado del proceso de creación de la tarea. Cuando se crea una tarea, se genera un evento en el stream. Una función Lambda, activada por este stream, toma el evento y se encarga de indexar la nueva tarea en Elasticsearch de forma asíncrona. Esto asegura que el tiempo de respuesta para el usuario al crear una tarea no se vea afectado y mejora la experiencia de búsqueda en la aplicación.

4. Auto-Archivado Utilizando TTL y Lambda
A medida que tu aplicación se expande a clientes empresariales, surge la necesidad de funcionalidad de archivado para gestionar listas de tareas crecientes. En lugar de eliminaciones suaves (soft deletes), que pueden inflar el tamaño de la tabla, puedes mantener una tabla separada, `todoListTaskArchived`, para tareas archivadas.
Aquí entra en juego la característica TTL (Time to Live) de DynamoDB. TTL permite especificar un atributo de marca de tiempo para cada elemento. Cuando el valor TTL de un elemento es menor que la hora actual, DynamoDB marca el elemento para su eliminación (aunque la eliminación real puede tardar hasta 48 horas). Puedes combinar TTL con Streams y Lambda para automatizar el archivado.
Configura un atributo `expires` en tu tabla `todoListTask` para indicar cuándo se debe archivar una tarea. Cuando el TTL de una tarea expira y DynamoDB la elimina (eventualmente), se genera un evento de eliminación en el stream. Una función Lambda, activada por este evento, puede tomar el elemento eliminado del stream (configurando el stream con `NEW_AND_OLD_IMAGES` para tener acceso al elemento antes de la eliminación) y moverlo a la tabla `todoListTaskArchived`. Esto automatiza el proceso de archivado basado en TTL y Streams.
¿DynamoDB tiene Triggers? Integración con AWS Lambda
Sí, efectivamente, DynamoDB se integra perfectamente con AWS Lambda para actuar como triggers. Como mencionamos, puedes usar funciones Lambda para procesar registros de Streams de DynamoDB. Cada vez que se actualiza una tabla DynamoDB y se genera un evento en el stream, se puede disparar una función Lambda para realizar trabajo adicional.
Lambda sondea los shards de tu stream de DynamoDB en busca de nuevos registros a una tasa base de 4 veces por segundo. Cuando hay registros disponibles, Lambda invoca tu función y espera el resultado. Si el procesamiento es exitoso, Lambda continúa sondeo en busca de más registros.
Por defecto, Lambda invoca tu función tan pronto como hay registros disponibles. Para evitar invocaciones frecuentes con pequeños lotes de registros, puedes configurar una ventana de procesamiento por lotes (batching window) de hasta 5 minutos. Lambda acumulará registros hasta que tenga un lote completo, expire la ventana de procesamiento, o se alcance el límite de carga útil de 6 MB, antes de invocar la función.

Es crucial tener en cuenta que el procesamiento de eventos en Lambda es al menos una vez, lo que significa que pueden ocurrir procesamientos duplicados. Para evitar problemas, se recomienda que tu código de función Lambda sea idempotente, es decir, que procesar el mismo evento varias veces tenga el mismo resultado que procesarlo una sola vez.
Para escalar el procesamiento de Streams con alto volumen de datos, puedes usar el ajuste ParallelizationFactor. Esto permite procesar un shard de un stream de DynamoDB con múltiples invocaciones de Lambda simultáneamente, aumentando el rendimiento.
Eventos de DynamoDB DAX
En el contexto de DynamoDB Accelerator (DAX), el servicio de caché en memoria para DynamoDB, también existen eventos DAX. Estos eventos representan ocurrencias importantes dentro del sistema DAX, como la creación de un clúster DAX, la adición o eliminación de nodos, o el reinicio de un nodo. Los eventos DAX proporcionan visibilidad sobre el estado y las operaciones de tu clúster DAX.
Cada evento DAX contiene información como la fecha y hora del evento, un mensaje descriptivo, la fuente del evento (por ejemplo, un ID de nodo o el clúster) y el tipo de fuente (CLUSTER, PARAMETER_GROUP, NODE_ID, etc.). Estos eventos son útiles para monitorizar y auditar las operaciones en tu clúster DAX.
Componentes Básicos de DynamoDB: Claves Primarias
Al crear una tabla en DynamoDB, un componente esencial es la clave primaria. La clave primaria identifica de forma única cada elemento dentro de la tabla, asegurando que no haya dos elementos con la misma clave. DynamoDB soporta dos tipos de claves primarias:
- Clave de Partición (Partition Key): Una clave primaria simple compuesta por un solo atributo. DynamoDB utiliza el valor de la clave de partición para determinar la partición física donde se almacenará el elemento. En una tabla con solo clave de partición, no puede haber dos elementos con el mismo valor de clave de partición.
- Clave Primaria Compuesta (Composite Primary Key): Compuesta por dos atributos: la clave de partición y la clave de ordenamiento (Sort Key). DynamoDB usa la clave de partición para la partición física y la clave de ordenamiento para ordenar los elementos dentro de la misma partición. Múltiples elementos pueden tener la misma clave de partición, pero deben tener diferentes valores de clave de ordenamiento.
En resumen, los eventos de DynamoDB, a través de los Streams, abren un mundo de posibilidades para el procesamiento de datos en tiempo real y la creación de aplicaciones reactivas y eficientes. Desde la replicación global hasta la indexación para búsquedas y el archivado automatizado, los Streams de DynamoDB son una herramienta poderosa para sacar el máximo provecho de tus datos en movimiento.
