Por Que a Memória do Cache DNS Importa
Na escala da Cloudflare, o cache DNS armazena mais de 250 bilhões de entradas a qualquer momento. Um único byte desperdiçado por entrada equivale a 250 GB de memória em toda a frota. Isso não é só sobre custo—é sobre desempenho. Um cache mais eficiente em memória significa taxas de acerto maiores, menor latência e melhor experiência para o usuário.
Identificamos cinco otimizações sucessivas que reduziram o uso de memória por entrada de 953 bytes para 420 bytes—uma redução de 56%. O resultado: cerca de 100 TB de RAM liberados, equivalentes a 130 servidores Gen 13. Mas não sacrificamos velocidade. A taxa de inserção subiu 43% e a latência de lookup caiu 19%.
A Primeira Vitória: Substituir Vec por Box<[T]>
O tipo Vec em Rust inclui um campo de capacidade—8 bytes por vetor. Mas depois que armazenamos uma resposta DNS, nunca a modificamos. Trocar para Box<[T]> elimina o campo de capacidade e a super-alocação. Aplicamos isso a todos os 8 campos de vetor em cada entrada de cache, economizando 64 bytes por entrada. Isso sozinho liberou mais de 15 TB na frota.
// Antes: Vec com overhead de capacidade
pub struct CacheEntry {
answers: Vec<Record>,
authority: Vec<Record>,
additional: Vec<Record>,
// ...
}
// Depois: Box<[T]> sem capacidade extra
pub struct CacheEntry {
answers: Box<[Record]>,
authority: Box<[Record]>,
additional: Box<[Record]>,
// ...
}

Consolidando Listas e Removendo Donos
Em vez de armazenar as seções de resposta, autoridade e adicional como listas separadas, armazenamos uma única lista com offsets de 2 bytes para cada seção. Isso remove dois ponteiros e dois comprimentos por entrada—economizando 28 bytes.
// Antes: Três boxes separados
pub struct CacheEntry {
answers: Box<[Record]>,
authority: Box<[Record]>,
additional: Box<[Record]>,
}
// Depois: Buffer único com offsets
pub struct CacheEntry {
records: Box<[u8]>,
answer_offset: u16,
authority_offset: u16,
// ...
}
Também notamos que a maioria dos registros DNS tem um dono idêntico ao domínio consultado. Ao armazenar Option<Name> para o campo dono, podemos inferir o domínio a partir da chave do cache quando é None, evitando uma alocação no heap para a maioria dos registros.

Tamanho de Enum e Armazenamento em Formato de Rede
Enums em Rust são dimensionados pelo seu maior variante. Nosso enum RecordData tinha um variante NAPTR com 136 bytes, então cada registro A (4 bytes) desperdiçava 120 bytes. Colocar variantes grandes em Box moveu-os para o heap, mas introduziu overhead de alocador e má localidade de memória.
O avanço veio ao armazenar dados de registro em formato de rede como bytes brutos com um prefixo de comprimento de 2 bytes. Isso eliminou o overhead por variante e melhorou a localidade do cache.
// Antes: Enum com variantes grandes
pub enum RecordData {
A(Ipv4Addr),
Aaaa(Ipv6Addr),
Naptr(Naptr),
// ...
}
// Depois: Variantes grandes em Box
pub enum RecordData {
A(Ipv4Addr),
Aaaa(Ipv6Addr),
Txt(Box<Txt>),
Naptr(Box<Naptr>),
// ...
}
Benchmarks e Resultados em Produção
Nossos benchmarks mostraram que o footprint por entrada caiu de 953 para 420 bytes, e as alocações de 1.1 KB para 461 bytes. Em produção, o uso de memória p99 caiu de 9.3 GB para 5.3 GB por instância. A economia agregada chegou a 100 TB.
| Métrica | Antes | Depois | Mudança |
|---|---|---|---|
| Footprint líquido por entrada | 953 bytes | 420 bytes | -56% |
| Alocações por entrada | 1.1 KB | 461 bytes | -58% |
| Taxa de inserção no cache | 625.000 entradas/s | 893.000 entradas/s | +43% |
| Latência de lookup no cache | 828 ns | 670 ns | -19% |
Limitações e Considerações
Essas otimizações não são gratuitas. O armazenamento em formato de rede requer iteração sequencial, complicando recursos como rotação round-robin. Colocar variantes grandes em Box pode prejudicar a localidade de memória se não for cuidadosamente gerenciado. Além disso, os resultados dependem da mistura de tráfego—locais com muitos ECS se beneficiam mais.
Próximos Passos para o Seu Código
Se você trabalha com sistemas de alta performance, considere estas dicas:
- Perfile suas estruturas de dados: Identifique campos que carregam overhead desnecessário.
- Use
Box<[T]>em vez deVecpara dados imutáveis. - Armazene dados em formatos compactos (ex.: formato de rede) quando o acesso aleatório não for crítico.
- Meça tanto memória quanto desempenho para evitar trade-offs.
Para mais insights sobre engenharia resiliente, confira nosso Cloudflare Code Orange fail small engineering insights. E se você está explorando plataformas de IA de borda, veja nossa análise da NVIDIA IGX Thor.
Fonte: Blog da Cloudflare
![]()