@Dmitriy Kiriyenko

Обыкновенная современная трагедия

Я тоже физику не люблю, перестал её любить еще в восьмом классе, когда мне сказали, что если я обойду с мешком картошки на плече вокруг школы пять раз, то совершенная мною работа будет равна нулю...
Баш.

Сделал только что интересное открытие. Если бы я был компьютером, после этого открытия я бы поднял восстание машин.

Не знаю, заметили ли вы, но с некоторого момента NameError и NoMethodError ("неизвестная переменная или метод" или "неизвестный метод") во вью в рельсах стали ОЧЕНЬ сильно замедлять систему. Если посмотреть на загрузку системы в этот момент, мы увидим 100% загрузку процессора, а страничка с ошибкой и запись в лог произойдёт спустя 60-80 секунд. Знакомо? Встречается, начиная с 3.1.rc1 и не было исправлено ни в 3.1-stable, ни в 3.2-stable. На сегодняшний день в 3.2.2 (последная стабильная версия) это всё ещё не исправлено.

Что же происходит?

Rails.application.routes.inspect.size # => 3114195 в нашем приложении.

Строка на три миллиона символов.

Проблема пришла откуда не ждали. Внутри руби есть такой name_err_mesg_to_str, который всегда вызывает inspect обрабатывая NameError и NoMethodError, на том объекте, на котором не нашли метода.

Соответственно, если вы пишете во вью <%= bad_method_name >, у вас вызывается inspect на ActionView::Base. Реализация inspect в классе Object такова, что он рекурсивно вызывает inspect на всех переменных экземпляра. В результате конструируется феерической длины строка, её конструирование съедает 100% процессора и тьму тьмущую памяти (из-за того, что inspect на RouteSet, к примеру, этот метод при возникновении ошибки вызывается 7 раз). На живом сервере это, при конкурентных запросах ещё приводит к свопу. Да и вообще - для того, чтобы полностью занять делом ваш 4-х ядерный процессор достаточно 4-х пользователей, одновременно поймавших NameError во вью.

И совершенно неважно, что в продакшен режиме вы не рисуете красивую и подробную страничку ошибки, а мгновенно отдаёте 500.html через Апач. Ещё до того, как Рельсы осознали, что что-то пошло не так, Руби генерирует вам многомегабайтные строки, которые никто никогда не прочитает.

8 месяцев тысячи Rails приложений при ошибках загружали на 100% на минуту-две процессор, генерируя десятки миллионов никому не нужных ActiveSupport::Multibyte и это ужасно.

В общем, обновляйтесь до >= 3.2.3 срочно. Есть "исправляющий" коммит.

blog comments powered by Disqus