Rails: ошибка «слишком большой уровень стека» при вызове метода первичного ключа «id» - PullRequest
2 голосов
/ 09 ноября 2010

Это репост на другой выпуск , лучше на этот раз изолированный. В моем файле environment.rb я изменил эту строку:

config.time_zone = 'UTC'

к этой строке:

config.active_record.default_timezone = :utc

С тех пор этот звонок:

Category.find(1).subcategories.map(&:id)

Сбой при ошибке "Уровень стека слишком глубокий" после второго запуска в среде разработки, когда config.cache_classes = false. Если config.cache_classes = true, проблема не возникает. Ошибка является результатом следующего кода в active_record / attribute_methods.rb вокруг строки 252:

def method_missing(method_id, *args, &block)
...

    if self.class.primary_key.to_s == method_name
        id
    ....

Вызов функции "id" повторно вызывает method_missing, и ничто не препятствует повторному вызову id, что приводит к слишком глубокому уровню стека.

Я использую Rails 2.3.8. Модель категории has_many: подкатегории. Вызов не удался в вариантах этой строки выше (например, Category.first.subcategory_ids, использование «each» вместо «map» и т. Д.).

Любые мысли будут высоко оценены.

Спасибо! Amit

Ответы [ 2 ]

4 голосов
/ 26 мая 2011

Несмотря на то, что это решено, я просто хотел вмешаться в это и сообщить, как я исправил эту проблему.У меня были те же симптомы, что и у OP, первоначальный запрос .id () работал нормально, последующие запросы .id () выдавали сообщение об ошибке «слишком большой стек».Это странная ошибка, так как обычно это означает, что у вас где-то есть бесконечный цикл.Я исправил это, изменив:

config.action_controller.perform_caching = true
config.cache_classes                     = false

на

config.action_controller.perform_caching = true
config.cache_classes                     = true

в environment / production.rb.

ОБНОВЛЕНИЕ: Основная причина этой проблемы оказаласьмагазин кешей.По умолчанию MemoryStore не сохраняет модели ActiveRecord.Это довольно старая ошибка, и довольно серьезная, я не уверен, почему она не была исправлена.В любом случае, обходной путь должен использовать другой cache_store.Попробуйте использовать это в вашем config / environment / development.rb:

config.cache_store = :file_store

ОБНОВЛЕНИЕ № 2: C. Бедард опубликовал этот анализ проблемы.Кажется, это хорошо подытожило.

Столкнувшись с этой проблемой сам (и неоднократно зацикливаясь на ней), я исследовал ошибку (и, надеюсь, нашел хорошее исправление).Вот что я знаю об этом: это происходит, когда ActiveRecord :: Base # reset_subclasses вызывается диспетчером между запросами (только в режиме dev).

ActiveRecord :: Base # reset_subclasses уничтожает хеш наследуемых_атрибутов (где #skip_time_zone_conversion_for_attributes сохраняется).Это будет происходить не только с объектами, сохраняемыми в запросах, как показывает «тестовое приложение обезьяны» из # 1290, но также и при попытке доступа к сгенерированным методам связи в AR, даже для объектов, которые живут только по текущему запросу.

Эта ошибка была введена этим коммитом , когда объявление #skip_time_zone_conversion_for_attributes было изменено с base.cattr_accessor на base.class_inheritable_accessor.Но опять же, тот же коммит также исправил что-то еще.Изначально представленный здесь патч, в котором просто избегается очистка instance_variables и instance_methods в reset_subclasses, приводит к серьезной утечке, и утечки кажутся прямо пропорциональными сложности приложения (т. Е. Количеству моделей, ассоциаций и атрибутов в каждой из них).У меня есть довольно сложное приложение, которое пропускает около 1 Мбайт при каждом запросе в режиме разработки при применении патча.Так что это нежизнеспособно (для меня в любом случае).

При попытке различных способов решить эту проблему, я исправил первоначальную ошибку (skip_time_zone_conversion_for_attributes, равную nil при втором запросе), но он обнаружил другую ошибку (которая только что не былаэто происходит потому, что первое исключение будет выдвинуто до того, как оно будет получено).Похоже, что это ошибка, о которой сообщалось в # 774 (переполнение стека в method_missing для метода 'id').

Теперь для решения мой патч (прилагается) делает следующее: Он добавляет методы-обертки для#skip_time_zone_conversion_for_attributes методы, следя за тем, чтобы оно всегда считывало / записывало значение как class_inheritable_attribute.Таким образом, nil больше никогда не возвращается.

Это гарантирует, что метод 'id' не будет уничтожен при вызове reset_subclasses.AR немного странно, потому что сначала определяет его непосредственно в источнике, но переопределяет себя с помощью #define_read_method при первом вызове.И это именно то, что делает его неудачным после перезагрузки (так как reset_subclasses затем уничтожает его).

Я также добавил тест в reload_models_test.rb, который вызывает reset_subclasses, чтобы попытаться симулировать перезагрузку между запросами в режиме dev.На данный момент я не могу сказать, действительно ли он запускает механизм перезагрузки, как это происходит в цикле запросов оперативного диспетчера.Я также проверил со скрипта / сервера, и ошибка исчезла.

Извините за длинную вставку, это отстой, что проект маяка рельсов является частным.Упомянутый выше патч является приватным.

2 голосов
/ 11 ноября 2010

- Этот ответ скопирован из моего исходного поста здесь .

Окончательно решено!После публикации третьего вопроса и с помощью trptcolin я смог подтвердить рабочее решение.

Проблема: я использовал require для включения моделей изнутриМодели без таблиц (классы, которые находятся в приложении / модели, но не расширяют ActiveRecord :: Base).Например, у меня был класс FilterCategory, который выполнял require 'category'.Это перепуталось с кэшированием классов в Rails.Я должен был использовать require в первую очередь, поскольку такие строки, как Category.find :all не удалось.

Решение (в кредит переходит к trptcolin): замените Category.find :all на ::Category.find :all.Это работает без необходимости явно требовать какой-либо модели и, следовательно, не вызывает проблем с кэшированием классов.

Проблема «слишком большой стек» также исчезает при использовании config.active_record.default_timezone = :utc

...