트래픽이 많은 한 비디오 사이트는 Varnish와 Edge Side Includes(ESI)를 활용해 전체 페이지 캐싱에서 프래그먼트(fragment) 캐싱 방식으로 전환함으로써, 트렌딩 페이지 피드를 위한 데이터베이스 쿼리 수를 분당 4,000개에서 50개 미만으로 줄였고 95퍼센타일(95th-percentile) 응답 시간을 380ms에서 40ms로 단축했습니다.

왜 사이트에 다른 캐시 전략이 필요했는가

그날의 가장 많이 본 클립을 보여주는 메인 페이지는 특정 지역의 거의 모든 방문자에게 동일하게 보입니다. 백만 명의 사용자에게 HTML의 약 95%는 동일하지만, 나머지 5%에는 로그인한 사용자의 이름이나 검색창과 같은 개인 데이터가 포함되어 있습니다. 엔지니어링 팀은 두 가지 까다로운 선택지에 직면했습니다.

  • 페이지 전체를 캐싱하여 로그인한 사용자에게 오래된(stale) 개인 데이터를 제공할 위험을 감수하거나,
  • 캐시를 완전히 우회하여 모든 요청이 데이터베이스에 과부하를 주게 하거나.

두 방식 모두 사용자 경험을 해쳤습니다. 팀은 리버스 프록시가 네트워크 에지(edge)에서 독립적으로 캐싱된 프래그먼트들을 조합해 페이지를 구성할 수 있게 해주는 기술인 ESI를 도입했습니다.

프래그먼트 캐싱의 구현 방식

오픈 소스 HTTP 가속기인 Varnish는 페이지를 세 개의 교체 가능한 조각으로 구성된 스켈레톤(skeleton)으로 취급했습니다.

  • 비디오 그리드(Video grid) – 비용이 많이 드는 지역 단위의 트렌딩 비디오 목록입니다. 자주 변경되지만 모든 익명 방문자에게 동일하므로 60초 동안 캐싱됩니다.
  • 언어 전환기(Language switcher) – 거의 변경되지 않는 정적 UI 요소입니다. 24시간 동안 캐싱됩니다.
  • 헤더(Header) – 유일하게 개인화된 프래그먼트(사용자 이름, 아바타, 알림)입니다. 캐싱하지 않으며, Varnish는 매번 애플리케이션 서버로 요청을 전달합니다.

요청이 들어오면 Varnish는 캐싱된 스켈레톤을 제공하고, 로컬 저장소에서 두 개의 캐싱된 프래그먼트를 가져온 뒤, 백엔드로부터 실시간 헤더를 삽입합니다.

주요 수치

전환 후:

  • 트렌딩 페이지의 데이터베이스 부하가 분당 4,000개 쿼리에서 50개 미만으로 감소했습니다.
  • 95퍼센타일 지연 시간이 380ms에서 40ms로 단축되었습니다.

도입 과정에서 얻은 세 가지 실무적 교훈

1. Grace period(유예 기간)를 통한 캐시 미스 완화 프래그먼트의 TTL이 만료되면, 일반적으로 Varnish는 새로운 콘텐츠를 가져오기 위해 일시 중지하며, 이로 인해 지연 시간이 급증하고 동시에 백엔드 호출이 폭주하는 "thundering herd" 현상이 발생할 수 있습니다. Grace period를 설정하면, Varnish는 백그라운드에서 조용히 캐시를 갱신하는 동안 기존의 오래된 프래그먼트를 계속 제공합니다. 사용자는 끊김을 느끼지 못하며, 백엔드는 안정적이고 관리 가능한 요청률을 유지합니다.

2. 익명 프래그먼트의 쿠키 제거 모든 요청에 쿠키가 포함되어 있으면 Varnish는 각 요청을 고유한 것으로 취급하여 캐시 히트(cache hit)를 무효화합니다. 팀은 비디오 그리드와 언어 전환기에서 쿠키를 제거하여 해당 프래그먼트들을 공격적으로 캐싱할 수 있도록 했습니다. 헤더 프래그먼트에만 쿠키를 포함시켜, 캐시 효율성을 희생하지 않으면서도 개인화 기능을 유지했습니다.

3. Surrogate key를 통한 즉각적인 퍼징(purging) 때로는 저작권 문제 등으로 인해 비디오를 즉시 삭제해야 할 때가 있습니다. 60초의 TTL이 만료될 때까지 기다리는 것은 용납되지 않습니다. 각 캐싱된 프래그먼트에 기반이 되는 비디오 ID를 반영하는 surrogate key를 태깅함으로써, 팀은 단 한 번의 purge 명령으로 모든 에지 노드에 있는 특정 비디오의 모든 복사본을 즉시 무효화할 수 있습니다. 이를 통해 전체 캐시를 스캔할 필요 없이 사이트의 규정 준수 상태를 유지할 수 있습니다.

요약: Varnish와 ESI를 활용한 프래그먼트 캐싱은 데이터베이스에 종속된 거대한 단일 페이지를 가볍고 재사용 가능한 조각들로 전환하여, 사용자별 개인화를 유지하면서도 백엔드 부하와 지연 시간을 획기적으로 줄여줍니다.