ゆう - 技術メモ

主に雑多な理解整理としてメモを投稿します。Webエンジニア | Ruby / Rails, AWS, Go | 3年目 | バックエンドとインフラ | 個人開発

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クラスの処理を実行

実際の処理フロー

  1. Railsアプリ(Sidekiq Client)が SomeWorker.perform_async(1, 2) を呼び出す
  2. Sidekiq Clientがジョブ情報をシリアライズしてRedisに保存
  3. Sidekiq Server(別プロセス)がRedisをポーリングし、ジョブを発見
  4. Sidekiq Serverがジョブを取り出し、指定されたWorkerクラスのメソッドを実行

Sidekiqを使うべき場面

1. まとめて処理する場合(大量データの一括処理)

:

  • 管理画面からCSVファイルをアップロードしてマスタデータを作成
  • ECサイトで10万件の商品データをCSVからインポート
  • 1年分の売上データを分析してレポート生成

メリット:

  • ユーザーは処理完了を待たずに他の作業を続けられる(UX向上)
  • 処理中にエラーが発生しても再試行できる
  • サーバーがクラッシュしても処理は失われない

2. 定期実行が必要な場合

軽量な処理の場合:

  • whenever gem + 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インポートなどの処理を非同期で実行できて、また、複雑な処理の定期実行などもできるみたいです

参考資料

qiita.com

zenn.dev

qiita.com