Scientist gem 조사

About scientist


과학tistgem은github사가 제작한 테스트 도구(=새로운 논리 실험을 할 수 있는 도구)이다.
Let's pretend you're changing the way you handle permissions in a large web app. Tests can help guide your refactoring, but you really want to compare the current and refactored behaviors under load.
심정으로 삼다
테스트가 팩스를 통과한 결과 식별 동작에 문제가 없을 때 매우 유용하지만 현행과 팩스 후의 행위가 정말 변했는지 비교할 수 없다.
통절히 알다.

왜?


Febrary032016 Scientist 1.0과 함께 발표된 후편
http://githubengineering.com/scientist/
대규모 팩스Branch by Abstraction를 위해서요.단, Abstraction layer를 넣으면 코드 path가 원래와 다르기 때문에 Refactor 후 이전 행동 테스트 방법과는 견고하지 않습니다.
- 예컨대 예상치 못한 곳에서 부르면 abstraction layer는 통과하지 않겠죠?

시험이 왜 불충분한가
- 매우 복잡한 시스템으로는 테스트 용례를 망라할 수 없다.망라를 해보면 테스트량이 많아지고 개발 속도가 떨어져요
- 원래 존재하는 데이터의 질에 문제가 있을 수 있습니다.나는 그런 데이터에 부딪혔을 때의 행동거지를 모른다.
따라서 Production data와 User는 시험이 필요합니다.

특징.


코드 path를 바꾸지 않고 제품 데이터의 행동을 테스트할 수 있습니다.
새로운 기능을 테스트하려면 다음과 같은 기분이 좋습니다.

Getting Started


Sample Code: https://github.com/threetreeslight/rails-lab/tree/scientist
다음 관계에 대한 Rails 애플리케이션에 대해 살펴보겠습니다.
Author 1-* Book
또한 과학자의method는 다음과 같습니다.
class Author < ApplicationRecord
  def luckey_number
    id
  end
end

oldway와 newway의 비교 메커니즘 구축


old way(기존 설치)와 new way(팩스 후 설치)
class Author < ApplicationRecord
  def luckey_number
    experiment = Scientist::Default.new "book.lucky_number"
    experiment.context author: id, name: name

    experiment.use { id } # old way
    experiment.try { rand(10) } # new way

    experiment.run
  end
end
luckey_number는use block과try block을 실행합니다.
이렇게 하면 새로 실시한 수익치와 다르다는 것을 검증할 수 있다.
또한,experiment는useblock의 실행 결과를 되돌려야 합니다.

try 실행 제어 및 게시

Scientist::Defaulttry의 실행 제어와compare 결과를 발송합니다.Scientist::Defaultclass는Scientist::Experiment용Interface의 임시 실현이기 때문에 실제적으로 어떠한 제어와 발송도 하지 않습니다.
https://github.com/github/scientist/blob/master/lib/scientist/default.rb
따라서 전용 experment를 제작한다.
class MyExperiment < Scientist::Default
  def publish(result)
    # Store the timing for the control value,
    StatsD.measure "science.#{name}.control", result.control.duration
    # for the candidate (only the first, see "Breaking the rules" below,
    StatsD.measure "science.#{name}.candidate", result.candidates.first.duration

    # and counts for match/ignore/mismatch:
    if result.matched?
      StatsD.increment "science.#{name}.matched"
    elsif result.ignored?
      StatsD.increment "science.#{name}.ignored"
    else
      StatsD.increment "science.#{name}.mismatched"
      # Finally, store mismatches in redis so they can be retrieved and examined
      # later on, for debugging and research.
      store_mismatch_data(result)
    end
  end

  def store_mismatch_data(result)
    payload = {
      :name            => name,
      :context         => context,
      :control         => observation_payload(result.control),
      :candidate       => observation_payload(result.candidates.first),
      :execution_order => result.observations.map(&:name)
    }

    key = "science.#{name}.mismatch"

    Rails.logger.warn "#{key}: #{payload}"

    REDIS.lpush key, payload
    REDIS.ltrim key, 0, 1000
  end

  def observation_payload(observation)
    if observation.raised?
      {
        :exception => observation.exception.class,
        :message   => observation.exception.message,
        :backtrace => observation.exception.backtrace
      }
    else
      {
        # see "Keeping it clean" below
        :value => observation.cleaned_value
      }
    end
  end
end
여기서 아래의 내용을 측정하고 있다.
  • graphite
  • 에 실행 시간 보내기
  • mismmatch,match의 발생 횟수를graphite
  • 에 발송
  • mismmatch가 발생하면 이 상세한 정보를 Redis에 저장
  • mismmatch가 발생했을 때 이 상세한 정보를log
  • 에 토로합니다

    Redis

    $ vim config/initialize/redis.rb
    
    require 'redis'
    
    REDIS = Redis.new(url: ENV['REDIS_URL'])
    

    metric


    Giithub의 Sample은 메트릭을 그레이트에 넣었다고 한다.
    metric의 인터페이스는 statsd 호환됩니다
    brubeck를 사용한다고 하는데, 이번statsd로 갑니다.
    설정이 번거롭기 때문에graphite와statsd의 설정이 번거롭기 때문에 이 그림을 사용했습니다.
    https://github.com/hopsoft/docker-graphite-statsd
    statusD의 유입에 사용되었다Shopify/statsd-instrument.
    $ vim config/initialize/status_d.rb
    
    require 'statsd-instrument'
    
    # Sets up a UDP backend. First argument is the UDP address to send StatsD packets to,
    # second argument specifies the protocol variant (i.e. `:statsd`, `:statsite`, or `:datadog`).
    StatsD.backend = StatsD::Instrument::Backends::UDPBackend.new(ENV['STATSD_URL'], :statsite)
    
    ref graphite의 장점Macerel의 시간 시퀀스 데이터베이스 기술 지원

    제작된 Experiment의 용도

    class Author < ApplicationRecord
      has_many :books
    
      def luckey_number
    +   experiment = Scientist::Experiment.new "book.lucky_number"
        experiment.context author: id, name: name
    
        experiment.use { id } # old way
        experiment.try { rand(10) } # new way
    
        experiment.run
      end
    end
    
    + module Scientist::Experiment
    +  def self.new(name)
    +     MyExperiment.new(name)
    +   end
    + end
    

    실행

    $ docker-compose up --build -d app
    
    $ docker-compose exec app bash
    > rails c
    > 100.times{ Author.first.luckey_number }
      Author Load (2.5ms)  SELECT  "authors".* FROM "authors" ORDER BY "authors"."id" ASC LIMIT ?  [["LIMIT", 1]]
    {:name=>{:name=>"book.lucky_number"}, :context=>{:author=>1, :name=>"Chuck norris"}, :control=>{:value=>1}, :candidate=>{:value=>6},:execution_order=>["control", "candidate"]}
    => 1
    

    실행 metric

    $ open http://`dinghy ip`:8080/dashboard
    
    노력하면 github 블로그에 이런 게 있어요.

    작은 실패 데이터 on redis

    $ docker-compose exec app bash
    > radis-cli -h redis
    
    redis:6379> KEYS science.*
    1) "science.book.lucky_number.mismatch"
    redis:6379> LRANGE science.book.lucky_number.mismatch 0 -1
    1) "{:name=>\"book.lucky_number\", :context=>{:author=>1, :name=>\"Chuck norris\"}, :control=>{:value=>1}, :candidate=>{:value=>8}, :execution_order=>[\"control\", \"candidate\"]}"
    2) "{:name=>\"book.lucky_number\", :context=>{:author=>1, :name=>\"Chuck norris\"}, :control=>{:value=>1}, :candidate=>{:value=>7}, :execution_order=>[\"candidate\", \"control\"]}"
    3) "{:name=>\"book.lucky_number\", :context=>{:author=>1, :name=>\"Chuck norris\"}, :control=>{:value=>1}, :candidate=>{:value=>0}, :execution_order=>[\"candidate\", \"control\"]}"
    

    오피스


    개인이 생각하는 사용 장소는 다음과 같다.
  • 영향 범위가 너무 넓어서 테스트할 수 없는 곳
  • 많이 변경되어 과거 데이터를 포함한 일관성 유지가 우려됨
  • 나는 정말 이런 것은 정식 공연에서만 짓밟을 수 있다고 생각한다.
    한결
  • 특정 사용자만try 항목을 실행
  • 특정 설정에서try항
  • 을 실행합니다
    등도 새로운 논리의 집행을 제어할 수 있다.사용이 편리하다.

    코드 흐름

    ├── scientist
    │   ├── default.rb
    │   ├── errors.rb
    │   ├── experiment.rb
    │   ├── observation.rb
    │   ├── result.rb
    │   └── version.rb
    └── scientist.rb
    
    라인 수량은 별거 아니에요.
    동작도 매우 간단하다.
  • Scientist::Experimentinterface가 실시한 실험 실행 계획(defaultScientist::Default이라면 작성
  • Scientist::Experiment.run에서 실행하면 비교 결과와 예외 정보 등을 저장Scientist::Result
  • 1에서 작성한 실행 계획에 설정된 정보를 활용하여 Scientist::Result의 정보를 배포
  • 좋은 웹페이지 즐겨찾기