drone 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|