これはShanon Advent Calendar 2017の 5日目の記事です(が、投稿が遅くなりました。申し訳ありません)
はじめに
我々が普段開発している、シャノンマーケティングプラットフォームは(コミットログを見ると)2005年頃から開発されている(らしい)歴史あるものです。そのため、様々な歴史的経緯や、古臭い実装・アーキテクチャが見受けられる部分が多々あり、最近では非同期処理まわり(ジョブキュー)で様々な問題がありました。
そのあたりの顛末は以前、吉祥寺.pm12で発表させていただきました。
今までの(名状しがたい)ジョブキュー(のようなもの)と、これからのジョブキュー
(その際の発表資料↑ スライドはスペースか矢印キーで移動できます)
謎の独自実装を卒業し、モダンなMinionに無事移行した我々ですが、大きな問題がありました。
マルチテナントのジョブキューについて
シャノンマーケティングプラットフォームはマルチテナントのシステムです。そして、ジョブキューのワーカーやデータベースは複数のドメインで共有されています。ドメインごとにジョブキューを用意することも可能ですが、費用やメンテナンス性などの問題で、複数ドメインで共有するアーキテクチャを取っているのです。この場合問題となるのは、「特定のドメインがリソースを占有しすぎてしまう」ことです。たとえば「ドメインAがメール10万通送信」、「ドメインBがメール5万通送信」、「ドメインCがメール100通送信 」といったことをすると、キューが詰まってしまい、「ドメインCのメールがいつまでたっても送られない」といったことが起こってしまうのです。
Webの場合もこういったドメインごとの偏りはあるのですが、ジョブキューの場合はどうしてもこの問題が顕著になってしまいます。
ジョブキューの基本的なスケジューリングアルゴリズム
ジョブキューのスケジューリングアルゴリズムは(機能として存在すれば)「優先度順」で、同一優先度であれば「ID順」または「実行予定時間順」となるのが基本形だと思います。私たちが使っているMinionも、「優先度, ID」の順です(このあたり)。そのため、同一優先度の場合は「早い者勝ち」となってしまい、メールの大量配信のような時間のかかるユースケースでは、後から入れたジョブは長時間待ち状態となってしまいます。件数の少ないジョブはあらかじめ高い優先度を設定することである程度解決できますが、限界があります。
解決策(その1)ID順をやめる
ID順だと「早い者勝ち」となってしまい、先ほどの例では、ドメインAの配信が終わるまで、ドメインB, Cは待つことになってしまいます。すると、お客様からはメールの配信が止まっているように見える(=障害に見える)ので、これは望ましくありません。そこで、ID順をやめて、「優先度, ランダム」となるように変えました。
解決策(その2)優先度調整
ID順をやめることで、キューに複数ドメインのジョブが大量にある状況になっても、特定のドメインが待ちになってしまうことはある程度避けられるようになりました。しかし、「ランダム順」であると、データ量が多いドメインのほうがジョブが選ばれる確率が高くなります。(ドメインAはCの1000倍ジョブがあるので、1000倍選ばれやすい)そのため、データの少ないドメインのジョブや長時間実行されていないジョブの優先度を上げるスクリプトを作成し、バッチとして運用しています。
本当にほしかったもの
ある程度賢いスケジューリングアルゴリズム、たとえば Linux のプロセス実行みたいな感じのものが あれば本当は良かったのですが、シンプルな SQL で実現できそうな手段が無かったため上記のような手段となりました。結果
ID順の廃止と優先度調整を投入した結果、ジョブの平均待ち時間を大きく下げることができました。移行当初は問い合わせもかなりあったのですが、最近は極めて安定して動作していると思います。以上、安定運用の舞台裏の泥臭い処理のお話でした。こういうのあまりやりたくないですが、安定動作には変えられませんからね。