【Rails】Active Job
Active Jobの概要
こんにちは。大ちゃんの駆け出し技術ブログです。
今回はRuby on Railsの標準機能であるActive Jobについて、簡単に紹介させていただきます。
Railsには代表的な標準機能であるActive Recordがありますよね! SQLを知らなくてもDBを操作することができるとても便利なものです。
Railsには「Active 〜」のような名前の標準機能がいくつもあります。 今回紹介するActive Jobもその一つです。
Active Jobはどんな機能なのかというと、「非同期処理を実行する機能」です。
非同期処理で代表的なAjax通信は、クライアントのレスポンスに対して非同期で処理がサーバ側で行われます。これによりクライアントはレスポンスを待たずに快適に操作を行えます。Ajaxとは言わば、クライアント側のための非同期処理機能です。
それとは別に、Active Jobは言うなればサーバー側だけで完結する非同期処理です。例えば、サーバー側にジョブのリクエストを送信し、一週間後にそのジョブの処理を行なって欲しいといった場合に使われます。
Active Jobの実装
それでは実際にハンズオン形式でActive Jobを体験していきます。なお、Active Jobの実装手順はRailsガイド にありますので、公式を参照したい方はこちらをご覧ください。
本記事ではActive Jobを使って非同期でDBに処理を行うように実装します。
まずは、Active Jobを使うために仮でアプリを作成します。
$ rails new active_job $ cd active_job
そして適当なモデルを作成します。今回はモデル名をexample_model
とし、string
型でtitle
カラムを定義しておきます。モデルを作成したらDBを作成し、モデルをDBに反映します。
$ rails generate model example_model title:string $ rails db:create $ rails db:migrate
ここでジョブクラスを作成します。rails generate job ジョブ名で作成することができます。ジョブ名は自由ですが、今回はexample_job
をジョブ名としてコマンドで実行します。
$ rails generate job example_job Running via Spring preloader in process 22919 invoke test_unit create test/jobs/example_job_test.rb create app/jobs/example_job.rb
コマンド実行後、テストファイルと共にapp/jobs/example_job.rb
というファイルが作成されているかと思います。このファイルでは非同期処理時に呼ばれるperform
メソッドがデフォルトで定義されています。
# app/jobs/example_job.rb class ExampleJob < ApplicationJob queue_as :default def perform(*args) # Do something later end end
Do something later
とあるように、後で何かしらの処理を実行するように設定することができます。今回はExampleModel
の作成処理を後で(非同期で)行なってもらうように設定します。ExampleModel
にはtitle
カラムがあるので引数を渡したら、渡した引数がtitle
カラムに登録されるように実装します。
# app/jobs/example_job.rb class ExampleJob < ApplicationJob queue_as :default def perform(title) ExampleModel.create(title: title) end end
ジョブの実行
それではターミナル上で非同期で実行してみます。
方法は先ほど定義したExampleJob
クラスをクラスメソッドとして使用しターミナル上で実行します。
irb(main):001:0> ExampleJob.perform_later("おはよう") Enqueued ExampleJob (Job ID: 47dbf1d6-78ee-4a57-89ad-630d210496cd) to Async(default) with arguments: "おはよう" => #<ExampleJob:0x00007fd51f0ff8b0 @arguments=["おはよう"], @job_id="47dbf1d6-78ee-4a57-89ad-630d210496cd", @queue_name="default", @priority=nil, @executions=0, @exception_executions={}, @provider_job_id="d3bf4ba7-60d8-456f-94df-6a9b4e55c535"> Performing ExampleJob (Job ID: 47dbf1d6-78ee-4a57-89ad-630d210496cd) from Async(default) enqueued at 2021-01-23T00:00:08Z with arguments: "おはよう" (0.1ms) SELECT sqlite_version(*) (0.1ms) begin transaction irb(main):005:0> ExampleModel Create (1.5ms) INSERT INTO "example_models" ("title", "created_at", "updated_at") VALUES (?, ?, ?) [["title", "おはよう"], ["created_at", "2021-01-23 00:00:08.976914"], ["updated_at", "2021-01-23 00:00:08.976914"]] (0.9ms) commit transaction Performed ExampleJob (Job ID: 47dbf1d6-78ee-4a57-89ad-630d210496cd) from Async(default) in 16.53ms
少し長いですが、ExampleModel
を作成するSQLが発行されています。
INSERT INTO "example_models" ("title", "created_at", "updated_at") VALUES (?, ?, ?) [["title", "おはよう"], ["created_at", "2021-01-23 00:00:08.976914"], ["updated_at", "2021-01-23 00:00:08.976914"]]
ExampleModel.first
を実行するとモデルが作成されていることも確認できます。
irb(main):002:0> ExampleModel.last ExampleModel Load (0.3ms) SELECT "example_models".* FROM "example_models" ORDER BY "example_models"."id" DESC LIMIT ? [["LIMIT", 1]] => #<ExampleModel id: 1, title: "おはよう", created_at: "2021-01-23 00:00:08", updated_at: "2021-01-23 00:00:08">
しかし、ジョブが直ぐに実行されてしまって非同期で行われているかわかりづらいですね、、、。
それでは、30秒後にジョブが実行されるように実装してみましょう。方法は簡単でset
メソッドを使っていつ実行するかを指定するだけです。
irb(main):003:0> ExampleJob.set(wait: 30.seconds).perform_later("30秒後のジョブ") Enqueued ExampleJob (Job ID: ca09e9e9-7b7f-4e18-b7e2-62f54269f1c0) to Async(default) at 2021-01-23 00:10:29 UTC with arguments: "30秒後のジョブ" => #<ExampleJob:0x00007fd52824f978 @arguments=["30秒後のジョブ"], @job_id="ca09e9e9-7b7f-4e18-b7e2-62f54269f1c0", @queue_name="default", @priority=nil, @executions=0, @exception_executions={}, @provider_job_id="25580038-707a-4103-bcd9-e1842d34ebbe", @scheduled_at=1611360629.818372>
先ほどと違い、実行後直ぐにターミナル上にSQLが発行されません。 しかし、30秒後にはSQLが発行されたコードがターミナル上に出現します。
Performing ExampleJob (Job ID: ca09e9e9-7b7f-4e18-b7e2-62f54269f1c0) from Async(default) enqueued at 2021-01-23T00:09:59Z with arguments: "30秒後のジョブ" (0.1ms) SELECT sqlite_version(*) (0.1ms) begin transaction ExampleModel Create (0.6ms) INSERT INTO "example_models" ("title", "created_at", "updated_at") VALUES (?, ?, ?) [["title", "30秒後のジョブ"], ["created_at", "2021-01-23 00:10:29.823096"], ["updated_at", "2021-01-23 00:10:29.823096"]] (1.7ms) commit transaction Performed ExampleJob (Job ID: ca09e9e9-7b7f-4e18-b7e2-62f54269f1c0) from Async(default) in 5.73ms
こうしてみると非同期で実行されていることがわかりますね。 Ajax通信とは違い、サーバー側だけで完結する非同期処理ということがよくわかりますね。
終わりに
Active Jobについて簡単に実装してみましたが、まだまだ奥が深そうです。 自分もまだ理解が浅いため、理解を深めたいと思います。
以上、大ちゃんの駆け出し技術ブログでした!!