× Install ThecoreGrid App
Tap below and select "Add to Home Screen" for full-screen experience.
B2B Engineering Insights & Architectural Teardowns

DNS cache memory: 5 optimizations in Big Pineapple

DNS cache memory at Cloudflare scale is not about elegance. It is about every byte, every allocation, and every cache line. In Big Pineapple, five storage changes cut the per-entry footprint by more than 50% and improved lookup behavior at the same time.

Big Pineapple, the platform behind 1.1.1.1, Gateway DNS, DNS Firewall, AS112, and other DNS services, stores over 250 billion cache entries. At that scale, even a single wasted byte per entry becomes a material memory cost across the fleet. The problem becomes sharper in ECS-heavy locations, where the same query can produce multiple cached answers for different client networks. That raises both entry count and per-entry memory pressure, so the cache layout itself starts to shape system cost.

The core challenge was simple to state and hard to ignore: the cache was paying for flexibility it did not need. DNS entries are written once, then read many times, so any field that helps mutation but not lookup is overhead. The cache also had to preserve performance under real traffic mix, not just shrink in a benchmark. That meant every optimization had to answer the same question: does it reduce memory without turning the hot path into a slower one?

The first step was to remove growth-oriented container overhead. Vec and String carry capacity fields and reserve room for future growth, but cache entries are immutable after insert. Replacing them with Box<[T]> and Box removed both the capacity metadata and the wasted heap slack. The team then collapsed multiple record lists into a single list with offsets, and packed booleans into bitflags to reduce alignment padding. This is a classic systems trade-off: less structural flexibility in exchange for denser storage and better locality.

The deeper change was in how DNS record data is represented. Instead of keeping every record as a large enum variant, the largest variants were boxed, and later the data was moved into a contiguous raw byte buffer. That cut padding, reduced per-variant heap allocations, and improved cache locality. It also meant the system could copy many record types directly into the outgoing DNS response instead of rebuilding them field by field. The trade-off was deliberate. The cache gave up random access within the record set and accepted sequential iteration, because the record counts per entry are small and the memory win matters more.

Another important optimization was dropping the owner name when it matches the queried domain. In the common case, the owner can be reconstructed from the cache key during lookup. That makes the entry less self-contained, but the key is already present on the read path, so this avoids a heap allocation in the dominant case. When the owner differs, as with CNAME chains, the full name is still stored. The same logic appears throughout the design: keep the expensive representation only when the data actually needs it.

The implementation was measured carefully. Benchmarks used randomly generated cache entries that approximate production traffic: 56% A records, 25% AAAA, and 19% TXT, with one to four records per entry. A custom allocator wrapped Rust’s System allocator to track allocation count and size per cache entry. Insert throughput and lookup latency were measured across the full cache flow, and production resident memory was tracked during rollout because process memory depends on more than the cache alone. That separation matters. Benchmarks explain where the savings come from. Production shows whether they survive contact with the rest of the system.

The results were substantial, and they came in stages rather than as a single drop. Across the fleet, the optimizations freed roughly 100 terabytes of memory, equivalent to the RAM in 130 Gen 13 servers. Per-entry footprint fell from 953 bytes to 420 bytes, a 56% reduction. Per-entry allocations dropped from 1.1 KB to 461 bytes. Insert throughput rose 43%, from 625,000 entries/s to 893,000 entries/s, while lookup latency dropped 19%, from 828 ns to 670 ns. The important part is that the team did not trade speed for space. Fewer allocations and better memory locality improved both.

The architectural takeaway is pragmatic. At this scale, the cache is not just a data structure. It is part of the cost model of the service. The right design is the one that fits immutable data, preserves hot-path locality, and spends heap memory only where the protocol truly needs it. In Big Pineapple, that produced a meaningful reduction in resident memory and left room to raise cache capacity later without raising overall memory use.

Reference source

×

🚀 Deploy the Blocks

Controls: ← → to move, ↑ to rotate, ↓ to drop.
Mobile: use buttons below.