pulse_meter_core 0.4.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. data/.gitignore +19 -0
  2. data/.rbenv-version +1 -0
  3. data/.rspec +1 -0
  4. data/.rvmrc +1 -0
  5. data/.travis.yml +8 -0
  6. data/Gemfile +2 -0
  7. data/LICENSE +22 -0
  8. data/README.md +40 -0
  9. data/Rakefile +20 -0
  10. data/lib/pulse_meter/command_aggregator/async.rb +83 -0
  11. data/lib/pulse_meter/command_aggregator/sync.rb +18 -0
  12. data/lib/pulse_meter/command_aggregator/udp.rb +48 -0
  13. data/lib/pulse_meter/mixins/dumper.rb +87 -0
  14. data/lib/pulse_meter/mixins/utils.rb +155 -0
  15. data/lib/pulse_meter/observer.rb +118 -0
  16. data/lib/pulse_meter/observer/extended.rb +32 -0
  17. data/lib/pulse_meter/sensor.rb +61 -0
  18. data/lib/pulse_meter/sensor/base.rb +88 -0
  19. data/lib/pulse_meter/sensor/configuration.rb +106 -0
  20. data/lib/pulse_meter/sensor/counter.rb +39 -0
  21. data/lib/pulse_meter/sensor/hashed_counter.rb +36 -0
  22. data/lib/pulse_meter/sensor/hashed_indicator.rb +24 -0
  23. data/lib/pulse_meter/sensor/indicator.rb +35 -0
  24. data/lib/pulse_meter/sensor/multi.rb +97 -0
  25. data/lib/pulse_meter/sensor/timeline.rb +236 -0
  26. data/lib/pulse_meter/sensor/timeline_reduce.rb +68 -0
  27. data/lib/pulse_meter/sensor/timelined/average.rb +32 -0
  28. data/lib/pulse_meter/sensor/timelined/counter.rb +23 -0
  29. data/lib/pulse_meter/sensor/timelined/hashed_counter.rb +31 -0
  30. data/lib/pulse_meter/sensor/timelined/hashed_indicator.rb +30 -0
  31. data/lib/pulse_meter/sensor/timelined/indicator.rb +23 -0
  32. data/lib/pulse_meter/sensor/timelined/max.rb +19 -0
  33. data/lib/pulse_meter/sensor/timelined/median.rb +14 -0
  34. data/lib/pulse_meter/sensor/timelined/min.rb +19 -0
  35. data/lib/pulse_meter/sensor/timelined/multi_percentile.rb +34 -0
  36. data/lib/pulse_meter/sensor/timelined/percentile.rb +22 -0
  37. data/lib/pulse_meter/sensor/timelined/uniq_counter.rb +22 -0
  38. data/lib/pulse_meter/sensor/timelined/zset_based.rb +37 -0
  39. data/lib/pulse_meter/sensor/uniq_counter.rb +24 -0
  40. data/lib/pulse_meter/server.rb +0 -0
  41. data/lib/pulse_meter/server/command_line_options.rb +0 -0
  42. data/lib/pulse_meter/server/config_options.rb +0 -0
  43. data/lib/pulse_meter/server/sensors.rb +0 -0
  44. data/lib/pulse_meter/udp_server.rb +45 -0
  45. data/lib/pulse_meter_core.rb +66 -0
  46. data/pulse_meter_core.gemspec +33 -0
  47. data/spec/pulse_meter/command_aggregator/async_spec.rb +53 -0
  48. data/spec/pulse_meter/command_aggregator/sync_spec.rb +25 -0
  49. data/spec/pulse_meter/command_aggregator/udp_spec.rb +45 -0
  50. data/spec/pulse_meter/mixins/dumper_spec.rb +162 -0
  51. data/spec/pulse_meter/mixins/utils_spec.rb +212 -0
  52. data/spec/pulse_meter/observer/extended_spec.rb +92 -0
  53. data/spec/pulse_meter/observer_spec.rb +207 -0
  54. data/spec/pulse_meter/sensor/base_spec.rb +106 -0
  55. data/spec/pulse_meter/sensor/configuration_spec.rb +103 -0
  56. data/spec/pulse_meter/sensor/counter_spec.rb +54 -0
  57. data/spec/pulse_meter/sensor/hashed_counter_spec.rb +43 -0
  58. data/spec/pulse_meter/sensor/hashed_indicator_spec.rb +39 -0
  59. data/spec/pulse_meter/sensor/indicator_spec.rb +43 -0
  60. data/spec/pulse_meter/sensor/multi_spec.rb +137 -0
  61. data/spec/pulse_meter/sensor/timeline_spec.rb +88 -0
  62. data/spec/pulse_meter/sensor/timelined/average_spec.rb +6 -0
  63. data/spec/pulse_meter/sensor/timelined/counter_spec.rb +6 -0
  64. data/spec/pulse_meter/sensor/timelined/hashed_counter_spec.rb +8 -0
  65. data/spec/pulse_meter/sensor/timelined/hashed_indicator_spec.rb +8 -0
  66. data/spec/pulse_meter/sensor/timelined/indicator_spec.rb +6 -0
  67. data/spec/pulse_meter/sensor/timelined/max_spec.rb +7 -0
  68. data/spec/pulse_meter/sensor/timelined/median_spec.rb +7 -0
  69. data/spec/pulse_meter/sensor/timelined/min_spec.rb +7 -0
  70. data/spec/pulse_meter/sensor/timelined/multi_percentile_spec.rb +21 -0
  71. data/spec/pulse_meter/sensor/timelined/percentile_spec.rb +17 -0
  72. data/spec/pulse_meter/sensor/timelined/uniq_counter_spec.rb +9 -0
  73. data/spec/pulse_meter/sensor/uniq_counter_spec.rb +28 -0
  74. data/spec/pulse_meter/udp_server_spec.rb +36 -0
  75. data/spec/pulse_meter_spec.rb +73 -0
  76. data/spec/shared_examples/timeline_sensor.rb +439 -0
  77. data/spec/shared_examples/timelined_subclass.rb +23 -0
  78. data/spec/spec_helper.rb +37 -0
  79. data/spec/support/matchers.rb +34 -0
  80. data/spec/support/observered.rb +40 -0
  81. metadata +342 -0
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+
3
+ describe PulseMeter::Observer::Extended do
4
+ context "instance methods observation" do
5
+ let!(:dummy) {ObservedDummy.new}
6
+ let!(:sensor) {PulseMeter::Sensor::Counter.new(:foo)}
7
+ before do
8
+ [:incr, :error].each {|m| described_class.unobserve_method(ObservedDummy, m)}
9
+ end
10
+
11
+ describe ".observe_method" do
12
+ it "passes exended parameters to block in normal execution" do
13
+ Timecop.freeze do
14
+ parameters = {}
15
+
16
+ described_class.observe_method(ObservedDummy, :incr, sensor) do |params|
17
+ parameters = params
18
+ end
19
+
20
+ dummy.incr(40)
21
+
22
+ parameters[:self].should == dummy
23
+ parameters[:delta].should >= 1000
24
+ parameters[:result].should == 40
25
+ parameters[:exception].should be_nil
26
+ parameters[:args].should == [40]
27
+ end
28
+ end
29
+
30
+ it "passes exended parameters to block with exception" do
31
+ Timecop.freeze do
32
+ parameters = {}
33
+
34
+ described_class.observe_method(ObservedDummy, :error, sensor) do |params|
35
+ parameters = params
36
+ end
37
+
38
+ lambda { dummy.error }.should raise_error(RuntimeError)
39
+
40
+ parameters[:self].should == dummy
41
+ parameters[:result].should == nil
42
+ parameters[:exception].class.should == RuntimeError
43
+ parameters[:args].should == []
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ context "class methods observation" do
50
+ let!(:sensor) {PulseMeter::Sensor::Counter.new(:foo)}
51
+ before do
52
+ [:incr, :error].each {|m| described_class.unobserve_class_method(ObservedDummy, m)}
53
+ end
54
+
55
+ describe ".observe_class_method" do
56
+ it "passes exended parameters to block in normal execution" do
57
+ Timecop.freeze do
58
+ parameters = {}
59
+
60
+ described_class.observe_class_method(ObservedDummy, :incr, sensor) do |params|
61
+ parameters = params
62
+ end
63
+
64
+ ObservedDummy.incr(40)
65
+
66
+ parameters[:self].should == ObservedDummy
67
+ parameters[:delta].should >= 1000
68
+ parameters[:result].should == 40
69
+ parameters[:exception].should be_nil
70
+ parameters[:args].should == [40]
71
+ end
72
+ end
73
+
74
+ it "passes exended parameters to block with exception" do
75
+ Timecop.freeze do
76
+ parameters = {}
77
+
78
+ described_class.observe_class_method(ObservedDummy, :error, sensor) do |params|
79
+ parameters = params
80
+ end
81
+
82
+ lambda { ObservedDummy.error }.should raise_error(RuntimeError)
83
+
84
+ parameters[:self].should == ObservedDummy
85
+ parameters[:result].should == nil
86
+ parameters[:exception].class.should == RuntimeError
87
+ parameters[:args].should == []
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,207 @@
1
+ require 'spec_helper'
2
+
3
+ describe PulseMeter::Observer do
4
+
5
+ context "instance methods observation" do
6
+ let!(:dummy) {ObservedDummy.new}
7
+ let!(:sensor) {PulseMeter::Sensor::Counter.new(:foo)}
8
+ before do
9
+ [:incr, :error].each {|m| described_class.unobserve_method(ObservedDummy, m)}
10
+ end
11
+
12
+ def create_observer(method = :incr, increment = 1)
13
+ described_class.observe_method(ObservedDummy, method, sensor) do |*args|
14
+ event(increment)
15
+ end
16
+ end
17
+
18
+ def remove_observer(method = :incr)
19
+ described_class.unobserve_method(ObservedDummy, method)
20
+ end
21
+
22
+ describe ".observe_method" do
23
+ it "executes block in context of sensor each time specified method of given class called" do
24
+ create_observer
25
+ 5.times {dummy.incr}
26
+ sensor.value.should == 5
27
+ end
28
+
29
+ it "passes arguments to observed method" do
30
+ create_observer
31
+ 5.times {dummy.incr(10)}
32
+ dummy.count.should == 50
33
+ end
34
+
35
+ it "passes methods' params to block" do
36
+ described_class.observe_method(ObservedDummy, :incr, sensor) do |time, cnt|
37
+ event(cnt)
38
+ end
39
+
40
+ 5.times {dummy.incr(10)}
41
+ sensor.value.should == 50
42
+ end
43
+
44
+ it "passes execution time in milliseconds to block" do
45
+ Timecop.freeze do
46
+ described_class.observe_method(ObservedDummy, :incr, sensor) do |time, cnt|
47
+ event(time)
48
+ end
49
+
50
+ dummy.incr
51
+ sensor.value.should >= 1000
52
+ end
53
+ end
54
+
55
+ it "does not break observed method even is observer raises error" do
56
+ described_class.observe_method(ObservedDummy, :incr, sensor) do |*args|
57
+ raise RuntimeError
58
+ end
59
+
60
+ lambda {dummy.incr}.should_not raise_error
61
+ dummy.count.should == 1
62
+ end
63
+
64
+ it "uses first observer in case of double observation" do
65
+ create_observer(:incr, 1)
66
+ create_observer(:incr, 2)
67
+ 5.times {dummy.incr}
68
+ sensor.value.should == 5
69
+ end
70
+
71
+ it "keeps observed methods' errors" do
72
+ create_observer(:error)
73
+ lambda {dummy.error}.should raise_error(RuntimeError)
74
+ sensor.value.should == 1
75
+ end
76
+
77
+ it "makes observed method return its value" do
78
+ create_observer
79
+ dummy.incr.should == 1
80
+ end
81
+
82
+ it "allows to pass blocks to observed method" do
83
+ create_observer
84
+ dummy.incr do
85
+ 2
86
+ end
87
+ dummy.count.should == 3
88
+ end
89
+ end
90
+
91
+ describe ".unobserve_method" do
92
+ it "does nothing unless method is observed" do
93
+ lambda {remove_observer}.should_not raise_error
94
+ end
95
+
96
+ it "removes observation from observed method" do
97
+ create_observer
98
+ dummy.incr
99
+ remove_observer
100
+ dummy.incr
101
+ sensor.value.should == 1
102
+ end
103
+ end
104
+ end
105
+
106
+ context "class methods observation" do
107
+ let!(:dummy) {ObservedDummy}
108
+ let!(:sensor) {PulseMeter::Sensor::Counter.new(:foo)}
109
+ before do
110
+ dummy.reset
111
+ [:incr, :error].each {|m| described_class.unobserve_class_method(ObservedDummy, m)}
112
+ end
113
+
114
+ def create_observer(method = :incr, increment = 1)
115
+ described_class.observe_class_method(ObservedDummy, method, sensor) do |*args|
116
+ event(increment)
117
+ end
118
+ end
119
+
120
+ def remove_observer(method = :incr)
121
+ described_class.unobserve_class_method(ObservedDummy, method)
122
+ end
123
+
124
+ describe ".observe_class_method" do
125
+ it "executes block in context of sensor each time specified method of given class called" do
126
+ create_observer
127
+ 5.times {dummy.incr}
128
+ sensor.value.should == 5
129
+ end
130
+
131
+ it "passes arguments to observed method" do
132
+ create_observer
133
+ 5.times {dummy.incr(10)}
134
+ dummy.count.should == 50
135
+ end
136
+
137
+ it "passes methods' params to block" do
138
+ described_class.observe_class_method(ObservedDummy, :incr, sensor) do |time, cnt|
139
+ event(cnt)
140
+ end
141
+
142
+ 5.times {dummy.incr(10)}
143
+ sensor.value.should == 50
144
+ end
145
+
146
+ it "passes execution time in milliseconds to block" do
147
+ Timecop.freeze do
148
+ described_class.observe_class_method(ObservedDummy, :incr, sensor) do |time, cnt|
149
+ event(time)
150
+ end
151
+
152
+ dummy.incr
153
+ sensor.value.should == 1000
154
+ end
155
+ end
156
+
157
+ it "does not break observed method even is observer raises error" do
158
+ described_class.observe_class_method(ObservedDummy, :incr, sensor) do |*args|
159
+ raise RuntimeError
160
+ end
161
+
162
+ lambda {dummy.incr}.should_not raise_error
163
+ dummy.count.should == 1
164
+ end
165
+
166
+ it "uses first observer in case of double observation" do
167
+ create_observer(:incr, 1)
168
+ create_observer(:incr, 2)
169
+ 5.times {dummy.incr}
170
+ sensor.value.should == 5
171
+ end
172
+
173
+ it "keeps observed methods' errors" do
174
+ create_observer(:error)
175
+ lambda {dummy.error}.should raise_error(RuntimeError)
176
+ sensor.value.should == 1
177
+ end
178
+
179
+ it "makes observed method return its value" do
180
+ create_observer
181
+ dummy.incr.should == 1
182
+ end
183
+
184
+ it "allows to pass blocks to observed method" do
185
+ create_observer
186
+ dummy.incr do
187
+ 2
188
+ end
189
+ dummy.count.should == 3
190
+ end
191
+ end
192
+
193
+ describe ".unobserve_class_method" do
194
+ it "does nothing unless method is observed" do
195
+ lambda {remove_observer}.should_not raise_error
196
+ end
197
+
198
+ it "removes observation from observed method" do
199
+ create_observer
200
+ dummy.incr
201
+ remove_observer
202
+ dummy.incr
203
+ sensor.value.should == 1
204
+ end
205
+ end
206
+ end
207
+ end
@@ -0,0 +1,106 @@
1
+ require 'spec_helper'
2
+
3
+ describe PulseMeter::Sensor::Base do
4
+ let(:name){ :some_sensor }
5
+ let(:description) {"Le awesome description"}
6
+ let!(:sensor){ described_class.new(name) }
7
+ let(:redis){ PulseMeter.redis }
8
+
9
+ describe '#initialize' do
10
+ context 'when PulseMeter.redis is not initialized' do
11
+ it "should raise RedisNotInitialized exception" do
12
+ PulseMeter.redis = nil
13
+ expect{ described_class.new(:foo) }.to raise_exception(PulseMeter::RedisNotInitialized)
14
+ end
15
+ end
16
+
17
+ context 'when PulseMeter.redis is initialized' do
18
+
19
+ context 'when passed sensor name is bad' do
20
+ it "should raise BadSensorName exception" do
21
+ ['name with whitespace', 'name|with|bad|characters'].each do |bad_name|
22
+ expect{ described_class.new(bad_name) }.to raise_exception(PulseMeter::BadSensorName)
23
+ end
24
+ end
25
+ end
26
+
27
+ context 'when passed sensor name is valid' do
28
+ it "should successfully create object" do
29
+ described_class.new(:foo).should_not be_nil
30
+ end
31
+
32
+ it "should initialize attributes #redis and #name" do
33
+ sensor = described_class.new(:foo)
34
+ sensor.name.should == 'foo'
35
+ sensor.redis.should == PulseMeter.redis
36
+ end
37
+
38
+ it "should save dump to redis automatically to let the object be restored by name" do
39
+ described_class.restore(name).should be_instance_of(described_class)
40
+ end
41
+
42
+ it "should annotate object if annotation given" do
43
+ described_class.new(:foo, :annotation => "annotation")
44
+ sensor = described_class.restore(:foo)
45
+ sensor.annotation.should == "annotation"
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ describe '#annotate' do
52
+
53
+ it "should store sensor annotation in redis" do
54
+ expect {sensor.annotate(description)}.to change{redis.keys('*').count}.by(1)
55
+ end
56
+
57
+ end
58
+
59
+ describe '#annotation' do
60
+ context "when sensor was annotated" do
61
+ it "should return stored annotation" do
62
+ sensor.annotate(description)
63
+ sensor.annotation.should == description
64
+ end
65
+ end
66
+
67
+ context "when sensor was not annotated" do
68
+ it "should return nil" do
69
+ sensor.annotation.should be_nil
70
+ end
71
+ end
72
+
73
+ context "after sensor data was cleaned" do
74
+ it "should return nil" do
75
+ sensor.annotate(description)
76
+ sensor.cleanup
77
+ sensor.annotation.should be_nil
78
+ end
79
+ end
80
+ end
81
+
82
+ describe "#cleanup" do
83
+ it "should remove from redis all sensor data" do
84
+ sensor.event(123)
85
+ sensor.annotate(description)
86
+ sensor.cleanup
87
+ redis.keys('*').should be_empty
88
+ end
89
+ end
90
+
91
+ describe "#event" do
92
+ context "when everything is ok" do
93
+ it "should do nothing and return true" do
94
+ sensor.event(nil).should be_true
95
+ end
96
+ end
97
+
98
+ context "when an error occures while processing event" do
99
+ it "should catch StandardErrors and return false" do
100
+ sensor.stub(:process_event) {raise StandardError}
101
+ sensor.event(nil).should be_false
102
+ end
103
+ end
104
+ end
105
+
106
+ end
@@ -0,0 +1,103 @@
1
+ require "spec_helper"
2
+
3
+ describe PulseMeter::Sensor::Configuration do
4
+ let(:counter_config) {
5
+ {
6
+ cnt: {
7
+ sensor_type: 'counter',
8
+ args: {
9
+ annotation: "MySensor"
10
+ }
11
+ },
12
+ }
13
+ }
14
+
15
+ describe "#add_sensor" do
16
+ let(:cfg) {described_class.new}
17
+
18
+ it "should create sensor available under passed name" do
19
+ cfg.has_sensor?(:foo).should be_false
20
+ cfg.add_sensor(:foo, sensor_type: 'counter')
21
+ cfg.has_sensor?(:foo).should_not be_true
22
+ end
23
+
24
+ it "should have event shortcut for the sensor" do
25
+ cfg.add_sensor(:foo, sensor_type: 'counter')
26
+ puts cfg.to_yaml
27
+ cfg.sensor(:foo){|s| s.should_receive(:event).with(321)}
28
+ cfg.foo(321)
29
+ end
30
+
31
+ it "should have event_at shortcut for the sensor" do
32
+ cfg.add_sensor(:foo, sensor_type: 'counter')
33
+ now = Time.now
34
+ cfg.sensor(:foo) do |sensor|
35
+ sensor.should_receive(:event_at).with(now, 321)
36
+ end
37
+ cfg.foo_at(now, 321)
38
+ end
39
+
40
+ it "should create sensor with correct type" do
41
+ cfg.add_sensor(:foo, sensor_type: 'counter')
42
+ cfg.sensor(:foo){|s| s.should be_kind_of(PulseMeter::Sensor::Counter)}
43
+ end
44
+
45
+ it "should not raise exception if sensor type is bad" do
46
+ expect{ cfg.add_sensor(:foo, sensor_type: 'baaaar') }.not_to raise_exception
47
+ end
48
+
49
+ it "should pass args to created sensor" do
50
+ cfg.add_sensor(:foo, sensor_type: 'counter', args: {annotation: "My Foo Counter"} )
51
+ cfg.sensor(:foo){|s| s.annotation.should == "My Foo Counter" }
52
+ end
53
+
54
+ it "should accept hashie-objects" do
55
+ class Dummy
56
+ def sensor_type
57
+ 'counter'
58
+ end
59
+ def args
60
+ Hashie::Mash.new(annotation: "My Foo Counter")
61
+ end
62
+ end
63
+
64
+ cfg.add_sensor(:foo, Dummy.new)
65
+ cfg.sensor(:foo){|s| s.annotation.should == "My Foo Counter"}
66
+ end
67
+ end
68
+
69
+ describe ".new" do
70
+ it "should add passed sensor setting hash using keys as names" do
71
+ opts = {
72
+ cnt: {
73
+ sensor_type: 'counter'
74
+ },
75
+ ind: {
76
+ sensor_type: 'indicator'
77
+ }
78
+ }
79
+ cfg1 = described_class.new(opts)
80
+ cfg2 = described_class.new
81
+ opts.each{|k,v| cfg2.add_sensor(k, v)}
82
+ cfg1.sensors.to_yaml.should == cfg2.sensors.to_yaml
83
+ end
84
+ end
85
+
86
+ describe "#sensor" do
87
+ it "should give access to added sensors via block" do
88
+ cfg = described_class.new(counter_config)
89
+ cfg.sensor(:cnt){ |s| s.annotation.should == "MySensor" }
90
+ cfg.sensor("cnt"){ |s| s.annotation.should == "MySensor" }
91
+ end
92
+ end
93
+
94
+ describe "#each_sensor" do
95
+ it "yields block for each name/sensor pair" do
96
+ cfg = described_class.new(counter_config)
97
+ sensors = {}
98
+ cfg.each {|s| sensors[s.name.to_sym] = s}
99
+ sensor = cfg.sensor(:cnt){|s| s}
100
+ sensors.should == {:cnt => sensor}
101
+ end
102
+ end
103
+ end