Railsにおける非同期処理 - Sidekiqを理解する
Sidekiqとは
Sidekiqは、Ruby/Railsアプリケーションのための非同期ジョブ処理ライブラリ。アプリケーションのパフォーマンスとユーザー体験を向上させるために、重い処理をバックグラウンドで実行できる
またJobとは、非同期で実行したい処理の単位のことで、Rubyのクラスとして定義する
主な用途
- 大量データのインポート処理
- 一斉メール送信
- データ集計・分析処理
- CSVデータの処理
Sidekiqのアーキテクチャ
Sidekiqシステムは3つの主要コンポーネントから構成されている
1. Sidekiq Client
- 役割: JobをRedisに追加(エンキュー)する
- 動作場所: アプリケーションコード内(Railsサーバープロセス内)
- 使用例:
MyWorker.perform_async(args)を呼び出したとき - 特徴: 同期的な処理で、JobをRedisに保存するだけ
2. Redis
- 役割: ジョブキューの保存場所
- 特徴:
- メモリ上にデータを保存して高速アクセス(NoSQL)
- Jobを永続化するために利用
- Active Jobのデフォルトのインメモリストレージと違い、サーバー再起動後もジョブが失われない
3. Sidekiq Server (Worker)
- 役割: ジョブを実行する
- 動作場所: 独立したプロセスとして(
bundle exec sidekiqで起動) - 特徴: Redisからジョブを取り出し、対応するWorkerクラスの処理を実行
実際の処理フロー
- Railsアプリ(Sidekiq Client)が
SomeWorker.perform_async(1, 2)を呼び出す - Sidekiq Clientがジョブ情報をシリアライズしてRedisに保存
- Sidekiq Server(別プロセス)がRedisをポーリングし、ジョブを発見
- Sidekiq Serverがジョブを取り出し、指定されたWorkerクラスのメソッドを実行
Sidekiqを使うべき場面
1. まとめて処理する場合(大量データの一括処理)
例:
メリット:
- ユーザーは処理完了を待たずに他の作業を続けられる(UX向上)
- 処理中にエラーが発生しても再試行できる
- サーバーがクラッシュしても処理は失われない
2. 定期実行が必要な場合
軽量な処理の場合:
whenevergem + cron で十分
複雑・重い定期処理の場合:
- Sidekiq + Sidekiq-Scheduler/Sidekiq-Cron を使用
- 例:ECサイトの日次・週次・月次の複合的なアナリティクスデータ集計
Docker環境でのSidekiq運用
Docker環境でRailsアプリケーションとSidekiqを使用するときのcompose.yml。Sidekiqの使用例としては、管理画面からマスターデータのCSVファイルをアップロードしてデータベースに保存するとする
この時、appコンテナ (Railsアプリ)は、Sidekiq ClientとしてJobをRedisにキューイングする役割を持っているので、redisコンテナが先に起動している必要がある
また、sidekiqコンテナは、RedisからJobを取り出してデータベースに保存する処理 (Workerクラスに定義) を担うので、redisコンテナとdbコンテナが先に立ち上がっている事を保証する必要がある
# compose.yml services: app: image: your-rails-app depends_on: db: condition: service_healthy redis: condition: service_healthy # ...その他の設定 db: image: mysql:8.0 volumes: - mysql_data:/var/lib/mysql healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 5s timeout: 5s retries: 5 # ...その他の設定 redis: image: redis:7.0 volumes: - redis_data:/data command: redis-server --appendonly yes healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 3s retries: 5 # ...その他の設定 sidekiq: image: your-rails-app command: bundle exec sidekiq depends_on: db: condition: service_healthy redis: condition: service_healthy volumes: mysql_data: redis_data:
Redisデータの永続化
Redisコンテナのデータを永続化するには、以下の2つの方法がある
1. ボリューム (Volumes)
volumes: - redis_data:/data
- Dockerが管理する専用の場所にデータを保存
- 複数コンテナ間で共有可能
- バックアップやマイグレーションが容易
2. バインドマウント (Bind Mounts)
volumes: - ./redis-data:/data
- ホストマシン上の特定パスをコンテナにマウント
- ホスト側のファイル変更がすぐにコンテナに反映される
注意点
- アプリ、Redis、DBなど全てのコンテナが立ち上がっている状態でSidekiqコンテナを起動する必要がある
- Redisコンテナは再起動するとデータが消えるため、DBコンテナ同様にvolumeを設定して永続化する
まとめ
バックエンド側での非同期処理についてあまりイメージがついていませんでしたが、Sidekiqを使うことで、重い一斉メール送信やCSVインポートなどの処理を非同期で実行できて、また、複雑な処理の定期実行などもできるみたいです