[Ruby on Rails]非同期処理の実行結果を待つAPIを作る
お疲れ様です、GroGroPHPです。
PHP最近やってないですが😅
さて、掲題の通りRuby on Railsで非同期処理(具体的にはAcrive Jobの処理)の実行結果を待って(ポーリングして)返却するAPIを作ります。
なぜ非同期処理なのにわざわざ実行結果を待つのかと言うと、並列処理するとデータ不整合が生じる処理があり、それをキューに渡して順番に処理させたかったためです。
※データは時間の枠内でユニークでなくてはならず、時間単位と枠数が変動するためDBにユニーク制約がかけられませんでした。
※データのINSERTで不整合が起きるため、テーブルロックしかかけられず、バグの温床になりそうだったのでロックも採用しませんでした。
SampleJobテーブルの用意
以下のテーブル(migrateファイル)を作ります。
このテーブルが必要な理由は、非同期実行されるJobの情報が取れないためで、Job内でレコードを作成することによって、このテーブルのレコードの状態を見てJobの状態がわかるようにしています。
class CreateSampleJobsTable < ActiveRecord::Migration[6.1]
def change
create_table :sample_jobs do |t|
t.string :job_id, comment: 'ActiveJobID'
t.string :error, comment: 'エラーメッセージ'
t.string :error_status, comment: 'エラーステータス'
t.timestamps
t.index :job_id, unique: true
end
end
end
SampleWorkerの作成
非同期に処理する部分です。
sidekiq-limit_fetchというgemを使用して、キュー名sampleに対する同時実行数を1に制限します。
処理の最後に、SampleJobレコードを登録する処理を入れました。
class SampleWorker < ApplicationJob
queue_as :sample
Sidekiq::Queue['sample'].limit = 1
def perform
...
sample_job = SampleJob.new
sample_job.job_id = @job_id
sample_job.error = @error
sample_job.error_status = @error_status
sample_job.save
end
end
非同期実行とポーリング
perform_laterで先ほどのSampleWorkerを非同期実行し、0.1秒ごとにSampleJobのレコードを確認しています。
レコードができたらbreakして、エラーを拾ってレスポンスとして返却します。
@sample_worker = SampleWorker.perform_later
100.times do |_|
SampleJob.connection.clear_query_cache
@sample_job = SampleJob.find_by(job_id: @sample_worker.job_id)
break if @sample_job.present?
sleep 0.1
end
@error = @sample_job.error if @sample_job.error.present?
@error_status = @sample_job.error_status.intern if @sample_job.error_status.present?
この際、 SampleJob.connection.clear_query_cache をしていますが、これをしないとSampleJobレコードの取得SQLがキャッシュされて、いつまで経ってもレコードが取得できないので注意してください。
ActiveRecord::Base.connection.clear_query_cache と書くこともできます。
まとめ
非同期処理なのに待つなんて、元も子もないようなことなので、なかなか情報が無くて困りました。
今回は内部のActive Jobの処理だったのですが、例えば外部の非同期APIで、実行時間が長くDBに結果を直接INSERTしてくるAPI(そんなの無いか!?)とかにも応用ができます。
本来はフロント側でポーリングしてAPI2本でやるのが筋かと思いますが、短時間でバックエンド側だけで対応できたので良かったです。
そもそものアーキテクチャが悪いとかあると思いますので、ご意見はコメントいただければと思います。
今後サービス規模が大きくなったときの課題もありますが、その時に考えるとします。