순서 메시지 서비스는 생산자와 소비자 사이에 로그 하나를 놓은 구조로 그리기 쉽습니다. 운영 규모에서는 문제가 달라집니다. 스트림 하나를 한 클러스터의 수천 복제본과 전 세계 수십만 복제본이 읽을 수 있습니다. 소비자는 서로 다른 속도로 진행하지만 모두 같은 바이트 순서와 최소 한 번 전달을 기대합니다. 이들이 같은 브로커나 분산 파일 블록의 끝을 반복해 읽으면 낮은 생산 속도도 큰 읽기 증폭으로 바뀝니다.

Google은 이 fan-out 문제를 위해 Fast ACS를 구축했습니다[1]. 50ms 미만을 목표로 하는 거래용 메시지 버스가 아니며 정확히 한 번 전달도 주장하지 않습니다. 광고나 온라인 쇼핑처럼 수요가 크게 변하고 여러 지역에 배치된 서비스 상태를 수 초 또는 1초 미만에 수렴시키는 환경이 대상입니다. 실제 시스템은 수십 개 운영 클러스터에 배포됐고 한 클러스터 안에서 합산 Tbps급 소비자 트래픽을 처리합니다.

핵심 결정은 파일을 순서의 기준으로 유지하면서 반복 tail read만 별도 고속 경로로 옮긴 것입니다. Colossus는 영속 원장과 복구 경로로 남습니다. 복제 인메모리 계층은 최근 청크와 현재 파일 길이를 보관합니다. 클러스터 사이의 작업자는 WAN에서 일반 RPC로 바이트를 복사하고, 목적지 클러스터 안의 소비자는 원격 메모리 직접 접근(RMA)을 사용합니다. 독자마다 서버 측 요청을 실행하지 않으므로 일반 브로커 구조에서 먼저 소진되는 CPU를 아낍니다.

영속 파일과 휘발성 가속 계층의 역할 분리

각 메시지 스트림 샤드는 시간순 이름을 가진 append-only 파일의 연속입니다. 생산자는 메시지를 직렬화해 바이트를 붙이고 소비자는 자체 프로토콜로 그 바이트를 해석합니다. 파일 롤오버는 크기를 제한하지만 순서 규칙을 바꾸지 않습니다. 목적지 클러스터에는 파일의 끝부분이 복사되며 로컬 독자에게 같은 순서가 보입니다.

Fast ACS는 최근 파일을 두 종류의 캐시에 반영합니다. 데이터 캐시는 전역에서 고유한 파일 경로와 청크 번호를 키로 사용하는 4KB 청크를 저장합니다. 메타데이터 캐시는 외부에 보일 파일 길이와 best-effort 쓰기 잠금을 저장합니다. 4KB는 관측한 메시지 크기에 맞고 클러스터 패브릭에서 한 패킷으로 전송할 수 있는 크기입니다. 오래된 청크는 TTL이나 용량 압박으로 제거되므로 캐시는 영속 복제본이 아닙니다.

쓰기 순서가 정확성을 결정합니다. 쓰기 작업자는 먼저 청크 데이터를 읽을 수 있게 만든 뒤 메타데이터의 공개 길이를 전진시킵니다. 독자는 이 길이를 폴링하고 그보다 앞에 있는 바이트만 요청합니다. 마지막 부분 청크는 항상 처음부터 다시 쓰므로 독자는 올바른 접두부를 보거나 캐시 미스를 만납니다. 만료되거나 없는 청크는 Colossus에서 읽습니다. 일부 캐시 쓰기가 실패해도 바이트 순서의 기준은 바뀌지 않으며, 캐시 가용성은 정확성이 아니라 지연과 부하에 영향을 줍니다.

캐시는 완전 복제본 세 개와 일부 용량을 가진 추가 복제본을 사용합니다. 일반 읽기는 임의 복제본 하나를 느슨하게 읽고 필요할 때 정족수 읽기로 재시도합니다. 메타데이터는 실제 바이트보다 길이를 먼저 공개하면 존재하지 않는 데이터를 약속하게 되므로 더 강하게 다뤄야 합니다. 모든 접근에 가장 강한 일관성 비용을 부과하지 않고 일반 경로와 검증·대체 경로를 분리했습니다.

Fast ACS는 순서 원장과 fan-out 경로를 분리합니다. 생산자는 Colossus에 append하고 4KB 청크를 데이터 캐시에 반영한 뒤 메타데이터 길이를 전진시킵니다. 클러스터 사이 작업자는 제한된 깊이의 트리로 파일 끝을 복사합니다. 목적지의 수천 독자는 길이를 폴링하고 RMA로 최근 청크를 읽으며, 캐시 미스에서는 Colossus로 돌아갑니다. 이 글을 위해 새로 만든 도판.

이 역할 분리가 특정 캐시 제품보다 중요합니다. 영속 스토리지가 복구, 뒤처진 독자, 캐시 손실을 담당하는 동안 휘발성 메모리는 소비자 수에 비례해 반복되는 작업을 흡수합니다. 캐시는 스트림 머리 근처의 독자만 처리하므로 전체 보존 이력이 길어도 필요한 용량을 작게 유지할 수 있습니다.

전송 순서와 소비자에게 보이는 순서의 분리

단순한 복사 작업자는 파일을 순차로 읽고 전송합니다. 순서는 보장하지만 읽기 하나가 기다릴 때 네트워크 대역폭이 비게 됩니다. Fast ACS는 파일을 청크로 나누고 여러 청크를 병렬로 가져오므로 네트워크 완료 순서가 바뀔 수 있습니다. 목적지는 각 오프셋에 청크를 기록하고, 소비자는 메타데이터가 가리키는 완성 접두부까지만 봅니다. 전송은 순서가 달라도 외부 스트림의 순서는 유지됩니다.

이 구분은 서버 병목을 없앱니다. 전역 순서는 모든 네트워크 패킷이 하나의 직렬 큐를 지나야 한다는 요구가 아니라 공개된 파일 위치의 속성입니다. 청크 키를 분산하면 마지막 대형 파일 블록을 가진 노드 하나에 모든 tail read가 몰리는 문제도 줄어듭니다. 논문은 hot partition이나 tail block 하나가 전체 fan-out을 받는 브로커 및 대형 블록 파일 구조와 이 차이를 설명합니다.

지역 간 배치는 또 다른 증폭을 만듭니다. 소스가 모든 목적지에 직접 바이트를 보내면 비싼 대륙 간 트래픽이 목적지 수만큼 늘어나므로, Fast ACS는 최소 신장 트리에 가까운 최적화로 복사 트리를 만듭니다. 첫 홉은 대륙을 건너고 뒤의 홉은 도시권 링크를 지나는 경우가 많으며, 최대 네 홉의 일반 경로로 수백 클러스터를 지원합니다. 각 홉에서는 읽기 작업과 쓰기 작업을 분리해 송신 및 수신 용량을 독립적으로 늘립니다.

복사 트리는 비용을 줄이지만 장애 전파 경로가 됩니다. 느린 부모는 모든 자손을 지연시킵니다. 클러스터나 링크가 비정상이면 제어기가 해당 노드와 링크에 불이익을 주고 트리를 다시 만들며, 문제가 있는 클러스터를 말단으로 옮깁니다. 영향받은 소비자는 가까운 정상 클러스터에서 임시로 읽을 수 있지만 로컬 RMA 이점은 잃습니다. 장애 범위는 제한할 수 있어도 이미 하위 홉에 누적된 지연은 즉시 없앨 수 없습니다.

정상 상태에서 확인된 확장 범위

주요 통제 실험은 클러스터마다 데이터 캐시 복제본 아홉 개와 메타데이터 복제본 여섯 개를 사용했습니다. 미국 중부와 유럽 서부에서 시작한 스트림 두 개를 15개 목적지로 복사했고, 합산 생산 속도는 약 240Mb/s였습니다. 각 스트림은 120개 샤드로 나뉘었습니다. 소비자 하나는 네 샤드에서 8Mb/s를 읽었고 평가한 말단 클러스터는 소스에서 세 홉 떨어져 있었습니다.

Fast ACS는 활성 소비자 7,950명까지 안정적으로 늘어났습니다. 데이터 캐시 대역폭은 70Gb/s로 단순 예상치 62Gb/s보다 약 13% 높았습니다. 요청 속도는 450만 QPS로 예상한 200만 QPS의 2.25배였습니다. 마지막 부분 청크를 채워질 때까지 반복해 읽고, 느슨한 읽기가 실패하면 일관 읽기를 재시도하기 때문입니다. 유효 페이로드만 계산한 용량 계획은 캐시의 패킷 처리량을 크게 부족하게 잡게 됩니다.

이 조건에서 모니터가 측정한 종단 간 p99 전달 지연은 약 500ms였습니다. 논문은 생산자 버퍼링과 데이터·인덱스 직렬 flush에 약 120ms, 네트워크 이동에 180ms, 소비자 폴링과 직렬 읽기에 100ms, 복사 홉마다 처리와 I/O에 약 25ms가 들었다고 설명합니다. 이 값을 단순히 RMA 지연이라고 부르면 안 됩니다. 사용자가 보는 지연은 메모리 접근 하나보다 batching, polling, WAN 거리, 홉 수의 영향을 더 크게 받습니다.

소비자를 0명에서 4,000명으로 갑자기 늘리자 캐시 읽기는 470만 QPS까지 올랐습니다. 읽기와 쓰기가 운영체제 네트워크 버퍼 및 대역폭을 공유해 읽기 폭증이 캐시 쓰기를 지연시켰습니다. 캐시 미스와 Colossus fallback이 늘면서 전달 지연은 약 150초 동안 1초가량 증가한 뒤 회복했습니다. 긴 복사 트리의 여러 클러스터에서 같은 부하를 만들었을 때 p99은 대체로 600ms 아래였지만 부모 지연이 전파되며 1.8초까지 튀었습니다.

장애 실험도 같은 경계를 보여 줍니다. 데이터 캐시 복제본 하나를 잃었을 때 정족수는 유지됐고 읽기·쓰기 실패가 없었습니다. 한 key shard의 복제본 두 개를 잃으면 백업 재구성 중 가용성이 낮아졌고 fallback이 초당 6만 읽기까지 늘며 지연이 2초에 도달했습니다. 반면 읽기·쓰기 작업자 프로세스 장애는 스케줄러가 약 50ms 안에 작업을 다시 배치해 영향이 작았습니다. 캐시는 fallback으로 내구성을 보호하지만, 영속 계층은 이 보호가 만드는 상관된 부하 급증까지 감당해야 합니다.

정상 상태 그래프보다 넓은 운영 꼬리

운영 트래픽에는 확장 실험에서 고정할 수 없는 조건이 들어옵니다. 이전한 광고 스트림을 하루 동안 측정한 전달 지연은 p95 500ms, p99 630ms, p99.9 730ms, p99.99 5.71초였습니다. 가장 큰 스트림은 p99.99가 8.59초였습니다. 이런 꼬리는 캐시 서비스 시간만이 아니라 서로 다른 스트림 속도, 불량 머신, 소프트웨어 교착, 드문 대륙 간 네트워크 문제에서 생겼습니다.

따라서 운영 목표는 중앙값만 낮추는 것이 아닙니다. 생산자가 멈춘 상황과 전송이 멈춘 상황, 캐시 미스와 Colossus throttling, 복사 트리 부모의 지연과 목적지 로컬 문제를 구분하는 관측 체계가 필요합니다. 논문은 일부 장시간 지연에서 클러스터 로컬 모니터만으로 생산자 중단과 작업자 교착을 구분하기 어려웠다고 보고합니다.

원문은 이전 이후 CPU 사용 감소로 전체 비용을 3분의 1 넘게 줄였다고도 설명합니다. RMA의 가치는 수천 독자마다 서버 측 요청을 실행하지 않는 데 있습니다. 다만 이 수치를 일반적인 인프라 할인율로 해석할 수는 없습니다. Google의 기존 스택, RMA 지원 머신, 캐시 구조, 작업 조합을 기준으로 한 운영 결과입니다.

이 구조가 맞는 작업 조건

Fast ACS는 쓰기보다 읽기가 훨씬 많고 최근 바이트에 읽기가 집중되며, 수백 ms에서 수 초의 전달을 허용할 때 적합합니다. 파일 계약은 뒤처진 소비자에게 영속 복구 경로를 제공합니다. 작은 청크 캐시는 tail read를 분산하고 one-sided 경로는 서비스 CPU를 줄입니다. 전역 복사 트리는 WAN 복제 비용을 줄이면서 정상 클러스터에서 로컬 읽기를 유지합니다.

50ms 미만이 필요한 소비자, 오래된 이력을 계속 스캔하는 스트림, 효율적인 RMA가 없는 환경에는 덜 적합합니다. 폴링을 자주 하면 지연은 줄지만 메타데이터 캐시 QPS가 증가합니다. 청크를 키우면 메타데이터 부하는 줄지만 hot block과 불필요한 읽기가 다시 커집니다. 4KB는 모든 패브릭에 그대로 적용할 형식이 아니라 이 시스템이 선택한 동작 지점입니다.

최소 한 번 전달은 중복 제거 책임을 소비자에게 남깁니다. 여러 지역에서 정확히 한 번을 보장하려면 생산자 상태, 전송, 모든 소비자 체크포인트에 걸친 트랜잭션이 필요하며 Fast ACS는 이를 범위에서 제외합니다. 애플리케이션은 순서가 맞는 중복을 허용할지 결정하고 필요한 갱신을 멱등하게 만들어야 합니다.

가장 일반화할 교훈은 계약의 분리입니다. 순서는 단조 증가하는 파일 위치에서, 내구성은 분산 파일 시스템에서, fan-out 처리량은 복제 메모리와 RMA에서, 지역 간 효율은 복사 트리에서 나옵니다. 속성을 독립 계층으로 나누면 장애 경로가 보이고 보수적인 fallback을 둘 수 있습니다. 동시에 용량 계산법도 분명해집니다. 유효 생산 바이트만으로는 부족하며 부분 청크 반복 읽기, 정족수 재시도, 하위 fan-out, fallback 폭증을 실제 네트워크 및 요청 예산에 넣어야 합니다.

출처와 저작권 안내

이 글은 Silicon & Systems가 작성한 편집 분석으로, 원문의 구조와 운영 근거, 한계를 우리 표현으로 다시 썼습니다. 원문의 문장, 표, 도판은 재수록하지 않았고 설명 도판은 이 글을 위해 새로 만들었습니다. 전체 논문은 USENIX ATC 2025 발표 페이지에서 확인할 수 있으며, 저작권은 저자에게 있습니다. 2025.