Pada September 2025, salah satu syarikat infrastruktur internet yang paling kritikal mengalami gangguan yang tidak disebabkan oleh penggodam canggih, serangan DDoS besar-besaran, atau kegagalan pelayan. Sebaliknya, Cloudflare—syarikat yang secara rutin menangkis serangan berskala terabit dan memastikan berjuta-juta laman web dalam talian—dijatuhkan oleh kesilapan pengaturcaraan React asas yang banyak pembangun junior belajar untuk mengelakkan dalam minggu pertama mereka.
Realiti Yang Mengejutkan
Ironinya hampir lucu: Cloudflare, yang telah berjaya mempertahankan serangan DDoS 7.3 terabit sesaat, dijatuhkan oleh hook useEffect React dengan array kebergantungan yang tidak betul. Ini bukan pasukan elit penggodam negara-bangsa—ia adalah anti-corak pembangunan frontend klasik yang mencipta gelung tak terhingga panggilan API.
Sumber: Butiran teknikal dan garis masa yang dirujuk dalam artikel ini berdasarkan laporan insiden rasmi Cloudflare: “A deep dive into Cloudflare’s September 12, 2025 dashboard and API outage”
Memahami Kesilapan Teknikal
Corak Kod Bermasalah
Isu ini berpunca daripada corak React biasa ini:
// Versi dipermudahkan kod bermasalah
function Dashboard() {
const params = { // Objek ini dicipta semula pada setiap render
organizationId: user.orgId,
filters: currentFilters
};
useEffect(() => {
// Fungsi ini memanggil API
fetchDashboardData(params);
}, [params]); // ← Masalahnya ada di sini
return "Dashboard dirender";
}
Mengapa Ini Mencipta Gelung Tak Terhingga
Hook useEffect membandingkan kebergantungan menggunakan perbandingan cetek. Inilah apa yang berlaku:
- Komponen dirender → Mencipta objek
paramsbaharu - useEffect berjalan → Mengambil data, berpotensi mengemas kini state
- Kemas kini state mencetuskan render semula → Mencipta objek
paramsbaharu lagi - React membandingkan kebergantungan → Rujukan
paramstelah berubah - useEffect berjalan lagi → Kembali ke langkah 2, mencipta gelung tak terhingga
Mengapa objek params dicipta semula setiap kali?
Dalam JavaScript, literal objek seperti { organizationId: user.orgId, filters: currentFilters } mencipta objek baharu dalam memori setiap kali ia dilaksanakan. Walaupun nilai di dalam adalah sama, rujukan objek adalah berbeza.
// Setiap kali fungsi ini berjalan, objek BAHARU dicipta
const params = { organizationId: user.orgId, filters: currentFilters };
// Ini setara dengan:
const params = new Object();
params.organizationId = user.orgId;
params.filters = currentFilters;
Perbandingan Rujukan Memori:
// Objek ini mempunyai kandungan yang sama tetapi rujukan memori berbeza
const obj1 = { name: "John" };
const obj2 = { name: "John" };
console.log(obj1 === obj2); // false - lokasi memori berbeza
// Hanya rujukan yang sama mengembalikan true
const obj3 = obj1;
console.log(obj1 === obj3); // true - rujukan memori sama
useEffect React menggunakan Object.is() (serupa dengan ===) untuk membandingkan kebergantungan. Oleh kerana objek params baharu dicipta pada setiap render, React fikir kebergantungan telah berubah, walaupun nilai di dalam mungkin sama.
Skala Masalah
Menurut laporan insiden Cloudflare, kesilapan mudah ini mengakibatkan:
- Beribu-ribu panggilan API seminit daripada satu sesi dashboard
- Beban lebihan API lengkap apabila didarab merentasi semua pengguna
- Kegagalan berjenjang di seluruh infrastruktur mereka
- Gangguan global menjejaskan berjuta-juta laman web
Penyelesaian Yang Betul
Penyelesaian 1: Memoisasi Kebergantungan
import { useMemo, useEffect } from 'react';
function Dashboard() {
const params = useMemo(() => ({
organizationId: user.orgId,
filters: currentFilters
}), [user.orgId, currentFilters]); // Hanya cipta semula apabila ini berubah
useEffect(() => {
fetchDashboardData(params);
}, [params]);
return "Dashboard dirender";
}
Penyelesaian 2: Asingkan Kebergantungan
function Dashboard() {
useEffect(() => {
const params = {
organizationId: user.orgId,
filters: currentFilters
};
fetchDashboardData(params);
}, [user.orgId, currentFilters]); // Kebergantungan langsung
return "Dashboard dirender";
}
Penyelesaian 3: Gunakan useCallback untuk Fungsi
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 dirender";
}
Pelajaran Kejuruteraan Yang Lebih Luas
1. Kegagalan Semakan Kod
Bagaimana gelung tak terhingga yang jelas ini berjaya ke pengeluaran? Insiden ini menyerlahkan beberapa kegagalan proses kejuruteraan:
- Ujian pembangunan tempatan sepatutnya segera menunjukkan beribu-ribu permintaan rangkaian
- Proses semakan kod sepatutnya menangkap anti-corak React asas
- Persekitaran staging sepatutnya meniru corak beban pengeluaran
- Sistem pemantauan sepatutnya memberi amaran tentang corak penggunaan API yang luar biasa
2. Masalah Thundering Herd
Apabila Cloudflare cuba membaiki isu dengan mengosongkan sesi pengguna, mereka secara tidak sengaja mencipta masalah “thundering herd”—berjuta-juta pengguna secara serentak mengesahkan semula apabila perkhidmatan kembali dalam talian, menyebabkan gangguan kedua.
3. Pengehadan Kadar dan Pemutus Litar
Insiden ini mendedahkan bahawa API dalaman Cloudflare kekurangan:
- Pengehadan kadar yang betul untuk mencegah penyalahgunaan
- Pemutus litar untuk gagal dengan anggun di bawah beban
- Mekanisme rollback automatik untuk penggunaan bermasalah
Strategi Pencegahan
Untuk Pembangun
- Gunakan peraturan ESLint seperti
exhaustive-depsuntuk menangkap isu kebergantungan - Pasang React Developer Tools untuk memantau render semula komponen
- Tambah pemantauan rangkaian untuk menangkap corak API yang luar biasa semasa pembangunan
- Amalkan pengaturcaraan defensif dengan sempadan ralat yang betul
Untuk Pasukan Kejuruteraan
- Laksanakan pelancaran beransur dan bukannya penggunaan global segera
- Sediakan pemantauan yang betul untuk corak penggunaan API
- Wujudkan senarai semak semakan kod untuk anti-corak React biasa
- Cipta persekitaran ujian beban yang mensimulasikan penggunaan sebenar
Konfigurasi ESLint Penting
{
"extends": ["plugin:react-hooks/recommended"],
"rules": {
"react-hooks/exhaustive-deps": "error"
}
}
Detik Pembelajaran Kritikal
Insiden ini berfungsi sebagai peringatan berkuasa tentang bagaimana kesilapan pengaturcaraan asas boleh mempunyai impak global yang besar. Gangguan itu menjejaskan berjuta-juta laman web dan perniagaan yang tidak terkira di seluruh dunia, semuanya daripada kesilapan yang boleh ditangkap dengan alatan dan proses yang betul.
Seperti yang diulas ThePrimeagen, YouTuber teknologi yang terkenal, tentang insiden itu: “Kita semua pernah membuat kesilapan ini—saya hanya gembira saya menangkap kesilapan saya dalam pembangunan, bukan pengeluaran.”
Perkara Utama
- Pengetahuan asas penting — Walaupun pada skala enterprise, prinsip pengaturcaraan asas adalah kritikal
- Perkakasan adalah penting — ESLint, React DevTools, dan pemantauan yang betul mencegah isu-isu ini
- Kegagalan proses mengkompaun kegagalan teknikal — Berbilang jaringan keselamatan gagal secara serentak
- Penggunaan beransur menyelamatkan nyawa — Pelancaran global segera berbahaya untuk infrastruktur kritikal
- Kuasa React memerlukan tanggungjawab — Fleksibiliti rangka kerja menuntut amalan pembangunan yang berdisiplin
Bergerak Ke Hadapan
Cloudflare sejak itu telah melaksanakan:
- Argo Rollouts untuk rollback penggunaan automatik
- Pemantauan yang dipertingkatkan untuk corak penggunaan API
- Pengehadan kadar dan corak pemutus litar yang lebih baik
- Proses semakan kod yang lebih baik untuk perubahan frontend
Insiden ini berfungsi sebagai peringatan berkuasa bahawa dalam dunia kita yang saling terhubung, walaupun perubahan kod terkecil boleh mempunyai impak global yang besar. Sama ada anda membina laman web mudah atau menguruskan infrastruktur kritikal, memahami asas React dan melaksanakan proses kejuruteraan yang betul bukan sekadar amalan yang baik—ia penting untuk kestabilan internet.
Lain kali anda menulis hook useEffect, ingat: jurutera Cloudflare mungkin juga sedang menyemak semula array kebergantungan mereka.