КАК МЫ 12 ЧАСОВ ЛОВИЛИ ПРИЗРАКА
Ребят, выдыхаем. Переезд на новую инфраструктуру официально завершен. Всё крутится в штатном режиме, причем с солидным запасом под новые нагрузки. Но путь к этому дзену был... скажем так, незабываемым.
Заваривайте чай, сейчас будет лонгрид и история про боль,
страдания и один маленький thread-lock
КАКОВ БЫЛ ПЛАН?
Идея была максимально логичной: собрать весь наш зоопарк под одной крышей. Раньше обычный DNS и шифрованный (DoT/DoH) жили на разных серверах. Мы решили объединить всё под одним балансировщиком.
Профит очевиден: общий быстрый кеш, один набор правил вместо трех, и главное — возможность наконец-то нормально масштабироваться без костылей. Звучало идеально...
А ПОТОМ НАЧАЛСЯ ЦИРК 🎪
Переключаем трафик. Первые минуты — красота. А дальше сервера начинают жестко флапать. То работает как швейцарские часы, то очереди залипают намертво.
Смотрим в мониторинг: процессор свободен, всё зелёное, ресурсы есть. А пакеты просто лежат в очереди, и их никто не забирает. В итоге консоли, роутеры, мобилки и Вы все сидите без интернета...
Мы начали копать. Раскидали по ядрам? Не помогло. Разнесли сетевые прерывания? Стало чуть легче, но суть не поменялась. Крутили буферы, воркеры, лимиты... Это было похоже на игру «ударь крота» — затыкаешь одну дыру, тут же рвется в другом месте. Просто вдумайтесь: 251 миллион переполнений TCP/UDP и дропов за 20 минут. Карл! Спасибо, чтот ваши DoT/DoH-клиенты так любят ретраить, как вне себя...
ЧТО ЭТО ВООБЩЕ БЫЛО? 🕵️♂️
Спустя 12 часов дебага мы полезли в документацию балансировщика и нашли там одну маленькую табличку, которая всё объяснила, су..а...
Оказывается, в софте есть такая архитектурная подлянка — «глобальная блокировка». Грубо говоря, один замок на весь процесс. Когда любой из 128 потоков обрабатывает кастомное правило через Lua-экшен, все остальные стоят в очереди и покорно ждут.
А у нас прямо на горячем пути висело два таких правила. При 100k QPS запросов в секунду это 200 000 захвата лока. DoT-потоки (которых больше и они агрессивнее) постоянно забирали замок себе, а легкие потоки обычного DNS тупо стояли в сторонке.
Чем дольше ждали — тем больше клиентов отваливалось в TCP, тем сильнее всё тормозило.
Вообщем каскадный отказ на ровном месте. И всё это на полупустых ядрах! Проблема была не в железе, а в одном узком горлышке софта.
ПОЧЕМУ НЕ СТРЕЛЯЛО РАНЬШЕ? 🤔
Стреляло. Просто мы этого не замечали.
Раньше DoT/DoH трафик жил отдельно и конкурировал сам с собой. А тут мы заперли тяжеловесов (TLS) и легковесов (обычный DNS) в одной комнате с одним ключом на выход. И тяжеловесы просто задавили мелких, а потом и себя...
КАК МЫ ЭТО ПОЧИНИЛИ 🛠
Переписали проблемные правила так, чтобы они вообще не дергали эту глобальную блокировку.
Жестко развели потоки по разным группам ядер, а процессы по отдельным NUMA-нодам. Шифрованный DNS теперь крутится на своих мощностях, обычный — на своих.
ИТОГ ✅
Мониторинг зелёный. Очереди пустые. Процессор отдыхает.
Мы спокойно перевариваем 100 000+ запросов в секунду и держим 80 000+ одновременных TLS-соединений.
Спасибо всем, кто ждал и не паниковал.
Выдыхаем. И едем дальше! 🚙
Если проект вам по душе — поддержите его, это помогает нам расти, оставаться быстрыми и независимыми 💫
С уважением,
Команда Xbox DNS 👍