初识 scientist gem
最近项目中的一个功能有性能问题,改进方案很简单,就是用缓存代替实时计算,但是缓存 需要及时更新,不能返回错误的结果。这个过程需要用生产环境的数据来检验,但是又不能 破坏现有的功能,正巧前几天读了 Changing Critical Code Paths With Scientist, 发现文中提到的 scientist gem 就是解决这个问题的。
🔬 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 使我能更平静地改进代码,我想这就足够了。