🔥

【AWS障害から学ぶ】レースコンディションと責務設計の話

に公開

概要

2025年10月19日、AWSの米国東部リージョン(us-east-1)で大規模なサービス障害が発生しました。
この影響で、CircleCIのジョブが実行できないSlackが断続的に接続できないなど、
開発者が日常的に使うツールが次々と止まりました。

「デプロイができない」「CIが回らない」
そんな声がSNS上でも多く見られました。

普段は空気のように使っているクラウドサービスが止まると、
「こんなに依存してたんだな」と思わず苦笑してしまいました。

本記事では、バックエンドエンジニアとして、

  • 「なぜこの障害が起きたのか」
  • 「私たちはどこを気をつけるべきか」

という視点で、レースコンディションと責務設計を振り返ります。

そもそもレースコンディションとは?

レースコンディション(Race Condition) とは、
複数の処理が同じリソースを同時に読み書きし、実行タイミングの違いによって結果が不定になる現象のことです。

例えば次のようなケース:

stock = Product.find(item_id)
stock.count -= 1
stock.save

2つのリクエストが同時に走ると、
どちらも「古い在庫数を読み取って、同じ値を保存」してしまう。
結果、在庫が2個減るはずが1個しか減らないといった不整合が発生します。

AWSの障害の概要

AWSの公式レポートによると、今回の障害はDNS管理システム内の「Enactor」間の競合、つまりレースコンディションが原因でした。

このシステムでは、次のような構成になっています。

  • Planner:新しいDNS設定プランを作るコンポーネント
  • Enactor:そのプランを実際に適用・削除するコンポーネント

障害直前、1つのEnactorがDNSエンドポイントの更新処理で 異常に時間がかかる遅延に陥りました。
この間にも、Plannerは新しいDNSプランを次々と生成しており、
別のEnactorが新しいプランの適用を進めていました。

ここで、次のような競合が発生しました。

  1. Enactor A:遅延が発生し、古いプランを処理中のまま停止
  2. Enactor B:新しいプランを正常に適用
  3. Enactor A:遅れていた処理が再開し、古いプランを誤って上書き適用
  4. Enactor B:その後、「古いプランを削除するクリーンアップ処理」を実行し、古いプランを削除してしまう

結果として、DNSレコードのIP情報がすべて消失し、各種サービスが接続できなくなりました。

責務の設計

今回の設計上の課題の一因として、
「どのコンポーネントが真実(Source of Truth)を持つべきか」
という責務の境界が明確でなかった点が挙げられます。

まず前提として、AWSがなぜこのような設計を採用しているのか、
その背景や詳細な意図については把握していません。

以下はあくまで一般論としての考察です。

現状では、DNSプランの最新判定や古い設定の削除といったロジックを、
各Enactorがそれぞれのローカル情報を基に独立して判断しています。
このような設計では、各コンポーネントが「自分の知る限りの最新状態」に基づいて動作するため、
その認識がずれた場合に、異なる更新や削除を同時に行ってしまうリスクがあります。
結果として、データの整合性が崩れたり、レースコンディションが発生する可能性があります。

理想的には、どの層が真実(Source of Truth)を持つのかを明確に定義し、
状態の判定や削除の責務を一箇所に集約することが望ましいです。
例えば、Route 53側が「最終的な状態を反映する層」であるなら、
Enactor側は「変更要求を宣言する層」として設計する — このように明確に分離することで、
整合性を維持しつつ、責務の衝突を防ぐことができます。

責務設計を明確にすることは、一見抽象的ですが、
実際にはシステムの安定性や可観測性を左右する非常に実践的な要素です。

どの層が「決定を下す側」で、どの層が「状態を反映する側」なのかを意識的に分けることで、
一貫した動作とスムーズな運用が実現しやすくなります。

このように、責務の曖昧さが結果的にレースコンディションを引き起こした一因となっている可能性があります。

ACIDの話

データベースの世界では、整合性を守るための原則としてACID特性がよく使われます。

Atomicity(原子性):中途半端な状態を残さない
Consistency(一貫性):不正な状態を作らない
Isolation(分離性):同時実行でもお互いに干渉しない
Durability(永続性):処理結果が確実に保存される

今回、特に Isolation(分離性)と Consistency(一貫性) が崩れたことで、
一貫性のない状態が永続化されてしまったのが今回の障害の本質です。

おわりに

今回の障害は、技術的には「レースコンディション」という単純な現象でした。
けれど、その背景には「責務の境界をどこに置くか」という設計上の選択がありました。

システムが複雑になるほど、“誰が真実を持つか”を明確にすることが重要になります。
これはAWSに限らず、私たちが日々向き合うシステムにも当てはまります。

日々の開発やレビューの中で、同じ構造を見つけたとき、
その小さな違和感に気づけることこそ、設計者としての成長の証だと思います。

Discussion