キューが支える非同期 — メッセージは消えない

要点
- メッセージキューは、処理を依頼する側と実行する側のあいだに緩衝を置き、両者を時間的に切り離す仕組みである。
- Apache Kafkaの設計は、メッセージをログとして追記・保持する発想を中心に据えている。
- 配送保証には「最低一回」「最大一回」「正確に一回」があり、それぞれ冪等性との付き合い方が異なる。
- キューは負荷の急変を吸収するが、ためすぎれば遅延として表面化する。緩衝は万能ではない。
注文ボタンを押した瞬間、画面はすぐ「受け付けました」と返す。だが実際には、在庫の引き当ても、決済の確定も、その瞬間には終わっていないことが多い。依頼はいったん列に並べられ、後ろで順に処理される。利用者が待たされないこの体験を支えているのが、メッセージキューと呼ばれる配管だ。表からは見えないが、現代のサービスの多くがこの上で動いている。
切り離すための緩衝
キューの役割は、依頼する側(プロデューサー)と実行する側(コンシューマー)を時間的に切り離すことにある。依頼はキューに置かれ、実行側は自分のペースで取り出して処理する。これにより、依頼が一時的に殺到しても、実行側がその速度に引きずられて倒れることがない。あふれた分はキューにたまり、落ち着いてから消化される。
この緩衝は、負荷の急変に対する保険として働く。セール開始の数秒間に注文が集中しても、画面はキューに積むだけで即座に返せる。過負荷時の振る舞いを穏やかにするうえで、キューは基本的な道具立てになっている。
ログとしてのメッセージ
Apache Kafkaの設計は、この発想をさらに推し進めた。Krepsらが示したKafkaのモデルでは、メッセージは消費されたら消える一時的なものではなく、追記され続けるログとして一定期間保持される。各コンシューマーは、ログのどこまで読んだかを示す位置(オフセット)を自分で管理する。
この設計の含意は大きい。同じメッセージ列を、複数の用途が独立に読める。注文ログを、出荷処理も、売上集計も、不正検知も、それぞれの速度で読む。処理の流れを束ねるとき、ログが共通の真実として中央に置かれる構図だ。
何回届くのか
キューを設計するとき避けて通れないのが、配送保証の問題である。ネットワークは不安定だから、メッセージが届いたという確認自体が失われうる。ここで三つの立場が現れる。確実に届けるが重複しうる「最低一回」、重複しないが失われうる「最大一回」、そして重複も欠落もない「正確に一回」だ。
もっとも、「正確に一回」は理想だが、実現には条件と代償が伴う。多くの実務は「最低一回」を選び、重複が来ても結果が変わらないよう処理を冪等に設計する。つまり、同じ注文メッセージが二度届いても、二重決済にならないようにしておく。配送保証の議論は、最終的に「受け取る側が重複に耐えられるか」へ帰着する。
緩衝の限界
キューは万能ではない。緩衝はあくまで一時的なずれを吸収する仕組みであって、実行側の処理能力そのものを増やすわけではない。依頼が来る速度が、消化する速度を恒常的に上回れば、キューは際限なく伸びる。たまった分は、そのまま遅延として利用者に返ってくる。「受け付けました」と返したのに、いつまでも処理されない注文は、キューが詰まっている兆候だ。
だからキューの健全さは、長さの監視で測られる。短く保たれているうちは緩衝が効いている。伸び続けているなら、消化能力の不足を先送りしているだけだ。配管は水を一時的にためられるが、流入が流出を超え続ければ、いつかあふれる。キューが教えてくれるのは、非同期という設計が「待たせない」ことと「いつか処理する」ことの約束の組み合わせでできている、という事実である。
出典
- Kreps et al.「Kafka: a Distributed Messaging System for Log Processing」(ログ指向のメッセージング設計)
- Amazon SQS 公式ドキュメント(配送保証と冪等性の扱い)