Development

Registros de comunicación

Este sistema almacena todos los registros de los pares de solicitudes y respuestas que se emitieron al procesar cualquier SMS A2P. Por Milan Mimica

December 11 2015

Alcance

La plataforma de mensajería de Infobip maneja alrededor de 250 millones de transacciones por día, es decir, 250 millones de SMS recibidos del cliente, facturados, direccionados, adaptados si es necesario, remitidos a un operador de telecomunicaciones, procesados con un informe de entrega que se reenvía al cliente. Esto conforma, por lo menos, 6 pares de solicitudes y respuestas en la red que utilizan varias API con (a veces) 4 protocolos completamente diferentes solo para procesar un único mensaje. Eso si todo sale bien y no se necesitan reintentos. Y solo estamos considerando la comunicación externa. No estamos teniendo en cuenta la comunicación interservicio. Además, algunos mensajes se relacionan, y considerar mensajes individuales no brinda el panorama completo.

El desafío de realizar un seguimiento de todas esas operaciones de la red es enorme. El departamento de asistencia técnica necesita resolver rápidamente las dificultades tanto del cliente como del operador. Necesitan una herramienta para buscar todos los pares de solicitudes y respuestas que fueron emitidos al procesar cualquier mensaje porque ver qué fue exactamente lo que envió el cliente y exactamente qué se reenvió al operador es el punto de partida de cualquier trabajo de resolución de problemas.

Registro distribuido

"Distribuido" suena bien. Está de moda. Por alguna razón, instintivamente, queremos que todo esté distribuido. Pero analicemos cómo es en realidad un sistema de registro distribuido.

Lo que tenemos aquí es un servicio independiente para cada protocolo de mensajería del lado del cliente y del operador. Cada servicio maneja sus propios registros. Lo más común es que se almacenen en una memoria escribiéndolos en un disco con un búfer circular. Como los servicios se desarrollan en diferentes equipos, cuesta unificar la apariencia de las interfaces web para administrar registros. Para que todo sea más fácil para quien brinda asistencia técnica, los servicios exponen los registros a través de sus API, y se introduce una interfaz de administración centralizada donde uno puede ver todos los registros de todos los servicios. Lograr que todos los servicios realicen un push de los registros a un sistema de almacenamiento de registros como Graylog es un gran paso hacia la unificación y la centralización. Graylog es bueno. Balancea bien, hace el trabajo. Está acoplada a un clúster de elasticsearch. Almacena un mensaje de registro con metadatos indexados de valor clave. La interfaz web es un poco incómoda al principio, pero es cuestión de acostumbrarse. También tiene algunas otras funciones analíticas útiles.

El problema es que estos registros no se relacionan. No existe ninguna relación entre los 6 pares de solicitudes y respuestas. Se necesita de un análisis engorroso, una extracción manual de identificadores de correlación desde la base de datos de la plataforma de procesamiento interno solo para conectar varios registros de comunicación de un único mensaje. Y esto sin mencionar la búsqueda de registros de comunicación para mensajes relacionados. Esto se debe a que el lado del cliente está separado del lado del operador. No comparten ni un identificador de correlación.

Además, al tener límites bien definidos entre el alcance de los servicios, los servicios del lado del operador no saben nada sobre el lado del cliente y viceversa. Esto quiere decir que los servicios del lado del operador no saben de qué cliente vino el mensaje que están procesando. Esto se denomina separación de intereses y generalmente es algo bueno. Solo que no funciona bien para los registros porque los servicios del lado del operador no pueden enriquecer registros con metadatos de la ID del cliente que podrían ayudar a relacionar los registros. Además, estos servicios no entienden de relaciones entre mensajes porque no lo necesitan. Solo la plataforma de procesamiento interno entiende de relaciones, pero no tiene comunicación externa y, por lo tanto, no produce ningún registro. Esto significa que nuestros registros no tienen ningún metadato de relación asociado con ellos.

Una forma de resolver esto sería que los servicios produzcan los registros y los hagan llegar a comunicación interna a través de la plataforma de procesamiento interno. La plataforma podría, entonces, enriquecer los registros con todos los metadatos y enviarlos al almacenamiento de registros. Esto funcionaría pero está básicamente mal. Ocuparía recursos de procesamiento valiosos y "distraería" a la plataforma de su trabajo de negocio.

Introducción del UUID

Bueno, en realidad, no es nada revolucionario. Nos dimos cuenta de que cada mensaje necesita un identificador único, el UUID (Identificador único universal), asociado al comienzo, cuando entra el sistema, y necesitamos pasar este identificador con el mensaje hacia el siguiente servicio en la cadena de procesamiento. Luego, cada servicio que produzca registros puede anexar este identificador a los metadatos del registro. ¡Qué fácil! Ahora podemos relacionar un grupo de entradas de registro a un único mensaje. Si solo...

Emparejamiento de informes de entrega


No se necesita mucho tiempo para darse cuenta de que hay un problema. Basta con mirar los registros para notar que los informes de entrega no están allí. De nuevo, el problema está en la separación de intereses, y, otra vez, no queremos romperlo porque todavía estamos de acuerdo con que es algo bueno. Los servicios que manejan un protocolo específico pueden enviar un mensaje que recibieron desde la plataforma y manejar un informe de entrega. Emparejar un informe de entrega con un mensaje es una tarea más compleja de lo que parece. Claramente, es un interés de la plataforma. Aun así, queremos que el servicio que recibió el informe de entrega produzca una entrada en el registro al respecto. Es su interés. Sin embargo, al momento de la recepción de la entrega, todavía no se sabe con qué mensaje emparejarlo (¡y si es que va a ser emparejado!). Por lo tanto el servicio no sabe qué UUID anexar en el registro.

Conexión de mensajes relacionados

Como dijimos antes, algunos mensajes se relacionan. En general, son SMS largos que se transfieren de manera individual, pero que se procesan y, a la larga, se muestran en el teléfono celular como un único mensaje. A veces se dividen o se combinan, así que tenemos relaciones de uno con varios, varios con uno y varios con varios. Sería bueno también tener registros de estos mensajes relacionados porque a veces necesitamos mirarlos completos. El problema es que solo la plataforma de procesamiento interno conoce la relación entre los mensajes, y allí es donde ocurre la división o la combinación. Los servicios que manejan comunicación externa no saben nada sobre estas relaciones. Estos manejan mensajes de manera individual y cada mensaje tiene su propio UUID.

Otra fuente de relaciones es el procesamiento en masa. El cliente puede simplemente enviar varios mensajes en una solicitud HTTP. El servicio que maneja HTTP analizará la solicitud y reenviará varios mensajes a la plataforma, pero solo registrará una solicitud y una respuesta. Esto generará un UUID para registrar la solicitud HTTP y un nuevo UUID para cada mensaje generado por esta solicitud.

Gráficas al rescate

Está claro que necesitamos almacenar las relaciones entre los UUID en algún lugar. Necesitamos ser capaces de encontrar todos los registros relacionados al buscar registros de un único mensaje. Intentemos visualizar una relación más compleja, pero bastante común. Tenemos un envío de un cliente (UUID-100) con dos mensajes largos (UUID-200 y UUID-210), que fueron luego divididos en varios mensajes y enviados al operador (UUID-201, UUID-202, UUID-211. UUID-212). Luego, llegó un envío en masa que contenía informes de entrega de todas las partes de los mensajes enviados (UUID-300).

Como era de esperar, es una gráfica. Por supuesto que uno podría esforzarse y almacenarlo en una base de datos relacional o transformarlo en un json para una base de datos de documento, pero tenemos bases de datos gráficas hoy en día. ¡Usemos la herramienta apropiada para el trabajo! Cuando se establece una nueva relación y ocurren divisiones o combinaciones, realizamos un push de un pequeño gráfico a la base de datos gráfica. Elegimos Neo4j porque tiene un buen trabajo de marketing. Son amigables en las conferencias y regalan libros y remeras. El software tampoco es malo. Debes pagar por el clúster y el HA, pero para los registros sirve la versión gratuita. Una sola instancia Neo4j en una máquina de 16 CPU se encarga de 100 millones de nodos con 150 millones de relaciones que solo ocupan 15 GB de almacenamiento. El problema con Neo4j es que eliminar nodos no libera memoria ni uso de disco, así que hay que purgar los datos de vez en cuando.

Ahora bien, cuando necesitamos localizar registros para un mensaje, buscamos el UUID de este mensaje, localizamos todos los UUID relacionados de Neo4j y luego localizamos los registros de Graylog con estos UUID. Es importante localizar solo los UUID relevantes, y no la gráfica completa. Por ejemplo, si queremos registros de comunicación para el mensaje UUID-200, solo necesitamos localizar las entradas del registro UUID-100, UUID-200, UUID-201, UUID-202 y UUID-300. La rama UUID-21x es irrelevante. El secreto está en usar diferentes tipos de relaciones al conectar las masas y al conectar las partes de los mensajes. Luego se usa un simple Neo4j cypher query para localizar los UUID relacionados.

Como resultado, cada servicio realiza un push de sus propios registros de la comunicación que manejan. Además, la parte del sistema que haya creado la nueva relación es responsable de publicar la información. Los intereses están bien separados, los registros y las relaciones se almacenan en sistemas dedicados donde se puedan localizar cuando sea necesario. (Por Milan Mimica, Ingeniero en software/Team Leader)