@kyanny's blog

My life. Opinions are my own.

非同期ジョブキューのアーキテクチャ設計、どうしてますか?

Gearman のメーリングリストに興味深いポストがあった。僕も色々な意見を聞いてみたいと思ったので、多くの人の目に触れることを期待して紹介する。

Google グループ

ウェブサイトに画像をアップロードすると、異なるバックエンドサーバが画像を処理する。まずリサイズし、それから顔認識をする。

一つ目の要求として、処理結果が期待通りかどうかユーザーに確認させたい。顔認識が不要であればリサイズした画像を、顔認識が必要ならばその処理が施された画像を、確認画面に表示したい。つまり、アップロード操作の完了には一つまたは二つのステップを踏む必要がある。

この要求を満たすために同期処理も検討したが、バックエンドサーバがダウンするとウェブアプリケーションサーバも巻き添えを食うため採用したくない。

ウェブアプリケーションのプロセスは Gearman にリサイズ処理を非同期ジョブとして送信し、リサイズ処理のワーカーが顔認識の処理を新しいジョブとして実行するのが良いだろうか?

その場合、ウェブアプリケーションのプロセスはリサイズ処理のジョブのステータスを確認するための id を得られるが、顔認識の処理はどうやってステータスを確認すれば良いだろうか?

二つの処理を管理する、親の役割をするジョブが必要だろうか?リサイズが終わったら、顔認識の処理を非同期ジョブとして登録し、その非同期ジョブが返す id を自分自身のレスポンスとして返すのはどうだろう?ウェブアプリケーションがステータスを確認すると、「半分終わった(リサイズのみ)」「完了した(リサイズと顔認識)」「新しいジョブを処理中(ココよくわからない・・・)」などの結果が得られる。

もう一つの要求は、ジョブの中断だ。例えば、ユーザーが誤って巨大な画像をアップロードし、その画像の処理中に小さい画像をアップロードし直した場合、最初のジョブを中止したい。二度目にアップロードした正しい画像を、最初にアップロードした間違った画像が上書きするのは避けたい。これはバージョン管理の問題だろうが、 Gearman に実行中のジョブを中止する機能はないだろうか。

http://groups.google.com/group/gearman/browse_thread/thread/6447d55eb2af29f2

これに対する回答が現時点で一つあり、 SEDA (Staged Event Driven Architecture) というソフトウェアデザインが使えるよとのこと。 SEDA の説明がなかなか詳しいが、ソフトウェアデザインに詳しいひとは Wikipedia を、そうでもないひとは dann さんのブログを読むとわかりやすい。要するにジョブを順番にリレー形式で処理していく方式で、質問の中にも書いてある。つまりこいつはあまり真面目に質問を読んでない気がする。

Staged event-driven architecture - Wikipedia
SEDA (Staged Event-Driven Architecture) - dann's blog - #

あとは Gearman のジョブと中止する方法は知らないけど SIGALRM でタイムアウト設定すれば長時間かかるジョブを中止できるよとか(そういうことじゃねえだろう)非同期処理の完了通知を受け取るにはコールバックが使えるとか(うーん? Gearman::Task はコールバック関数も受け取れるけどこれ結局ウェブアプリケーションからみると同期処理ってことにならないか?)継続的にポーリングで監視すれば完了したかわかるよねとか(そりゃそうだけどもっと良い方法はないか?という質問なのでは)、みていてちょっともやもやする回答だった。

ちなみに僕なら「リサイズだけするワーカー」と「リサイズと顔認識を両方やるワーカー」を二種類動かして、ウェブアプリケーション側でジョブを投げる先を変えるかな、と思う。リソースがちょっと無駄になりそうだけど、各ワーカーの中では必要な処理を順番にやればいいだけなのでシステム構成も実装もシンプルだ。節約の必要に迫られたら、リサイズの画像処理部分だけさらに別のワーカーに切り出して、それぞれのワーカーの中からさらに丸投げすれば多少はメモリを減らせそうな気がする。ジョブの中断は諦めてもらいたいところだけど、どうしても必要ならジョブの追加時刻を DB に保存しておいて画像保存前にチェックするかな。

リレー形式というか SEDA は、すべてのジョブの状態を管理するグローバルな領域を用意してそこを都度更新しなければならなそうだし(おそらく RDBMS か KVS にでも入れるのだろう)自分で作るのは大変そうに思える。それこそ Cassandra みたいなものを作るのならばこういう論文に書かれているようなアーキテクチャにのっとって設計し、その上できっちり実装すべきなのだろう。しかし何もそこまでやらなくても、他にもっとコストパフォーマンスに優れたやり方があるんじゃないかと思うのだ。あとリレー形式でやった場合、途中で不正終了してしまったジョブを再実行させる必要もあるし、 Gearman だとやり直しは面倒くさそうなので敬遠したい気持ちもある。

というわけで、皆さんならどうしますか?もしくは、どうやって解決しましたか?ブログや Twitter や Facebook などで話のネタにして、ついでに僕に教えてくれると嬉しいです(さらにそれを英訳してメーリングリストにポストしてあげると質問者も喜ぶと思います)