生产环境sorbet-runtime中T.cast偶发NoMethodError问题咨询
生产环境sorbet-runtime T.cast瞬态NoMethodError问题排查与咨询
问题背景
我们在生产服务的T.cast调用链路中遇到了瞬态NoMethodError报错,相关已知信息整理如下:
- 问题出现于
0.5.10012版本的sorbet-runtime中 - 故障每次仅影响单台主机,通常呈短时间爆发特征(例如报错持续时长小于10分钟后自行消失)
- 部分生产服务每日都会出现该报错,其余服务约每周出现一次
- 该问题是在我们将
sorbet-runtime从0.5.6001版本升级至0.5.9976版本(跨度2年的版本变更)后才出现的;此前我们一直锁定旧版本,直到完成对已废弃的--dsl-plugins特性的迁移才执行升级 - 当前使用的Ruby版本为2.7.4
- 我们已在生产环境禁用运行时校验,配置为:
T::Configuration.default_checked_level = :never
初步排查结论
短时间内T::Utils.coerce(type)会返回不符合预期的值(并非T::Types::Base的子类),导致在执行error_message_for_obj校验时抛出错误。
采集到的报错栈
NoMethodError: undefined method `valid?' for {:mode=>:compat}:Hash from sorbet-runtime (0.5.10012) lib/types/types/intersection.rb:33:in `block in valid?' from sorbet-runtime (0.5.10012) lib/types/types/intersection.rb:33:in `all?' from sorbet-runtime (0.5.10012) lib/types/types/intersection.rb:33:in `valid?' from sorbet-runtime (0.5.10012) lib/types/types/base.rb:133:in `error_message_for_obj' from sorbet-runtime (0.5.10012) lib/types/private/casts.rb:8:in `cast' from sorbet-runtime (0.5.10012) lib/types/_types.rb:133:in `cast' from < our code>
触发报错的业务代码行
T.cast(self, T.all(T::Props::Serializable, Object)).as_json
临时调试方案
目前尚未找到本地复现该问题的方法,因此针对性为T.cast添加了调试用猴子补丁,用于捕获错误、打印更多上下文日志,且不再抛出错误直接返回原值,补丁代码如下:
# frozen_string_literal: true module SorbetMonkeyPatches module TcastDebug module ClassMethods def cast(value, type, checked: true) super rescue NoMethodError => e Rails.logger.error( message: "[sorbet] - #{value.try(:class)} to #{type.try(:class)}", src_value: value.inspect, src_type: type.inspect, coerce_res: T::Utils.coerce(type).inspect, ) value # ultimately don't want assertions from T.cast...this should return the same value unchanged end end end end if ENV["SORBET_DEBUG_UPGRADE"] == "true" ::T.singleton_class.prepend(SorbetMonkeyPatches::TcastDebug::ClassMethods) end
异常日志样例
开启调试补丁后,采集到的异常日志样例如下:
[sorbet] - Class to Module {"src_value":"ProductsDomain::Api::LocaleCode","src_type":"T::Props::CustomType","coerce_res":"\"ssl_min_protocol_version\""} ... [sorbet] - Class to Module {"src_value":"ProductsDomain::Api::LocaleCode","src_type":"T::Props::CustomType","coerce_res":"[\"workflow_state\", \"active\"]"}
正常情况下coerce_res应当返回T::Types::Base的子类实例,而非上述随机字符串。我们初步推测是类型强制转换缓存被意外污染,从而导致该问题。
咨询问题
- 该问题是否有过已知的上报案例?
- 对于问题修复或后续排查方向,有无相关建议?
- 我们期望在禁用运行时校验时,
T.cast不会抛出任何错误,是否需要重写T.cast方法定义中的可选checked参数?
感谢您的协助,若需要更多调试信息可随时告知。
内容的提问来源于stack exchange,提问作者Osman Khwaja
相关产品推荐
相关产品推荐

