zerovoids.dev
zerovoids.dev

Ø NEWS

인프라 · 성능 최적화

Cloudflare, 1.1.1.1 DNS 캐시에서 메모리 100테라바이트 확보

1.1.1.1을 떠받치는 DNS 캐시의 항목 저장 구조를 다섯 차례 손봐, 캐시 항목당 메모리 사용량을 절반 넘게 줄이고 전체 서버 플릿에서 100테라바이트를 되찾았다.

키워드rustperformanceinfra

Cloudflare의 DNS 캐시 시스템 "Big Pineapple"은 1.1.1.1, Gateway DNS, DNS Firewall 등 여러 서비스를 함께 떠받치며 언제나 2500억 개가 넘는 항목을 메모리에 들고 있다. 이 규모에서는 항목 하나에 여분의 바이트 1개가 붙는 것만으로 전체 메모리 사용량이 250기가바이트 넘게 불어난다.

Cloudflare는 이 캐시 항목의 저장 구조를 다섯 차례에 걸쳐 손봐 항목당 메모리 사용량을 절반 넘게 줄였다. 그 결과 전체 서버 플릿에서 약 100테라바이트의 메모리를 되찾았는데, 이는 자사 Gen 13 서버 130대에 들어가는 RAM 총량과 맞먹는다.

바이트가 어디서 새고 있었나

가장 큰 손실은 러스트(Rust)의 Vec<T>String이었다. 이 타입들은 포인터·길이 외에 나중에 항목을 추가할 때를 대비한 "capacity" 필드를 8바이트 더 들고 다니고, 실제로 쓰는 양보다 더 많은 힙 공간을 미리 확보해 둔다. 캐시에 한 번 저장된 DNS 응답은 다시 수정되지 않으므로 이 여유 공간은 통째로 낭비였다. 늘어날 수 없는 Box<[T]>Box<str>로 바꾸면 capacity 필드와 과할당된 힙 공간이 함께 사라진다. 항목마다 이런 필드가 8개씩 있어 필드당 8바이트, 항목당 64바이트가 줄었고 2500억 개 항목 전체로는 15테라바이트 넘게 절약됐다.

두 번째는 응답을 answer·authority·additional 세 개의 리스트로 나눠 저장하던 방식이다. 섹션별 레코드 수가 u16 하나로 충분하다는 점에 착안해, 세 섹션을 하나의 리스트에 몰아넣고 경계는 2바이트짜리 오프셋 두 개로만 표시하도록 바꿨다. 8바이트 포인터·8바이트 길이짜리 리스트 두 개가 통째로 사라지고 그 자리를 4바이트가 대신하면서 항목당 28바이트를 더 줄였다. 여러 불리언 필드를 비트플래그 하나로 합친 것도, 러스트의 정렬(alignment) 규칙 덕에 필드 크기 이상으로 패딩을 걷어내는 효과를 냈다.

할당 자체가 줄고 메모리 지역성이 좋아지면서 속도도 함께 올랐다. 삽입 처리량은 43% 늘었고 조회 지연은 19% 줄었다.

왜 이 규모에서만 크게 체감되나

이 최적화가 특히 중요한 이유는 EDNS Client Subnet(ECS) 때문이다. ECS가 켜진 질의는 클라이언트의 네트워크 대역에 따라 권한 서버가 답을 다르게 주므로, 같은 질의라도 캐시에 여러 버전을 따로 저장해야 한다. 항목 수와 항목당 크기가 동시에 늘어나는 조건이라, 이번 구조 변경은 ECS 트래픽이 많은 데이터센터일수록 더 크게 체감된다.

측정은 실제 트래픽 비율(A 레코드 56%, AAAA 25%, TXT 19%)을 흉내 낸 벤치마크로 했고, 배포 과정에서는 실제 프로덕션 인스턴스의 상주 메모리도 함께 추적해 벤치마크 수치가 현실과 어긋나지 않는지 확인했다.