rubyのsidekiqというqueueに詰めて非同期でjobを実行できるライブラリがあるのですが、いつタスクをqueueに詰めてtaskを実行すればいいかを悩んだためまとめてみようと思います。
なぜbackground jobが必要なのか?
端的に言えば、めちゃくちゃ重い処理は別processなどで動かさないとメインの処理が止まってしまうからです。一番わかりやすいのはnodeだと思います。
nodeはシングルスレッドシングルプロセスで駆動するため、数十秒かかるようなDBへのqueryや画像処理などの重い処理を入れると全ての処理が止まります。これがAPIなどの場合スループットに大きな影響が出ることが懸念されます。
background jobの種類
ではbackground jobの種類、とりわけ起動の起動の仕方はどういったものがあるのかと考えた場合に大きく二つがあると思います。
イベント駆動型
なんらかの別の処理から起動して処理が走るタイプ。
わかりやすい例としてはwebhookやfluxもイベント駆動になると思います。
スケジュール駆動型
特定の日時に処理を起動させるやり方。
代表例はcron。
back ground jobの扱い方(イベント)
例としてAPIを考えた場合、APIのプロセスからqueueに詰めるタスクは戻り値を考慮しないもののみにすべきだと思います。
例えば、何かユーザーが予約処理したとして結果を即座に受け取らないとユーザーに対して適切にメッセージを表示できません。ユーザーからしてみれば、予約をして即座に成功の可否を表示しないと誤解を招くことになると思います。
もし、仮にタスクの実行結果を受け取りたい場合は別個にそれ相応のアーキテクチャー、それこそイベント駆動のようなものが必要だと考えないといけません。それを無視してqueueに詰めまくるのはアーキテクチャーの複雑性を増大させ自分の首を絞めます。
back ground jobの扱い方(スケジュール)
次にスケジュール駆動型の場合でこれもAPIを考えましょう。なんらかの処理を特定の時間に実行させることを考えた場合、問題は問題はその時間を設定するタイミングいつか?ということです。もし相対的なスケジュール(処理を受け取って10分後にタスク実行)とかでなく絶対的なスケジュール(夜の1時に実行/毎分ごとに実行)の場合はAPIの中でqueueに詰めるべきではないと思います。なぜなら、APIはrequestを受け取ったタイミングで処理が実行されるため、根本的に絶対的なスケジュールと実行するタイミングが違います。この実行タイミングが違うものを一つのコードで表現すると密依存になってしまいます。
それでも必要な場合、taskをqueueに詰める代わりにDBに実行対象の情報やフラグなどをもたせて、cronかなんかでtaskを実行させて、taskの中で実行対象の情報を取得して処理させるべきだと思います。
最後に
これは個人的な考え方なので、こんなやり方があるよ!があればぜひ教えてください。
それでは良い background job ライフを!
参考資料