Rails:调用“id”主键方法时出现“Stack level too deep”错误

时间:2010-11-09 16:52:16

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

这是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,则不会发生此问题。 该错误是第252行的active_record / attribute_methods.rb中的以下代码的结果:

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”等)。

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

谢谢! 阿米特

2 个答案:

答案 0 :(得分:4)

即使这已经解决了,我只是想对此进行讨论,并报告我是如何解决这个问题的。我有与OP相同的症状,初始请求.id()工作正常,后续请求.id()会抛出“stack too deep”错误消息。这是一个奇怪的错误,因为它通常意味着你在某个地方有一个无限循环。我修改了这个:

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

config.action_controller.perform_caching = true
config.cache_classes                     = true
环境/ production.rb中的

更新:此问题的根本原因是cache_store。默认的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_subbles中的instance_variables和instance_methods确实引入了大量泄漏,泄漏的数量似乎与应用程序的复杂性成正比(即每个模型的数量,关联和属性)。我有一个非常复杂的应用程序,当应用补丁时,在开发模式下每个请求泄漏近1Mb。所以它不可行(对我来说无论如何)。

     

在尝试不同的方法来解决这个问题时,我已经纠正了初始错误(skip_time_zone_conversion_for_attributes在第二次请求时为nil),但是它发现了另一个错误(之所以没有发生,因为第一个异常会在到达它之前引发)。该错误似乎是#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来尝试在dev模式下模拟请求之间的重新加载。在这一点上我无法分辨的是,它是否真的触发了重载机制,就像在实时调度程序请求周期中那样。我也从脚本/服务器测试,错误消失了。

很抱歉长粘贴,很糟糕,导轨灯塔项目是私人的。上面提到的补丁是私有的。

答案 1 :(得分: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

时,“堆栈太深”问题也会消失