【Rails】Sidekiq でスケジューリングされた非同期処理を実行する

sidekiq 平八重

こんにちは! エンジニアインターンの平八重です。今回取り組んだプロジェクトの中で、指定した日時に自動的に処理を行う非同期処理をSidekiqを用いて実装しました。

その際に使用したライブラリと構築について紹介します。

  1. Sidekiq : 高速な処理とスケジューリング
  2. ジョブの管理
  3. フェイルオーバー
  4. まとめ

Sidekiq : 高速な処理とスケジューリング

今回のシステムでは、指定した日時にデータベースに登録されている全てのページ(URL)に対して HTTPリクエストを行うため、非同期処理を定期実行する必要がありました。

Sidekiq は、非同期ジョブ処理のための Ruby 向けのシステムであり、Railsとの親和性が非常に高いことが特徴です。

Rails は、Webアプリケーションの構築に必要な要素を提供するフレームワークであり、Sidekiq は、このフレームワークの機能を拡張して非同期ジョブ処理を実現するためのツールとして位置づけられます。

Rails には、ActiveRecordというオブジェクトリレーショナルマッピング(ORM)ライブラリがあり、データベースとのやり取りを行うことができます。

Sidekiq は、この ActiveRecord をサポートしているため、データベースにアクセスするジョブを実行することができます。

またジョブを定義し、非同期で実行する API のフレームワークであるActiveJobをサポートしています。

その他にも Sidekiq の構築にはRedisというインメモリデータベースを使用します。

Redis を使用してジョブをキューに格納し、非同期に処理するため、Webアプリケーションのレスポンス時間を短縮することができます。

また、ジョブを定期実行するための拡張gemである `sidekiq-scheduler` を使用しました。

開発環境の設定

今回は Rails + PostgreSQL + Redis + Sidekiq の環境で構築を行ったため、以下に docker-compose.yml ファイルの例を示します。

docker-compose.yml

version: 'version: '3'
services:
  db:
    image: postgres:13
    environment:
      POSTGRES_USER: <%= ENV.fetch('POSTGRES_USER') { 'postgres' } %>
      POSTGRES_PASSWORD: <%= ENV.fetch('POSTGRES_PASSWORD') { 'postgres' } %>
      POSTGRES_DB: <%= ENV.fetch('POSTGRES_DB') { 'myapp_development' } %>
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  redis:
    image: redis:latest
    volumes:
      - ./tmp/redis:/data
    ports:
      - "6379:6379"

  web:
    build:
      context: .
      dockerfile: Dockerfile
    env_file:
      - .env
    volumes:
      - .:/app
      - bundle:/bundle
    ports:
      - "3000:3000"
    links:
      - db
    depends_on:
      - db
      - redis
      - sidekiq
    tty: true
    stdin_open: true

  sidekiq:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - .:/app
      - bundle:/bundle
    env_file:
      - .env

volumes:
  bundle:


各サービスには、必要な環境変数、ポートマッピング、ボリューム、依存関係が含まれています。また、上記のコードでは、データベースとRedisのデータを永続化するためにボリュームが定義されています。

これにより、データがコンテナを再起動しても失われなくなります。

Sidekiq と Redis のインストール

Gemfile に以下の行を追加し `bundle install` コマンドでインストールします。

gem 'redis', '~> 5.0', '>= 5.0.6'
gem 'sidekiq', '~> 6.5', '>= 6.5.8'

Sidekiqの設定

アダプターを Sidekiq にします。

config/application.rb

config.active_job.queue_adapter = :sidekiq


Sidekiq
 の設定ファイルを追加します。

config/sidekiq.yml

:concurrency: 5
:pidfile: ./tmp/pids/sidekiq.pid
:logfile: ./log/sidekiq.log
production:
  :concurrency: 5
:queues:
  - default
:daemon: true


次に Sidekiq から接続する Redis の URL を設定します。

config/initializes/sidekiq.rb

Sidekiq.configure_server do |config|
  config.redis = { url: ENV["REDIS_URL"]  }
end

Sidekiq.configure_client do |config|
  config.redis = { url: ENV["REDIS_URL"]  }
end

ジョブの管理

以下の手順で、実行するジョブの作成と実行スケジュールの追加、そして管理画面の追加を行います。

ジョブの作成

ジョブを作成するために、下記のコマンドを実行します。

rails generate job Sample


これにより app/jobs ディレクトリ以下にジョブファイルが生成されます。ジョブファイルには perform メソッドが定義されています。このメソッド内に、ジョブで実行したい処理を記述します。

今回実装した機能では、HTTPリクエスト処理を行うため `Typhoeus` ライブラリを使用しました。

Typhoeus などの並行HTTPリクエストライブラリを使用することで、処理速度を向上させることができます。Typhoeus の利用はこちらの記事を参考にしました。

以下は、ジョブファイルの例です。

app/jobs/sample_job.rb

require 'typhoeus' 

class Sample < ApplicationJob 
queue_as :default 

def 
  perform(*args) 
    # HTTPリクエスト処理を行う 
  end 
end 

ジョブの実行スケジュールの追加

ジョブの実行スケジュールを追加するためには、sidekiq-scheduler というGemを使用します。

gem 'sidekiq-scheduler' 


次に config/sidekiq.yml にスケジュールを追記します。以下は、毎朝午前3時に SampleJob を実行するスケジュールの例です。

config/sidekiq.yml

:schedule:
  sample:
    cron: "0 3 * * * Asia/Tokyo"
    class: SampleJob

管理画面の追加

Sidekiq には、ジョブを管理するためのWebインターフェイスがあります。

config/routes.rb

require "sidekiq/web"

Rails.application.routes.draw do
    mount Sidekiq::Web => "/sidekiq" # 追記   
end


これにより Sidekiq を立ち上げた状態で `/sidekiq` にアクセスすることで管理画面にアクセスできます。

sidekiq管理画面


管理画面ではジョブの状態や履歴の管理、再試行や削除などの操作も簡単に行うことができます。

フェイルオーバー

Sidekiq は、ジョブの実行中にプロセスがクラッシュした場合に、自動的にジョブを再試行するフェイルオーバーを持っています。デフォルトでは、Sidekiq は25回のリトライを行いますが、リトライ回数を変更することもできます。

例えば sidekiq.yml の中で `max_retries` を指定することでデフォルト値を変更することができます。また、ジョブごとにリトライ回数を指定する場合は `sidekiq_options` を使用します。

リトライを無効にする場合は

sidekiq_options retry: 0 #または
sidekiq_options retry: false


のいずれかを設定します。

両者の違いは `retry: 0` の場合はエラー時にジョブがデッド状態になり、画面から再試行が可能であるのに対して `retry: false` の場合は画面から再試行ができなくなるという点です。

リトライに失敗した場合に実行される処理も指定することができます。

リトライに関する設定は、ジョブの実行中に発生するエラーに対処するために重要な設定であるため、今回の実装でも慎重に検討を行いました。

まとめ

今回は Sidekiq を使用して指定した日時に非同期処理を行う方法を紹介しました。一度構築を終えているため、新たなジョブも簡単に設定できます。スケジューリングされた非同期処理を実装する場合には、この方法を参考にしてみてください。

※2023年3月14日時点

Techブログ 新着記事一覧