Por Qué Importa la Memoria del Caché DNS
A la escala de Cloudflare, el caché DNS almacena más de 250 mil millones de entradas en cualquier momento. Un solo byte desperdiciado por entrada equivale a 250 GB de memoria en toda la flota. Esto no es solo sobre costos—es sobre rendimiento. Un caché más eficiente en memoria significa tasas de acierto más altas, menor latencia y mejor experiencia de usuario.
Identificamos cinco optimizaciones sucesivas que redujeron el uso de memoria por entrada de 953 bytes a 420 bytes—una reducción del 56%. El resultado: aproximadamente 100 TB de RAM liberados, equivalentes a 130 servidores Gen 13. Pero no sacrificamos velocidad. La tasa de inserción aumentó 43% y la latencia de búsqueda bajó 19%.
La Primera Victoria: Reemplazar Vec por Box<[T]>
El tipo Vec en Rust incluye un campo de capacidad—8 bytes por vector. Pero una vez que almacenamos una respuesta DNS, nunca la modificamos. Cambiar a Box<[T]> elimina el campo de capacidad y la sobre-asignación. Aplicamos esto a los 8 campos de vector en cada entrada de caché, ahorrando 64 bytes por entrada. Esto solo liberó más de 15 TB en la flota.
// Antes: Vec con overhead de capacidad
pub struct CacheEntry {
answers: Vec<Record>,
authority: Vec<Record>,
additional: Vec<Record>,
// ...
}
// Después: Box<[T]> sin capacidad extra
pub struct CacheEntry {
answers: Box<[Record]>,
authority: Box<[Record]>,
additional: Box<[Record]>,
// ...
}

Consolidando Listas y Eliminando Propietarios
En lugar de almacenar las secciones de respuesta, autoridad y adicional como listas separadas, almacenamos una sola lista con offsets de 2 bytes para cada sección. Esto elimina dos punteros y dos longitudes por entrada—ahorrando 28 bytes.
// Antes: Tres boxes separados
pub struct CacheEntry {
answers: Box<[Record]>,
authority: Box<[Record]>,
additional: Box<[Record]>,
}
// Después: Buffer único con offsets
pub struct CacheEntry {
records: Box<[u8]>,
answer_offset: u16,
authority_offset: u16,
// ...
}
También notamos que la mayoría de los registros DNS tienen un propietario idéntico al dominio consultado. Al almacenar Option<Name> para el campo propietario, podemos inferir el dominio a partir de la clave del caché cuando es None, evitando una asignación en el heap para la mayoría de los registros.

Tamaño de Enum y Almacenamiento en Formato de Red
Los enums en Rust se dimensionan según su variante más grande. Nuestro enum RecordData tenía una variante NAPTR de 136 bytes, por lo que cada registro A (4 bytes) desperdiciaba 120 bytes. Colocar variantes grandes en Box los movió al heap, pero introdujo overhead del asignador y mala localidad de memoria.
El avance vino al almacenar los datos de registro en formato de red como bytes crudos con un prefijo de longitud de 2 bytes. Esto eliminó el overhead por variante y mejoró la localidad del caché.
// Antes: Enum con variantes grandes
pub enum RecordData {
A(Ipv4Addr),
Aaaa(Ipv6Addr),
Naptr(Naptr),
// ...
}
// Después: Variantes grandes en Box
pub enum RecordData {
A(Ipv4Addr),
Aaaa(Ipv6Addr),
Txt(Box<Txt>),
Naptr(Box<Naptr>),
// ...
}
Benchmarks y Resultados en Producción
Nuestros benchmarks mostraron que el footprint por entrada cayó de 953 a 420 bytes, y las asignaciones de 1.1 KB a 461 bytes. En producción, el uso de memoria p99 bajó de 9.3 GB a 5.3 GB por instancia. El ahorro agregado alcanzó los 100 TB.
| Métrica | Antes | Después | Cambio |
|---|---|---|---|
| Footprint neto por entrada | 953 bytes | 420 bytes | -56% |
| Asignaciones por entrada | 1.1 KB | 461 bytes | -58% |
| Tasa de inserción en caché | 625,000 entradas/s | 893,000 entradas/s | +43% |
| Latencia de búsqueda en caché | 828 ns | 670 ns | -19% |
Limitaciones y Consideraciones
Estas optimizaciones no son gratuitas. El almacenamiento en formato de red requiere iteración secuencial, complicando características como la rotación round-robin. Colocar variantes grandes en Box puede perjudicar la localidad de memoria si no se gestiona cuidadosamente. Además, los resultados dependen de la mezcla de tráfico—los lugares con muchos ECS se benefician más.
Próximos Pasos para Tu Código
Si trabajas en sistemas de alto rendimiento, considera estos consejos:
- Perfila tus estructuras de datos: Identifica campos que cargan overhead innecesario.
- Usa
Box<[T]>en lugar deVecpara datos inmutables. - Almacena datos en formatos compactos (ej.: formato de red) cuando el acceso aleatorio no sea crítico.
- Mide tanto memoria como rendimiento para evitar trade-offs.
Para más insights sobre ingeniería resiliente, revisa nuestro Cloudflare Code Orange fail small engineering insights. Y si estás explorando plataformas de IA en el borde, mira nuestro análisis de NVIDIA IGX Thor.
Fuente: Blog de Cloudflare
