.jpg)
Photo by Alex Kondratiev of Unsplash
最近项目中的一个功能有性能问题,改进方案很简单,就是用缓存代替实时计算,但是缓存需要及时更新,不能返回错误的结果。这个过程需要用生产环境的数据来检验,但是又不能破坏现有的功能,正巧前几天读了 Changing Critical Code Paths With Scientist,发现文中提到的 scientist 就是解决这个问题的。
scientist 项目的描述是:
🔬 A Ruby library for carefully refactoring critical paths.
这个项目的命名很形象,我们开发者就好比是科学家(scientist),要改进现有的方案,一个稳妥地方法是进行对照实验(controlled experiment),把现有方案设为对照组(control),新方案设为实验组(candidate),在同一环境、同一时间进行实验,减少不确定的因素带来的影响。
下面言归正传,让我们看一下如何使用 scientist。
首先,引入 Science,它提供了一些 DSL,使用 science block 可以很方便地定义实验:
class Query include Science def fetch_data science 'fetch-data' do |experiment| experiment.use { ... } # control experiment.try { ... } # candidate 1 experiment.try { ... } # candidate 2 end end end
要使实验开始进行,还需要实现 Scientist::Experiment,我把它放在了项目的 config/initializers/scientist.rb:
require "scientist/experiment" class ScientistExperiment include Scientist::Experiment attr_accessor :name def initialize(name) @name = name end def enabled? true end def raised(operation, error) super end def publish(result) puts "science.#{name}.mismatched" if result.matched? puts result.control.duration puts result.candidates.first.duration puts result.observations.map(&:name) end end ScientistExperiment.raise_on_mismatches = true if Rails.env.test?
- enabled? 返回 true 表示启用实验;
- publish 表示发布实验结果,在这里一般是把结果输出到可以查看的地方,比如日志或错误监控系统;
- ScientistExperiment.raise_on_mismatches = true 表示如果实验结果不一致,就抛出异常,在测试环境中开启这项可以及早发现问题。
设置完以后启动 Rails 服务,就可以实验了。这种方式适用于在生产环境改进算法、性能、重构,不用担心带来破坏性的问题。
scientist 当然也有其局限性,如果实验结果无法比较,或者不能仅通过比较结果来判断方案的可行性,那么它可能就不适用。不过,使用 scientist 使我能更平静地改进代码,我想这就足够了。
- Yejun