drone 1.0.2 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,141 @@
1
+ require File.expand_path('../../common', __FILE__)
2
+
3
+ require 'drone/utils/ewma'
4
+
5
+ describe 'EWMA' do
6
+ before do
7
+ Drone::init_drone
8
+ end
9
+
10
+ describe 'A 1min EWMA with a value of 3' do
11
+ before do
12
+ @ewma = Drone::EWMA.one_minute_ewma('id2')
13
+ @ewma.update(3)
14
+ @ewma.tick()
15
+ end
16
+
17
+
18
+ def mark_minutes(minutes)
19
+ 1.upto( (minutes*60.0) / 5 ) do
20
+ @ewma.tick()
21
+ end
22
+ end
23
+
24
+ should "have a rate of 0.6 events/sec after the first tick" do
25
+ @ewma.rate.should.be.close(0.6, 0.000001)
26
+ end
27
+
28
+ {
29
+ 1 => 0.22072766,
30
+ 2 => 0.08120117,
31
+ 3 => 0.02987224,
32
+ 4 => 0.01098938,
33
+ 5 => 0.00404277,
34
+ 6 => 0.00148725,
35
+ 7 => 0.00054713,
36
+ 8 => 0.00020128,
37
+ 9 => 0.00007405,
38
+ 10 => 0.00002724,
39
+ 11 => 0.00001002,
40
+ 12 => 0.00000369,
41
+ 13 => 0.00000136,
42
+ 14 => 0.00000050,
43
+ 15 => 0.00000018
44
+
45
+ }.each do |minutes, expected|
46
+ should "have a rate of #{expected} events/sec after #{minutes} minute(s)" do
47
+ mark_minutes(minutes)
48
+ @ewma.rate.should.be.close(expected, 0.00000001)
49
+ end
50
+ end
51
+
52
+ end
53
+
54
+
55
+
56
+ describe 'A 5min EWMA with a value of 3' do
57
+ before do
58
+ @ewma = Drone::EWMA.five_minutes_ewma('id1')
59
+ @ewma.update(3)
60
+ @ewma.tick()
61
+ end
62
+
63
+ should "have a rate of 0.6 events/sec after the first tick" do
64
+ @ewma.rate.should.be.close(0.6, 0.000001)
65
+ end
66
+
67
+ def mark_minutes(minutes)
68
+ 1.upto( (minutes*60.0) / 5 ) do
69
+ @ewma.tick()
70
+ end
71
+ end
72
+
73
+ {
74
+ 1 => 0.49123845,
75
+ 2 => 0.40219203,
76
+ 3 => 0.32928698,
77
+ 4 => 0.26959738,
78
+ 5 => 0.22072766,
79
+ 6 => 0.18071653,
80
+ 7 => 0.14795818,
81
+ 8 => 0.12113791,
82
+ 9 => 0.09917933,
83
+ 10 => 0.08120117,
84
+ 11 => 0.06648190,
85
+ 12 => 0.05443077,
86
+ 13 => 0.04456415,
87
+ 14 => 0.03648604,
88
+ 15 => 0.02987224
89
+ }.each do |minutes, expected|
90
+ should "have a rate of #{expected} events/sec after #{minutes} minute(s)" do
91
+ mark_minutes(minutes)
92
+ @ewma.rate.should.be.close(expected, 0.00000001)
93
+ end
94
+ end
95
+
96
+ end
97
+
98
+
99
+ describe 'A 15min EWMA with a value of 3' do
100
+ before do
101
+ @ewma = Drone::EWMA.fifteen_minutes_ewma('id')
102
+ @ewma.update(3)
103
+ @ewma.tick()
104
+ end
105
+
106
+ should "have a rate of 0.6 events/sec after the first tick" do
107
+ @ewma.rate.should.be.close(0.6, 0.000001)
108
+ end
109
+
110
+ def mark_minutes(minutes)
111
+ 1.upto( (minutes*60.0) / 5 ) do
112
+ @ewma.tick()
113
+ end
114
+ end
115
+
116
+ {
117
+ 1 => 0.56130419,
118
+ 2 => 0.52510399,
119
+ 3 => 0.49123845,
120
+ 4 => 0.45955700,
121
+ 5 => 0.42991879,
122
+ 6 => 0.40219203,
123
+ 7 => 0.37625345,
124
+ 8 => 0.35198773,
125
+ 9 => 0.32928698,
126
+ 10 => 0.30805027,
127
+ 11 => 0.28818318,
128
+ 12 => 0.26959738,
129
+ 13 => 0.25221023,
130
+ 14 => 0.23594443,
131
+ 15 => 0.22072766
132
+ }.each do |minutes, expected|
133
+ should "have a rate of #{expected} events/sec after #{minutes} minute(s)" do
134
+ mark_minutes(minutes)
135
+ @ewma.rate.should.be.close(expected, 0.00000001)
136
+ end
137
+ end
138
+
139
+ end
140
+
141
+ end
@@ -0,0 +1,86 @@
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
+ describe "A sample of 100 out of 1000 elements" do
8
+ before do
9
+ Drone::init_drone(nil, Storage::Memory.new)
10
+
11
+ @population = (0...100)
12
+ @sample = ExponentiallyDecayingSample.new('id1', 1000, 0.99)
13
+ @population.step(1){|n| @sample.update(n) }
14
+ end
15
+
16
+
17
+ should "have 100 elements" do
18
+ @sample.size.should == 100
19
+ @sample.values.size.should == 100
20
+ end
21
+
22
+ should "only have elements from the population" do
23
+ arr = @sample.values - @population.to_a
24
+ arr.should == []
25
+ end
26
+
27
+ end
28
+
29
+
30
+ describe "A sample of 100 out of 10 elements" do
31
+ before do
32
+ @population = (0...10)
33
+ @sample = ExponentiallyDecayingSample.new('id1', 100, 0.99)
34
+ @population.step(1){|n| @sample.update(n) }
35
+ end
36
+
37
+ should "have 10 elements" do
38
+ @sample.size.should == 10
39
+ @sample.values.size.should == 10
40
+ end
41
+
42
+ should "only have elements from the population" do
43
+ arr = @sample.values - @population.to_a
44
+ arr.should == []
45
+ end
46
+
47
+ end
48
+
49
+
50
+ describe "A heavily-biased sample of 100 out of 1000 elements" do
51
+ before do
52
+ @population = (0...100)
53
+ @sample = ExponentiallyDecayingSample.new('id1', 1000, 0.99)
54
+ @population.step(1){|n| @sample.update(n) }
55
+ end
56
+
57
+ should "have 100 elements" do
58
+ @sample.size.should == 100
59
+ @sample.values.size.should == 100
60
+ end
61
+
62
+ should "only have elements from the population" do
63
+ arr = @sample.values - @population.to_a
64
+ arr.should == []
65
+ end
66
+
67
+ should "rescale after 1 hour" do
68
+ @sample.expects(:rescale).with(anything())
69
+
70
+ Delorean.time_travel_to("2 hours from now") do
71
+ @sample.update(1)
72
+ end
73
+
74
+ @sample.size.should == 101
75
+ @sample.values.size.should == 101
76
+ end
77
+
78
+ it 'can rescale' do
79
+ @sample.rescale(Time.now)
80
+ @sample.values.should.not == []
81
+ # TODO: add a real test here, for now it only tests
82
+ # that the code actually runs
83
+ end
84
+ end
85
+
86
+ end
@@ -0,0 +1,91 @@
1
+ require File.expand_path('../../common', __FILE__)
2
+
3
+ require 'drone/metrics/histogram'
4
+
5
+ include Drone
6
+
7
+ describe 'Histogram' do
8
+ before do
9
+ Drone::init_drone()
10
+ end
11
+
12
+ describe "A histogram with zero recorded valeus" do
13
+ before do
14
+ @histogram = Histogram.new("id1", UniformSample.new("id1:sample", 100))
15
+ end
16
+
17
+ should "have a count of 0" do
18
+ @histogram.count.should == 0
19
+ end
20
+
21
+ should "have a max of 0" do
22
+ @histogram.max.should == 0
23
+ end
24
+
25
+ should "have a min of 0" do
26
+ @histogram.min.should == 0
27
+ end
28
+
29
+ should "have a mean of 0" do
30
+ @histogram.mean.should == 0.0
31
+ end
32
+
33
+ should "have a standard deviation of 0" do
34
+ @histogram.stdDev.should == 0
35
+ end
36
+
37
+ should "calculate percentiles" do
38
+ percentiles = @histogram.percentiles(0.5, 0.75, 0.99)
39
+
40
+ percentiles[0].should.be.close?(0, 0.01)
41
+ percentiles[1].should.be.close?(0, 0.01)
42
+ percentiles[2].should.be.close?(0, 0.01)
43
+ end
44
+
45
+ should "have no values" do
46
+ @histogram.values.should == []
47
+ end
48
+ end
49
+
50
+
51
+ describe "A histogram of the numbers 1 through 10000" do
52
+ before do
53
+ @histogram = Histogram.new("id1", UniformSample.new("id1:sample", 100000) )
54
+ (1..10000).each{|n| @histogram.update(n) }
55
+ end
56
+
57
+ should "have a count of 10000" do
58
+ @histogram.count.should == 10000
59
+ end
60
+
61
+ should "have a max value of 10000" do
62
+ @histogram.max.should == 10000
63
+ end
64
+
65
+ should "have a min value of 1" do
66
+ @histogram.min.should == 1
67
+ end
68
+
69
+ should "have a mean value of 5000.5" do
70
+ @histogram.mean.should.be.close?(5000.5, 0.01)
71
+ end
72
+
73
+ should "have a standard deviation of X" do
74
+ @histogram.stdDev.should.be.close?(2886.89, 0.1)
75
+ end
76
+
77
+ should "calculate percentiles" do
78
+ percentiles = @histogram.percentiles(0.5, 0.75, 0.99)
79
+
80
+ percentiles[0].should.be.close?(5000.5, 0.01)
81
+ percentiles[1].should.be.close?(7500.75, 0.01)
82
+ percentiles[2].should.be.close?(9900.99, 0.01)
83
+ end
84
+
85
+ should "have 10000 values" do
86
+ @histogram.values.should == (1..10000).to_a
87
+ # histogram.values.toList must beEqualTo((1 to 10000).toList)
88
+ end
89
+ end
90
+
91
+ end
@@ -0,0 +1,129 @@
1
+ require File.expand_path('../../common', __FILE__)
2
+
3
+ require 'drone'
4
+ require 'drone/monitoring'
5
+
6
+ EM.describe 'Monitoring' do
7
+ describe 'rate monitor' do
8
+ before do
9
+
10
+ Drone::init_drone()
11
+
12
+ @klass = Class.new() do
13
+ include Drone::Monitoring
14
+
15
+ monitor_rate("users/no_args")
16
+ def a_method_without_args; 42; end
17
+
18
+ monitor_rate("users/with_args")
19
+ def method_with_args(a, b); a + b; end
20
+
21
+ monitor_rate("users/with_block")
22
+ def method_with_block(&block); block.call; end
23
+
24
+ end
25
+ @obj = @klass.new
26
+
27
+ end
28
+
29
+ should 'reuse same meter for every instances of this class' do
30
+ meter = Drone::find_metric("users/no_args")
31
+ meter.count.should == 0
32
+
33
+ obj1 = @klass.new
34
+ obj2 = @klass.new
35
+
36
+ obj1.a_method_without_args()
37
+ meter.count.should == 1
38
+
39
+ obj2.a_method_without_args()
40
+ meter.count.should == 2
41
+
42
+ done
43
+ end
44
+
45
+ should 'increment counter on call' do
46
+ Drone::Metrics::Meter.any_instance.expects(:mark)
47
+
48
+ ret = @obj.a_method_without_args()
49
+ ret.should == 42
50
+ done
51
+ end
52
+
53
+ should 'be transparent for method with arguments' do
54
+ Drone::Metrics::Meter.any_instance.expects(:mark)
55
+
56
+ ret = @obj.method_with_args(4, 5)
57
+ ret.should == 9
58
+ done
59
+ end
60
+
61
+ should 'be transparent for method with block argument' do
62
+ Drone::Metrics::Meter.any_instance.expects(:mark)
63
+
64
+ ret = @obj.method_with_block(){ 32 }
65
+ ret.should == 32
66
+ done
67
+ end
68
+
69
+ end
70
+
71
+
72
+ describe 'timing monitor' do
73
+ before do
74
+
75
+ Drone::init_drone()
76
+
77
+ klass = Class.new() do
78
+ include Drone::Monitoring
79
+
80
+ monitor_time("users/no_args")
81
+ def a_method_without_args; 42; end
82
+
83
+ monitor_time("users/with_args")
84
+ def method_with_args(a, b); a + b; end
85
+
86
+ monitor_time("users/with_block")
87
+ def method_with_block(&block); block.call; end
88
+
89
+ end
90
+ @obj = klass.new
91
+
92
+ end
93
+
94
+ should 'time call with no args' do
95
+ Drone::Metrics::Timer.any_instance.expects(:update).with{|delay|
96
+ delay.should.be.close?(0, 0.1)
97
+ true
98
+ }
99
+
100
+ ret = @obj.a_method_without_args()
101
+ ret.should == 42
102
+ done
103
+ end
104
+
105
+ should 'time call with args' do
106
+ Drone::Metrics::Timer.any_instance.expects(:update).with{|delay|
107
+ delay.should.be.close?(0, 0.1)
108
+ true
109
+ }
110
+
111
+ ret = @obj.method_with_args(2, 4)
112
+ ret.should == 6
113
+ done
114
+ end
115
+
116
+ should 'time call with a block' do
117
+ Drone::Metrics::Timer.any_instance.expects(:update).with{|delay|
118
+ delay.should.be.close?(0, 0.1)
119
+ true
120
+ }
121
+
122
+ ret = @obj.method_with_block(){ 42 }
123
+ ret.should == 42
124
+ done
125
+ end
126
+
127
+ end
128
+
129
+ end