2025年9月、インターネットの最も重要なインフラ企業の一つが経験した障害は、洗練されたハッカー、大規模なDDoS攻撃、サーバー障害によるものではありませんでした。代わりに、テラビット規模の攻撃を日常的に防ぎ、数百万のウェブサイトを正常に保つCloudflareが、多くのジュニア開発者が最初の週に避けることを学ぶ基本的なReactプログラミングの間違いによって停止したのです。
衝撃的な現実
その皮肉はほとんど喜劇的です:7.3テラビット毎秒のDDoS攻撃を成功裏に防御してきたCloudflareが、不適切な依存関係配列を持つReact useEffectフックによってオフラインになったのです。これは国家レベルのエリートハッカー部隊による攻撃ではなく、API呼び出しの無限ループを作成する典型的なフロントエンド開発のアンチパターンでした。
出典: この記事で参照されている技術的詳細とタイムラインは、Cloudflareの公式インシデント報告書に基づいています:「Cloudflareの2025年9月12日のダッシュボードとAPI障害への深掘り」
技術的な間違いの理解
問題のあるコードパターン
問題は、この一般的なReactパターンから生じました:
// 問題のあるコードの簡略版
function Dashboard() {
const params = { // このオブジェクトは毎レンダリング時に再作成される
organizationId: user.orgId,
filters: currentFilters
};
useEffect(() => {
// この関数がAPIを呼び出す
fetchDashboardData(params);
}, [params]); // ← 問題はここ
return "Dashboard rendered";
}
なぜこれが無限ループを作成するのか
useEffectフックは浅い比較を使用して依存関係を比較します。以下のことが起こります:
- コンポーネントがレンダリング → 新しい
paramsオブジェクトを作成 - useEffectが実行 → データを取得し、状態を更新する可能性
- 状態更新が再レンダリングを引き起こす → 別の新しい
paramsオブジェクトを作成 - Reactが依存関係を比較 →
paramsの参照が変更されている - useEffectが再び実行 → ステップ2に戻り、無限ループを作成
なぜparamsオブジェクトは毎回再作成されるのか?
JavaScriptでは、{ organizationId: user.orgId, filters: currentFilters }のようなオブジェクトリテラルは、実行されるたびにメモリ内に新しいオブジェクトを作成します。内部の値が同じでも、オブジェクトの参照は異なります。
// この関数が実行されるたびに、新しいオブジェクトが作成される
const params = { organizationId: user.orgId, filters: currentFilters };
// これは以下と同等です:
const params = new Object();
params.organizationId = user.orgId;
params.filters = currentFilters;
メモリ参照の比較:
// これらのオブジェクトは同じ内容ですが、異なるメモリ参照を持ちます
const obj1 = { name: "John" };
const obj2 = { name: "John" };
console.log(obj1 === obj2); // false - 異なるメモリ位置
// 同じ参照のみがtrueを返します
const obj3 = obj1;
console.log(obj1 === obj3); // true - 同じメモリ参照
ReactのuseEffectは依存関係を比較するためにObject.is()(===と同様)を使用します。レンダリングごとに新しいparamsオブジェクトが作成されるため、内部の値が同じでも、Reactは依存関係が変更されたと判断します。
paramsオブジェクトが同じ値を含んでいても、Reactはそれを毎回メモリ内の新しいオブジェクト参照として「異なる」と見なします。
問題の規模
Cloudflareのインシデント報告によると、このシンプルな間違いの結果:
- 1分間に数千のAPI呼び出しが単一のダッシュボードセッションから
- 完全なAPIオーバーロードがすべてのユーザーで倍増
- カスケード障害がインフラ全体に広がる
- グローバル障害が数百万のウェブサイトに影響
正しい解決策
解決策1:依存関係をメモ化する
import { useMemo, useEffect } from 'react';
function Dashboard() {
const params = useMemo(() => ({
organizationId: user.orgId,
filters: currentFilters
}), [user.orgId, currentFilters]); // これらが変更された時のみ再作成
useEffect(() => {
fetchDashboardData(params);
}, [params]);
return "Dashboard rendered";
}
解決策2:依存関係を分離する
function Dashboard() {
useEffect(() => {
const params = {
organizationId: user.orgId,
filters: currentFilters
};
fetchDashboardData(params);
}, [user.orgId, currentFilters]); // 直接的な依存関係
return "Dashboard rendered";
}
解決策3:関数にuseCallbackを使用する
import { useCallback, useEffect } from 'react';
function Dashboard() {
const fetchData = useCallback(async () => {
const params = {
organizationId: user.orgId,
filters: currentFilters
};
await fetchDashboardData(params);
}, [user.orgId, currentFilters]);
useEffect(() => {
fetchData();
}, [fetchData]);
return "Dashboard rendered";
}
より広範囲なエンジニアリングの教訓
1. コードレビューの失敗
この明らかな無限ループがどのようにプロダクションに到達したのでしょうか?このインシデントは、いくつかのエンジニアリングプロセスの失敗を浮き彫りにします:
- ローカル開発テストでは、数千のネットワークリクエストがすぐに表示されるはず
- コードレビュープロセスでは、基本的なReactのアンチパターンをキャッチするべき
- ステージング環境では、プロダクションの負荷パターンを複製するべき
- モニタリングシステムでは、異常なAPI使用パターンについてアラートするべき
2. サンダリングハード問題
Cloudflareがユーザーセッションをクリアすることで問題を修正しようとした時、彼らは無意識に「サンダリングハード」問題を作成しました—サービスが復旧した時に数百万のユーザーが同時に再認証を行い、2回目の障害を引き起こしました。
3. レート制限とサーキットブレーカー
このインシデントは、Cloudflareの内部APIに適切な以下が欠けていることを明らかにしました:
- レート制限による悪用防止
- サーキットブレーカーによる負荷下での優雅な失敗
- 問題のあるデプロイメントに対する自動ロールバック機構
防止戦略
開発者向け
- ESLintルール(
exhaustive-depsなど)を使用して依存関係の問題をキャッチ - React Developer Toolsをインストールしてコンポーネントの再レンダリングを監視
- ネットワーク監視を追加して開発中の異常なAPIパターンをキャッチ
- 適切なエラーバウンダリを使用した防御的プログラミングの実践
エンジニアリングチーム向け
- 段階的ロールアウトを瞬時のグローバルデプロイメントの代わりに実装
- API使用パターンのための適切な監視を設定
- 一般的なReactアンチパターンのためのコードレビューチェックリストを確立
- 実際の使用を模擬する負荷テスト環境を作成
必須ESLint設定
{
"extends": ["plugin:react-hooks/recommended"],
"rules": {
"react-hooks/exhaustive-deps": "error"
}
}
重要な学習の瞬間
このインシデントは、基本的なプログラミングの間違いがいかに大規模なグローバル影響を与える可能性があるかを強力に思い出させてくれます。この障害は数百万のウェブサイトと世界中の無数の企業に影響を与えました。すべては適切なツールとプロセスでキャッチできた間違いから。
有名で著名な技術系YouTuberであるThePrimeagenは、このインシデントについてコメントしました:「私たちは皆この間違いを犯したことがある—私は開発環境で自分のを捕まえて幸運だった、プロダクションではなく。」
重要なポイント
- 基礎知識が重要 — 企業規模でも、基本的なプログラミング原則は重要
- ツールが不可欠 — ESLint、React DevTools、適切な監視がこれらの問題を防ぐ
- プロセスの失敗が技術的失敗を複合化 — 複数の安全網が同時に失敗
- 段階的デプロイメントが命を救う — 重要なインフラにとって瞬時のグローバルロールアウトは危険
- Reactの力には責任が伴う — フレームワークの柔軟性には規律ある開発慣行が必要
前進
Cloudflareはその後以下を実装しました:
- 自動デプロイメントロールバックのためのArgo Rollouts
- API使用パターンの強化された監視
- より良いレート制限とサーキットブレーカーパターン
- フロントエンド変更のための改善されたコードレビュープロセス
このインシデントは、私たちの相互接続された世界では、最小のコード変更でさえ大規模なグローバル影響を与える可能性があることを強力に思い出させてくれます。シンプルなウェブサイトを構築していても、重要なインフラを管理していても、React基礎を理解し、適切なエンジニアリングプロセスを実装することは、単なる良い慣行ではなく、インターネットの安定性にとって不可欠です。
次回useEffectフックを書く時は覚えておいてください:Cloudflareのエンジニアたちもおそらく彼らの依存関係配列を二重チェックしています。