Durante la serie de post sobre CDC que hemos estado desarrollando en el blog, hemos comentado en múltiples ocasiones una de las principales dificultades que tiene esta tecnología: el bajo nivel semántico de la información que se obtiene.

Entendiendo el problema

Cuando aplicamos CDC, escuchando los logs transaccionales, obtenemos la misma información que se mueve en nuestra base de datos, lo que en una situación típica en la que trabajamos sobre un esquema normalizado se traduce en información demasiado simple con muy poco significado.

Vamos a reutilizar el pequeño modelo de datos del artículo CDC, más allá de la modernización para ejemplificar esta situación:

En este caso, podríamos pensar que el proceso de una venta se podría reducir a una inserción en la tabla de venta y otra en la tabla de facturas. Esta operación se realizaría dentro de un contexto transaccional en nuestra base de datos, por lo que se asegura su atomicidad.

Si nos centramos en la información de venta, observamos que gran parte de la información se aporta a través de identificadores que relacionan la venta con información contenida en otras tablas. Esa es la información que nosotros vamos a obtener si aplicamos técnicas de CDC sobre esa tabla: eventos llenos de identificadores que apuntan a otras tablas.
En el caso de la tabla de facturas, vemos que la situación es muy similar, puesto que tendremos nuevamente un conjunto de identificadores en nuestro evento referenciando información que no viene incluida en el evento en sí.

Hasta ahora habíamos visto que el tratamiento de los eventos requería, en muchos casos, fases de enriquecimiento (en algunos casos complejas) en las que aportamos semántica a la información de cara a su posterior procesado. En este post vamos a ver una alternativa que puede ser útil en algunos casos.

Entendiendo el patrón outbox

El nombre ‘oficial’ del patrón es transactional outbox y, como su nombre indica, es un patrón que sirve para ejecutar operaciones dentro de un contexto transaccional.

Vamos a describirlo brevemente (podéis encontrar muchas más información y detalle en internet al respecto):

Caso de uso del patrón

El caso de uso es el siguiente:

Tenemos un flujo de negocio que nos obliga a actualizar (ya sea inserción, modificación o borrado) nuestra base de datos y, además, notificar esta operación a través de un evento. Este flujo debe ser atómico, de forma que se garantice la consistencia e integridad de la información.

Volviendo a nuestro ejemplo, supongamos que con cada venta (cada inserción en nuestra tabla venta) debemos producir un evento para que se pueda descontar la cantidad de producto asociado a la venta del almacén, de forma que la información de stock siempre sea coherente.

Uno de los mecanismos habituales para acometer este tipo de operación en el que intervienen más de un sistema es utilizar 2 phase commit (2PC a partir de ahora). Existe mucha información sobre 2PC, os animo a que lo consultéis si no conocéis su funcionamiento.

Sin embargo, existen varias situaciones en las que no es aconsejable utilizar 2PC: el sistema de notificación de eventos puede no soportarlo, podría ser no aconsejable debido a los bloqueos que produce en la base de datos hasta que se hace commit de la transacción… Pensemos, entonces, que en este caso de uso 2PC no es posible / aconsejable.

Bajo este caso de uso, el patrón outbox puede ser una buena alternativa.

Funcionamiento del patrón

La solución que nos propone el patrón outbox aplicado a nuestro ejemplo es el siguiente:

Vamos a generar en nuestra base de datos una tabla de ‘outbox,’ donde vamos a almacenar información relevante a las operaciones que realizamos sobre la tabla venta. Todo ello, bajo la misma transacción que utilizamos para realizar la modificación y, por lo tanto, de forma atómica (ya que estamos bajo el paraguas de la transacción que realizamos en nuestra base de datos).

Aquí debemos prestar atención a un punto importante: la tabla de outbox va a describir la información relevante de la operación, contando con que una operación puede ser una inserción, una actualización o un borrado (las operaciones de consulta en este caso no son relevantes). La tabla de outbox, por lo tanto, va a recibir únicamente inserts describiendo el insert, update o delete que se produzca en la tabla venta.

Una vez tenemos la información en la tabla outbox, podemos crear un proceso que recoja recurrentemente la información de los cambios almacenados (un simple proceso batch o a través de un mecanismo que sea reactivo en función de las necesidades).

Este mecanismo garantiza una consistencia eventual de la información: la información de la tabla venta y outbox es transaccional, por lo que siempre será consistente.

Por otro lado, el proceso de lectura va a poder recoger la información de la tabla outbox y publicarla en el broker en algún momento en el tiempo, consiguiendo así esa consistencia eventual entre toda la información que hemos comentado.

El patrón tiene algunos detalles en los que no vamos a entrar ahora mismo (posibles problemas de mensajes duplicados en caso de error, por ejemplo) y que sería necesario evaluar antes de su aplicación.

Uso del patrón outbox para aportar semántica a los eventos

Una vez hemos visto el patrón, retomamos la problemática inicial y vamos a ver cómo podemos utilizar sus ideas para que nos ayude con el problema semántico.

Analizando la tabla outbox (su contenido y su finalidad) estamos haciendo ‘a mano’ un event-sourcing de la tabla ventas, similar a lo que se obtiene aplicando técnicas CDC a través de logs transaccionales. A diferencia de CDC, la información que incorporamos en la tabla de outbox es la información que nosotros, en base a código, definimos que debe incluirse.

Vamos a aprovechar esta posibilidad y nos vamos a tomar la libertad de desvirtuar ligeramente el propósito del patrón para incluir en esta tabla toda la información semántica que necesitemos y hacer CDC sobre esta tabla outbox en vez de la tabla ventas original.

En este caso, aprovechamos para introducir información relativa a la categoría del producto que se ha vendido, de forma que la información está presente en la tabla de outbox.

Aplicamos CDC sobre esta tabla, por lo que sobre cada inserción vamos a producir un evento que va a incluir ese ‘dato enriquecido’.

Las inserciones en la tabla outbox se realizan dentro del contexto transaccional (se inserta dentro de la transacción sobre la tabla venta), por lo que aseguramos siempre una coherencia de la información.

Recordemos que la información de la tabla outbox debe reflejar cualquier operación de la tabla principal, por lo que los borrados y modificaciones deben representarse igualmente. De esta forma, a través de CDC podremos también recoger esa información.

Agregando información bajo un solo evento

Vamos a darle una vuelta más a este método y aprovecharemos para tratar, además, un problema que en algunos casos es difícil de solventar al trabajar con CDC: la unificación de eventos de la misma transacción.

Originalmente, indicábamos que “el proceso de una venta se podría reducir a una inserción en la tabla de venta y otra en la tabla de facturas”. Es decir, se están realizando dos inserciones en tablas distintas bajo la misma transacción.

Si aplicamos CDC, el resultado serán dos eventos generados, cada uno representando la inserción en cada una de las tablas.

Intentar conciliar la información para generar un único evento enriquecido con la información de los dos eventos es un poco complicado. Hay que esperar a que lleguen los dos para unificarlos y generar el evento resultado, lo que en muchos casos se traduce en meter piezas intermedias donde poder acumular aquellos eventos sobre los que todavía no se ha encontrado la ‘segunda parte’ (por ejemplo, se ha recibido el evento de la inserción en ventas, pero el de facturas todavía no ha llegado).

Esta situación se puede resolver nuevamente de forma sencilla haciendo uso de nuestra tabla outbox:

Nuevamente, nos encontramos con un contexto transaccional para ejecutar los insert en las tablas Facturas, Ventas y Outbox, lo que nos garantiza la atomicidad.

Volvemos a tener el control sobre el almacenamiento en la tabla outbox, y, por lo tanto, podemos incorporar la información relevante de la venta y de la factura, unificándola y generando un único evento a través de CDC con todos los datos necesarios. Esto nos permite evitar una conciliación o enriquecimiento en un proceso separado.

Aspectos negativos de esta alternativa

Hasta ahora hemos visto cómo aplicar este mecanismo, pero este proceso no está exento de efectos negativos o complicaciones que es necesario estudiar antes de plantearse su aplicación:

No modificar la aplicación era una de las características que resultaba más interesantes de modernización a través de CDC. Con esta alternativa, está claro que es imprescindible alterar la aplicación para soportar el uso de la tabla outbox.

El trabajo sobre la tabla outbox es ‘manual’ y no siempre trivial si estamos hablando de enriquecer o agregar información. Nos vamos a encontrar casos en los que estaremos incorporando mucha lógica para gestionar la tabla outbox, pero que es necesario para poder realizarla dentro de la misma transacción.

Hemos hablado de insertar datos en una tabla extra, enriquecer estos datos leyendo de una tabla adicional… estamos incrementando la complejidad de la transacción y alargando el tiempo que va a requerir, por lo tanto, su procesamiento (a todos los niveles, cómputo en la propia aplicación, carga sobre la BD por consultas y la nueva inserción…).

En ciertas operaciones críticas, este incremento puede tener consecuencias muy importantes, haciendo esta opción no viable.

Conclusión

En este post hemos visto un uso alternativo de un patrón conocido y cómo podemos utilizarlo para solucionar ciertos problemas asociados a la utilización de CDC.

Está claro que no es un mecanismo que podamos aplicar ‘alegremente’, ya que se ajusta a situaciones muy particulares. Pero es en estos casos donde su utilidad puede dispararse y proporcionarnos soluciones muy sencillas a problemas que, de otra forma, tendrían cierta complejidad. En definitiva, una herramienta más que incluir dentro de nuestro catálogo para que nos ayude en nuestros proyectos.

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.

Suscríbete

Estamos comprometidos.

Tecnología, personas e impacto positivo.