사이드킥 큐에서 태스크나 예약작업 삭제하기
들어가며
루비 온 레일스Ruby on Rails에서는 비동기 작업 처리로 사이드킥Sidekiq을 주로 사용합니다. 사이드킥은 등록된 작업을 레디스 큐에 저장해놓고 워커들에서 가져가서 처리하는 방식으로 동작합니다. 실무에서는 의도치 않게 실행되서는 안 되는 작업이 큐나 예약 작업에 등록되는 경우가 종종 있습니다. 이런 경우 사이드킥의 API를 사용해 특정 작업들만 삭제해줄 필요가 있습니다. 이 글에서는 사이드킥 API를 사용해 예약 작업을 가져오고 특정 조건으로 필터링해서 삭제하는 방법을 소개합니다.
샘플 루비 온 레일스 프로젝트 준비
먼저 루비 2.6과 최신 버전의 레일스(6.0)로 샘플 프로젝트를 만들어 보겠습니다.*
* 레일스6에는 자바스크립트 초기화 작업이 포함되어, 적절한 환경이 구축되어있지 않을 경우 초기화가 번거로울 수 있습니다. 여기서는 --skip-javascript
옵션으로 자바스크립트 관련 초기화 작업을 우회했습니다.
$ rails --version
6.0.3
$ rails new --skip-javascript awesome_sidekiq
$ cd awesome_sidekiq
사이드킥과 비동기 처리를 위한 잡 클래스 추가
초기화된 레일스 프로젝트에 sidekiq 의존성을 추가해줍니다. Gemfile
에 다음 내용을 추가합니다.
gem 'sidekiq', '~> 6.0.0'
추가한 의존성을 설치해줍니다.
bundle install
다음으로 rails
명령어를 사용해 AwesomeJob
을 생성합니다.
$ bundle exec rails g job awesome
Running via Spring preloader in process 8705
invoke test_unit
create test/jobs/awesome_job_test.rb
create app/jobs/awesome_job.rb
app/jobs/awesome_job
파일을 다음과 같이 작성합니다. AwesomeJob
이 하는 일은 obj
객체를 받아 출력하는 일입니다.
사이드킥 상태를 확인하기 위해 라우터에 sidekiq
대시보드를 마운트 해줍니다. config/routes.rb
파일에 다음 내용을 추가합니다.
require 'sidekiq/web'
Rails.application.routes.draw do
mount Sidekiq::Web => '/sidekiq'
# ...
end
이걸로 코드 준비는 끝났습니다. 하지만 사이드킥에 잡을 등록하기 위해서는 태스크를 저장할 레디스 서버가 필요합니다. 여기서는 도커Docker 컨테이너로 레디스 서버를 준비하겠습니다.
$ docker run --name sidekiq_redis -d -p 6399:6379 redis
아래 내용을 config/initializers/sidekiq.rb
파일에 작성합니다.
Sidekiq.configure_server do |config|
config.redis = { url: 'redis://0.0.0.0:6399/0' }
end
Sidekiq.configure_client do |config|
config.redis = { url: 'redis://0.0.0.0:6399/0' }
end
이것으로 모든 준비를 마쳤습니다. 레일스 서버를 실행합니다.
$ rails s
이제 0.0.0.0:3000/sidekiq
페이지에 접속하면, 사이드킥 대시보드를 확인할 수 있습니다.

사이드킥에 스케줄 잡 추가하고 삭제하기
여기서부터는 레일스 콘솔에서 작업해보도록 하겠습니다.
$ rails c
5분 후에 실행되는 태스크를 하나 추가해보겠습니다. 인자값으로는 {foo: 'bar', time: Time.now}
를 넘겨주었습니다.
> AwesomeJob.set(wait: 5.minutes).perform_later({foo: 'bar', time: Time.now})
Enqueued AwesomeJob (Job ID: 4d9a3417-c746-4a07-ab5c-ae2fb4c91bb3) to Sidekiq(awesome) at 2020-05-16 04:19:59 UTC with arguments: {:foo=>"bar", :time=>2020-05-16 13:14:59 +0900}
=> #<AwesomeJob:0x00007fed41838c50 @arguments=[{:foo=>"bar", :time=>2020-05-16 13:14:59 +0900}], @job_id="4d9a3417-c746-4a07-ab5c-ae2fb4c91bb3", @queue_name="awesome", @priority=nil, @executions=0, @exception_executions={}, @scheduled_at=1589602799.83359, @provider_job_id="95b306e3849ffeb3499ca0ee">
추가된 결과는 사이드킥 대시보드의 Scheduled 메뉴에서 확인할 수 있습니다. 따로 사이드킥 워커를 실행해놓지 않았기 때문에 5분이 지나도 이 작업이 실행되지는 않습니다.

그렇다면 이번에는 이 작업을 객체로 가져온 다음에 삭제해보도록 하겠습니다.
sq = Sidekiq::ScheduledSet.new
sq.each {|j| puts j.value }
# {"retry":true,"queue":"awesome", ...
sq
변수는 모든 예약 작업을 가지고 있습니다. 이 변수는 이터레이터를 지원해서 each
와 같은 메서드를 사용하면 모든 예약 작업의 내용을 확인하는 게 가능합니다. 잡은 사이드킥의 Sidekiq::SortedEntry
클래스이며, 잡의 내용은 value
속성에 담겨있습니다. 이 값은 레디스에 JSON 문자열로 저장되어있기 때문에 객체로 접근하기 위해서는 먼저 JSON으로 파싱을 해주어야합니다.
sq.each {|j| puts JSON.parse(j.value)['queue'] }
# awesome
파싱한 결과에서 queue
속성을 확인해보면 awesome
이 출력됩니다. JSON을 파싱하는 대신 args
메서드를 사용해도 결과는 같습니다.
sq.each {|j| j.args[0]['arguments'][0]['queue'] }
# awesome
앞서 잡에 넘겨준 인자값을 확인해봅니다. 조금 까다롭습니다만, 데이터 형식을 쫓아가면 넘겨준 값을 찾을 수 있습니다.
sq.each {|j| puts j.args[0]['arguments'][0]['foo'] }
# bar
args
는 배열로 되어있습니다. 그 안에 다시 arguments
안에 넘겨준 인자값들이 배열로 저장되어있습니다. 첫 번째 인자의 foo
속성을 확인하면 bar
가 들어있는 것을 확인할 수 있습니다.
큐의 이름이나 인자값들의 내용을 통해서 삭제하고자하는 작업을 필터링 할 수 있습니다.
간단한 예제를 위해 먼저 100개의 작업을 추가해보겠습니다.
1.upto(100).each do |i|
AwesomeJob.set(wait: 1.hour).perform_later({id: i})
end
sq = Sidekiq::ScheduledSet.new
sq.count
# 101
101개의 예약 작업이 쌓여있는 것을 확인할 수 있습니다.
id가 55번인 작업만 골라서 삭제해보겠습니다.
sq.each do |j|
id = j.args[0]['arguments'][0]['id']
j.delete if id == 55
end
sq.count
# 100
사이드킥 대시보드에서도 확인해볼 수 있습니다.

이번에는 id 값이 80 이상인 잡들만 삭제해보겠습니다.
sq.each do |j|
id = j.args[0]['arguments'][0]['id']
j.delete if id && id >= 80
end
sq.count
# 79
사이드킥 대시보드에서도 확인할 수 있습니다.

사이드킥 대시보드에도 잡을 삭제하는 기능이 추가되어서 한 2개의 작업을 삭제하는 경우에는 이 방법을 써도 무방합니다. 하지만 실무에서는 수만개 이상의 작업에서 조건에 맞는 작업만을 삭제하려면 사이드킥의 API를 활용하는 게 유용합니다.
큐에 있는 작업을 삭제하기
큐에 있는 작업은 다음과 같이 가져올 수 있습니다.
Sidekiq::Queue.new("queue_name")
필터링해서 삭제하는 방법은 스케줄 잡과 같습니다.
리트라이 셋과 데드 셋도 가져올 수 있습니다.
# 리트라이 셋에 저장된 작업들
Sidekiq::RetrySet.new
# 데드 셋에 저장된 작업들
Sidekiq::DeadSet.new
이외에도 사이드킥 API를 사용해 다양한 작업들을 수행할 수 있으니 더 자세한 내용은 다음 문서를 참고해주세요.