drone 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +13 -0
 - data/Guardfile +12 -0
 - data/Rakefile +22 -37
 - data/drone.gemspec +2 -6
 - data/examples/collectd.rb +51 -0
 - data/examples/common.rb +24 -0
 - data/examples/json.rb +49 -0
 - data/examples/redis_storage.rb +60 -0
 - data/examples/simple.rb +3 -3
 - data/extensions/drone_collectd/Gemfile +7 -0
 - data/extensions/drone_collectd/LICENSE +20 -0
 - data/extensions/drone_collectd/README.md +24 -0
 - data/extensions/drone_collectd/drone_collectd.gemspec +28 -0
 - data/extensions/drone_collectd/lib/drone_collectd.rb +7 -0
 - data/extensions/drone_collectd/lib/drone_collectd/collectd.rb +97 -0
 - data/extensions/drone_collectd/lib/drone_collectd/parser.rb +86 -0
 - data/extensions/drone_collectd/specs/common.rb +3 -0
 - data/extensions/drone_collectd/specs/unit/parser_spec.rb +49 -0
 - data/extensions/drone_json/Gemfile +6 -0
 - data/extensions/drone_json/LICENSE +20 -0
 - data/extensions/drone_json/README.md +9 -0
 - data/extensions/drone_json/drone_json.gemspec +32 -0
 - data/extensions/drone_json/lib/drone_json.rb +9 -0
 - data/extensions/drone_json/lib/drone_json/json.rb +100 -0
 - data/extensions/drone_json/specs/common.rb +63 -0
 - data/extensions/drone_redis/Gemfile +7 -0
 - data/extensions/drone_redis/drone_redis.gemspec +22 -0
 - data/extensions/drone_redis/lib/drone_redis.rb +8 -0
 - data/extensions/drone_redis/lib/drone_redis/redis.rb +218 -0
 - data/lib/drone.rb +1 -0
 - data/lib/drone/errors.rb +11 -0
 - data/lib/drone/metrics/histogram.rb +7 -6
 - data/lib/drone/metrics/meter.rb +1 -1
 - data/lib/drone/monitoring.rb +2 -2
 - data/lib/drone/storage/memory.rb +1 -0
 - data/lib/drone/utils/exponentially_decaying_sample.rb +79 -24
 - data/lib/drone/version.rb +1 -1
 - data/specs/{unit → metrics}/histogram_spec.rb +5 -1
 - data/specs/metrics/meter_spec.rb +10 -2
 - data/specs/metrics/timer_spec.rb +7 -1
 - data/specs/{unit/monitoring_spec.rb → monitoring_spec.rb} +25 -1
 - data/specs/{unit → utils}/ewma_spec.rb +1 -0
 - data/specs/utils/exponentially_decaying_sample_spec.rb +140 -0
 - data/specs/{unit → utils}/uniform_sample_spec.rb +0 -0
 - metadata +72 -93
 - data/specs/unit/exponentially_decaying_sample_spec.rb +0 -86
 
    
        data/lib/drone.rb
    CHANGED
    
    
    
        data/lib/drone/errors.rb
    ADDED
    
    
| 
         @@ -41,12 +41,13 @@ module Drone 
     | 
|
| 
       41 
41 
     | 
    
         | 
| 
       42 
42 
     | 
    
         
             
                def clear
         
     | 
| 
       43 
43 
     | 
    
         
             
                  @sample.clear()
         
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
                  @ 
     | 
| 
       46 
     | 
    
         
            -
                  @ 
     | 
| 
       47 
     | 
    
         
            -
                  @ 
     | 
| 
       48 
     | 
    
         
            -
                  @ 
     | 
| 
       49 
     | 
    
         
            -
                  @ 
     | 
| 
      
 44 
     | 
    
         
            +
                        
         
     | 
| 
      
 45 
     | 
    
         
            +
                  @count = Drone::request_number("#{name}:count", 0)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  @_min = Drone::request_number("#{name}:min", MAX)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  @_max = Drone::request_number("#{name}:max", MIN)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  @_sum = Drone::request_number("#{name}:max", 0)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  @varianceM = Drone::request_number("#{name}:varianceM", -1)
         
     | 
| 
      
 50 
     | 
    
         
            +
                  @varianceS = Drone::request_number("#{name}:varianceS", 0)
         
     | 
| 
       50 
51 
     | 
    
         
             
                end
         
     | 
| 
       51 
52 
     | 
    
         | 
| 
       52 
53 
     | 
    
         
             
                def update(val)
         
     | 
    
        data/lib/drone/metrics/meter.rb
    CHANGED
    
    | 
         @@ -7,7 +7,7 @@ require File.expand_path('../../utils/ewma', __FILE__) 
     | 
|
| 
       7 
7 
     | 
    
         
             
            module Drone
         
     | 
| 
       8 
8 
     | 
    
         
             
              module Metrics
         
     | 
| 
       9 
9 
     | 
    
         
             
                ##
         
     | 
| 
       10 
     | 
    
         
            -
                #  
     | 
| 
      
 10 
     | 
    
         
            +
                # This meter measures mean throughput and one-, five-, and
         
     | 
| 
       11 
11 
     | 
    
         
             
                # fifteen-minute exponentially-weighted moving average throughputs.
         
     | 
| 
       12 
12 
     | 
    
         
             
                # 
         
     | 
| 
       13 
13 
     | 
    
         
             
                class Meter < Metric
         
     | 
    
        data/lib/drone/monitoring.rb
    CHANGED
    
    | 
         @@ -28,7 +28,7 @@ module Drone 
     | 
|
| 
       28 
28 
     | 
    
         
             
                  def monitor_rate(name)
         
     | 
| 
       29 
29 
     | 
    
         
             
                    meter = Drone::find_metric(name) || Metrics::Meter.new(name)
         
     | 
| 
       30 
30 
     | 
    
         
             
                    unless meter.is_a?(Metrics::Meter)
         
     | 
| 
       31 
     | 
    
         
            -
                      raise 
     | 
| 
      
 31 
     | 
    
         
            +
                      raise AlreadyDefined, "metric #{name} is already defined as #{meter.class}"
         
     | 
| 
       32 
32 
     | 
    
         
             
                    end
         
     | 
| 
       33 
33 
     | 
    
         | 
| 
       34 
34 
     | 
    
         
             
                    Drone::register_metric(meter)
         
     | 
| 
         @@ -47,7 +47,7 @@ module Drone 
     | 
|
| 
       47 
47 
     | 
    
         
             
                  def monitor_time(name)
         
     | 
| 
       48 
48 
     | 
    
         
             
                    timer = Drone::find_metric(name) || Metrics::Timer.new(name)
         
     | 
| 
       49 
49 
     | 
    
         
             
                    unless timer.is_a?(Metrics::Timer)
         
     | 
| 
       50 
     | 
    
         
            -
                      raise 
     | 
| 
      
 50 
     | 
    
         
            +
                      raise AlreadyDefined, "metric #{name} is already defined as #{timer.class}"
         
     | 
| 
       51 
51 
     | 
    
         
             
                    end
         
     | 
| 
       52 
52 
     | 
    
         
             
                    Drone::register_metric(timer)
         
     | 
| 
       53 
53 
     | 
    
         
             
                    @_timer_waiting = timer
         
     | 
    
        data/lib/drone/storage/memory.rb
    CHANGED
    
    
| 
         @@ -1,10 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'flt'
         
     | 
| 
       1 
2 
     | 
    
         
             
            require File.expand_path('../../core', __FILE__)
         
     | 
| 
       2 
3 
     | 
    
         | 
| 
       3 
4 
     | 
    
         
             
            module Drone
         
     | 
| 
       4 
     | 
    
         
            -
              class ExponentiallyDecayingSample
         
     | 
| 
       5 
     | 
    
         
            -
                # 1 hour in ms
         
     | 
| 
       6 
     | 
    
         
            -
                RESCALE_THRESHOLD = (1 * 60 * 60 * 1000).freeze
         
     | 
| 
       7 
5 
     | 
    
         | 
| 
      
 6 
     | 
    
         
            +
              ##
         
     | 
| 
      
 7 
     | 
    
         
            +
              # An exponentially-decaying random sample
         
     | 
| 
      
 8 
     | 
    
         
            +
              # 
         
     | 
| 
      
 9 
     | 
    
         
            +
              class ExponentiallyDecayingSample
         
     | 
| 
      
 10 
     | 
    
         
            +
                # 1 hour
         
     | 
| 
      
 11 
     | 
    
         
            +
                RESCALE_THRESHOLD = (1 * 60 * 60).freeze
         
     | 
| 
      
 12 
     | 
    
         
            +
                
         
     | 
| 
      
 13 
     | 
    
         
            +
                ##
         
     | 
| 
      
 14 
     | 
    
         
            +
                # Create a new dataset, if the decay factor is too big
         
     | 
| 
      
 15 
     | 
    
         
            +
                # the flt ruby library will be used for internal computations
         
     | 
| 
      
 16 
     | 
    
         
            +
                # to allow greater precision, the switch happens if alpha is
         
     | 
| 
      
 17 
     | 
    
         
            +
                # higher than 0.1.
         
     | 
| 
      
 18 
     | 
    
         
            +
                # 
         
     | 
| 
      
 19 
     | 
    
         
            +
                # @param [String] id A unique id representing this
         
     | 
| 
      
 20 
     | 
    
         
            +
                #   dataset.
         
     | 
| 
      
 21 
     | 
    
         
            +
                # @param [Integer] reservoir_size the number of samples
         
     | 
| 
      
 22 
     | 
    
         
            +
                #   to keep.
         
     | 
| 
      
 23 
     | 
    
         
            +
                # @param [Number] alpha the decay factor, the higher this
         
     | 
| 
      
 24 
     | 
    
         
            +
                #   number, the more biased the sample will be towards
         
     | 
| 
      
 25 
     | 
    
         
            +
                #   newer values.
         
     | 
| 
      
 26 
     | 
    
         
            +
                # 
         
     | 
| 
       8 
27 
     | 
    
         
             
                def initialize(id, reservoir_size, alpha)
         
     | 
| 
       9 
28 
     | 
    
         
             
                  @id = id
         
     | 
| 
       10 
29 
     | 
    
         
             
                  @values = Drone::request_hash("#{@id}:values")
         
     | 
| 
         @@ -28,25 +47,28 @@ module Drone 
     | 
|
| 
       28 
47 
     | 
    
         
             
                  (@values.size < count) ? @values.size : count
         
     | 
| 
       29 
48 
     | 
    
         
             
                end
         
     | 
| 
       30 
49 
     | 
    
         | 
| 
       31 
     | 
    
         
            -
              
         
     | 
| 
       32 
50 
     | 
    
         
             
                def update(val, time = current_time)
         
     | 
| 
       33 
     | 
    
         
            -
                  priority = weight(time - @start_time.get) /  
     | 
| 
       34 
     | 
    
         
            -
                   
     | 
| 
       35 
     | 
    
         
            -
                   
     | 
| 
      
 51 
     | 
    
         
            +
                  priority = weight(time - @start_time.get) / generate_random()
         
     | 
| 
      
 52 
     | 
    
         
            +
                  new_count = @count.inc
         
     | 
| 
      
 53 
     | 
    
         
            +
                  
         
     | 
| 
      
 54 
     | 
    
         
            +
                  if new_count <= @reservoir_size
         
     | 
| 
       36 
55 
     | 
    
         
             
                    @values[priority] = val
         
     | 
| 
       37 
56 
     | 
    
         
             
                  else
         
     | 
| 
       38 
     | 
    
         
            -
                    first = @values.keys 
     | 
| 
      
 57 
     | 
    
         
            +
                    first = @values.keys[0]
         
     | 
| 
       39 
58 
     | 
    
         
             
                    if first < priority
         
     | 
| 
       40 
     | 
    
         
            -
                      @values[priority] = val
         
     | 
| 
       41 
     | 
    
         
            -
                       
     | 
| 
       42 
     | 
    
         
            -
                         
     | 
| 
      
 59 
     | 
    
         
            +
                      old_val, @values[priority] = @values[priority], val
         
     | 
| 
      
 60 
     | 
    
         
            +
                      unless old_val
         
     | 
| 
      
 61 
     | 
    
         
            +
                        while @values.delete(first) == nil
         
     | 
| 
      
 62 
     | 
    
         
            +
                          first = @values.keys[0]
         
     | 
| 
      
 63 
     | 
    
         
            +
                        end
         
     | 
| 
       43 
64 
     | 
    
         
             
                      end
         
     | 
| 
       44 
65 
     | 
    
         
             
                    end
         
     | 
| 
       45 
66 
     | 
    
         
             
                  end
         
     | 
| 
       46 
67 
     | 
    
         | 
| 
       47 
68 
     | 
    
         
             
                  now = current_time()
         
     | 
| 
       48 
     | 
    
         
            -
                   
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
      
 69 
     | 
    
         
            +
                  next_scale = @next_scale_time.get
         
     | 
| 
      
 70 
     | 
    
         
            +
                  if now >= next_scale
         
     | 
| 
      
 71 
     | 
    
         
            +
                    rescale(now, next_scale)
         
     | 
| 
       50 
72 
     | 
    
         
             
                  end
         
     | 
| 
       51 
73 
     | 
    
         
             
                end
         
     | 
| 
       52 
74 
     | 
    
         | 
| 
         @@ -55,26 +77,59 @@ module Drone 
     | 
|
| 
       55 
77 
     | 
    
         
             
                    buff << @values[key]
         
     | 
| 
       56 
78 
     | 
    
         
             
                  end
         
     | 
| 
       57 
79 
     | 
    
         
             
                end
         
     | 
| 
       58 
     | 
    
         
            -
              
         
     | 
| 
       59 
     | 
    
         
            -
                def rescale(now)
         
     | 
| 
       60 
     | 
    
         
            -
                  @next_scale_time.set( current_time() + RESCALE_THRESHOLD )
         
     | 
| 
       61 
     | 
    
         
            -
                  new_start = current_time()
         
     | 
| 
       62 
     | 
    
         
            -
                  old_start = @start_time.get_and_set(new_start)
         
     | 
| 
       63 
     | 
    
         
            -
                
         
     | 
| 
       64 
     | 
    
         
            -
                  @values = Hash[ @values.map{ |k,v|
         
     | 
| 
       65 
     | 
    
         
            -
                      [k * Math.exp(-@alpha * (new_start - old_start)), v]
         
     | 
| 
       66 
     | 
    
         
            -
                    }]
         
     | 
| 
       67 
80 
     | 
    
         | 
| 
      
 81 
     | 
    
         
            +
                def rescale(now, next_scale)
         
     | 
| 
      
 82 
     | 
    
         
            +
                  if @next_scale_time.compare_and_set(next_scale, now + RESCALE_THRESHOLD)
         
     | 
| 
      
 83 
     | 
    
         
            +
                    new_start = current_time()
         
     | 
| 
      
 84 
     | 
    
         
            +
                    old_start = @start_time.get_and_set( new_start )
         
     | 
| 
      
 85 
     | 
    
         
            +
                    time_diff = new_start - old_start
         
     | 
| 
      
 86 
     | 
    
         
            +
                    
         
     | 
| 
      
 87 
     | 
    
         
            +
                    @values = Hash[ @values.map{ |k,v|
         
     | 
| 
      
 88 
     | 
    
         
            +
                        [k * math_exp(-@alpha * time_diff), v]
         
     | 
| 
      
 89 
     | 
    
         
            +
                      }]
         
     | 
| 
      
 90 
     | 
    
         
            +
                    
         
     | 
| 
      
 91 
     | 
    
         
            +
                  end
         
     | 
| 
       68 
92 
     | 
    
         
             
                end
         
     | 
| 
       69 
93 
     | 
    
         | 
| 
       70 
94 
     | 
    
         
             
              private
         
     | 
| 
      
 95 
     | 
    
         
            +
                
         
     | 
| 
      
 96 
     | 
    
         
            +
                def use_flt?
         
     | 
| 
      
 97 
     | 
    
         
            +
                  @alpha > 0.1
         
     | 
| 
      
 98 
     | 
    
         
            +
                end
         
     | 
| 
      
 99 
     | 
    
         
            +
                
         
     | 
| 
      
 100 
     | 
    
         
            +
                def math_exp(n)
         
     | 
| 
      
 101 
     | 
    
         
            +
                  if use_flt?
         
     | 
| 
      
 102 
     | 
    
         
            +
                    Flt::DecNum(Rational(n)).exp()
         
     | 
| 
      
 103 
     | 
    
         
            +
                  else
         
     | 
| 
      
 104 
     | 
    
         
            +
                    Math.exp(n)
         
     | 
| 
      
 105 
     | 
    
         
            +
                  end
         
     | 
| 
      
 106 
     | 
    
         
            +
                end
         
     | 
| 
      
 107 
     | 
    
         
            +
                
         
     | 
| 
      
 108 
     | 
    
         
            +
                ##
         
     | 
| 
      
 109 
     | 
    
         
            +
                # Generates a non-zero random number
         
     | 
| 
      
 110 
     | 
    
         
            +
                # According to the ruby documentation rand() could return 0
         
     | 
| 
      
 111 
     | 
    
         
            +
                # so we ensure this will never happen
         
     | 
| 
      
 112 
     | 
    
         
            +
                # 
         
     | 
| 
      
 113 
     | 
    
         
            +
                # @return [Float] The random number
         
     | 
| 
      
 114 
     | 
    
         
            +
                # 
         
     | 
| 
      
 115 
     | 
    
         
            +
                def generate_random()
         
     | 
| 
      
 116 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 117 
     | 
    
         
            +
                    r = Kernel.rand()
         
     | 
| 
      
 118 
     | 
    
         
            +
                  end while r == 0.0
         
     | 
| 
      
 119 
     | 
    
         
            +
                  
         
     | 
| 
      
 120 
     | 
    
         
            +
                  if use_flt?
         
     | 
| 
      
 121 
     | 
    
         
            +
                    Flt::DecNum(Rational(r))
         
     | 
| 
      
 122 
     | 
    
         
            +
                  else
         
     | 
| 
      
 123 
     | 
    
         
            +
                    r
         
     | 
| 
      
 124 
     | 
    
         
            +
                  end
         
     | 
| 
      
 125 
     | 
    
         
            +
                end
         
     | 
| 
       71 
126 
     | 
    
         | 
| 
       72 
127 
     | 
    
         
             
                def current_time
         
     | 
| 
       73 
     | 
    
         
            -
                  Time.now.to_f 
     | 
| 
      
 128 
     | 
    
         
            +
                  Time.now.to_f
         
     | 
| 
       74 
129 
     | 
    
         
             
                end
         
     | 
| 
       75 
130 
     | 
    
         | 
| 
       76 
131 
     | 
    
         
             
                def weight(n)
         
     | 
| 
       77 
     | 
    
         
            -
                   
     | 
| 
      
 132 
     | 
    
         
            +
                  math_exp(@alpha * n)
         
     | 
| 
       78 
133 
     | 
    
         
             
                end
         
     | 
| 
       79 
134 
     | 
    
         | 
| 
       80 
135 
     | 
    
         
             
              end
         
     | 
    
        data/lib/drone/version.rb
    CHANGED
    
    
| 
         @@ -9,7 +9,7 @@ describe 'Histogram' do 
     | 
|
| 
       9 
9 
     | 
    
         
             
                Drone::init_drone()
         
     | 
| 
       10 
10 
     | 
    
         
             
              end
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
              describe "A histogram with zero recorded  
     | 
| 
      
 12 
     | 
    
         
            +
              describe "A histogram with zero recorded values" do
         
     | 
| 
       13 
13 
     | 
    
         
             
                before do
         
     | 
| 
       14 
14 
     | 
    
         
             
                  @histogram = Histogram.new("id1", UniformSample.new("id1:sample", 100))
         
     | 
| 
       15 
15 
     | 
    
         
             
                end
         
     | 
| 
         @@ -17,6 +17,10 @@ describe 'Histogram' do 
     | 
|
| 
       17 
17 
     | 
    
         
             
                should "have a count of 0" do
         
     | 
| 
       18 
18 
     | 
    
         
             
                  @histogram.count.should == 0
         
     | 
| 
       19 
19 
     | 
    
         
             
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
                
         
     | 
| 
      
 21 
     | 
    
         
            +
                should "have a variance of 0" do
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @histogram.send(:variance).should == 0
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
       20 
24 
     | 
    
         | 
| 
       21 
25 
     | 
    
         
             
                should "have a max of 0" do
         
     | 
| 
       22 
26 
     | 
    
         
             
                  @histogram.max.should == 0
         
     | 
    
        data/specs/metrics/meter_spec.rb
    CHANGED
    
    | 
         @@ -48,14 +48,22 @@ EM.describe 'Meter Metrics' do 
     | 
|
| 
       48 
48 
     | 
    
         | 
| 
       49 
49 
     | 
    
         
             
              describe "A meter metric with three events" do
         
     | 
| 
       50 
50 
     | 
    
         
             
                before do
         
     | 
| 
       51 
     | 
    
         
            -
                   
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
      
 51 
     | 
    
         
            +
                  Delorean.time_travel_to("2 second ago") do
         
     | 
| 
      
 52 
     | 
    
         
            +
                    @meter = Metrics::Meter.new("thangs")
         
     | 
| 
      
 53 
     | 
    
         
            +
                    @meter.mark(3)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
       53 
55 
     | 
    
         
             
                end
         
     | 
| 
       54 
56 
     | 
    
         | 
| 
       55 
57 
     | 
    
         
             
                should "have a count of three" do
         
     | 
| 
       56 
58 
     | 
    
         
             
                  @meter.count.should == 3
         
     | 
| 
       57 
59 
     | 
    
         
             
                  done
         
     | 
| 
       58 
60 
     | 
    
         
             
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
                
         
     | 
| 
      
 62 
     | 
    
         
            +
                should "have a mean rate of 0 events/sec" do
         
     | 
| 
      
 63 
     | 
    
         
            +
                  @meter.mean_rate.should.be.close?(1.5, 0.01)
         
     | 
| 
      
 64 
     | 
    
         
            +
                  done
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
                
         
     | 
| 
       59 
67 
     | 
    
         
             
              end
         
     | 
| 
       60 
68 
     | 
    
         | 
| 
       61 
69 
     | 
    
         
             
            end
         
     | 
    
        data/specs/metrics/timer_spec.rb
    CHANGED
    
    | 
         @@ -9,7 +9,7 @@ EM.describe 'Timer Metrics' do 
     | 
|
| 
       9 
9 
     | 
    
         
             
                Drone::start_monitoring()
         
     | 
| 
       10 
10 
     | 
    
         
             
              end
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
              describe "A  
     | 
| 
      
 12 
     | 
    
         
            +
              describe "A newly created timer" do
         
     | 
| 
       13 
13 
     | 
    
         
             
                before do
         
     | 
| 
       14 
14 
     | 
    
         
             
                  @timer = Metrics::Timer.new('id')
         
     | 
| 
       15 
15 
     | 
    
         
             
                end
         
     | 
| 
         @@ -91,6 +91,12 @@ EM.describe 'Timer Metrics' do 
     | 
|
| 
       91 
91 
     | 
    
         
             
                  @timer.stdDev.should.be.close?(11.401, 0.001)
         
     | 
| 
       92 
92 
     | 
    
         
             
                  done
         
     | 
| 
       93 
93 
     | 
    
         
             
                end
         
     | 
| 
      
 94 
     | 
    
         
            +
                
         
     | 
| 
      
 95 
     | 
    
         
            +
                it 'can be cleared' do
         
     | 
| 
      
 96 
     | 
    
         
            +
                  @timer.clear()
         
     | 
| 
      
 97 
     | 
    
         
            +
                  @timer.count.should == 0
         
     | 
| 
      
 98 
     | 
    
         
            +
                  done
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
       94 
100 
     | 
    
         | 
| 
       95 
101 
     | 
    
         
             
                should "calculate the median/p95/p98/p99/p999" do
         
     | 
| 
       96 
102 
     | 
    
         
             
                  median, p95, p98, p99, p999 = @timer.percentiles(0.5, 0.95, 0.98, 0.99, 0.999)
         
     | 
| 
         @@ -1,4 +1,4 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require File.expand_path(' 
     | 
| 
      
 1 
     | 
    
         
            +
            require File.expand_path('../common', __FILE__)
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            require 'drone'
         
     | 
| 
       4 
4 
     | 
    
         
             
            require 'drone/monitoring'
         
     | 
| 
         @@ -21,11 +21,35 @@ EM.describe 'Monitoring' do 
     | 
|
| 
       21 
21 
     | 
    
         
             
                    monitor_rate("users/with_block")
         
     | 
| 
       22 
22 
     | 
    
         
             
                    def method_with_block(&block); block.call; end
         
     | 
| 
       23 
23 
     | 
    
         | 
| 
      
 24 
     | 
    
         
            +
                    monitor_time("users/timed")
         
     | 
| 
      
 25 
     | 
    
         
            +
                    def timed_method; end;
         
     | 
| 
      
 26 
     | 
    
         
            +
                    
         
     | 
| 
       24 
27 
     | 
    
         
             
                  end
         
     | 
| 
       25 
28 
     | 
    
         
             
                  @obj = @klass.new
         
     | 
| 
       26 
29 
     | 
    
         | 
| 
       27 
30 
     | 
    
         
             
                end
         
     | 
| 
       28 
31 
     | 
    
         | 
| 
      
 32 
     | 
    
         
            +
                should "raise an error if the metric name os already used (rate => time)" do
         
     | 
| 
      
 33 
     | 
    
         
            +
                  proc{
         
     | 
| 
      
 34 
     | 
    
         
            +
                    @klass.instance_eval do
         
     | 
| 
      
 35 
     | 
    
         
            +
                      monitor_time("users/no_args")
         
     | 
| 
      
 36 
     | 
    
         
            +
                    end
         
     | 
| 
      
 37 
     | 
    
         
            +
                  }.should.raise(Drone::AlreadyDefined)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  
         
     | 
| 
      
 39 
     | 
    
         
            +
                  done
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
                
         
     | 
| 
      
 42 
     | 
    
         
            +
                should "raise an error if the metric name os already used (time => rate)" do
         
     | 
| 
      
 43 
     | 
    
         
            +
                  
         
     | 
| 
      
 44 
     | 
    
         
            +
                  proc{
         
     | 
| 
      
 45 
     | 
    
         
            +
                    @klass.instance_eval do
         
     | 
| 
      
 46 
     | 
    
         
            +
                      monitor_rate("users/timed")
         
     | 
| 
      
 47 
     | 
    
         
            +
                    end
         
     | 
| 
      
 48 
     | 
    
         
            +
                  }.should.raise(Drone::AlreadyDefined)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  
         
     | 
| 
      
 50 
     | 
    
         
            +
                  done
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
                
         
     | 
| 
       29 
53 
     | 
    
         
             
                should 'reuse same meter for every instances of this class' do
         
     | 
| 
       30 
54 
     | 
    
         
             
                  meter = Drone::find_metric("users/no_args")
         
     | 
| 
       31 
55 
     | 
    
         
             
                  meter.count.should == 0
         
     | 
| 
         @@ -0,0 +1,140 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.expand_path('../../common', __FILE__)
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'drone/utils/exponentially_decaying_sample'
         
     | 
| 
      
 4 
     | 
    
         
            +
            include Drone
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            describe 'Exponentially Decaying Sample' do
         
     | 
| 
      
 7 
     | 
    
         
            +
              before do
         
     | 
| 
      
 8 
     | 
    
         
            +
                Drone::init_drone(nil, Storage::Memory.new)
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
              
         
     | 
| 
      
 11 
     | 
    
         
            +
              describe "A sample of 100 out of 1000 elements" do
         
     | 
| 
      
 12 
     | 
    
         
            +
                before do
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @population = (0...100)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @sample = ExponentiallyDecayingSample.new('id1', 1000, 0.99)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @population.step(1){|n| @sample.update(n) }
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
                
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                should "have 100 elements" do
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @sample.size.should == 100
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @sample.values.size.should == 100
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                should "only have elements from the population" do
         
     | 
| 
      
 25 
     | 
    
         
            +
                  arr = @sample.values - @population.to_a
         
     | 
| 
      
 26 
     | 
    
         
            +
                  arr.should == []
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
                
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
              
         
     | 
| 
      
 31 
     | 
    
         
            +
              
         
     | 
| 
      
 32 
     | 
    
         
            +
              describe "A sample of 100 out of 10 elements" do
         
     | 
| 
      
 33 
     | 
    
         
            +
                before do
         
     | 
| 
      
 34 
     | 
    
         
            +
                  @population = (0...100)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  @sample = ExponentiallyDecayingSample.new('id1', 100, 0.99)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  @population.step(1){|n| @sample.update(n) }
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                should "have 100 elements" do
         
     | 
| 
      
 40 
     | 
    
         
            +
                  @sample.size.should == 100
         
     | 
| 
      
 41 
     | 
    
         
            +
                  @sample.values.size.should == 100
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                should "only have elements from the population" do
         
     | 
| 
      
 45 
     | 
    
         
            +
                  arr = @sample.values - @population.to_a
         
     | 
| 
      
 46 
     | 
    
         
            +
                  arr.should == []
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
                
         
     | 
| 
      
 49 
     | 
    
         
            +
                should "rescale after 1 hour2" do
         
     | 
| 
      
 50 
     | 
    
         
            +
                  Delorean.time_travel_to("1 hours from now") do
         
     | 
| 
      
 51 
     | 
    
         
            +
                    @sample.update(42)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
                  
         
     | 
| 
      
 54 
     | 
    
         
            +
                  @sample.size.should == 100
         
     | 
| 
      
 55 
     | 
    
         
            +
                  @sample.values.size.should == 100
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
                
         
     | 
| 
      
 58 
     | 
    
         
            +
              end
         
     | 
| 
      
 59 
     | 
    
         
            +
              
         
     | 
| 
      
 60 
     | 
    
         
            +
              
         
     | 
| 
      
 61 
     | 
    
         
            +
              describe "A heavily-biased sample of 100 out of 1000 elements" do
         
     | 
| 
      
 62 
     | 
    
         
            +
                before do
         
     | 
| 
      
 63 
     | 
    
         
            +
                  @population = (0...100)
         
     | 
| 
      
 64 
     | 
    
         
            +
                  @sample = ExponentiallyDecayingSample.new('id1', 100, 0.01)
         
     | 
| 
      
 65 
     | 
    
         
            +
                  @population.step(1){|n| @sample.update(n) }
         
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                should "have 100 elements" do
         
     | 
| 
      
 69 
     | 
    
         
            +
                  @sample.size.should == 100
         
     | 
| 
      
 70 
     | 
    
         
            +
                  @sample.values.size.should == 100
         
     | 
| 
      
 71 
     | 
    
         
            +
                end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                should "only have elements from the population" do
         
     | 
| 
      
 74 
     | 
    
         
            +
                  arr = @sample.values - @population.to_a
         
     | 
| 
      
 75 
     | 
    
         
            +
                  arr.should == []
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
                
         
     | 
| 
      
 78 
     | 
    
         
            +
                should "rescale after 1 hour" do
         
     | 
| 
      
 79 
     | 
    
         
            +
                  Delorean.time_travel_to("1 hours from now") do
         
     | 
| 
      
 80 
     | 
    
         
            +
                    @sample.update(42)
         
     | 
| 
      
 81 
     | 
    
         
            +
                  end
         
     | 
| 
      
 82 
     | 
    
         
            +
                  
         
     | 
| 
      
 83 
     | 
    
         
            +
                  @sample.size.should == 100
         
     | 
| 
      
 84 
     | 
    
         
            +
                  @sample.values.size.should == 100
         
     | 
| 
      
 85 
     | 
    
         
            +
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
                
         
     | 
| 
      
 87 
     | 
    
         
            +
              end
         
     | 
| 
      
 88 
     | 
    
         
            +
              
         
     | 
| 
      
 89 
     | 
    
         
            +
              describe "A heavily-biased sample of 1000 out of 1000 elements" do
         
     | 
| 
      
 90 
     | 
    
         
            +
                before do
         
     | 
| 
      
 91 
     | 
    
         
            +
                  @population = (0...1000)
         
     | 
| 
      
 92 
     | 
    
         
            +
                  @sample =  ExponentiallyDecayingSample.new('id1', 1000, 0.01)
         
     | 
| 
      
 93 
     | 
    
         
            +
                  @population.step(1){|n| @sample.update(n) }
         
     | 
| 
      
 94 
     | 
    
         
            +
                end
         
     | 
| 
      
 95 
     | 
    
         
            +
                
         
     | 
| 
      
 96 
     | 
    
         
            +
                it "should have 1000 elements" do
         
     | 
| 
      
 97 
     | 
    
         
            +
                  @sample.size.should == 1000
         
     | 
| 
      
 98 
     | 
    
         
            +
                  @sample.values.length.should == 1000
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                it "should only have elements from the population" do
         
     | 
| 
      
 102 
     | 
    
         
            +
                  values = @sample.values
         
     | 
| 
      
 103 
     | 
    
         
            +
                  @population.each do |datum|
         
     | 
| 
      
 104 
     | 
    
         
            +
                    values.should.include?(datum)
         
     | 
| 
      
 105 
     | 
    
         
            +
                  end
         
     | 
| 
      
 106 
     | 
    
         
            +
                end
         
     | 
| 
      
 107 
     | 
    
         
            +
                
         
     | 
| 
      
 108 
     | 
    
         
            +
                it "should replace an element when updating" do
         
     | 
| 
      
 109 
     | 
    
         
            +
                  Delorean.time_travel_to("10 minutes from now") do
         
     | 
| 
      
 110 
     | 
    
         
            +
                    @sample.update(4242)
         
     | 
| 
      
 111 
     | 
    
         
            +
                    @sample.size.should == 1000
         
     | 
| 
      
 112 
     | 
    
         
            +
                    @sample.values.should.include?(4242)
         
     | 
| 
      
 113 
     | 
    
         
            +
                  end
         
     | 
| 
      
 114 
     | 
    
         
            +
                end
         
     | 
| 
      
 115 
     | 
    
         
            +
                
         
     | 
| 
      
 116 
     | 
    
         
            +
                it "should rescale so that newer events are higher in priority in the hash" do 
         
     | 
| 
      
 117 
     | 
    
         
            +
                  Delorean.time_travel_to("1 hour from now") do
         
     | 
| 
      
 118 
     | 
    
         
            +
                    @sample.update(2121)
         
     | 
| 
      
 119 
     | 
    
         
            +
                    @sample.size.should == 1000
         
     | 
| 
      
 120 
     | 
    
         
            +
                  end
         
     | 
| 
      
 121 
     | 
    
         
            +
                  
         
     | 
| 
      
 122 
     | 
    
         
            +
                  Delorean.time_travel_to("2 hours from now") do
         
     | 
| 
      
 123 
     | 
    
         
            +
                    @sample.update(4242)
         
     | 
| 
      
 124 
     | 
    
         
            +
                    @sample.size.should == 1000
         
     | 
| 
      
 125 
     | 
    
         
            +
                    
         
     | 
| 
      
 126 
     | 
    
         
            +
                    values = @sample.values
         
     | 
| 
      
 127 
     | 
    
         
            +
                    
         
     | 
| 
      
 128 
     | 
    
         
            +
                    values.length.should == 1000
         
     | 
| 
      
 129 
     | 
    
         
            +
                    values.should.include?(4242)
         
     | 
| 
      
 130 
     | 
    
         
            +
                    values.should.include?(2121)
         
     | 
| 
      
 131 
     | 
    
         
            +
                    
         
     | 
| 
      
 132 
     | 
    
         
            +
                    # Most recently added values in time should be at the end with the highest priority
         
     | 
| 
      
 133 
     | 
    
         
            +
                    values[999].should == 4242
         
     | 
| 
      
 134 
     | 
    
         
            +
                    values[998].should == 2121
         
     | 
| 
      
 135 
     | 
    
         
            +
                  end
         
     | 
| 
      
 136 
     | 
    
         
            +
                  
         
     | 
| 
      
 137 
     | 
    
         
            +
                end
         
     | 
| 
      
 138 
     | 
    
         
            +
              end
         
     | 
| 
      
 139 
     | 
    
         
            +
              
         
     | 
| 
      
 140 
     | 
    
         
            +
            end
         
     |