【问题标题】:Rails: "Stack level too deep" error when calling "id" primary key methodRails:调用“id”主键方法时出现“堆栈级别太深”错误
【发布时间】:2010-11-09 16:52:16
【问题描述】:

这是another issue 的转贴,这次最好隔离。 在我的 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。 Category 模型 has_many :subcategories。 上述行的变体调用失败(例如 Category.first.subcategory_ids、使用“each”而不是“map”等)。

任何想法都将受到高度赞赏。

谢谢! 阿米特

【问题讨论】:

  • 您使用的是哪个版本的 ruby​​?

标签: ruby-on-rails activerecord method-missing


【解决方案1】:

即使解决了这个问题,我也只是想插话,并报告我是如何解决这个问题的。我有与 OP 相同的症状,初始请求 .id() 工作正常,后续请求 .id() 会抛出“堆栈太深”错误消息。这是一个奇怪的错误,因为它通常意味着你在某处有一个无限循环。我通过更改解决了这个问题:

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

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

在环境/production.rb 中。

更新:这个问题的根本原因原来是缓存存储。默认的 MemoryStore 不会保留 ActiveRecord 模型。这是一个相当古老的错误,而且相当严重,我不知道为什么它没有被修复。无论如何,解决方法是使用不同的 cache_store。尝试在您的 config/environments/development.rb 中使用它:

config.cache_store = :file_store

更新 #2:C. Bedard 发布了对此问题的分析。好像总结的很好。

我自己也遇到过这个问题(并且反复被卡住),我已经调查了这个错误(并希望找到一个好的修复方法)。这是我所知道的: 当调度程序在请求之间调用 ActiveRecord::Base#reset_subclasses 时会发生这种情况(仅在开发模式下)。

ActiveRecord::Base#reset_subclasses 清除了inheritable_attributes Hash(存储了#skip_time_zone_conversion_for_attributes)。 它不仅会发生在通过请求持久化的对象上,正如 #1290 中的“猴子测试应用程序”所示,而且在尝试访问 AR 上生成的关联方法时也会发生,即使是仅在当前请求上存在的对象也是如此。

此错误由 this commit 引入,其中 #skip_time_zone_conversion_for_attributes 声明从 base.cattr_accessor 更改为 base.class_inheritable_accessor。但话又说回来,同样的提交也修复了其他问题。 最初在这里提交的补丁只是避免清除 reset_subclasses 中的 instance_variables 和 instance_methods 确实会引入大量泄漏,并且泄漏的数量似乎与应用程序的复杂性成正比(即每个模型、关联和属性的数量)。我有一个非常复杂的应用程序,当应用补丁时,它在开发模式下的每个请求都会泄漏近 1Mb。所以它不可行(无论如何对我来说)。

在尝试不同的方法来解决这个问题时,我已经更正了最初的错误(skip_time_zone_conversion_for_attributes 在第二次请求中为零),但它发现了另一个错误(只是没有发生,因为第一个异常会在到达它之前引发)。该错误似乎是 #774 中报告的错误(“id”方法的 method_missing 中的堆栈溢出)。

现在,对于解决方案,我的补丁(附件)执行以下操作: 它为#skip_time_zone_conversion_for_attributes 方法添加了包装器方法,确保它始终将值作为class_inheritable_attribute 读取/写入。这样一来,nil 就不再返回了。

它确保在调用 reset_subclasses 时不会清除 'id' 方法。 AR 在那个方面有点奇怪,因为它首先直接在源代码中定义它,但在第一次调用时使用 #define_read_method 重新定义自己。这正是它在重新加载后失败的原因(因为 reset_subclasses 然后将其清除)。

我还在 reload_models_test.rb 中添加了一个测试,它调用 reset_subclasses 来尝试在开发模式下模拟请求之间的重新加载。在这一点上我不能说的是它是否真的像在实时调度程序请求周期中那样触发重新加载机制。我还从脚本/服务器进行了测试,错误消失了。

抱歉,贴太长了,rails 灯塔项目是私有的,这很糟糕。上面提到的补丁是私有的。

【讨论】:

  • 它是私有的,意味着你不能访问它?还是不能分享?
  • “私人”的意思是当我写这篇文章时,管理员关闭了灯塔项目并迁移到了 Github。所有旧内容都被隐藏了。奇怪的是,它现在已经开放了,票可以是found here
【解决方案2】:

-- 这个答案是从我原来的帖子here复制过来的。

终于解决了! 在发布a third question 并在trptcolin 的帮助下,我可以确认一个可行的解决方案。

问题:我使用require 来包含来自无表模型(在 app/models 中但不扩展 ActiveRecord::Base 的类)中的模型。例如,我有一个班级FilterCategory 执行require 'category'。这弄乱了 Rails 的类缓存。 我必须首先使用require,因为Category.find :all 之类的行失败了。

解决方案(归功于 trptcolin):将 Category.find :all 替换为 ::Category.find :all。这无需明确要求任何模型即可工作,因此不会导致任何类缓存问题。

使用config.active_record.default_timezone = :utc 时,“堆栈太深”问题也消失了

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-07-24
    • 1970-01-01
    • 1970-01-01
    • 2017-08-19
    • 2018-09-12
    • 1970-01-01
    • 2023-04-02
    • 1970-01-01
    相关资源
    最近更新 更多