HeadlinesBriefing favicon HeadlinesBriefing.com

Cloudflare ahorra 100TB de RAM con Rust

Hacker News •
×

Cloudflare opera a una escala tan grande que, incluso después de trabajar aquí durante años, no parece real. Tenemos miles de servidores en todo el mundo con petabytes de RAM y millones de núcleos de CPU, y todo ello se lleva al máximo. Por vastos que parezcan esos recursos, siguen siendo finitos, y cuando necesitas que cada servicio se ejecute en cada nodo, no queda espacio para desperdiciar. A esta escala, las pequeñas mejoras se magnifican enormemente, por lo que incluso las mejoras del 1% a la vez merecen celebrarse. Y algunos ajustes suman mucho más: en esta publicación, veremos cómo pequeños cambios en un solo algoritmo redujeron significativamente la huella de memoria de uno de nuestros servicios basados en Pingora. Eso nos permitió recuperar más de 100TB de RAM a nivel global, además de los 100TB de memoria que el equipo de DNS pudo liberar el mes pasado.

No desperdiciar Mantener una distribución equitativa de recursos entre equipos no es fácil, especialmente en organizaciones grandes. Una de las formas en que Cloudflare garantiza que se mantenga el equilibrio es mediante los incansables esfuerzos del maravilloso equipo de Performance. Esta historia comienza con un ticket presentado por Ivan, quien encontró: Uso excesivo de memoria por parte de pingora-ketama en Pingora Backend Router. El hallazgo fue que nuestro servicio interno de balanceo de carga, Pingora Backend Router (sí, PBR), estaba usando mucha más memoria de la esperada — específicamente en estructuras asociadas con pingora-ketama, que es nuestra biblioteca de código abierto para manejar hashing consistente. Para hablar de cómo abordamos este aparente uso excesivo de memoria, necesitamos hablar de qué es el hashing consistente, por qué lo usamos en PBR y cómo se volvió tan hambriento de memoria. En el camino, aprenderemos algo de Rust e incluso un poco de matemáticas.

Hashing consistente El hashing consistente es un método ampliamente utilizado para distribuir tareas entre múltiples servidores de una manera que no requiere grandes cambios cuando se añaden o eliminan servidores. Internamente lo usamos para enrutar solicitudes cacheables a servidores por URL. Esto nos permite mantener solo una copia de un archivo almacenada por centro de datos y ofrece una forma estable de encontrar la ubicación de cada archivo. Hemos mencionado este sistema antes, pero tomémonos el tiempo de recorrer cómo y por qué se usa este algoritmo y cómo funciona. El concepto clave del hashing consistente es que, si bien las funciones hash pueden aceptar cualquier tipo de entrada, su salida se limita a un único entero sin signo (enteros de 32, 64 o 128 bits según la función hash). Esto nos permite relacionar tareas y servidores entre sí de manera consistente. La mayoría de las discusiones sobre hashing consistente te hacen pensar en ese espacio de salida como un anillo continuo y circular que da la vuelta desde su valor máximo hasta cero. Esta representación permite algunas visualizaciones agradables, pero también puede hacer que el simple concepto de rangos de enteros parezca más complicado de lo necesario. Para nuestra discusión, representaremos la salida de 32 bits de nuestra función hash como una recta numérica. Ahora, digamos que tenemos un conjunto de servidores, A, B y C, y un conjunto de tareas t-z. Podemos mapear cada uno en la recta numérica según el hash de sus valores representativos, como direcciones IP para servidores y claves de caché para tareas. Asignar tareas a servidores ahora es solo cuestión de encontrar el primer servidor a la izquierda de cada tarea. Podemos representar esto visualmente coloreando la región de hashes que se asociará con cada servidor. Observa que el rango cubierto por el servidor C da la vuelta hasta el principio, de ahí la idea de que los hashes existen en un anillo. Y eso es todo. En un nivel básico, el hashing consistente es así de simple — pero no pasa mucho tiempo antes de ver que hay margen de mejora. Observa que el rango cubierto por el servidor A en nuestro ejemplo es significativamente mayor que el de B o C. Esto es un problema porque la fracción de solicitudes que maneja un servidor será proporcional al tamaño de su rango en la recta numérica. Idealmente querríamos garantizar que cada servidor tenga un tamaño igual, pero como los hashes son esencialmente números aleatorios, tenemos que...