О том, как подкрадывается песец…
Есть у нас приложение, которое мы пишем на основе DotNetNuke (DNN). Причина почему выбрали DNN - настоял заказчик. Мол, DNN самый “взрослый”, “стабильный”, “корпоративный” и тому подобное.
Вот, на днях у нас упал продакшен. Хорошо так упал.
Начал исследовать причину, поковырялся - оказалось что падает вся ферма из веб серверов (их 3 штуки), если запускается операция создания сайта. DNN поддерживает несколько сайтов работающих в одной инсталляции DNN.
После исследования, получилась весьма интересная картина, которая касается внутренностей ASP.NET, а так же архитектурных решений DNN.
В ASP.NET есть фича, которая занимается тем, что отслеживает изменения файлов в проекте ASP.NET. Если оно находит файл который изменился в определенных директориях - пересобирает приложение и перезапускает его.
Кроме того, в ASP.NET есть кеш. Кеш это key/value storage, который локален для процесса ASP.NET. Операции стандартные - добавить, получить, удалить и т.д. Есть expiration settings.
Есть стандартные рекомендации по deployment’у DNN - выкладываем файлы в расшаренной директории (SMB share), все физические сервера работают с проектом с этой расшареной директории, т.к. изменения файлов тоже надо синхронизировать и все сервера должны об этом знать.
Так вот, есть веб ферма с 3 физическими серверами. Работает с одной папкой на шаре. У всех серверов - свой локальный кеш, который как-бы надо синхронизировать.
Вместо использования централизированного кеша (memcached/redis/etc), разработчики DNN делают велосипед - синхронизационный провайдер. Это такая штука, которая каким-то образом дает другим серверам в ферме знать, что такой вот ключ уже не действителен и его надо удалить.
В бесплатной версии ДНН таких провайдеров есть два: один через базу данных, другой через файлы.
Первый фигачит в таблицу все удаленные ключи (таблица жутко разрастается). Сервера время от времени синхронизируются с таблицей (читают все что есть) и удаляют такие ключи локально. Время от времени таблица подчищается. Способ тормозной, сервер БД ложится на лопатки и вообще все плохо.
Второй работает еще веселее. На каждый ключ создается файл. Все файлы в одной директории - файлов получается over 100500. Поскольку все сервера работают с шары, файлы доступны для всех. Сервера мониторят изменения таких файлов и если меняется дата создания или файл удаляется - удаляют соответствующий ключ у себя.
И тут подкрался песец…
Создание сайта (стандартная операция) работает с обычным API DNN а-ля “создать страницу”, “положить модуль на страницу” и т.д. Каждая из таких операций инвалидирует кеш по нужным им ключам (“удалить ключ Page_1234”, “Создать ключ Module_1234” и т.д). И таких операций _очень_ много. Около 40 тысяч для полноценного портала.
В результате, происходит 40 тысяч добавлений/удалений файлов с шары за очень короткий промежуток времени. ASP.NET на каждом из серверов обрабатывает file change notifications, что жрет процессор (их много, пускай даже они и ерундовые), потом еще происходит удаление ключей, а в результате - полный армагеддон на всех серверах.
Клиенты отписались в саппорт DNN - они ответили, что у них в платной версии есть специальный синхронизационный провайдер, который дает супер производительность в ферме. Купили. Ничего не изменилось - сервера все равно падают.
Оказалось что новый провайдер работает через HTTP: каждый web сервер говорит соседям что у них удалился ключ. В результате, при создании портала, эти 40 тысяч запросов идут на все соседние сервера в течении небольшого промежутка времени и они успешно захлебываются. Что в лоб, что по лбу.
В результате, как quick fix, пока выключили апдейты кеша при создании сайтов и руками удаляем ключики после таких операций. Теперь думаем как фиксить правильно - то ли переехать на какой-нить memcached, то ли писать свой синхронизатор на сокетах.