drone 1.0.1 → 1.0.2
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.
- metadata +3 -41
- data/.gitignore +0 -8
- data/.rvmrc +0 -1
- data/.yardopts +0 -1
- data/Gemfile +0 -4
- data/LICENSE +0 -20
- data/README.md +0 -162
- data/Rakefile +0 -20
- data/drone.gemspec +0 -29
- data/examples/simple.rb +0 -50
- data/lib/drone.rb +0 -23
- data/lib/drone/core.rb +0 -141
- data/lib/drone/interfaces/base.rb +0 -17
- data/lib/drone/interfaces/console.rb +0 -82
- data/lib/drone/metrics/counter.rb +0 -40
- data/lib/drone/metrics/gauge.rb +0 -25
- data/lib/drone/metrics/histogram.rb +0 -153
- data/lib/drone/metrics/meter.rb +0 -82
- data/lib/drone/metrics/metric.rb +0 -16
- data/lib/drone/metrics/timer.rb +0 -57
- data/lib/drone/monitoring.rb +0 -107
- data/lib/drone/schedulers/eventmachine.rb +0 -70
- data/lib/drone/storage/base.rb +0 -122
- data/lib/drone/storage/memory.rb +0 -58
- data/lib/drone/utils/ewma.rb +0 -55
- data/lib/drone/utils/exponentially_decaying_sample.rb +0 -81
- data/lib/drone/utils/uniform_sample.rb +0 -52
- data/lib/drone/version.rb +0 -3
- data/specs/all.rb +0 -11
- data/specs/common.rb +0 -63
- data/specs/metrics/counter_spec.rb +0 -43
- data/specs/metrics/gauge_spec.rb +0 -28
- data/specs/metrics/meter_spec.rb +0 -61
- data/specs/metrics/timer_spec.rb +0 -111
- data/specs/schedulers/eventmachine_spec.rb +0 -76
- data/specs/unit/ewma_spec.rb +0 -141
- data/specs/unit/exponentially_decaying_sample_spec.rb +0 -86
- data/specs/unit/histogram_spec.rb +0 -91
- data/specs/unit/monitoring_spec.rb +0 -129
- data/specs/unit/uniform_sample_spec.rb +0 -46
@@ -1,82 +0,0 @@
|
|
1
|
-
require File.expand_path('../base', __FILE__)
|
2
|
-
|
3
|
-
module Drone
|
4
|
-
module Interfaces
|
5
|
-
##
|
6
|
-
# This interface is meant for debug mainly, it will
|
7
|
-
# simply output all the available metrics at a regular
|
8
|
-
# interval on the console.
|
9
|
-
#
|
10
|
-
# @example
|
11
|
-
# require 'drone'
|
12
|
-
# Drone::init_drone()
|
13
|
-
# Drone::add_output(:console, 1)
|
14
|
-
#
|
15
|
-
class Console < Base
|
16
|
-
|
17
|
-
def output()
|
18
|
-
puts ""
|
19
|
-
puts "[#{Time.now.strftime('%M:%S')}] Drone report:"
|
20
|
-
Drone::each_metric do |m|
|
21
|
-
case m
|
22
|
-
when Metrics::Gauge
|
23
|
-
puts "[Gauge] #{m.name} : #{m.value}"
|
24
|
-
|
25
|
-
when Metrics::Counter
|
26
|
-
puts "[Counter] #{m.name} : #{m.value}"
|
27
|
-
|
28
|
-
when Metrics::Timer
|
29
|
-
puts "[Timer] #{m.name}"
|
30
|
-
print_histogram(m)
|
31
|
-
|
32
|
-
when Metrics::Meter
|
33
|
-
puts "[Meter] #{m.name}"
|
34
|
-
print_meter(m)
|
35
|
-
|
36
|
-
when Metrics::Histogram
|
37
|
-
puts "[Histogram] #{m.name}"
|
38
|
-
print_histogram(m)
|
39
|
-
|
40
|
-
else
|
41
|
-
puts "Unknown metric: #{m}"
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
|
47
|
-
private
|
48
|
-
def print_meter(m)
|
49
|
-
puts format("%20s : %d", "count", m.count)
|
50
|
-
|
51
|
-
{
|
52
|
-
'mean rate' => m.mean_rate,
|
53
|
-
'1-minute rate' => m.one_minute_rate,
|
54
|
-
'5-minute rate' => m.five_minutes_rate,
|
55
|
-
'15-minute rate' => m.fifteen_minutes_rate
|
56
|
-
}.each do |label, value|
|
57
|
-
puts format("%20s : %2.2f", label, value)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def print_histogram(m)
|
62
|
-
percentiles = m.percentiles(0.5, 0.75, 0.95, 0.98, 0.99, 0.999)
|
63
|
-
{
|
64
|
-
'min' => m.min,
|
65
|
-
'max' => m.max,
|
66
|
-
'mean' => m.mean,
|
67
|
-
'stddev' => m.stdDev,
|
68
|
-
'median' => percentiles[0],
|
69
|
-
'75%' => percentiles[1],
|
70
|
-
'%95' => percentiles[2],
|
71
|
-
'%98' => percentiles[3],
|
72
|
-
'%99' => percentiles[4],
|
73
|
-
'%99.9' => percentiles[5]
|
74
|
-
}.each do |label, value|
|
75
|
-
puts format("%20s : %2.2f", label, value)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
end
|
80
|
-
|
81
|
-
end
|
82
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
require File.expand_path('../metric', __FILE__)
|
2
|
-
|
3
|
-
module Drone
|
4
|
-
module Metrics
|
5
|
-
|
6
|
-
##
|
7
|
-
# A Counter store a number which can go up or down,
|
8
|
-
# the counter can change a counter value with
|
9
|
-
# the methods increment and decrement aliased
|
10
|
-
# as inc and dec
|
11
|
-
#
|
12
|
-
class Counter < Metric
|
13
|
-
|
14
|
-
def initialize(name, initial_value = 0)
|
15
|
-
super(name)
|
16
|
-
|
17
|
-
@value = Drone::request_number("#{name}:value", initial_value)
|
18
|
-
end
|
19
|
-
|
20
|
-
def value
|
21
|
-
@value.get
|
22
|
-
end
|
23
|
-
|
24
|
-
def increment(n = 1)
|
25
|
-
@value.inc(n)
|
26
|
-
end
|
27
|
-
alias :inc :increment
|
28
|
-
|
29
|
-
def decrement(n = 1)
|
30
|
-
@value.dec(n)
|
31
|
-
end
|
32
|
-
alias :dec :decrement
|
33
|
-
|
34
|
-
def clear
|
35
|
-
@value.set(0)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
end
|
40
|
-
end
|
data/lib/drone/metrics/gauge.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
require File.expand_path('../metric', __FILE__)
|
2
|
-
|
3
|
-
module Drone
|
4
|
-
module Metrics
|
5
|
-
|
6
|
-
##
|
7
|
-
# Gauge are linked to a block of code which
|
8
|
-
# will be called when the value is asked, the block
|
9
|
-
# is expected to return a number
|
10
|
-
#
|
11
|
-
class Gauge < Metric
|
12
|
-
|
13
|
-
def initialize(name, &block)
|
14
|
-
raise "Block expected" unless block
|
15
|
-
super(name)
|
16
|
-
@block = block
|
17
|
-
end
|
18
|
-
|
19
|
-
def value
|
20
|
-
@block.call()
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
end
|
@@ -1,153 +0,0 @@
|
|
1
|
-
require File.expand_path('../../utils/uniform_sample', __FILE__)
|
2
|
-
require File.expand_path('../../utils/exponentially_decaying_sample', __FILE__)
|
3
|
-
require File.expand_path('../metric', __FILE__)
|
4
|
-
|
5
|
-
module Drone
|
6
|
-
|
7
|
-
##
|
8
|
-
# An Histogram store a list of values (1028) and can
|
9
|
-
# compute on demand statistics on those values:
|
10
|
-
# - min/max
|
11
|
-
# - mean
|
12
|
-
# - stddev
|
13
|
-
# - percentiles
|
14
|
-
#
|
15
|
-
class Histogram < Metric
|
16
|
-
MIN = (-(2**63)).freeze
|
17
|
-
MAX = ((2**64) - 1).freeze
|
18
|
-
|
19
|
-
def initialize(name, sample_or_type = :uniform)
|
20
|
-
super(name)
|
21
|
-
|
22
|
-
if sample_or_type.is_a?(Symbol)
|
23
|
-
case sample_or_type
|
24
|
-
when :uniform then @sample = UniformSample.new("#{name}:sample", 1028)
|
25
|
-
when :biased then @sample = ExponentiallyDecayingSample.new("#{name}:sample", 1028, 0.015)
|
26
|
-
else
|
27
|
-
raise ArgumentError, "unknown type: #{sample_or_type}"
|
28
|
-
end
|
29
|
-
else
|
30
|
-
@sample = sample_or_type
|
31
|
-
end
|
32
|
-
|
33
|
-
@count = Drone::request_number("#{name}:count", 0)
|
34
|
-
@_min = Drone::request_number("#{name}:min", MAX)
|
35
|
-
@_max = Drone::request_number("#{name}:max", MIN)
|
36
|
-
@_sum = Drone::request_number("#{name}:max", 0)
|
37
|
-
@varianceM = Drone::request_number("#{name}:varianceM", -1)
|
38
|
-
@varianceS = Drone::request_number("#{name}:varianceS", 0)
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
def clear
|
43
|
-
@sample.clear()
|
44
|
-
@count = 0
|
45
|
-
@_min = MAX
|
46
|
-
@_max = MIN
|
47
|
-
@_sum = 0
|
48
|
-
@varianceM = -1
|
49
|
-
@varianceS = 0
|
50
|
-
end
|
51
|
-
|
52
|
-
def update(val)
|
53
|
-
@count.inc
|
54
|
-
@sample.update(val)
|
55
|
-
set_max(val);
|
56
|
-
set_min(val);
|
57
|
-
@_sum.inc(val)
|
58
|
-
update_variance(val)
|
59
|
-
end
|
60
|
-
|
61
|
-
def count
|
62
|
-
@count.get
|
63
|
-
end
|
64
|
-
|
65
|
-
def max
|
66
|
-
(@count.get > 0) ? @_max.get : 0.0
|
67
|
-
end
|
68
|
-
|
69
|
-
def min
|
70
|
-
(@count.get > 0) ? @_min.get : 0.0
|
71
|
-
end
|
72
|
-
|
73
|
-
def mean
|
74
|
-
(@count.get > 0) ? @_sum.get.to_f / @count.get : 0.0
|
75
|
-
end
|
76
|
-
|
77
|
-
def stdDev
|
78
|
-
(@count.get > 0) ? Math.sqrt( variance() ) : 0.0
|
79
|
-
end
|
80
|
-
|
81
|
-
def percentiles(*percentiles)
|
82
|
-
scores = Array.new(percentiles.size, 0)
|
83
|
-
if @count.get > 0
|
84
|
-
values = @sample.values.sort
|
85
|
-
percentiles.each.with_index do |p, i|
|
86
|
-
pos = p * (values.size + 1)
|
87
|
-
if pos < 1
|
88
|
-
scores[i] = values[0]
|
89
|
-
elsif pos >= values.size
|
90
|
-
scores[i] = values[-1]
|
91
|
-
else
|
92
|
-
lower = values[pos - 1]
|
93
|
-
upper = values[pos]
|
94
|
-
scores[i] = lower + (pos - pos.floor) * (upper - lower)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
scores
|
100
|
-
end
|
101
|
-
|
102
|
-
def values
|
103
|
-
@sample.values
|
104
|
-
end
|
105
|
-
|
106
|
-
private
|
107
|
-
|
108
|
-
def doubleToLongBits(n)
|
109
|
-
[n].pack('D').unpack('q')[0]
|
110
|
-
end
|
111
|
-
|
112
|
-
def longBitsToDouble(n)
|
113
|
-
[n].pack('q').unpack('D')[0]
|
114
|
-
end
|
115
|
-
|
116
|
-
def update_variance(val)
|
117
|
-
if @varianceM.get == -1
|
118
|
-
@varianceM.set( doubleToLongBits(val) )
|
119
|
-
else
|
120
|
-
oldMCas = @varianceM.get
|
121
|
-
oldM = longBitsToDouble(oldMCas)
|
122
|
-
newM = oldM + ((val - oldM) / count())
|
123
|
-
|
124
|
-
oldSCas = @varianceS.get
|
125
|
-
oldS = longBitsToDouble(oldSCas)
|
126
|
-
newS = oldS + ((val - oldM) * (val - newM))
|
127
|
-
|
128
|
-
@varianceM.set( doubleToLongBits(newM) )
|
129
|
-
@varianceS.set( doubleToLongBits(newS) )
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
def variance
|
134
|
-
count = @count.get
|
135
|
-
if count <= 1
|
136
|
-
0.0
|
137
|
-
else
|
138
|
-
longBitsToDouble(@varianceS.get) / (count - 1)
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
def set_max(val)
|
143
|
-
(@_max.get >= val) || @_max.set(val)
|
144
|
-
end
|
145
|
-
|
146
|
-
def set_min(val)
|
147
|
-
(@_min.get <= val) || @_min.set(val)
|
148
|
-
end
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
end
|
153
|
-
end
|
data/lib/drone/metrics/meter.rb
DELETED
@@ -1,82 +0,0 @@
|
|
1
|
-
require 'eventmachine'
|
2
|
-
|
3
|
-
require File.expand_path('../metric', __FILE__)
|
4
|
-
require File.expand_path('../../core', __FILE__)
|
5
|
-
require File.expand_path('../../utils/ewma', __FILE__)
|
6
|
-
|
7
|
-
module Drone
|
8
|
-
module Metrics
|
9
|
-
##
|
10
|
-
# A meter measures mean throughput and one-, five-, and
|
11
|
-
# fifteen-minute exponentially-weighted moving average throughputs.
|
12
|
-
#
|
13
|
-
class Meter < Metric
|
14
|
-
INTERVAL = 5
|
15
|
-
|
16
|
-
def initialize(name)
|
17
|
-
super(name)
|
18
|
-
@start_time = Drone::request_number("#{name}:start_time", Time.now)
|
19
|
-
@next_tick = Drone::request_number("#{name}:next_tick_lock", 1)
|
20
|
-
|
21
|
-
@count = Drone::request_number("#{name}:count", 0)
|
22
|
-
@rates = {
|
23
|
-
1 => EWMA.one_minute_ewma("#{name}:rate1"),
|
24
|
-
5 => EWMA.five_minutes_ewma("#{name}:rate5"),
|
25
|
-
15 => EWMA.fifteen_minutes_ewma("#{name}:rate15")
|
26
|
-
}
|
27
|
-
|
28
|
-
Drone::schedule_periodic(INTERVAL) do
|
29
|
-
Fiber.new{ tick() }.resume
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def tick
|
34
|
-
# init if required
|
35
|
-
@local_next_tick ||= @next_tick.get
|
36
|
-
|
37
|
-
# ensure only one process will trigger the tick
|
38
|
-
if @next_tick.compare_and_set(@local_next_tick, @local_next_tick + 1)
|
39
|
-
@rates.values.each(&:tick)
|
40
|
-
@local_next_tick += 1
|
41
|
-
else
|
42
|
-
# reset the tick counter to give a chance to this
|
43
|
-
# process to trigger the next tick
|
44
|
-
@local_next_tick = @next_tick.get()
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def mark(events = 1)
|
49
|
-
@count.inc(events)
|
50
|
-
@rates.each do |_, r|
|
51
|
-
r.update(events)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def count
|
56
|
-
@count.get
|
57
|
-
end
|
58
|
-
|
59
|
-
def mean_rate
|
60
|
-
count = @count.get
|
61
|
-
if count == 0
|
62
|
-
0.0
|
63
|
-
else
|
64
|
-
count / (Time.now.to_f - @start_time.get.to_f)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def one_minute_rate
|
69
|
-
@rates[1].rate()
|
70
|
-
end
|
71
|
-
|
72
|
-
def five_minutes_rate
|
73
|
-
@rates[5].rate()
|
74
|
-
end
|
75
|
-
|
76
|
-
def fifteen_minutes_rate
|
77
|
-
@rates[15].rate()
|
78
|
-
end
|
79
|
-
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
data/lib/drone/metrics/metric.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
module Drone
|
2
|
-
class Metric
|
3
|
-
##
|
4
|
-
# Every metric must have a name to be referenced by
|
5
|
-
#
|
6
|
-
# @attr_reader [String] name The metric's name
|
7
|
-
# (which is also its id)
|
8
|
-
#
|
9
|
-
attr_reader :name
|
10
|
-
|
11
|
-
def initialize(name)
|
12
|
-
@name = name
|
13
|
-
end
|
14
|
-
|
15
|
-
end
|
16
|
-
end
|
data/lib/drone/metrics/timer.rb
DELETED
@@ -1,57 +0,0 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
require File.expand_path('../histogram', __FILE__)
|
3
|
-
require File.expand_path('../meter', __FILE__)
|
4
|
-
require File.expand_path('../metric', __FILE__)
|
5
|
-
|
6
|
-
module Drone
|
7
|
-
module Metrics
|
8
|
-
##
|
9
|
-
# The timer metric will record the time spent in a given method
|
10
|
-
# or any block of code.
|
11
|
-
#
|
12
|
-
# All the times are in milliseconds.
|
13
|
-
#
|
14
|
-
class Timer < Metric
|
15
|
-
extend Forwardable
|
16
|
-
|
17
|
-
def_delegators :@histogram, :count, :min, :max, :mean, :stdDev, :percentiles, :values
|
18
|
-
|
19
|
-
def initialize(name)
|
20
|
-
super(name)
|
21
|
-
|
22
|
-
@histogram = Histogram.new("#{name}:histogram", :biased)
|
23
|
-
end
|
24
|
-
|
25
|
-
def count
|
26
|
-
@histogram.count
|
27
|
-
end
|
28
|
-
|
29
|
-
def clear
|
30
|
-
@histogram.clear()
|
31
|
-
end
|
32
|
-
|
33
|
-
##
|
34
|
-
# Method used to record a new duration
|
35
|
-
#
|
36
|
-
# @param [Float] duration A duration in milliseconds
|
37
|
-
#
|
38
|
-
def update(duration)
|
39
|
-
if duration >= 0
|
40
|
-
@histogram.update(duration)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
##
|
45
|
-
# time and record the duration of the block
|
46
|
-
# @yield [] The block to time
|
47
|
-
#
|
48
|
-
def time
|
49
|
-
started_at = Time.now.to_f
|
50
|
-
yield()
|
51
|
-
ensure
|
52
|
-
update((Time.now.to_f - started_at.to_f) * 1000)
|
53
|
-
end
|
54
|
-
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|