El efecto pasado por alto del tamaño de los datos

Un agradecimiento especial a Özgür Karakaya con quien coescribí esta publicación de blog.
PDP (Página de detalles del producto) en Trendyol es un equipo responsable de proporcionar información sobre productos específicos. Esta información incluye el precio, tamaño, color u otra información relevante de un producto que los clientes desean saber antes de realizar una compra. La información se puede presentar de dos maneras diferentes, la primera es en forma de una sola página de detalles del producto, principalmente cuando hace clic en un producto y navega a una página donde se muestra la información específica del producto. El equipo también es responsable de proporcionar datos detallados del producto a otras vistas en el sitio web, es decir, su lista de favoritos, productos recomendados y pago. Ambas formas fueron manejadas por un servicio llamado Product Detail API respaldado por la base de datos NoSQL de couchbase que atiende solicitudes individuales (página de detalles de un solo producto) así como solicitudes masivas (lista de favoritos, etc.).
Antes del evento del Black Friday de 2021, el equipo se había fijado el objetivo de manejar 8 millones de solicitudes por minuto para solicitudes masivas. Un problema era que el servicio funcionaba mal en forma de mayor uso de memoria y una cantidad considerable de errores cuando recibía un alto rendimiento. El desafío era encontrar una manera de optimizar el servicio.
Así que aquí está el viaje.
Lluvia de ideas y análisis
Durante la fase de análisis, el equipo de PDP descubrió que, en cuanto al dominio, los clientes que envían solicitudes masivas no utilizan todos los campos del documento de la base de datos. El primer punto de optimización fue introducir un nuevo modelo de documento sin esos campos innecesarios. Las ventajas de esta reestructuración son:
- optimización del tamaño de los datos
- enfoque de dominio de contenido masivo
A continuación se muestra un diagrama de la arquitectura existente y nueva en ese momento:

Los cuadros exteriores punteados definen los límites de un servicio. Observe que en esta primera ilustración, un servicio era responsable de manejar toda la canalización de operaciones. La optimización en sí consistía en dividir la canalización de conversión en dos procesos separados, como se ve en la segunda arquitectura: el proceso de conversión de tiempo de índice y el servicio de contenido masivo. Este último es un servicio de lectura que maneja solicitudes HTTP. El primero resultó ser un proceso impulsado por eventos que lee datos de ProductContent CB (Fuente de la verdad), los procesa parcialmente y luego los almacena en un cubo de Counchbase para que los use el último. Pero más sobre eso más adelante.
Bajo la nueva arquitectura propuesta, la principal ganancia es que se optimizará el tiempo de ejecución del proceso, lo que a su vez reduce el tiempo de respuesta del servicio.
En este blog, nos centraremos en el proceso Index-time. Para obtener más información sobre el "Servicio de contenido masivo", consulte esta publicación de Enes Turgut
PD: estoy usando el término tiempo de ejecución aquí de manera bastante vaga. En el contexto de este blog, estamos definiendo el tiempo de ejecución como la secuencia de operaciones desencadenadas por una solicitud HTTP entrante.
Soluciones existentes
Hasta ahora, el equipo se había decidido por la nueva arquitectura. Ahora es el momento de implementarlo. Lo que estamos tratando de lograr es una solución de conversión de base de sofá a base de sofá casi en tiempo real. Couchbase ya ofrece replicación en todo el clúster, sin embargo, la forma de los datos debe cambiarse, por lo que la replicación normal obviamente no es el camino a seguir. ¿No sería increíble si hubiera una forma de manipular los datos durante el proceso de replicación? Mientras hojeábamos la documentación de Couchbase, nos topamos con Couchbase Eventing Service. ¿Acabamos de ganar el premio gordo?
Eventos de base de sofá:
El servicio de eventos de Couchbase es un marco para operar sobre cambios en los datos en tiempo real. Los eventos son cambios en los datos del clúster de Couchbase
Couchbase Eventing parecía ser una buena opción para nosotros porque puedes :
- Propagar cambios a otros sistemas
- Enriquecer un documento en tiempo real
- No requiere mantenimiento
Sin bote.
Nuestra solución (convertidor de datos de contenido masivo)
Inspirado en CB Elasticsearch Connector .
Couchbase Elasticsearch Connector replica sus documentos desde Couchbase Server a Elasticsearch casi en tiempo real. El conector utiliza el Protocolo de cambio de base de datos (DCP) de alto rendimiento para recibir notificaciones cuando los documentos cambian en Couchbase.
Reemplace Elasticsearch con Couchbase y obtendrá exactamente lo que necesitamos. Todo lo que tenemos que hacer es aceptar las mutaciones de documentos (actualizaciones) de la cola de DCP, procesarlas y luego escribirlas en un nuevo depósito.
Tenga en cuenta que la solución debe ser capaz de:
- Escale a medida que crece el número de documentos y mutaciones.
- Realice el proceso de extremo a extremo (aceptar mensajes de la cola, convertir el modelo de datos y escribirlo en un nuevo depósito) casi en tiempo real.
- Manipule los documentos existentes y no solo los nuevos cuando se agreguen o eliminen nuevos campos del modelo de documento.
- Proporcione un mecanismo para regenerar documentos desde cero en caso de daños graves en los datos.
Bueno, hay mucho que desempacar aquí, resumamos primero:

La arquitectura anterior muestra la solución final. En última instancia, el Servicio de detalles del producto se encargará de las solicitudes individuales, mientras que el Servicio de contenido masivo se ocupará de las solicitudes a granel. Los servicios están respaldados por diferentes fuentes de base de datos. Mientras que ProductContent CB representa la fuente de la verdad, ProductSummary CB representa una versión preprocesada resumida de la misma. La conversión se manejará en Bulk Content Data Converter casi en tiempo real mediante la aceptación de mutaciones de documentos de la cola de DCP. Cada mutación de documento representa el estado del documento después de que se haya producido la actualización. Esta es otra forma de decir que el convertidor obtendrá automáticamente todo el documento actualizado de ProductContent CBen cada actualización.
Para entender cómo se escala exactamente esta solución, se debe echar un vistazo al archivo de configuración del convertidor:
{
"group": {
"name": "v20220909",
"membership": {
"memberNumber": 1,
"totalMembers": n
},
"limit": {
"bytes": "5m",
"actions": 100,
"timeout": "1m",
"concurrency": 2
},
"version": 1,
"ignoreDelete": false
},
"dcp": {
"sourceConfig": {
"hosts": ["source_couchbase_host"],
"network": "auto",
"bucket": "bucketName",
"metadataBucket": "metadataBucketName",
"scope": "",
"collections": [],
"compression": true,
"flowControlBuffer": "16m",
"persistencePollingInterval": "100ms"
},
"targetConfig": {
"hosts": ["taget_couchbase_host"],
"bucket": "targetBucketName"
}
}
}
Otra pieza importante de la configuración es group.name , que representa el grupo de consumidores donde se almacena una compensación, como los grupos de consumidores de Kafka. Cambiar esta configuración restablece el índice de compensación, lo que significa que el estado completo de la base de datos se enviará a través de la cola DCP. Esto es especialmente práctico cuando se necesita una actualización en el modelo de documento, agregar o eliminar nuevos campos del depósito de destino requiere una actualización completa de todos los documentos almacenados, esto incluye los documentos que de otro modo no se habrían actualizado normalmente en el depósito de destino simplemente porque no se habrían producido mutaciones en los documentos fuente. También puede funcionar como un mecanismo de regeneración de base de datos completo en caso de daños graves en los datos.
Para obtener más información sobre la configuración, consulte el enlace de documentación .
Otro punto de optimización fue reducir el tamaño del documento de destino al tener abreviaturas o caracteres únicos como nodos JSON. No se espera que los expertos en dominios y los administradores de proyectos le den sentido a este documento y es por eso que podemos salirnos con la nuestra con hacks tan malvados. A continuación se muestra un ejemplo del documento:

Y a 31 de octubre, el resultado es…

¡Este simple truco nos permitió reducir el tamaño del depósito de 2,92 TB a 595 GB! ¡Una gran reducción de tamaño de aproximadamente un 80%!
También se debe haber notado que el recuento de documentos en la base de datos de destino se reduce en aproximadamente 12 millones. El motivo es que excluimos los productos agotados que no se han actualizado durante 12 meses. En el depósito de origen, es posible que aún necesitemos esos documentos, pero no tendría sentido tenerlos aquí, de ahí la diferencia de recuento.
Monitorear el desempeño
Hasta ahora tenemos la solución en funcionamiento. Pero, ¿cómo podemos saber exactamente si tiene el rendimiento suficiente? ¿Qué pasa si, hipotéticamente, está haciendo la conversión pero con un retraso de, digamos, 2 segundos? ¡Eso sería un completo desastre! dicha consistencia eventual se filtrará hasta la interfaz de usuario del sitio web y afectará gravemente la experiencia del usuario. La activación manual de un cambio en la base de datos de producción no es posible y no debería ser la forma de probar en primer lugar. Se necesita un enfoque sistemático para identificar la fuente del retraso si sucede. ¿El retraso se debe a la congestión en la cola de DCP? ¿O es porque el convertidor tiene algún tipo de error?
Para responder a estas preguntas, presentamos tres métricas, como se muestra en la siguiente figura:

El documento fuente contiene la marca de tiempo de la última actualización, lo llamamos LMD (Última fecha de modificación). Una vez que el convertidor recibe una mutación, el tiempo de " espera en la cola DCP" se puede calcular fácilmente restando LMD de time.now() . Luego, las métricas se exponen a Prometheus como se muestra en los gráficos a continuación:

Limitaciones:
¿Recuerdas cuando dije antes que escalar es solo una cuestión de aumentar el número total de convertidores? Bueno, esto viene con una pequeña advertencia: la configuración de la solución está un poco conectada a la infraestructura con la que habla; El reequilibrio automático del consumidor en la cola de DCP y la distribución de los consumidores a través de vBuckets están conectados a la configuración estática que tiene el consumidor al inicio. Para cambiar eso, uno necesita cambiar el archivo de configuración en sí mismo, lo que requiere una nueva implementación. Además, cada consumidor necesita su propio archivo de configuración estática, lo que significa que cada consumidor necesita, en la terminología de Kubernetes, tener sus propios archivos de recursos de implementación. En Trendyol usamos ampliamente ArgoCD con Kustomize para administrar nuestras implementaciones, escalar la solución requiere agregar, en la terminología de ArgoCD, un nuevo ApplicationSet.
Sin embargo, hasta ahora nuestra escala de 8 consumidores está manejando la gran carga con eficiencia casi en tiempo real. Pero a medida que el cubo de origen se hace más grande y la cantidad de mutaciones crece, se necesita reconfigurar manualmente a los consumidores.
Uso interno de otros equipos
Sintiéndose confiado con el conector CB a CB desarrollado, el equipo PDP decidió presentarlo a otros equipos dentro de Trendyol. El equipo recibió muchos comentarios positivos y, antes de que nos demos cuenta, el equipo de búsqueda decidió bifurcar el proyecto y usarlo en su dominio, donde almacena los eventos del dominio en una base de datos de sofá y usa el conector CB a CB para generar otro modelo de documento. para uso posterior. Tal adopción de soluciones desarrolladas internamente nos inspira a todos a seguir adelante y seguir mejorando y refinando nuestro uso de la tecnología.
¡ Solicite nuestras vacantes aquí !