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

这是另一个问题的转贴,这次更好地隔离了。 在我的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”等)。

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

谢谢! 阿米特

即使这已经解决了,我只是想对此进行讨论,并报告我是如何解决这个问题的。 我有与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 

在environment / 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上生成的关联方法时,即使对于仅存在于当前请求的对象也是如此。

此提交引入了此错误,其中#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),但是它发现了另一个错误(之前没有发生错误,因为第一个exception会在到达之前引发)。 该错误似乎是#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模式下模拟请求之间的重新加载。 在这一点上我无法分辨的是它是否真的触发了重载机制,就像在实时调度程序请求周期中那样。 我也从脚本/服务器测试,错误消失了。

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

– 这个答案是从我原来的post复制的。

终于解决了! 在发布第三个问题并在trptcolin的帮助下,我可以确认一个有效的解决方案。

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

解决方案(信用转到trptcolin):用::Category.find :all替换::Category.find :all 。 这可以在不需要明确要求任何模型的情况下工作,因此不会导致任何类缓存问题。

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