弊社製品 Marketing Platform には、Webページのアクセス履歴を取得したり、メール文面にあるURLをクリックした履歴を取得したり、メールを開封した履歴を取得したりする機能がある。そしてこれらの履歴データを統計情報として活用するため、どのWebページが何回アクセスされたか、どのURLが何回クリックされたかといった集計を行っている。
少し前のリリースで、これらの履歴データを集計していたバッチ処理を廃止して、アクセスを受け付けるたびにリアルタイムで集計するように変更した。これはリアルタイムにすることが目的だったのではないので副産物ではあるが、なかなか意味のある変更ではないかと思っている。
少し前のリリースで、これらの履歴データを集計していたバッチ処理を廃止して、アクセスを受け付けるたびにリアルタイムで集計するように変更した。これはリアルタイムにすることが目的だったのではないので副産物ではあるが、なかなか意味のある変更ではないかと思っている。
これは検索機能についての言葉ではあったが(検索結果を早く出すためにあらかじめ検索結果を持っておく方式はダメだよねーとかいう話だったような)、そもそも集計バッチ処理を待たないとデータが反映されないのでは、リアルタイム性なんて生まれない。
そういう意味では、本来の目的になかったことでも、製品コンセプトに合うという意味では良かったと思っている。
最初はデータの格納場所を変えることだけ考えていたので、単にアクセスを受け付けた後に適切な場所へ転送することを考えた。しかし、大量アクセスがただ転送されるだけでは、転送先サーバでは今までなかったアクセスが増えるだけである。ムダに負荷を増加させるわけにはいかないので、アクセス集中や集計処理による高負荷を避けることが目的に加わった。
しかし、効果のある形で使うには大量ノードが必要だ。また、集計対象をある程度の期間で区切ってバッチ処理に回すとき、例えば1時間に1回のバッチ処理をすると、集計処理が1時間以内に終わってくれないと厄介なことになる。もちろんそこまで考慮して、一定期間で区切るのではなくて1つ終わったら次に進むようにすればいいのだが、どんどん後続の処理が遅れていくのも困る。
こうしたことから、急にアクセス数が増えてもサーバの負荷が一定になるように、キューシステムを採用することになった。キューにタスクを登録する側(アクセスを受け付ける側)と、キューからタスクを取り出して処理する側(アプリから検索したりできるようにDBに記録する側)を切り離しておくことで、どちらか足りないほうだけを増やすことができる。
ということで、キューシステムが負荷を一定にしてくれるのに便乗して、キューからタスクを取り出してDBに入れる間に、集計も一緒にやってしまうことにした。集計処理もほとんどが数字をプラス1するだけの簡単なものなので、1アクセスごとに処理することも可能だろうという判断だ。
Gearman 自体の使い方については特殊なことはしていないが、Gearmanの不確実なところをカバーするようにしている。アクセス履歴がGearmanを通じて正常に集計されて登録まで完了したら、逆にアプリ本体側からGearmanに完了フラグをつけるというタスクを登録し、完了フラグをアクセス履歴DBに書き戻している。こうして、定期的に完了フラグがついていないものだけを Gearman に再登録することで、処理を再実行できるようにしている。
処理するデータ量の桁がいくつか変わったら、またシステム構成も見直すことになるだろうが、現在のところとりあえずこれで動いている。
参考