加入收藏 | 设为首页 | 会员中心 | 我要投稿 站长网 (https://www.028zz.com.cn/)- 高性能计算、基础存储、混合云网络、云安全、数据计算!
当前位置: 首页 > 访谈 > 正文

Ruby老兵亲述:16年技术栈进化实录

发布时间:2026-09-28 08:49:57 所属栏目:访谈 来源:DaWei
导读:  Ruby老兵亲述:16年技术栈进化实录——这标题不是我起的,是半年前在东京RubyKaigi茶歇时,一个穿蓝袜子的德国人边啃抹茶KitKat边往我笔记本上写的。他指了指我正调试的Rack::Builder中间件链,说:“You’re still patchi

  Ruby老兵亲述:16年技术栈进化实录——这标题不是我起的,是半年前在东京RubyKaigi茶歇时,一个穿蓝袜子的德国人边啃抹茶KitKat边往我笔记本上写的。他指了指我正调试的Rack::Builder中间件链,说:“You’re still patching 2008’s middleware pipeline? Try `concurrent-ruby` with fiber-aware mutexes.” 我当时没接话,但当晚就删掉了自己维护了9年的`thread_safe_hash.rb`。


  2008年上线的Rails 2.0项目,数据库用MySQL 5.0,部署在Dell PowerEdge 1950物理机上,RAID 1阵列跑着monit+thin集群。我记得特别清楚:某天凌晨3点收到PagerDuty告警,`ActiveRecord::ConnectionTimeoutError`每秒涨27次——查下来是因为`mysql.gem`没做connection pool reuse,每个forked unicorn worker都在init时新建5个连接,4核机器扛不住12个worker。后来加了`reconnect: true`,又掉坑里:MySQL server因超时断连后,AR不重试事务,直接抛`Mysql::Error: Lost connection to MySQL server during query`。那周我手写了17版`rescue_from`兜底逻辑,最后靠在`config/initializers/mysql_fix.rb`里打补丁才稳住——但这补丁在Rails 3.2里就失效了,因为`ActiveRecord::Base.connection_pool`接管了全部连接生命周期。


  新技术。


  2012年切换到PostgreSQL 9.1时,我们把`serialize :metadata, JSON`字段全迁成`jsonb`,结果发现Rails 3.2.13原生不支持`jsonb`的索引下推查询。团队小哥硬生生用`Arel.sql("jsonb_path_exists(metadata, '$.tags[] ? (@ == \\"urgent\\")')")`拼了三个月的SQL片段,直到2014年Rails 4.2发布`store_accessor`才换成`metadata->'tags' @> '["urgent"]'::jsonb`——可悲的是,上线当天DBA发现PostgreSQL的GIN索引对深层嵌套路径的`@>`查询根本不走索引,实际执行计划全是seq scan。最后用`pgroonga`扩展才解决,但代价是每台DB服务器多吃1.2GB内存。


  2019年用Opal写前端,把`app/models/payment.rb`编译成JS跑在浏览器里校验PCI合规性。结果Safari 13的WebAssembly引擎对`BigDecimal#round`有精度漂移——用户输入`123.456`,Opal算出来是`123.45599999999999`。我们测了87个浏览器版本,只有Firefox 72+和Chrome 83修复了该问题。最后妥协方案是:所有金额计算强制转成Integer分处理,再用`Number((val 100).toFixed(0))`双保险截断——这个细节现在搜Ruby+Opal+PCI的GitHub Issues,第一页全是我们的PR链接。


文章配图,仅供参考

  半年前我接手一个用Dry-Validation 1.5的微服务,Schema里定义了`required(:email).filled(format?: EMAIL_REGEX)`,结果发现它默认不校验UTF-8字符长度——某越南用户注册时用了含ZWNJ(零宽非连接符)的邮箱名,在Redis缓存层被截断成非法字节序列,触发`Encoding::InvalidByteSequenceError`。翻Dry-Validation源码才发现,它的正则匹配走的是`Regexp.new(pattern, Regexp::IGNORECASE)`,而MRI 3.1的`String#valid_encoding?`在处理混合Unicode标记时有状态缓存bug。我提了PR被拒,理由是“应由调用方确保输入已normalize”。这事让我怀疑:我们到底是在写Ruby,还是在给Ruby解释器写兼容层?


  新技术。


  上周我重装了rbenv,拉取了ruby-head(commit 6d7e1b2),跑了趟`make install`。`bundle exec ruby -v`输出“ruby 3.4.0dev (2024-05-17 trunk-6d7e1b2)”——可当我跑`bundle exec rspec spec/models/user_spec.rb`时,`User.create!(email: "test@example.com")`居然抛出`FrozenError: can't modify frozen String`。追踪下去,发现是`activemodel-7.1.3.2`里`AttributeSet#assign_attributes`调用了`Hash#transform_values!`,而ruby-head把`Hash#transform_values!`内部`String#upcase!`改成了更激进的freeze策略。我临时打了`--disable-frozen-string-literal`参数,但测试覆盖率从92.4%跌到89.7%——因为某个mock对象的`to_s`返回值被新策略污染了。这事儿暂时没法修,得等AS maintainer发新版,或者我把自己16年前写的`attr_accessible`补丁重新挖出来适配一下。


  我现在的Gemfile.lock里有117个runtime依赖,其中39个带`~> 3.0`或`>= 3.2`的ruby-version限制。昨天删掉了一个用了8年的`redis-namespace`,换成了`redis-client`,结果发现它在Resque的job retry机制里不保持connection identity,导致重复任务概率从0.003%升到0.12%。这数字还在监控里飘着——我还没决定要不要回滚。

(编辑:站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!