调用where方法触发SystemStackError(stack level too deep)问题求助
Member.where(skill_primary: 8)触发的SystemStackError问题 这种栈溢出的问题在Rails模型查询里真的很让人头疼,十有八九和模型里的方法重写、关联逻辑或者回调有关。我帮你梳理几个最可能的排查方向:
1. 先排查skill_primary的方法重写是否导致递归
直接去app/models/member.rb里搜skill_primary,看看有没有不小心写出递归的getter方法:
# 这种错误写法会直接导致栈溢出! def skill_primary skill_primary # 这里不停调用自己,无限递归 end
如果是关联字段(比如belongs_to :skill_primary, class_name: 'Skill'),也别在模型里重写同名方法,否则查询时触发关联加载就会递归调用。
2. 检查关联和回调里的循环逻辑
如果Member和Skill有双向关联,或者模型里有after_find、before_load这类回调,要看看是不是回调里的逻辑触发了循环调用。比如:
class Member < ApplicationRecord belongs_to :skill_primary, class_name: 'Skill' after_find :sync_skill_data def sync_skill_data # 这里如果不小心调用了skill_primary属性,可能触发关联加载,进而再次触发回调 self.skill_name = skill_primary.name end end
这种情况下,查询时加载每条记录都会触发回调,回调又调用关联,可能形成循环导致栈溢出。
3. 用简化查询定位问题根源
先试试绕开ActiveRecord的查询语法,用原始SQL查:
Member.find_by_sql("SELECT * FROM members WHERE skill_primary = 8")
如果这个不报错,说明是ActiveRecord在解析查询时触发了模型里的某个递归方法;如果还是报错,那就要考虑数据库层面的触发器(不过这种情况极少)。另外也可以试试查询其他字段,比如Member.where(id: 500),看看会不会报错,以此判断是不是只有skill_primary相关的查询有问题。
4. 检查默认作用域(default_scope)或自定义作用域
看看模型里有没有定义和skill_primary相关的默认作用域,而且作用域里调用了模型的实例方法:
# 这种写法会在所有查询时触发递归 default_scope { where(skill_primary: default_skill_id) } def default_skill_id self.skill_primary || 1 # 这里调用了skill_primary属性,触发递归 end
默认作用域会在where查询时自动叠加,里面的逻辑如果不注意就会引发无限调用。
总的来说,先从skill_primary相关的模型定义入手,递归调用是栈溢出最常见的原因,一步步排查很快就能找到问题点。
内容的提问来源于stack exchange,提问作者Shpigford

