La demanda de tiendas de características de aprendizaje automático de baja latencia es mayor que nunca, pero implementarlas a escala sigue siendo un desafío.Esto se hizo claro cuando los ingenieros de ShareChat Ivan Burmistrov y Andrei Manakov tomaron la etapa P99 CONF 23 para compartir cómo construyeron una tienda de características de ML de baja latencia basada en ScyllaDB. Este no es un estudio de caso ordenado en el que la adopción de un nuevo producto salva el día.Es una historia de "lecciones aprendidas", un vistazo al valor de la optimización de rendimiento implacable - con algunas tomas importantes de ingeniería. La implementación original del sistema cayó muy por debajo de los requisitos de escalabilidad de la compañía.El objetivo final era soportar mil millones de características por segundo, pero el sistema fracasó bajo una carga de sólo un millón.Con un poco de resolución de problemas inteligentes, el equipo lo retiró.Vamos a ver cómo sus ingenieros lograron pivotar el fracaso inicial para cumplir su objetivo de rendimiento elevado sin escalar la base de datos subyacente. ¿Obsesionado por la optimización del rendimiento y la ingeniería de baja latencia? Únete a sus compañeros en P99 24 CONF, una conferencia virtual gratuita y altamente técnica sobre “el rendimiento de todas las cosas”. Michael Stonebraker, creador de Postgres y profesor del MIT Bryan Cantrill, cofundador y CTO de Oxide Computer Avi Kivity, creador de KVM, cofundador y CTO de ScyllaDB Liz Rice, Chief Open Source Officer con los especialistas de eBPF Isovalent Andy Pavlo, profesor de CMU Ashley Williams, fundador/CEO de Axo, ex equipo principal de Rust, fundador de la Fundación Rust Carl Lerche, creador de Tokio, colaborador de Rust e ingeniero de AWS Regístrate ahora - es gratis Regístrate ahora - es gratis Regístrate ahora - es gratis Además de otra gran charla de Ivan de ShareChat, espera más de 60 charlas de ingeniería sobre optimización de rendimiento en Disney/Hulu, Shopify, Lyft, Uber, Netflix, American Express, Datadog, Grafana, LinkedIn, Google, Oracle, Redis, AWS, ScyllaDB y más. ShareChat: la principal plataforma de redes sociales de la India Para comprender el alcance del desafío, es importante saber un poco sobre ShareChat, la plataforma de redes sociales líder en la India. En la aplicación ShareChat, los usuarios descubren y consumen contenido en más de 15 idiomas diferentes, incluyendo vídeos, imágenes, canciones y más. Entre las dos aplicaciones, sirven a una base de usuarios en rápido crecimiento que ya tiene más de 325 millones de usuarios activos mensuales.Y su motor de recomendación de contenido basado en IA es esencial para impulsar la retención y el compromiso de los usuarios. Tienda de funciones de aprendizaje automático en ShareChat Esta historia se centra en el sistema detrás de las tiendas de características ML para la aplicación de vídeo de formato corto Moj. Ofrece feeds totalmente personalizados a alrededor de 20 millones de usuarios activos diarios, 100 millones de usuarios activos mensuales. Los feeds sirven a 8.000 solicitudes por segundo, y hay una media de 2.000 candidatos a contenido que se clasifican en cada solicitud (por ejemplo, para encontrar los 10 mejores artículos para recomendar). Ivan Burmistrov, ingeniero de software principal en ShareChat, explicó: “Calculamos características para diferentes ‘entidades’. Post es una entidad, Usuario es otro y así sucesivamente. Desde la perspectiva computacional, son bastante similares. Sin embargo, la diferencia importante es en el número de características que necesitamos recoger para cada tipo de entidad. Cuando un usuario solicita un feed, recogemos características de usuario para ese usuario único. Sin embargo, para clasificar todas las publicaciones, necesitamos recoger características para cada candidato (post) que se está clasificando, por lo que la carga total en el sistema generada por las características de post es mucho mayor que la generada por las características de usuario. Esta diferencia juega un papel importante en nuestra historia”. Lo que pasó mal Al principio, el foco principal estaba en construir una tienda de características de usuario en tiempo real porque, en ese momento, las características de usuario eran las más importantes.El equipo comenzó a construir la tienda de características con ese objetivo en mente.Pero luego las prioridades cambiaron y las características de publicación se convirtieron también en el foco.Este cambio ocurrió porque el equipo comenzó a construir un sistema de clasificación completamente nuevo con dos grandes diferencias respecto a su predecesor: Los mensajes en tiempo real eran más importantes El número de puestos a clasificar aumentó de cientos a miles Ivan explicó: “Cuando fuimos a probar este nuevo sistema, fracasó miserablemente. A alrededor de 1 millón de características por segundo, el sistema se volvió irresponsable, las latencias pasaron por el techo y así sucesivamente”. En última instancia, el problema surgió de cómo la arquitectura del sistema utilizó buckets de datos preagregados llamados ladrillos. Por ejemplo, pueden agregar el número de likes para una publicación en un minuto dado u otro rango de tiempo. Esto les permite calcular métricas como el número de likes para múltiples posts en las últimas dos horas. Aquí está una mirada de alto nivel a la arquitectura del sistema. Hay algunos temas en tiempo real con datos brutos (me gusta, clics, etc.). Un trabajo de Flink los agrega en ladrillos y los escribe a ScyllaDB. Luego hay un servicio de características que solicita ladrillos de ScyllaDB, los agrega y devuelve los resultados al servicio de alimentación. El esquema de base de datos inicial y la configuración de alfombras llevaron a problemas de escalabilidad. Originalmente, cada entidad tenía su propia partición, con filas timestamp y nombre de función ordenadas columnas de agrupación. [ ]. Las placas fueron calculadas para segmentos de un minuto, 30 minutos y un día. Querer una hora, un día, siete días o 30 días requería recoger alrededor de 70 placas por característica en promedio. Aprende más en esta clase de maestría de modelado de datos NoSQL Si haces la matemática, queda claro por qué fracasó.El sistema necesitaba manejar alrededor de 22 mil millones de líneas por segundo. Sin embargo, la capacidad de la base de datos era sólo 10 millones de líneas por segundo. Optimización inicial En ese momento, el equipo realizó una misión de optimización. El esquema de base de datos inicial se actualizó para almacenar todas las filas de características juntas, serializadas como buffers de protocolo para un timestamp dado. Debido a que la arquitectura ya estaba utilizando Apache Flink, la transición al nuevo esquema de alfiler fue bastante fácil, gracias a las capacidades avanzadas de Flink en la construcción de tuberías de datos. Con esta optimización, el multiplicador "Funciones" ha sido eliminado de la ecuación anterior, y el número de filas requeridas para recuperar se ha reducido por 100X: de alrededor de 2 mil millones a 200 millones de filas/sec. El equipo también optimizó la configuración de la alfombra, añadiendo alfombras adicionales durante cinco minutos, tres horas y cinco días a las alfombras de un minuto, 30 minutos y un día. Para manejar más filas/sec en el lado de la base de datos, cambiaron la estrategia de compactación de ScyllaDB de incremental a nivelado. Esa opción mejor se ajustaba a sus patrones de consulta, manteniendo las líneas relevantes juntas y reduciendo la lectura de I/O. El resultado: la capacidad de ScyllaDB se duplicó efectivamente. Aprende más sobre estrategias de compactación La forma más fácil de acomodar la carga restante habría sido escalar ScyllaDB 4x. Sin embargo, los clusters más grandes aumentarían los costos y eso simplemente no estaba en su presupuesto. Mejora de la localización del cache Una de las posibles maneras de reducir la carga en ScyllaDB fue mejorar la tasa de golpes de caché local, por lo que el equipo decidió investigar cómo se podría lograr esto. La elección obvia fue utilizar un enfoque de hashing consistente, una técnica bien conocida para dirigir una solicitud a una réplica determinada del cliente basada en cierta información sobre la solicitud. Dado que el equipo estaba utilizando NGINX Ingress en su configuración Kubernetes, el uso de las capacidades de hashing consistente de NGINX parecía una elección natural. Esta configuración simple no funcionó.Especificamente: El subconjunto del cliente llevó a un enorme remaping de la clave - hasta el 100% en el peor de los casos. puesto que las claves de los nodos se pueden cambiar en un anillo de hash, era imposible usar escenarios de la vida real con autoscaling. [Ver la implementación de ingress] Fue difícil proporcionar un valor de hash para una solicitud porque Ingress no soporta la solución más obvia: un encabezado gRPC. La latencia sufrió una severa degradación, y no estaba claro qué estaba causando la latencia de la cola. Para apoyar un subconjunto de los pods, el equipo modificó su enfoque. Crearon una función de hash de dos pasos: primero hashando una entidad, luego agregando un prefixo aleatorio. Esto distribuyó la entidad en el número deseado de pods. En teoría, este enfoque podría causar una colisión cuando una entidad se mapea al mismo pod varias veces. Sin embargo, el riesgo es bajo dado el gran número de réplicas. Ingress no apoya el uso del encabezado gRPC como una variable, pero el equipo encontró una solución: usando la reescritura del camino y proporcionando la clave hash necesaria en el camino mismo. Desafortunadamente, identificar la causa de la degradación de la latencia habría requerido un tiempo considerable, así como mejoras en la observabilidad. Para cumplir con el plazo, el equipo dividió el servicio Feature en 27 servicios diferentes y dividió manualmente todas las entidades entre ellos en el cliente. No fue el enfoque más elegante, pero, fue sencillo y práctico – y logró grandes resultados. La tasa de hit de caché mejoró a 95% y la carga de ScyllaDB se redujo a 18,4 millones de filas por segundo. Con este diseño, ShareChat escaló su tienda de características a 1B características por segundo para marzo. Sin embargo, este enfoque de despliegue de "viejos colegios" todavía no era el diseño ideal. Mantener 27 desplegos era tedioso e ineficiente. Además, la tasa de impacto de la caché no era estable, y la escalación se limitaba al tener que mantener un alto número mínimo de podes en cada desplegamiento. Así que aunque este enfoque técnicamente cumplió con sus necesidades, el equipo continuó su búsqueda de una mejor solución a largo plazo. La siguiente fase de optimización: hashing consistente, servicio de características Listo para otra ronda de optimización, el equipo revisó el enfoque de hashing consistente utilizando un sidecar, llamado Envoy Proxy, desplegado con el servicio de características. Envoy Proxy proporcionó una mejor observabilidad que ayudó a identificar el problema de la cola de latencia. El problema: los patrones de solicitud diferentes al servicio de características causaron una enorme carga en la capa y el cache de gRPC. Luego, el equipo optimizó el servicio de Características. Forked la biblioteca de caché (FastCache de VictoriaMetrics) y implementado batch writes y mejor expulsión para reducir la contención de mutex por 100x. Forjado gprc-go e implementado buffer pool en diferentes conexiones para evitar contención durante el alto paralelismo. Usó el agrupamiento de objetos y los parámetros del colector de basura ajustado (GC) para reducir las tasas de asignación y los ciclos de GC. Con el Proxy de Envoy que maneja el 15% del tráfico en su prueba de concepto, los resultados fueron prometedores: una tasa de hit de caché del 98%, que redujo la carga en ScyllaDB a 7,4 millones de filas por segundo. Lecciones aprendidas Aquí está lo que este viaje parecía desde una perspectiva de la línea de tiempo: Para concluir, Andrei resumió las principales lecciones aprendidas por el equipo de este proyecto (hasta ahora): A pesar de que el equipo de ShareChat cambió drásticamente su diseño del sistema, ScyllaDB, Apache Flink y VictoriaMetrics continuaron trabajando bien. Cada optimización es más difícil que la anterior y tiene menos impacto. Las soluciones simples y prácticas (como dividir la tienda de características en 27 implementaciones) realmente funcionan. La solución que ofrece el mejor rendimiento no siempre es fácil de usar. Por ejemplo, su esquema de base de datos revisado ofrece un buen rendimiento, pero es difícil de mantener y entender. A veces puede ser necesario forjar una biblioteca predeterminada y ajustarla para su sistema específico para obtener el mejor rendimiento. ¡Mira la conversación completa de P99 CONF! ¡Mira la conversación completa de P99 CONF! ¡Mira la conversación completa de P99 CONF! Más sobre Cynthia Dunlop Cynthia es directora senior de estrategia de contenido en ScyllaDB. Ha estado escribiendo sobre desarrollo de software e ingeniería de calidad durante más de 20 años.