Sidekiq 장시간 비동기 처리의 중단 및 재개 구현

Sidekiqで実現する長時間非同期処理の中断と再開 / hypermkt - Kaigi on Rails 2024

3줄 요약

  • Sidekiq 장시간 비동기 처리는 배포 시 데이터 불일치 및 재실행 시간 증가 등 문제를 야기합니다.
  • 안정적인 배포를 위해 비동기 처리의 중단 및 재개 구현이 필수적이며, SmartHR은 Redis를 활용한 독자적인 방식을 개발했습니다.
  • 최근 Sidekiq Iteration 기능이 공식적으로 도입되어 중단/재개 구현이 더 용이해질 전망입니다.

본 문서는 SmartHR에서 Sidekiq을 활용한 장시간 비동기 처리 중 발생하는 배포 관련 문제를 해결하기 위해 도입한 중단 및 재개 기능의 구현 사례를 상세히 설명합니다. 장시간 실행되는 백그라운드 작업은 애플리케이션 배포 시 강제 종료될 경우 데이터 정합성 문제나 재실행으로 인한 불필요한 시간 소요를 야기할 수 있습니다. 이러한 문제를 해결하고 안정적인 시스템 운영 및 배포를 위해 비동기 작업의 안전한 중단 및 중단된 시점부터 재개하는 기술의 중요성이 부각됩니다. SmartHR 팀은 이러한 필요성에 따라 자체적인 중단/재개 처리 방식을 개발하여 적용해왔으며, 본 발표는 해당 구현의 구체적인 방법과 고려사항을 공유하고자 합니다.

Sidekiq에서 장시간 실행되는 작업이 배포 과정에서 중단될 경우, 데이터 불일치나 처음부터 다시 실행해야 하는 비효율이 발생합니다. 이를 해결하기 위해 ‘중단’은 Sidekiq 프로세스 종료를 감지하여 임의의 타이밍에 작업을 안전하게 멈추는 것을 의미하며, ‘재개’는 중단된 시점부터 처리를 이어가는 것을 의미합니다. 이 기술을 통해 배포 담당자는 안심하고 배포를 진행할 수 있고, 사용자는 비동기 작업 결과를 최소한의 시간으로 얻을 수 있습니다.

SmartHR의 구현 방식은 다음과 같습니다. 먼저, Sidekiq 설정 파일에 이벤트 핸들링을 설정하여 Sidekiq 프로세스 종료 신호를 감지하고 플래그를 설정합니다. 작업(Worker) 내에서는 이 플래그를 확인하는 헬퍼 메서드(raise_if_shutting_down)를 정의하여, 플래그가 설정되었을 경우 예외를 발생시켜 작업을 안전하게 중단시킵니다. 중단점은 데이터 등록 완료 후나 각 처리 단위의 끝 등 데이터 정합성이 유지되는 ‘처리 단위’ 별로 설정하는 것이 중요합니다.

재개 처리를 위해서는 작업의 ‘진행 상태’를 관리해야 합니다. SmartHR은 Redis를 활용하여 이 진행 상태를 기록합니다. 진행 상태 관리 방식은 처리 대상 데이터의 특성에 따라 두 가지 패턴으로 나뉩니다.

  1. ID 번호 기반 방식: 데이터베이스 레코드의 고유 ID를 사용하여 처리된 항목을 기록합니다. Redis 키는 job_id와 ID 종류 이름을 조합하여 사용하며, 처리 완료된 ID를 Redis SET에 저장합니다. 작업 재개 시, 이전에 저장된 ID 목록을 확인하여 이미 처리된 항목은 건너뛰고(skip) 미처리된 항목부터 작업을 재개합니다. 작업 완료 시에는 해당 job_id와 관련된 Redis 데이터를 삭제합니다.

  2. 행 번호 기반 방식: 주로 CSV 파일 가져오기와 같이 순차적인 처리에 사용됩니다. 처리된 행 번호, 실패한 행 번호, 행별 오류 메시지 등 세 가지 종류의 정보를 Redis에 저장하여 진행 상태를 관리합니다. CSV 처리 시 한 행에 여러 오류가 발생할 수 있다는 점을 고려하여, 행 번호별 오류 메시지는 Redis Sorted Set에 저장하고, 스코어는 ‘행 번호 * 10000 + 오류 메시지 인덱스’와 같이 계산하여 행 내 오류 메시지 순서 및 행 간 순서를 보장합니다. 재개 시에는 처리된 행 번호를 확인하여 스킵하고, 오류 정보는 작업 완료 후 사용자에게 결과 보고 시 활용합니다.

실제 적용 사례로는 실행 시간이 짧고 파일 용량이 작은 파일 내보내기의 경우 중단 처리만으로 충분할 수 있으나, 실행 시간이 길고 파일 용량이 수백 MB 이상인 대용량 파일 내보내기의 경우 중단 및 재개 처리가 모두 필요합니다. 특히 대용량 파일 처리 중 중단 시 임시 파일을 저장하는 문제가 발생하며, Redis에 직접 저장하는 것은 메모리 부담으로 인해 부적합하다고 판단, Google Cloud Storage(GCS)와 같은 오브젝트 스토리지에 압축하여 저장하는 방식을 채택했습니다. 재개 시에는 GCS에서 임시 파일을 다운로드하여 중단된 시점부터 이어 작성합니다.

이러한 중단/재개 기능 구현의 유효성을 검증하기 위한 테스트 코드 작성도 중요합니다. 중단 테스트는 의도한 시점에 작업이 중단되고 데이터 일관성이 유지되는지 확인하며, 재개 테스트는 중단 후 재실행 시 올바르게 중단 시점부터 처리가 이어지고 완료되는지를 검증합니다. RSpec의 allow(...).and_wrap_original과 같은 기법을 활용하여 특정 시점에 강제로 중단을 발생시키는 방식으로 테스트를 수행할 수 있습니다.

한편, 2024년 4월 3일 Sidekiq 버전 7.3.0부터 ‘Sidekiq Iteration’이라는 공식적인 중단/재개 기능이 도입되었습니다. 현재는 베타 단계이지만, 커서 저장 및 재개 기능, 콜백(on_start, on_resume) 지원, 액티브 레코드/CSV 처리를 위한 헬퍼 메서드 등을 제공하여 기존의 커스텀 구현보다 훨씬 간편하게 중단/재개 기능을 도입할 수 있을 것으로 예상됩니다. SmartHR의 자체 구현은 수년간 안정적으로 운영되었으나, 향후 Sidekiq Iteration의 안정화 여부를 지켜보며 도입을 검토할 가치가 충분합니다.

결론적으로, Sidekiq을 사용하는 시스템에서 장시간 실행되는 비동기 작업은 배포 프로세스에 큰 영향을 미칠 수 있습니다. 이를 극복하기 위해 작업 중단 및 재개 기능을 구현하는 것이 매우 중요하며, 이를 통해 안전하고 효율적인 배포 및 작업 실행이 가능해집니다. SmartHR의 Redis를 활용한 ID 기반 및 행 번호 기반의 진행 상태 관리와 GCS를 활용한 대용량 파일 처리 중단/재개 방식은 이러한 문제에 직면한 다른 엔지니어들에게 유용한 참고 사례가 될 것입니다. 또한, 최근 공식적으로 발표된 Sidekiq Iteration 기능은 향후 중단/재개 구현을 더욱 단순화할 잠재력을 가지고 있어 주목할 필요가 있습니다.