[Ruby on Rails]Active Job、Sidekiqで非同期に

初めに


筆者は未経験からweb系エンジニアを目指しており、技術面接の対策として、Rails開発者が採用面接で聞かれる想定Q&A 53問(翻訳)を参考にしている。

問題の中で、聞いたことがあっても手元で動かしたことがない機能があり、今回はその1つのActive Jobについて、実際にコードを書きながら、理解を深めていく。

前提


Active Job

ジョブの処理をバックグラウンドで、非同期で実行できる機能のことをいう。主に重たい処理やリアルタイム性を伴わない処理に用いられるとのこと。

Ex.

  • メールの送信
  • 画像の処理
  • データを集計してCSVに落とす等

Job

コンピューターがする仕事の単位。タスクのようなもの

Queue(キュー)

最も基本的なデータ構造の一つで、要素を入ってきた順に一列に並べ、先に入れた要素から順に取り出すという規則で出し入れを行う、タスクの入れ物のようなもの。

順番を待つ人の行列と同じ仕組みであるため待ち行列とも訳される。

アダプタ

Active Jobにはキューイングバックエンドに接続するためのアダプタが用意されている。

  • async•••デフォルト。プロセスを再起動した時に、登録したジョブがなくなる
  • Sidekiq•••アダプタの1つ。Redisを必要としており、Redisはコンピューターから直接アクセスできるメモリ上で動作するため、高速処理が可能



実装


今回はTaskモデルを作成しアダプタはSidekiqにて、実装していく。

1.ジョブクラスの作成

 bin/rails g job task
Running via Spring preloader in process 36345
      invoke  test_unit
      create    test/jobs/task_job_test.rb
      create  app/jobs/task_job.rb

2. app/jobs/task_job.rbにて、jobを追加

class TaskJob < ApplicationJob
  queue_as :default

  def perform(title)
    Task.create!(title: title)
  end
end

perform •••非同期処理時に呼ばれるメソッドであり、ジョブで実行したい処理を実装する

3.キューイングバックエンドの設定

  • gem sidekiq の追加。bundle install
gem 'sidekiq'
  • config/application.rbでアダプタをSidekiqに指定
...
module ActiveJobTodo
  class Application < Rails::Application

    config.load_defaults 6.0
    config.active_job.queue_adapter = :sidekiq # 追加
  end
end

4.Redis環境の構築

  • dockerを使用して、redis環境をpullする
 % docker pull redis
Using default tag: latest
latest: Pulling from library/redis
1fe172e4850f: Pull complete 
6fbcd347bf99: Pull complete
...
  • redis環境を立ち上げる
% docker run -p 6379:6379 redis
...
1:M 25 Apr 2022 03:46:25.214 # Server initialized
1:M 25 Apr 2022 03:46:25.214 * Ready to accept connections

5.ジョブをキューへ追加する(コンソール)

irb(main):001:0> TaskJob.perform_later(title: "ブログを作成")
Enqueued TaskJob (Job ID: b1095411-52eb-4c2e-bcf7-5dd135aff3cd) to Sidekiq(default) with arguments: {:title=>"ブログを作成"}
...

perform_later •••バックエンドキューにジョブを追加する。

6.Sidekiqを起動して、ジョブを実行させる

% bundle exec sidekiq


               m,
               `$b
          .ss,  $$:         .,d$
          `$$P,d$P'    .,md$P"'
           ,$$$$$b/md$$$P^'
         .d$$$$$$/$$$P'
         $$^' `"/$$$'       ____  _     _      _    _
         $:     ,$$:       / ___|(_) __| | ___| | _(_) __ _
         `b     :$$        \___ \| |/ _` |/ _ \ |/ / |/ _` |
                $$:         ___) | | (_| |  __/   <| | (_| |
                $$         |____/|_|\__,_|\___|_|\_\_|\__, |
              .d$$                                       |_|
      
...

7.ジョブが実行されたことが確認できる

irb(main):002:0> Task.last
  Task Load (0.3ms)  SELECT "tasks".* FROM "tasks" ORDER BY "tasks"."id" DESC LIMIT ?  [["LIMIT", 1]]
=> #<Task id: 1, title: "{:title=>\"ブログを作成\"}", body: nil, created_at: "2022-04-25 04:13:42", updated_at: "2022-04-25 04:13:42">



参考