Pitchfork 이야기

The Pitchfork Story | byroot’s blog

3줄 요약

  • Pitchfork는 Shopify에서 개발된 Ruby HTTP 서버로, Unicorn의 메모리 사용량 문제 해결을 위해 리포킹(Reforking) 기법을 도입했습니다.
  • 리포킹은 warm-up된 워커를 복제하여 Copy-on-Write 효율을 높이고, 메모리 사용량 감소와 평균 레이턴시 개선 효과를 가져왔습니다.
  • grpc fork-safety, 파일 디스크립터 누수 등 난관을 극복하고 프로덕션에 적용되었으며, 현재 대규모 Ruby 모놀리스에 효과적인 솔루션으로 활용됩니다.

Shopify의 Ruby 및 Rails 인프라 팀에서 개발된 새로운 Ruby HTTP 서버인 Pitchfork가 소개됩니다. 이 서버는 기존 Unicorn의 한계를 극복하기 위한 독특한 설계와 절충안을 바탕으로 하며, 특히 메모리 사용량 최적화에 중점을 둡니다.

저자는 Unicorn의 다중 프로세스 설계가 대규모 모놀리스의 복원력에 기여했음을 인정하면서도, 메모리 효율성 문제를 지적합니다. Ruby 코드의 지연 로딩 패턴과 VM의 인라인 캐시가 Copy-on-Write(CoW) 효율을 제한하는 주된 원인임을 분석합니다. Puma의 아이디어와 Linux의 PR_SET_CHILD_SUBREAPER 기능을 결합하여 프로세스 계층 구조 문제를 해결하고, socketpair(2)UNIXSocket#send_io를 활용한 IPC 개선을 통해 리포킹 메커니즘의 기술적 기반을 마련합니다. Unicorn을 포크하여 Pitchfork를 개발한 것은 기존 프로젝트의 제약 때문이었습니다. Pitchfork는 monitor, mold, worker 구조에서 warm-up된 mold 프로세스로부터 새로운 worker를 복제하는 리포킹을 통해 CoW 효율을 높입니다. 초기 개발 중 grpc gem의 fork-safety 문제와 raindrops gem 사용으로 인한 파일 디스크립터 누수 등 심각한 버그들이 발생했으나, CI 시뮬레이션 및 프로덕션 디버깅을 통해 해결했습니다. 이러한 과정을 거쳐 Pitchfork는 메모리 사용량을 30% 줄였고, 예상치 못하게 평균 레이턴시를 9% 감소시키는 성과를 보였습니다. 레이턴시 개선은 warm-up된 상태를 복제하는 리포킹의 부수적 효과였습니다. YJIT 오버헤드를 줄이기 위해 초기 YJIT 활성화를 특정 워커로 제한하는 등의 추가 최적화도 적용했습니다.

Pitchfork는 fork-safety 문제의 복잡성 때문에 모든 환경에 적합한 것은 아니지만, 현재 Ruby의 병렬 처리 한계와 에코시스템의 느린 변화 속도를 고려할 때, 대규모 Ruby 모놀리스의 메모리 및 성능 문제를 해결하는 실용적이고 효과적인 솔루션입니다. 저자는 Ruby 자체의 미래 발전을 통해 Pitchfork가 불필요해지기를 바라지만, 현실적으로는 앞으로 몇 년간 유용할 것으로 전망합니다.