コンピューターサイエンス:アルゴリズム / OS / ネットワーク / DB
CAP 定理
線が切れている 30 秒、どう答えるか
3 台でデータを持ち合っているシステムがあります。ネットワークの不調で、1 台だけが他の 2 台と通信できなくなりました。数十秒で復旧する見込みですが、その間もリクエストは来ます。
孤立した 1 台に「残高を見せて」と来たとき、返せる答えは 2 つしかありません。手元にある値をそのまま返すか、今は答えられないとエラーを返すかです。他の 2 台で更新が起きていれば手元の値は古いので、前者を選べば古い値を配ることになります。後者を選べば、その 1 台につながっている利用者は何もできません。
CAP 定理が言っているのは、この二択が原理的に避けられないということです。通信の分断 (Partition tolerance) が起きている間、どこから読んでも同じ値が返ること (Consistency) と、必ず応答が返ること (Availability) は両立しません。
「3 つのうち 2 つ」という言い方の落とし穴
CAP はよく「3 つのうち 2 つを選ぶ」と説明されます。この言い方は、分断を選ばないという選択肢があるように聞こえるのが問題です。
ネットワークは切れます。ケーブルも抜けますし、クラウドのゾーン間の通信も落ちます。複数台に分けて持つと決めた時点で、分断は起きるものとして扱うしかありません。選べるのは、分断が起きたときに古い値を返すか、エラーを返すか、それだけです。
分断は、線が物理的に切れることだけを指すわけでもありません。片方向だけパケットが落ちる、遅延が数十秒に伸びる、といった中途半端な状態も、相手から見れば「応答がない」ので同じ扱いになります。判定はタイムアウトで行うので、実際には切れていないのに切れたと見なされることもあります。
つまり CAP は、設計の初期に机上で 2 つ選ぶ話ではありません。障害の最中に自分のシステムがどちらの顔をするかを、事前に知っておく話です。知らないまま本番で分断に遭うと、開発チームは「なぜ古い値が出るのか」を障害対応の最中に調べ始めることになります。
答えは機能ごとに違う
システム全体でどちらかに決める必要はありません。むしろ機能ごとに分けた方が実態に合います。
決済の残高で古い値を返すのは論外です。エラーで待たせる方がましなので、応答を諦める側に倒します。記事の閲覧数や「いいね」の表示は、数秒古くても誰も困りません。エラーで真っ白になる方がよほど問題なので、古い値でも返す側に倒します。
分散データベースの多くは、この選択を 1 リクエスト単位で指定できるようにしています。「何台の応答が揃ったら成功とみなすか」という設定がそれで、要求する台数を上げれば古い値を返しにくくなり、そのぶん遅くなり、障害時に失敗しやすくなります。1 台の応答で済ませれば、その逆になります。
決めたら、その挙動を一度は確かめておきます。試験環境でノード間の通信を落とし、アプリに何が返るかを見る。ここを本番の障害で初めて知ることになると、対応がまるごと後手に回ります。
ACID の C と CAP の C は別物です。前者は宣言した制約を破らないこと、後者は全ノードが同じ値を見せることを指します。