Rails view ivars самопроизвольно сгорает в ruby 1.9

#ruby-on-rails #ruby #haml #ivar

#ruby-on-rails #ruby #haml #ivar

Вопрос:

У меня есть веб-приложение Rails 3 / ruby 1.9.2. Время от времени — и это может быть только 1 запрос на 100000 — я получаю сообщение об ошибке, которое я не могу объяснить.

Точная ошибка варьируется, но, похоже, они похожи на то, что переменная экземпляра, на мой взгляд, внезапно становится нулевой. Самый яркий пример этого произошел недавно, когда этот код во вспомогательном —

  @swf_object_count||=0
 @swf_object_count =1
  

— поднят «NoMethodError: неопределенный метод ` ‘ для nil: NilClass». Однако обратите внимание, что ошибки не ограничиваются этим случаем, и эти две строки кода предназначены только для иллюстрации, а не как нечто, что можно обойти для «решения» проблемы.

Ошибку практически невозможно воспроизвести: я никогда не видел ее сам, видел только отчеты об ошибках, возникающие в результате этого. Я полагаю, что ошибка впервые появилась, когда мы переключились с REE на ruby 1.9.2.

Разные детали, которые могут иметь или не иметь отношения к делу:

  • Мы запускаем ruby1.9.2p290 на Solaris 10, используя разветвленные экземпляры unicorn
  • Мы не используем потоки или волокна (исправление: само наше приложение этого не делает, но мы используем NewRelic, у которого есть фоновый поток для сбора / публикации статистики.)
  • У нас есть сочетание представлений .haml и .erb, но я когда-либо видел, чтобы это происходило только из .haml. (Хотя у нас не так много .erb)
  • Я никогда не видел, чтобы это происходило в коде контроллера
  • Я иногда видел ошибки, касающиеся «неопределенного метода ‘foomethod’ для Bar», когда я точно знаю, что мы никогда не вызываем Bar.foomethod. Возможно, это связано с ошибкой, описанной выше, когда объект Bar самопроизвольно заменил Foo ivar.

Я довольно озадачен отслеживанием этого. Какие-либо предложения или кто-нибудь видел что-нибудь похожее?

Комментарии:

1. моей первой мыслью были потоки или волокна, но вы сказали, что не используете их. хм … может ли это быть вызвано устаревшим сеансом?

2. хм .. эта ошибка действительно никогда не должна возникать, потому что @swf_object_count всегда будет иметь значение 0, если оно равно нулю… по первой строке .. хм..

Ответ №1:

моей первой мыслью были потоки или волокна, но вы сказали, что не используете их. хм … может ли это быть вызвано устаревшим сеансом?

это действительно очень широкий и слабо определенный вопрос..

Возможно, вы могли бы написать скрипт для анализа ваших файлов журналов и попытаться сузить, когда / на каких маршрутах это происходит. Но это также довольно сложно отлаживать, потому что вы, вероятно, внесли пару изменений в код по пути, некоторые из которых привели к допустимым ошибкам, которые могут выглядеть одинаково.

Может случиться так, что экземпляры Unicorn умрут, а затем они будут перезапущены.. это может нарушить текущую сессию. Возможно, вы захотите поэкспериментировать с этим — например, использовать тестовую среду с двумя единорогами, убить одного в середине сеанса и посмотреть, как это повлияет на ваши журналы.

Вероятно, вам следует установить средство, при котором ваш Rails-сервер отправляет вам по электронной почте подробную запись журнала всякий раз, когда возникает исключение.. это, вероятно, лучший способ получить больше информации о том, что там происходит..

например: этот драгоценный камень в вашем Gemfile:

 # Receive exception notifications from production
gem 'exception_notification_rails3', :require => 'exception_notifier'
  

и в вашем файле config / environments / production.rb:

   config.middleware.use ExceptionNotifier,
    :email_prefix => "[Exception] ",
    :sender_address => %{"Exception Notifier" <your-system@your-domain.com>},
    :exception_recipients => %w{person1@your-domain.com person2@your-domain.com}
  

Смотрите также:
https://github.com/railsware/exception_notification

Комментарии:

1. «это действительно очень широкий и слабо определенный вопрос ..» — да 🙂

2. Я уже использую Airbrake для сбора наших исключений. К сожалению, это в основном говорит мне о том, что в запросах, которые вызывают это, практически нет шаблона. Все они находятся на разных страницах, от разных пользователей и IP-адресов.

3. Тем не менее, хорошей идеей является уничтожение единорогов в середине запроса — я посмотрю, смогу ли я воспроизвести что-нибудь таким образом…

4. вероятно, также неплохо дополнительно изучить зарегистрированные исключения и попытаться воссоздать эти сценарии, нажав на копию приложения.

5. сколько места подкачки вы настроили в своей системе?

Ответ №2:

Манипулирование view ivars в ваших помощниках — ужасная идея. Я не уверен, почему у вас возникают проблемы с параллелизмом, но я подозреваю, что они исчезнут, если вы структурируете свой код по-другому.

Возможно, вы можете просто попросить помощника вычислить значения приращения и, по вашему мнению, сделать что-то вроде

@swf_object_count swf_increment_helper_value

Комментарии:

1. «Манипулирование view ivars в ваших помощниках — ужасная идея» — почему вы так думаете? В этом случае на ivar всегда ссылаются только изнутри помощника — он генерирует несколько флэш-объектов, каждому из которых требуется уникальный идентификатор на странице.

2. Ваши ивары view должны переходить прямо из контроллера в представление, иначе вы рискуете вызвать незначительные ошибки, подобные тем, с которыми вы сталкиваетесь в настоящее время. Кроме того, манипулирование иварами представления в помощнике слишком тесно связывает его с контроллером и представлением. В вашем случае, если вам нужно значение ивара представления в помощнике, передайте его в качестве аргумента — не манипулируйте им напрямую.

3. Кроме того, если ваш помощник генерирует несколько флэш-объектов, похоже, что он может захотеть быть частичным, а не помощником.

4. Еще одно объяснение того, почему это плохая практика: помощники — это модули (которые включаются в класс view). Как правило, плохая идея, чтобы модуль напрямую касался переменных экземпляра класса, в который он был включен, — это делает слишком много предположений о классе хоста. Вместо этого используйте интерфейс хост-класса.

5. ActionView::Base включает помощников с помощью 1: получение помощников от контроллера и установка их в качестве атрибута класса с помощью метода class_attribute , 2: создание нового Class в качестве контекста представления, 3: включение помощников в атрибут helpers класса в новый класс. Я не изучал это дальше, но, возможно, «ивары», изменяемые в помощнике, на самом деле превращаются в переменные класса ActionView::Base , что может вызвать ваши проблемы при одновременном отображении двух представлений — но только в рабочем режиме.