Та сама компанія, яка є останнім вартовим цифрового світу. Це той Cloudflare, який здійснив монументальне відбиття DDoS-атаки потужністю 7,3 терабіта на секунду, подвиг цифрового захисту, що вражає уяву. Ми говоримо про найкращу компанію у вирішенні проблем, справжню фортецю проти хаотичних штормів інтернету. Отже, коли цей титан спіткнувся, коли Cloudflare впав, негайна думка, єдиний логічний висновок, полягає в тому, що це, мабуть, була материнський корабель усіх атак. Це мала бути робота елітного загону елітного загону, цифровий напад настільки глибокий, настільки руйнівний, що його могли організувати лише найдосвідченіші супротивники на планеті. Розум одразу переноситься до Корейської Народно-Демократичної Республіки, викликаючи образи їхніх найбільш нагороджених цифрових солдатів, зелених беретів хакерства, що розпочинають скоординовану, світо-руйнівну атаку.
Ви, мабуть, думаєте так само, як і я, що, чоловіче, що б не змусило Cloudflare почати падати, це має бути найвеличніша річ в історії, правда? Масштаб мав бути безпрецедентним. Ми не говоримо про просту атаку скрипт-кідді; це мала бути державна кампанія біблійних масштабів, розроблена для того, щоб паралізувати саму основу мережі. Зрештою, це компанія, яка колись автоматично пом'якшила світовий рекорд DDoS-атаки потужністю 3,8 терабіта на секунду, а потім заблокувала монументальну DDoS-атаку на 7,3 Тбіт/с. Щоб зупинити таку силу, протидіюча сила мала бути катаклізмічною. Це мало бути кульмінацією років планування, ідеально виконаного цифрового удару, який знайшов єдину щілину в інакше непробивній броні Cloudflare. Інтернет затамував подих, очікуючи на розбір польотів, який розкриє генія, що стоїть за цим падінням, новий експлойт нульового дня, який поставив гіганта на коліна. Але що насправді сталося?
Насправді стався React. А ще краще — useEffect. Справжній зелений берет зі знищення життів джуніорів, і, мабуть, інфраструктури найбільшого захисника інтернету. Материнський корабель усіх атак не був зовнішньою загрозою; це був баг, проста, майже соромно поширена помилка в кодуванні, що ховалася в їхній власній панелі керування. Елітний загін не був іноземною державою; це був проблемний об'єкт у масиві залежностей. Cloudflare, компанія, відома своєю здатністю зупиняти проблеми, нарешті була зупинена сама, не з гуркотом, а з нескінченним циклом. Це був не акт кібервійни; це був класичний випадок "рушниця, нога, бах". Вони вистрелили собі в ногу, і весь світ спостерігав.
Для тих з вас, хто не має уявлення, що відбувається, благослови вас Господь. Що за чортівня цей React, і що в біса таке useEffect? Простими словами, React — це популярна бібліотека для створення користувацьких інтерфейсів, а useEffect — це інструмент у React, "хук", який дозволяє розробникам виконувати дії, або "побічні ефекти", наприклад, отримувати дані з сервера. Розробник може сказати useEffect виконувати свою функцію лише тоді, коли змінюються певні дані, надавши "масив залежностей". Якщо щось у цьому масиві відрізняється від минулого разу, коли компонент рендерився, ефект запускається знову. Саме тут уся карткова хатка й завалилася.
Безпосереднім тригером інциденту став баг у панелі керування. Проблемний код включав хук useEffect, який був налаштований для отримання даних для панелі. Масив залежностей для цього хука містив об'єкт params, який, що вкрай важливо, створювався заново при кожному рендері компонента. У світі JavaScript, навіть якщо два об'єкти виглядають ідентично, вони не є однаковими, якщо займають різні місця в пам'яті. Порівняння цих залежностей у React є поверхневим; воно перевіряє, чи однаковим є посилання на об'єкт, а не його вміст. Оскільки новий об'єкт створювався щоразу, React трактував його як "завжди новий", змушуючи useEffect перезапускатися щоразу. Це викликало виклик API. Цей виклик API потім призводив до оновлення стану, що викликало повторний рендер. Повторний рендер створював новий об'єкт params. Новий об'єкт розглядався як "завжди новий". useEffect спрацьовував знову. Це створило порочне, самовідтворюване нескінченне коло, що робило тисячі й тисячі викликів до бекенду за хвилину з однієї користувацької сесії. Cloudflare, по суті, створив інструмент, який дозволив його власній панелі керування DDoS-ити власний Tenant Service API. Вони помилково включили проблемний об'єкт у його масив залежностей — помилка React 101.
Іронія настільки густа, що її можна різати ножем. Та сама компанія, яка пропонує захист від DDoS світового класу, була повалена самоспричиненим DDoS, що виник з однієї з найпоширеніших пасток у сучасній фронтенд-розробці. Це той вид "Скубі-Ду упсі-дейзі", який очікуєш від стажера в його перший день, а не від багатомільярдної компанії-гіганта інтернет-інфраструктури.
Але історія на цьому не закінчується. У поспіху, намагаючись виправити початкову проблему, вони ухвалили рішення, яке призвело до другого, каскадного збою. Щоб повернути систему до здорового стану, вони вирішили перезапустити Tenant Service, що змусило панелі керування всіх користувачів повторно автентифікуватися з API. Це знову зробило API нестабільним, що призвело до так званої проблеми "Громового стада". "Громове стадо" — це коли величезна кількість процесів або клієнтів, які всі чекають на доступність певного ресурсу, кидаються до нього в ту ж мить, коли він стає доступним, перевантажуючи ресурс. Кожен користувач Cloudflare на панелі керування, одночасно намагаючись повторно автентифікуватися, створив масивний, синхронізований сплеск трафіку, з яким сервіс, що відновлювався, просто не зміг впоратися. Їхня спроба виправити проблему посилила баг, поглибивши нестабільність і гарантувавши, що сервіс не зможе відновитися, впавши ще раз.
Весь цей епізод піднімає глибоко спантеличуюче питання: як це взагалі потрапило в продакшн? Код, подібний до цього, що створює нескінченний цикл запитів, мав бути виявлений. Чому тестування на локальному середовищі не виявило, що вкладка браузера зависає, роблячи тисячі запитів? Як це пройшло через код-рев'ю? Де були автоматизовані "канарки" або повільні розгортання, які могли б виявити 1000-кратне збільшення обсягу запитів від одного сервісу та автоматично відкотити розгортання? Для компанії, чия бізнес-модель повністю обертається навколо стабільності, стійкості та зупинки аномальних патернів трафіку, дивно, що у них не було внутрішніх механізмів для зупинки цього дуже очевидного, самогенерованого аномального патерну трафіку.
Звичайно, Cloudflare тепер все виправив і вчиться на помилці. Вони підвищують пріоритет міграції сервісів на свою систему Argo Rollouts, яка моніторить розгортання на наявність помилок і автоматично відкочує сервіс при виявленні помилки. Якби вона була на місці, ця система автоматично відкотила б оновлення Tenant Service, щойно воно почало поводитися неправильно, обмежуючи збій. Це чудовий крок, але здається, що це урок, засвоєний важким шляхом. Більша невдача полягала не лише в коді React; це був весь конвеєр, який дозволив такому простому, "м'ясо-картопляному" багу дістатися до продакшену та наробити лиха. API не впорався з навантаженням, у нього не було достатньо надійного обмеження швидкості, і він не зміг відновитися після збою. Проте, вину покладають на баг на стороні клієнта.
Зрештою, ми всі робили цю помилку. Я просто радий, що зловив свою на етапі розробки. Cloudflare, ви зловили свою в продакшені. Є щось напрочуд смішне в тому, що Cloudflare була компанією, яка могла блокувати найбільші у світі DDoS-атаки, лише для того, щоб її повалив useEffect. Це не була якась складна хакерська ситуація; це навіть не був скомпрометований пакет NPM. Це була найпростіша з помилок, фундаментальне нерозуміння того, як працює масив залежностей React. Це служить смиренним нагадуванням для всієї галузі: незалежно від того, наскільки ви великі чи складні, ви ніколи не застраховані від основ. Ім'я йому — Реактаген.