1. ActiveJob Continuable의 핵심 개념
Job Continuations는 작업에 소형 상태 머신을 추가하는 방식입니다. 특정 작업을 continuable로 선언하고 안전한 경계 지점에 checkpoint를 배치하면, 런타임이 인자, 마지막 체크포인트 레이블, 최소한의 로컬 변수를 포함한 압축된 스냅샷을 큐 저장소에 저장합니다. 종료 신호(SIGTERM)가 발생하면 작업은 처음부터 다시 시작하는 대신 최신 체크포인트에서 재개됩니다.
- 명시적 선택(Opt-in): 모든 작업이 아닌
continuable!로 마킹된 작업만 참여합니다. - 구조적 멱등성: 체크포인트는 개발자가 단계를 명확한 입력과 출력을 가진 순수 연산에 가깝게 설계하도록 유도합니다.
- 저장소 호환성: Solid Queue, Sidekiq 등 작은 페이로드를 저장할 수 있는 어댑터와 함께 작동합니다.
2. 구현 방법 및 코드 예시
설정 파일에서 기능을 활성화한 후, 작업 클래스 내에서 체크포인트를 설정합니다. 주로 I/O 경계나 루프의 청크 단위에 배치하는 것이 권장됩니다.
```ruby # config/initializers/continuations.rb Rails.application.configure do config.active_job.continuations.enabled = true config.active_job.continuations.ttl = 24.hours end
app/jobs/reindex_catalog_job.rb
class ReindexCatalogJob < ApplicationJob continuable!
def perform(store_id) checkpoint! :load_ids do @ids = Product.where(store_id: store_id).pluck(:id) end
checkpoint_each :batch, @ids.each_slice(500) do |batch|
Search::Client.bulk_index(batch)
end end end ```
3. 배포 도구와의 통합 (Kamal)
Kamal을 사용하면 워커를 완전히 비우지(drain) 않고도 안전하게 배포할 수 있습니다. 컨테이너가 중단되기 전 훅을 통해 상태를 저장하고, 새 컨테이너 시작 시 재개 명령을 실행합니다.
- Pre-stop:
bin/rails jobs:continuations:checkpoint를 호출하여 실행 중인 작업의 스냅샷을 찍습니다. - Post-start:
bin/rails jobs:continuations:resume을 호출하여 새 환경에서 작업을 다시 시작합니다.
4. 성능 및 주의 사항
체크포인트는 직렬화로 인해 약간의 오버헤드를 발생시키지만, 재작업을 제거함으로써 얻는 이득이 더 큽니다.
- 성능: 일반 실행 시 오버헤드는 0~2% 수준이며, 루프 체크포인트 사용 시 약 3~6% 정도의 부하가 발생합니다.
- 상태 관리: 대규모 객체를 스냅샷에 저장하지 말고, ID와 같은 최소한의 데이터만 유지해야 합니다.
- 멱등성 확보: 외부 API 호출과 같은 사이드 이펙트가 있는 단계는 멱등성 키를 사용하거나 업서트(upsert) 방식을 활용하여 중복 처리를 방지해야 합니다.