A look at how Tencent Games built service architecture based on CQRS and event sourcing patterns with Pulsar and ScyllaDB. Como parte de Tencent Interactive Entertainment Group Global (IEG Global), Proxima Beta se compromete a apoyar a nuestros equipos y estudios para traer juegos únicos y emocionantes a millones de jugadores de todo el mundo. Nuestro equipo en Level Infinite (la marca para la publicación global) es responsable de la gestión de una amplia gama de riesgos para nuestro negocio – por ejemplo, actividades fraudulentas y contenido dañino. Desde un punto de vista técnico, esto nos obligó a construir un eficiente sistema de análisis en tiempo real para monitorear consistentemente todos los tipos de actividades en nuestro dominio empresarial. En este blog, compartimos nuestra experiencia de construir este sistema de análisis orientado a eventos en tiempo real. Primero, exploraremos por qué construimos nuestra arquitectura de servicios basada en la segregación de responsabilidad de comandos y consultas ( ) y eventos sourcing patrones con y ScyllaDB. A continuación, examinaremos cómo utilizamos ScyllaDB para resolver el problema de enviar eventos a numerosas sesiones de juego. Finalmente, cubriremos cómo utilizamos los espacios clave y la replicación de datos de ScyllaDB para simplificar nuestra gestión global de datos. CQRs Apache Pulsar Un vistazo al caso de uso: abordar los riesgos en los juegos de Tencent Comencemos con un ejemplo del mundo real de lo que estamos trabajando y los desafíos que enfrentamos. Esta es una captura de pantalla de Tower of Fantasy, un juego de rol de acción en 3D. Los jugadores pueden usar este diálogo para presentar un informe contra otro jugador por varias razones. El primer reto sería determinar qué equipo será el propietario de la base de datos para almacenar este formulario. Hay diferentes razones para hacer un informe (incluida una opción llamada “Otros”), por lo que un caso puede ser tratado por diferentes equipos funcionales. Es por eso que es una elección natural para nosotros capturar este caso como un evento, como “informar un caso”.Toda la información se captura en este evento como es.Todos los equipos funcionales solo necesitan suscribirse a este evento y hacer su propia filtración.Si piensan que el caso cae en su dominio, pueden simplemente capturarlo y desencadenar nuevas acciones. CQRS y Sourcing de Eventos La arquitectura del servicio detrás de este ejemplo se basa en los patrones de CQRS y de suministro de eventos. Si estos términos son nuevos para usted, no se preocupe! Al final de esta revisión, debe tener una sólida comprensión de estos conceptos. . Blog dedicado a este tema El primer concepto a comprender aquí es el suministro de eventos.La idea básica detrás del suministro de eventos es que cada cambio al estado de un sistema se captura en un objeto de evento y estos objetos de eventos se almacenan en el orden en que se aplicaron al estado del sistema. En otras palabras, en lugar de simplemente almacenar el estado actual, utilizamos un almacenamiento de apéndice para registrar la serie completa de acciones tomadas en ese estado. Este concepto es simple pero poderoso ya que los eventos que representan cada acción se registran para que cualquier posible modelo que describa el sistema pueda ser construido a partir de los eventos. El siguiente concepto es CQRS, que significa Comand Query Responsibility Segregation. CQRS fue inventado por Greg Young hace más de una década y se originó del Principio de separación de comandos y consultas. La idea fundamental es crear modelos de datos separados para leer y escribir, en lugar de usar el mismo modelo para ambos propósitos. Siguiendo el patrón CQRS, cada API debería ser un comando que realice una acción, o una consulta que devuelva datos al llamador - pero no ambos. Esto divide naturalmente el sistema en dos partes: el lado de escribir y el lado de leer. Esta separación ofrece varios beneficios.Por ejemplo, podemos escalar la capacidad de escritura y lectura de forma independiente para optimizar la eficiencia de costes. Desde una perspectiva de trabajo en equipo, diferentes equipos pueden crear vistas diferentes de los mismos datos con menos conflictos. El flujo de trabajo de alto nivel del lado de la escritura se puede resumir de la siguiente manera: los eventos que ocurren en numerosas sesiones de juego se alimentan en un número limitado de procesadores de eventos. La implementación también es sencilla, normalmente involucrando un bus de mensajes como Pulsar, Kafka, o un sistema de cola más simple que actúa como una tienda de eventos. Los eventos de los clientes persisten en la tienda de eventos por tema y los procesadores de eventos consumen eventos al suscribirse a temas. . Blog referido anteriormente Aunque los sistemas de cola son generalmente eficientes en el manejo del tráfico que fluye en una dirección (por ejemplo, fan-in), pueden no ser tan eficaces en el manejo del tráfico que fluye en la dirección opuesta (por ejemplo, fan-out). En nuestro escenario, el número de sesiones de juego será grande, y un sistema de cola típico no se ajusta bien ya que no podemos permitirnos crear una cola dedicada para cada sesión de juego. Debemos encontrar una manera práctica de distribuir los hallazgos y las métricas a las sesiones de juego individuales a través de las API de consulta. Antes de continuar, aquí está un resumen de nuestra arquitectura de servicios. A partir del lado de la escritura, los servidores de juegos continúan enviando eventos a nuestro sistema a través de los puntos finales de comandos y cada evento representa un tipo de actividad que ocurrió en una sesión de juego. Los procesadores de eventos producen hallazgos o métricas contra los flujos de eventos de cada sesión de juego y actúan como un puente entre dos lados. En el lado de la lectura, tenemos servidores de juegos u otros clientes que mantienen las métricas y los hallazgos de encuestas a través de los puntos finales de la consulta y toman acciones adicionales si se han observado actividades anormales. Tienda de eventos distribuida como la queja para eventos de la serie de tiempo Ahora vamos a ver cómo usamos ScyllaDB para resolver el problema de enviar eventos a numerosas sesiones de juego. Por cierto, si Google “Cassandra” y “queue”, es posible que te encuentres con un artículo de hace más de una década en el que se afirma que usar Cassandra como una cola es un antipattern. Para apoyar el envío de eventos a cada sesión de juego, usamos el id de sesión como la clave de partición para que cada sesión de juego tenga su propia partición y los eventos pertenecientes a una sesión de juego en particular puedan ser localizados por el id de sesión de manera eficiente. Cada evento también tiene un id de evento único, que es un UUID de tiempo, como la clave de agrupación. Debido a que los registros dentro de la misma partición se ordenan por la clave de agrupación, el id de evento se puede usar como el id de posición en una cola. Finalmente, los clientes de ScyllaDB pueden recuperar de manera eficiente los eventos recién llegados rastreando el id de evento del evento más reciente que se ha recibido. Hay una advertencia a tener en cuenta al utilizar este enfoque: el problema de consistencia. Recuperar nuevos eventos rastreando el ID de evento más reciente se basa en el supuesto de que ningún evento con un ID más pequeño se cometerá en el futuro. Sin embargo, este supuesto puede no ser siempre cierto. Por ejemplo, si dos nodos generan dos identificadores de evento al mismo tiempo, un evento con un ID más pequeño podría ser insertado más tarde que un evento con un ID más grande. Este problema, al que me refiero como una “leer fantasma”, es similar al fenómeno en el mundo SQL donde repetir la misma consulta puede dar resultados diferentes debido a cambios no comprometidos realizados por otra transacción. Sin embargo, la causa raíz del problema en nuestro caso es diferente. Hay varias maneras de abordar este problema.Una solución es mantener un estado de clúster, que llamo un "pseudo ahora", basado en el valor más pequeño de los timestamps en movimiento entre todos los procesadores de eventos. Otra consideración importante es permitir TimeWindowCompactionStrategy, que elimina el impacto negativo del rendimiento causado por las piedras sepulcrales.La acumulación de piedras sepulcrales fue un problema importante que impidió el uso de Cassandra como una cola antes de que TimeWindowCompactionStrategy estuviera disponible. Ahora pasemos a discutir otros beneficios más allá de usar ScyllaDB como una cola de envío. Simplificar los complejos retos de la distribución global de datos Dado que estamos construyendo un sistema multi-tenancy para servir a los clientes en todo el mundo, es esencial garantizar que las configuraciones de los clientes sean consistentes en clusters en diferentes regiones. Resolvimos este problema simplemente permitiendo la replicación de datos en un espacio clave en todos los centros de datos. Esto significa que cualquier cambio realizado en un centro de datos eventualmente se propagará a otros. Gracias a ScyllaDB, así como a DynamoDB y Cassandra, por el duro levantamiento que hace que este problema desafiante parezca trivial. Puede estar pensando que el uso de cualquier RDBMS típico podría lograr el mismo resultado ya que la mayoría de las bases de datos también admiten la replicación de datos. Esto es cierto si solo hay una instancia del panel de control que se ejecuta en una región dada. En una arquitectura primaria/replica típica, solo el nodo primario admite lectura/escritura mientras que los nodos de replicación son sólo de lectura. Sin embargo, cuando necesita ejecutar múltiples instancias del panel de control en diferentes regiones, por ejemplo, cada inquilino tiene un panel de control que se ejecuta en su región de origen, o incluso cada región tiene un panel de control que se ejecuta para equipos locales, se vuelve mucho más difícil implementar esto usando una arquitectura primaria/replica típica. Si ha utilizado AWS DynamoDB, es posible que esté familiarizado con una característica llamada Tabla Global, que permite a las aplicaciones leer y escribir localmente y acceder a los datos globalmente. Keyspaces como contenedores de datos A continuación, veamos cómo utilizamos los espacios clave como contenedores de datos para mejorar la transparencia de la distribución global de datos. Veamos el diagrama a continuación. muestra una solución a un problema típico de distribución de datos impuesto por las leyes de protección de datos. Por ejemplo, supongamos que la región A permite que ciertos tipos de datos se procesen fuera de sus fronteras siempre que una copia original se mantenga en su región. * El * El Una solución potencial es realizar pruebas end-to-end (E2E) para asegurarse de que las aplicaciones envíen correctamente los datos correctos a la región correcta como se esperaba. Este enfoque requiere que los desarrolladores de aplicaciones tomen la plena responsabilidad de implementar la distribución de datos correctamente. sin embargo, a medida que el número de aplicaciones crece, se vuelve impracticable para cada aplicación manejar este problema individualmente y las pruebas de E2E también se vuelven cada vez más caras en términos de tiempo y dinero. Al permitir la replicación de datos en los espacios clave, podemos dividir la responsabilidad de distribuir correctamente los datos en dos tareas: 1) identificar los tipos de datos y declarar sus destinos, y 2) copiar o mover los datos a los lugares esperados. Al separar estas dos tareas, podemos abstraer configuraciones y reglamentos complejos de las aplicaciones.Esto se debe a que el proceso de transferencia de datos a otra región a menudo es la parte más complicada a tratar, como cruzar las fronteras de la red, cifrar correctamente el tráfico y manejar interrupciones. Después de separar estas dos tareas, las aplicaciones solo están obligadas a realizar correctamente el primer paso, que es mucho más fácil de verificar a través de pruebas en etapas anteriores del ciclo de desarrollo.Además, la corrección de las configuraciones para la distribución de datos se vuelve mucho más fácil de verificar y auditar. Consejos para que otros tomen un camino similar Para concluir, te dejaremos con lecciones importantes que hemos aprendido, y que te recomendamos que apliques si terminas tomando un camino similar al nuestro: Cuando utilice ScyllaDB para manejar datos de serie de tiempo, como usarlos como una cola de envío de eventos, recuerde utilizar la Estrategia de compactación de ventanas de tiempo. Considere utilizar los espacios clave como contenedores de datos para separar la responsabilidad de la distribución de datos. Esto puede hacer que los problemas de distribución de datos complejos sean mucho más fáciles de gestionar. Tecnología en conversaciones on-demand Este artículo está basado en un discurso de tecnología presentado en la Cumbre de ScyllaDB 2023.Usted ve este discurso - así como discursos de ingenieros de Discord, Epic Games, Disney, Strava, ShareChat y más - en demanda. Ver conversaciones de tecnología en demanda