← 컴퓨팅 인프라
심층 리포트DEEPER INTO TECHNOLOGY

Cloudflare DNS 부분 빌드: 작은 변경에 전체 재계산을 피하는 법

작은 설정 변경마다 전체 데이터를 다시 만드는 비용을 어떻게 줄이고, 부분 갱신의 결과가 같은지 확인할까?

댓글 남기기 ↓
먼저 읽는 핵심

변경된 값만 저장해도 그 값을 찾으려고 전체 데이터를 읽고 계산한다면 갱신 비용은 여전히 크다. Cloudflare의 새 DNS 빌드 사례는 이 비용을 줄이는 출발점을 보여 준다. 부분 갱신을 설계할 때는 변경된 입력뿐 아니라 그 입력이 만드는 모든 출력, 사라져야 할 이전 키, 공유 설정의 영향까지 찾아야 한다. 작은 상품 검색 사례로 재계산 범위와 완료 조건을 구체화한다.

상품 하나의 이름을 고쳤는데 검색 인덱스를 전부 다시 만든다면, 상품이 늘수록 사소한 수정도 느려진다. 새 결과와 옛 결과를 비교해 달라진 값만 저장하더라도, 비교를 위해 모든 상품을 읽었다면 비싼 작업의 상당 부분은 이미 끝난 뒤다.

DNS 운영에도 비슷한 문제가 있다. 여기서 존(zone)은 한 도메인의 레코드와 설정을 관리하는 단위이고, 빌드는 그 관리 데이터를 DNS 서버가 읽을 출력으로 만드는 과정이다. 이 글은 DNS 응답 캐시를 지우는 방법보다, 관리 데이터의 작은 변경을 얼마나 작은 재계산으로 끝낼 수 있는가에 초점을 둔다.

11월 2일 사례에서 달라진 것

Cloudflare의 11월 2일 설명은 설정 변경의 영향을 분류하는 계획기와 같은 존의 작업을 합치는 스케줄러를 소개한다. 가능한 경우 특정 이름, 이름과 메타데이터, 최상위 이름과 메타데이터만 재구축한다. 삭제와 전체 빌드 경로도 남긴다. 불명확한 이벤트는 통상 전체 빌드로 보내지만, 메모리에 들어가지 않는 초대형 존에는 별도 제한과 수동 복구 경로가 있다.

실행 전에는 최신 게시 상태를 다시 확인한다. 공급자가 보고한 Zone Builder 배포 전체의 최고 작업 메모리(working set)는 79GiB에서 12GiB로, 이후 대기 시간 조정까지 포함한 빌드 완료 지연 중앙값은 약 7.7초에서 3.3초로 줄었다. 후자의 종료점은 Quicksilver에 쓰는 시점이다. 전 세계 리졸버의 캐시가 모두 바뀐 시각은 아니다. 직접 재현한 수치도 아니다.

이 변화는 새 DNS API나 별도 설치 제품의 출시가 아니라 내부 운영 구조에 관한 발표다. 공개된 설명만으로 고객별 지연 보장이나 플랜별 정확한 반영 시간을 도출할 수는 없다. 확인일은 2026년 11월 4일이다.

“조금만 쓰기”보다 앞에 있는 비용

2022년의 레코드별 빌드 설명을 보면 문제의 출발점이 분명하다. 전체 빌드는 존의 레코드를 모아 기존 출력과 비교했다. 이 구조는 변화의 원인을 자세히 몰라도 현재 상태로 결과를 다시 만들 수 있지만, 존이 커질수록 읽기와 비교량도 커진다. 당시 개선에서는 레코드 변경 이벤트에 정보를 추가하고 별도 스케줄링을 도입했다. 이름 변경 때 이전 레코드의 정보가 빠져 오래된 값이 남는 문제도 다뤘다. 2022년의 성능 수치를 이번 발표의 새 결과로 합쳐 읽어서는 안 된다.

일반적인 갱신 작업의 비용을 다음처럼 나누면 설계할 위치가 보인다. 이는 설명을 위한 비용 모형이며 실제 실행 시간을 예측하는 식은 아니다.

한 번의 갱신 비용
= 입력 읽기 + 출력 계산 + 기존 결과 비교 + 변경분 쓰기

상품 10만 개 중 하나를 고치는 상황을 가정하자. 매번 10만 개를 읽고 변환한 뒤 쓰기 한 건만 수행하는 구현과, 영향받는 상품만 읽고 변환하는 구현은 최종 쓰기량이 같아도 작업량이 다르다. 다만 “입력 10만 개가 1개로 줄었다”를 곧바로 “10만 배 빠르다”로 바꾸면 안 된다. 고정 비용, 인덱스 조회, 공유 데이터, 동시 실행과 저장 장치가 실제 시간을 바꾼다.

Quicksilver 자체의 역할도 구분할 필요가 있다. 2020년 설계 소개는 관리 지점에서 받은 설정을 여러 위치의 읽기 경로로 배포하는 저장 시스템을 설명한다. 출력의 계산과 계산된 결과의 배포는 다른 단계다. 관리 화면에서 저장이 끝났다는 관측과 사용자가 새 결과를 읽었다는 관측도 따로 남겨야 한다. 이 역사적 문서의 당시 규모를 현재 규모로 사용하지 않는다.

상품 검색에 적용해 보는 변경 범위

다음은 원리를 옮겨 생각하기 위해 직접 만든 가상 설계다. Cloudflare의 코드나 실제 상품 서비스를 재현한 결과는 아니다. 상품 원본을 검색용 문서로 바꾸는 과정을 생각하자. 이런 별도 읽기용 결과를 여기서는 투영이라고 부른다.

설정은 간단하다. 공개 상품은 카테고리/상품이름을 키로 가지고, 값에는 상품 ID와 표시 가격을 넣는다. 비공개 상품은 검색 결과에 없어야 한다. 실제 제품에서는 충돌하지 않는 키, 다국어·통화·권한 등을 더 설계해야 하지만 지금은 이름 중복이 없고 원본의 한 버전을 일관되게 읽는다고 가정한다.

원본 상품 A의 이름이 머그, 카테고리가 주방, 가격이 12,000원이라면 출력은 다음과 같다.

주방/머그 → {상품: A, 표시가격: 12000}
원본 변경 다시 계산할 범위 함께 처리할 이전 출력
A 가격을 13,000원으로 수정 A의 검색 문서 같은 키의 옛 값 교체
A 이름을 큰머그로 수정 A의 새 문서 주방/머그 제거
A를 주방에서 선물로 이동 A의 새 문서 옛 카테고리의 키 제거
A를 비공개로 변경 A의 공개 여부 검색 문서 제거, 새 문서는 없음
모든 표시 가격에 적용하는 규칙 변경 그 규칙을 쓰는 모든 상품 새 규칙 기준으로 값 비교

이 표에서 가장 쉽게 놓치는 것은 두 번째 열보다 세 번째 열이다. 이름을 바꿀 때 주방/큰머그만 추가하면 검색 결과에 같은 상품이 두 번 남는다. 비공개 전환은 새로운 검색 문서를 만들지 않으므로 “새 문서가 생겼는가”만 검사하면 삭제 실패를 잡지 못한다.

반대로 전역 가격 규칙을 바꿨는데 이벤트에 딸린 상품 하나만 재계산하면, 나머지 문서가 옛 규칙을 유지한다. 작은 입력 변경과 작은 출력 영향은 같은 말이 아니다. 수정한 데이터의 크기보다 그 데이터를 참조하는 출력의 범위를 알아야 한다.

대표 설명도는 이 가상의 이름 변경을 그린 것이다. 새 키 생성과 옛 키 제거가 함께 있어야 전체 재계산과 같은 검색 결과가 된다는 관계를 보여 준다. 실제 서비스 화면이나 DNS 서버 구조도는 아니다.

이벤트가 도착한 뒤 원본이 한 번 더 바뀌면

위 사례에서 다음 순서로 수정이 이어진다고 해 보자.

  1. A의 이름을 머그에서 큰머그로 바꾼다.
  2. 처리기가 첫 변경을 기다리는 사이 A를 비공개로 바꾼다.
  3. 처리기가 이름 변경 이벤트를 실행한다.

이 설계의 목표가 최신 검색 상태라면, 3번에서 과거 이벤트에 들어 있던 공개 상태를 그대로 써서 주방/큰머그를 만들면 안 된다. 이름 변경 이벤트는 다시 볼 대상과 이전 키를 알려 주고, 최종 공개 여부는 적용하려는 원본 버전에서 판단하도록 설계할 수 있다.

단, “그때그때 최신 값을 읽는다”는 말만으로 충분하지 않다. 다른 처리기가 더 새 버전을 이미 썼는데 오래된 처리기가 뒤늦게 덮어쓸 수도 있다. 원본 버전과 적용한 버전을 기록하고, 같은 대상의 쓰기를 직렬화하거나 저장 시 버전 조건을 검사하는 등 늦은 출력이 최신 출력을 되돌리지 못할 규칙이 필요하다. 이 글의 예시는 그런 조건이 제공된다고 가정하며 분산 동시성 구현을 제공하지 않는다.

또한 중간 이벤트를 모두 보존해야 하는 감사 기록이나 결제 내역에는 이 모델을 그대로 적용할 수 없다. 검색용 최신 상태는 여러 변경을 한 결과로 합쳐도 되지만, 발생한 거래 각각을 남겨야 하는 데이터는 완료 조건부터 다르다. 같은 “이벤트 처리”라는 이름만으로 처리 정책을 공유하면 안 된다.

빠른지 측정하기 전에 같은 결과인지 비교한다

우리 상품 사례의 검증 목표는 다음처럼 적을 수 있다.

S0 = 변경 전 원본, S1 = 변경 후의 고정된 원본 버전
F(S) = 원본 S로 전체 검색 출력을 새로 만든 결과

F(S0)에 부분 갱신을 적용한 최종 결과 == F(S1)

비교 대상은 부분 갱신이 새로 쓴 값만이 아니다. 최종 키 집합에서 빠진 것, 더 남은 것, 값이 다른 것을 모두 찾는다. 손대지 않아야 할 상품 B도 그대로인지 확인한다. 시간이나 난수로 출력이 달라지는 항목이 있으면 두 경로에 같은 값을 주거나, 의미상 제외할 항목을 먼저 명시해야 한다.

아래는 위 가정에서 직접 도출한 기대 결과다. 실서비스 테스트 결과가 아니라 구현 전에 팀이 합의할 작은 시험 명세다.

시험 입력 통과해야 할 결과 실패가 뜻하는 것
A 가격만 변경 A 값만 바뀌고 B는 유지 영향 범위가 과도하거나 출력 누락
A 이름 변경 새 키 하나 존재, 옛 키 없음 추가만 처리하고 삭제를 놓침
A 비공개 전환 A의 공개 키가 모두 사라짐 생성 경로만 검증함
전역 가격 규칙 변경 해당 규칙을 쓰는 A·B 모두 변경 의존 범위 누락
이름 변경 뒤 비공개 전환 최종 버전에서는 A가 없음 과거 상태가 다시 공개됨
같은 변경을 다시 처리 최종 키·값이 한 번 처리한 결과와 같음 재처리가 중복 또는 역행을 만듦

Cloudflare는 동일 입력에서 전체·부분 빌드의 출력을 비교하고, 고객 트래픽을 받지 않는 별도 환경에서도 차이를 관측했다고 설명한다. 서로 다른 처리 시점 때문에 잠깐 생기는 차이와 따라잡은 뒤에도 남는 차이를 구분한다. 공식 검증 설명

우리 설계에도 적용할 판단은 두 단계다. 먼저 고정 입력으로 삭제와 공유 규칙의 논리를 확인한다. 다음으로 실제 변경 순서와 재시작·지연을 포함한 환경에서 수렴하는지 확인한다. 첫 단계만 통과한 결과를 운영 중 갱신 성공으로 확대하지 않는다.

성능 그래프를 비교할 때 남길 조건

부분 갱신을 도입한 뒤에는 전체 갱신 비율만 보지 말고, 읽은 원본 수, 만든 출력 수, 최고 메모리, 가장 오래 기다린 변경의 나이를 함께 기록하는 편이 좋다. 이는 이 글의 제안이다. 빠른 개별 작업이 늘어도 특정 대형 작업이 계속 밀린다면 사용자가 겪는 지연은 남을 수 있다.

지연 측정은 시작과 끝도 고정해야 한다. “원본 저장부터 검색 문서 쓰기까지”와 “검색 요청이 새 값을 반환하기까지”를 같은 그래프에 섞으면 어느 단계가 개선됐는지 설명하기 어렵다. DNS 변경 뒤 실제 사용자에게 옛 값이 남는 문제는 TTL과 응답 경로 진단에서 이어서 다룬다.

또 Prometheus의 히스토그램 설명처럼 분위수는 버킷 안의 분포를 추정한 값일 수 있다. 예를 들어 같은 관측들이 1초와 5초 사이에 몰려 있어도 그 구간을 하나의 버킷으로 보관하면 정확히 어디에 몰렸는지는 사라진다. 버킷을 촘촘하게 바꾼 뒤 p99가 달라졌다고 실행 속도 자체가 그만큼 좋아졌다고 단정할 수 없다.

그래서 도입 전후 비교에는 관측 기간, 데이터 크기 분포, 작업 종류, 재시도 포함 여부, 버킷 구성을 남긴다. 평균은 시간 합과 건수로 따로 확인할 수 있지만 평균 하나로 느린 소수 작업을 대신 설명하지는 못한다. 공급자의 다른 환경에서 얻은 절감률을 우리 서비스의 예상 절감률로 복사하지 않는 이유도 여기에 있다.

도입 전에 한 장에 적을 것

부분 갱신 검토 기록지는 위 사례를 자신의 검색 인덱스나 설정 배포에 옮길 때 쓰는 빈 양식이다. 실행 프로그램이 아니며 성능 수치를 채워 넣지 않았다.

사용자가 기다리는 결과와 완료 시점:
원본 버전 / 전체 출력의 정의:
변경 종류별 영향을 받는 출력 키:
이름 변경·삭제 때 제거해야 할 이전 키:
공유 규칙 변경 때 범위를 넓힐 조건:
오래된 원본 읽기·늦은 출력 덮어쓰기를 막는 규칙:
전체 재계산과 비교할 누락 / 초과 / 값 차이:
재처리·재시작 뒤 같아야 할 최종 상태:
전체 재계산도 감당할 수 없을 때의 복구 경로:
측정 기간·작업 분포·시간 구간·버킷 구성:

작은 데이터에서 전체 재계산이 이미 충분히 싸다면 이 복잡성을 도입할 이유가 약하다. 영향 관계를 아직 설명하지 못하거나, 복구를 위해 필요한 전체 재계산조차 끝낼 수 없다면 먼저 데이터 분할과 복구 절차를 설계해야 한다. 부분 갱신의 이점은 읽기·계산량을 줄이는 데 있지만, 그 대가로 변경의 의미를 더 정확히 알아야 한다.

갱신 속도보다 유입 속도가 계속 빠른 상황이라면 스트림과 백프레셔에서 처리량과 대기열을 함께 살펴볼 수 있다. 이 검토표를 다음 설계 회의에 쓰려면 상단 저장 기능을 이용하고, 후속 글은 인프라 글 모음과 RSS에서 찾아볼 수 있다.

다시 읽을 때의 기준
  • 최종 쓰기량이 작아도 전체 입력을 읽고 변환하는 비용은 남는다. 갱신 한 건이 건드리는 출력 범위를 먼저 정의한다.
  • 이름 변경과 삭제는 새 값을 넣는 것만으로 끝나지 않는다. 이전 키의 제거와 공유 출력의 갱신도 비교 대상이다.
  • 전체 재계산과 부분 갱신을 같은 입력 버전에서 비교한다. 처리 시간과 관측 방식의 변경도 나눠 기록한다.
SOURCES & CONTEXT

근거와 원자료

자료의 발행 시점과 이번 글에서 참고한 범위를 함께 기록합니다.

  1. How we halved DNS build delay and cut memory use by 85% ↗Cloudflare · Alex Fattouche · 2026-11-02

    부분 빌드·통합 스케줄러·대형 존 예외와 공급자 관측. 공식 운영 설명이며 공개 릴리스/코드나 자체 실행 아님.

  2. How we improved DNS record build speed by more than 4,000x ↗Cloudflare · Alex Fattouche · 2022-05-25

    과거 전체/레코드별 빌드와 오래된 키 삭제 문제. 2022년 구조·수치를 2026년 성과로 혼합하지 않음.

  3. Introducing Quicksilver: Configuration Distribution at Internet Scale ↗Cloudflare · 2020-03-30

    설정의 전역 배포와 읽기 경로를 설명하는 역사적 설계 자료. 현재 규모·SLA 근거 아님.

  4. Histograms and summaries ↗Prometheus · 상시 갱신 문서

    분위수 추정 오차, 버킷 경계와 합/개수의 평균 계산. 특정 설치 버전 실행 아님.

갱신 기록

최신 공식 운영 사례와 과거 설계를 구분하고 자체 상품 검색 사례·검토 기록지를 작성했다. Cloudflare 시스템은 직접 실행하지 않았다.

오류를 발견했다면 댓글로 알려 주세요 →
S
Starhunter

테크독해 운영·편집. AI·IT의 개념과 기술을 연결해 읽습니다.

QUESTIONS & DISCUSSION

이 글에 댓글 남기기

댓글 공개 방식을 확인하고 있습니다. 개인정보나 비밀 값은 적지 마세요.

댓글을 불러오는 중입니다.

    새 댓글

    8자 이상. 비밀번호를 잊으면 직접 수정·삭제할 수 없습니다. 다른 서비스와 다른 비밀번호를 사용해 주세요.

    내 댓글 확인·수정·삭제

    등록 후 안내된 댓글 번호와 비밀번호를 입력하세요. 승인 대기 중인 댓글도 확인할 수 있습니다.