Computer >> 컴퓨터 >  >> 프로그래밍 >> Ruby

Rails 백그라운드 작업 처리기 선택 가이드: Delayed Job vs Sidekiq 완벽 비교

대부분의 애플리케이션은 메일 발송, 주기적인 데이터 정리, 사용자가 대기할 필요 없는 시간이 오래 걸리는 작업 등을 위해 백그라운드 잡(job) 기능을 필요로 합니다.

Rails 생태계에는 잡 큐와 백그라운드 처리를 지원하는 여러 젬(gem)이 있으며, 그중 Delayed JobSidekiq가 가장 널리 사용되는 두 가지입니다.

이 글에서는 Delayed Job과 Sidekiq를 자세히 살펴보고, 두 도구가 서로 어떤 차이를 보이는지 비교해 보겠습니다.

그럼 시작해 보겠습니다!

Delayed Job 간단 소개

Delayed Job은 Shopify에서 직접 추출된 프로젝트로, 데이터베이스 테이블을 사용해 모든 백그라운드 잡을 관리합니다. 구조가 매우 단순해서, perform 메서드에 응답하는 모든 Ruby 객체를 잡 테이블에 enqueue할 수 있습니다.

또한 별도의 잡 객체를 만들고 싶지 않다면(다만 테스트 용이성과 장기 실행 작업의 명확한 분리를 위해 잡 객체 생성을 강력히 권장합니다), 어떤 Ruby 객체에든 .delay.method(params)를 호출하여 해당 메서드를 백그라운드에서 처리하도록 할 수도 있습니다.

Delayed Job의 README에서는 일반적인 사용 패턴을 잘 설명하고 있으니 참고하시기 바랍니다.

많은 팀이 Delayed Job을 선택하는 이유는 단순함과 함께 이미 사용 중인 데이터베이스를 그대로 활용할 수 있어 추가 리소스를 운영·유지할 필요가 없기 때문입니다.

다만 그만큼 데이터베이스 테이블 공간을 차지하게 됩니다. 동시에 대기 중인 잡이 너무 많아지면 이를 수용하기 위해 더 많은 디스크 공간이 필요할 수 있습니다.

Sidekiq 간단 소개

반면 Sidekiq는 Redis를 데이터 저장소로 사용하여 모든 잡 메타데이터를 관리합니다. 덕분에 Delayed Job이 사용하는 일반적인 데이터베이스 시스템보다 훨씬 빠르다는 명확한 장점이 있습니다. 여기에 더해 각 Sidekiq 프로세스는 여러 스레드를 생성해 잡을 한층 빠르게 처리합니다.

Sidekiq에서 각 백그라운드 잡은 Sidekiq::Worker 모듈을 include하고 perform 메서드에 응답하는 전용 클래스로 정의합니다. 잡을 enqueue하려면 워커 클래스에서 인자와 함께 perform_async(arg1, arg2)를 호출하면 됩니다.

Sidekiq의 'Getting Started' 가이드에서 이러한 내용과 다양한 사용 패턴을 상세히 설명하고 있습니다.

Active Job으로 Delayed Job 또는 Sidekiq 사용하기

Rails는 잡을 선언하고 처리하기 위한 성숙한 프레임워크인 Active Job을 이미 제공합니다. Delayed Job과 Sidekiq 모두 Active Job의 통합 API를 통해 잡을 실행하는 것을 지원합니다.

ApplicationJob을 상속받고 잡 클래스에서 perform_later를 호출하면 설정된 큐잉 백엔드로 잡이 enqueue됩니다.

Active Job을 사용하면 애플리케이션 코드가 특정 프레임워크에 종속되지 않아, Delayed Job에서 Sidekiq로(또는 그 반대로) 전환이 매우 쉬워진다는 장점이 있습니다. ActiveJob::TestHelper를 활용하면 enqueue된 잡의 테스트도 손쉽게 작성할 수 있습니다.

하지만 Active Job이 제공하는 추상화 계층에는 성능 오버헤드가 따릅니다. 잡 데이터가 저장소에 push되기 전에 래핑 과정을 거쳐야 하기 때문입니다. Sidekiq 측에 따르면 Active Job을 거치면 Redis에 push할 때 약 2~20배 느려지며, 처리 오버헤드는 약 3배 증가한다고 합니다.

Delayed Job vs Sidekiq 심층 비교

두 도구의 기본 사항을 알았으니, 이제 차이점과 각각이 제공하는 강점을 더 깊이 들여다보겠습니다.

기능 비교

기본적인 애플리케이션이라면 Sidekiq와 Delayed Job 모두 즉시 사용 가능한 충실한 기능 세트를 제공합니다. 여기에는 잡 우선순위 지정, 이름 있는 큐(named queue), 실패 시 자동 재시도 등이 포함됩니다.

Delayed Job은 최대 실행 시간(max run time) 설정을 기본으로 지원하는 반면, Sidekiq는 이를 기본 제공하지 않습니다.

반면 Sidekiq는 미들웨어(Middleware)를 지원하여 잡 메타데이터 업데이트, 잡 큐잉 건너뛰기, 잡 실행 등을 유연하게 제어할 수 있습니다. Sidekiq가 더 많은 콜백을 지원하지만, Delayed Job용으로도 일부 훅(hook)을 사용할 수 있습니다. 콜백 대신 Delayed Job을 Active Job과 함께 사용하면 Rails에 내장된 before_enqueue, around_perform 콜백을 활용할 수 있습니다.

웹 UI 역시 Sidekiq가 기본으로 제공하는 기능입니다. 잡에 대한 통계 기록, 워커 정보, 현재 대기 중인 잡과 실패(dead)한 잡 정보를 확인할 수 있으며, 콘솔에 접속하지 않고도 잡 삭제나 즉시 실행 같은 작업을 수행할 수 있습니다.

Delayed Job에는 내장 웹 UI가 없지만, delayed_job_web 젬을 사용하면 Sidekiq의 것과 유사한 기본 웹 UI를 이용할 수 있습니다.

성능은 Sidekiq의 승리

성능 면에서 Sidekiq는 Delayed Job을 압도적으로 앞섭니다. Sidekiq의 오픈소스 벤치마크에 따르면 Delayed Job보다 약 30배 빠릅니다. 그 이유는 크게 두 가지입니다.

  1. Redis는 디스크가 아닌 메모리에 데이터를 저장하기 때문에 Postgres 같은 전통적인 데이터베이스보다 훨씬 빠르게 데이터를 조회할 수 있습니다.
  2. Delayed Job이 잡 처리에 단일 스레드를 사용하는 반면, Sidekiq는 여러 스레드를 사용합니다.

다만 이런 차이가 숫자상 아무리 커 보여도, 대규모(예: 분당 약 1만 개 잡) 환경이 아니라면 체감 차이는 크지 않습니다. 실제 차이는 잡의 평균 실행 시간에도 좌우되는데, 실행 시간이 길수록 Delayed Job의 성능 오버헤드는 상대적으로 덜 중요해집니다.

Delaeyd Job의 성능이 걱정된다면 몇 가지 최적화를 적용할 수 있습니다. 어떤 인덱스를 추가할지는 잡 시스템의 통계에 따라 달라집니다. 예를 들어 여러 큐를 사용하는데 특정 하나의 큐에 잡이 집중된다면, 해당 큐 컬럼에 간단한 인덱스(add_index :delayed_jobs, :queue)만 추가해도 성능이 크게 향상됩니다.

AppSignal을 사용하면 Sidekiq 매직 대시보드가 자동으로 생성되어 큐 길이, 큐 지연 시간, 잡 실행 시간, 잡 상태, 메모리 사용량 등을 모니터링할 수 있습니다.

Rails 백그라운드 작업 처리기 선택 가이드: Delayed Job vs Sidekiq 완벽 비교

배포 방식

Delayed Job과 Sidekiq 모두 워커 배포 전략이 비슷합니다. Heroku를 사용한다면 Procfile에 잡 프로세서를 시작하는 항목을 추가해 워커를 실행하기만 하면 됩니다.

Sidekiq의 경우:


Delayed Job의 경우:


메모리 사용

여기부터는 상황이 조금 더 흥미로워집니다. Sidekiq에는 실행할 스레드 수를 조절하는 concurrency 옵션이 있습니다. Sidekiq vs Delayed Job 벤치마크 대부분은 최대 25개 스레드에 달하는 Sidekiq의 높은 동시성을 언급하며, 이것이 초고속 성능의 원천이라고 설명합니다.

하지만 실제 운영 환경에서는 스레드 수를 훨씬 보수적으로 제한해야 합니다. 적절한 값은 애플리케이션의 무게와 실행하는 잡의 종류에 따라 달라집니다. 실무 경험상 512MB 메모리(Heroku의 standard-1x에 해당)로 워커를 실행하면 스레드 수는 25개가 아니라 2~5개 수준이 되곤 합니다.

Sidekiq 창시자 Mike Perham이 쓴 'Taming Rails memory bloat' 글에서는 메모리 문제를 더 자세히 다루고 있으니 읽어볼 가격이 충분합니다. 전체 내용을 다루지는 않겠지만, 그는 Sidekiq를 실행하는 모든 워커에 MALLOC_ARENA_MAX=2를 설정할 것을 권장합니다.

일반 malloc 대신 jemalloc을 사용하는 것도 도움이 됩니다. 적용 방법은 플랫폼마다 다르지만 Heroku에서는 매우 간단합니다. heroku-buildpack-jemalloc을 첫 번째 빌드팩으로(heroku/ruby 빌드팩보다 앞에) 설정하기만 하면 됩니다.

Delayed Job은 더 단순한 리소스로 운영

앞서 언급했듯이 Delayed Job은 기존 데이터베이스 인스턴스 위에서 동작합니다. 잡 부하나 워커 수에 따라 다음 항목들을 늘려야 할 수 있습니다.

  • 사용 가능한 메모리
  • 디스크 공간
  • 최대 연결 수

하지만 필요한 리소스는 잡 프로세서 하나뿐입니다.

반면 Sidekiq는 잡을 처리하기 위해 Redis 인스턴스가 필요합니다. Redis를 캐시 저장소로도 사용하고 있다면, Sidekiq 잡 전용으로 "영구 저장소(persistent store)"로 설정된 별도 인스턴스를 사용하는 것이 좋습니다.

Redis는 모든 데이터가 메모리에 맞을 때 가장 잘 동작하므로, 잡이 너무 많이 쌓이면(예: 애플리케이션 문제로 Sidekiq가 일정 시간 잡을 처리하지 못한 경우) 이를 정리하는 데 다운타임이 필요할 수 있습니다. 특히 Redis를 애플리케이션과 같은 서버에서 운영한다면 곤란합니다. 두 프로세스가 메모리를 두고 경쟁하게 되어 스왑(swap)이 발생하고, 결국 애플리케이션 성능이 크게 저하됩니다.

Redis에 관해 한 가지 중요하게 기억할 점은, Sidekiq 데이터가 조용히 유실되는 것을 막으려면 maxmemory-policy noeviction으로 설정해야 한다는 것입니다. 그렇지 않으면 실행해야 할 잡들이 흔적도 없이 사라지는 경험을 하게 될 수 있습니다.

참고: Sidekiq의 유료 업그레이드

추가 기능이 필요하다면 Sidekiq의 ProEnterprise 버전을 고려할 수 있습니다.

Pro 버전의 가장 두드러진 추가 기능은 병렬로 실행되고 모니터링 가능하며 그룹으로 상호작용하는 Batch Jobs(일괄 잡)입니다. 모든 잡이 완료되면 콜백을 호출합니다. Pro에는 네트워크 문제 상황에서도 잡이 조용히 유실되지 않도록 보장하는 향상된 신뢰성 기능도 포함되어 있습니다.

Enterprise 버전에는 더 많은 기능이 담겨 있습니다. 일반 Sidekiq 설치로 해결할 수 없는 요구사항이 있다면 유료 기능들을 살펴보시기 바랍니다.

실제로 무료 버전의 Sidekiq도 충분히 훌륭하게 동작합니다. 다만 필요할 때 다른 솔루션으로 갈아타는 대신 유료 옵션으로 업그레이드할 수 있다는 점을 알아두면 좋습니다.

커뮤니티와 개발 현황: Sidekiq 우위

Sidekiq와 Delayed Job 모두 그 뒤에는 거대한 커뮤니티가 있습니다. 다만 StackOverflow나 공식 문서에서 질문에 대한 빠른 답을 찾기란 항상 쉬운 일은 아닙니다.

개발 측면에서 Delayed Job의 전망은 밝지 않습니다. 2021년 12월과 2022년 1월에 일부 소규모 작업이 이루어졌지만, 이후 큰 개발이 진행될 기미는 보이지 않습니다. 사실상 유지보수(maintenance-only) 모드에 들어간 상태이며, GitHub에는 열린 이슈가 많이 남아 있습니다.

반면 Sidekiq는 여전히 활발히 개발되고 있으며, 창시자가 풀타임으로 프로젝트에 매달리고 있습니다. 열린 이슈가 거의 없고, 발생한 이슈도 꾸준히 해결되고 있습니다.

마무리: Sidekiq인가, Delayed Job인가? — 필요에 따라 선택하라

이 글에서는 Rails 애플리케이션의 두 대표 잡 처리 시스템인 Sidekiq와 Delayed Job의 장단점을 살펴보았습니다.

두 도구各有 각자의 적합한 사용 사례가 있으며, 결국 예산과 운영 규모에 따라 선택이 달라집니다.

성능과 장기적인 유지보수성이 중요하다면 Sidekiq가 자연스러운 선택입니다. 반대로 운영 비용이 부담스럽다면 Delayed Job이 좋은 대안이 될 수 있습니다.

Delayed Job을 선택하든 Sidekiq를 선택하든, 프로젝트가 순조롭게 진행되기를 바라며 즐거운 코딩 되세요!

👋 이 글이 마음에 드셨다면, Ruby 성능 모니터링 체크리스트에서 다른 Ruby(on Rails) 성능 관련 글들도 확인해 보세요.

P.S. Ruby Magic 포스트를 발행 즉시 읽고 싶으시다면 Ruby Magic 뉴스레터를 구독하고 어떤 글도 놓치지 마세요!