차세대 아웃 오브 밴드 가비지 컬렉션

Next Generation Out of Band Garbage Collection | Rails at Scale

3줄 요약

  • Shopify는 Ruby 요청 처리 중 주요 GC 발생을 막기 위해 `GC.config(rgengc_allow_full_mark: false)`를 활용한 새로운 OOB GC 기법을 개발했습니다.
  • 이 기법은 필요한 경우에만 요청 외 시간에 수동으로 주요 GC를 트리거하며, Ruby 3.4.0-preview2에 포함되었습니다.
  • 새로운 OOB GC는 테일 응답 시간의 GC 시간을 크게 줄이고 서비스 지연 시간을 개선하는 등 상당한 성능 향상을 가져왔습니다.

Shopify는 대규모 Ruby 모놀리스 애플리케이션 운영에서 가비지 컬렉션(GC)으로 인한 지연 시간 문제를 해결하기 위해 지속적으로 노력해왔습니다. 이전에는 아웃 오브 밴드 GC(OOB GC)를 구현하여 주요 GC의 영향을 줄였지만, 평균 기반의 휴리스틱으로 인해 용량과 지연 시간 사이의 절충이 필요했으며 주요 GC가 요청 주기에서 완전히 배제되지 않는 한계가 있었습니다. 이러한 배경 하에, Shopify 엔지니어들은 주요 GC가 요청 처리 중에 절대 발생하지 않도록 하는 새로운 차세대 OOB GC 기법을 모색하게 되었습니다.

새로운 접근 방식의 핵심 목표는 요청 처리 중 Ruby GC가 자동으로 주요 컬렉션을 수행하거나 객체를 올드 세대로 승격시키는 것을 완전히 방지하는 것입니다. 웹 애플리케이션의 특성상 대부분의 객체는 요청이 완료되기 전에 사라지므로, 요청 중에 올드 세대로 승격되는 객체는 비효율적이라고 판단했습니다. 이를 위해 Ruby 커미터들과의 논의를 거쳐 GC.config(rgengc_allow_full_mark: true/false)라는 새로운 설정 옵션이 도입되었고, 주요 GC가 필요한지 여부를 확인할 수 있는 :needs_major_by 키가 GC.latest_gc_info에 추가되었습니다. 이 기능들은 Ruby 3.4.0-preview2부터 사용 가능합니다. Shopify는 프로덕션 환경의 50%에 이 새로운 OOB GC 구현을 적용했으며, 결과는 매우 고무적이었습니다. 예상대로 테일 응답 시간(p95/p99/p99.99)에서 GC 시간이 현저히 감소했으며, 놀랍게도 중앙값 지연 시간까지 개선되었습니다. 전반적인 서비스 지연 시간은 평균 5%, p99에서 10% 감소하는 효과를 보였습니다. 용량 측면의 개선은 기대보다 적었지만, 배포가 없는 안정적인 시간대에는 기존 구현보다 OOB GC 실행 횟수가 훨씬 줄어드는 장점이 있었습니다. 이 새로운 구현은 Pitchfork와 같은 도구를 활용하여 after_worker_fork에서 주요 GC를 비활성화하고 after_request_complete에서 :needs_major_by를 확인하여 필요한 경우에만 GC.start를 호출하는 방식으로 매우 간결하게 이루어졌습니다.

주요 GC를 요청 주기에서 성공적으로 분리함에 따라, 다음 최적화 대상은 마이너 컬렉션으로 옮겨가고 있습니다. 마이너 컬렉션은 대량 할당으로 인한 메모리 부족을 막기 위해 완전히 비활성화할 수는 없지만, `GC.stat` 정보 등을 활용하여 요청 외 시간에 마이너 GC를 미리 수행하는 방안을 고려할 수 있습니다. 그러나 마이너 컬렉션은 이미 비교적 빠르기 때문에, 이를 통한 잠재적 성능 개선 폭은 주요 GC 최적화에 비하면 작을 것으로 예상됩니다. 그럼에도 불구하고 Shopify는 Ruby GC 성능 개선을 위한 노력을 지속할 것입니다.