instrumental_agent 1.0.1 → 3.0.0.beta

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.
@@ -1,47 +1,5 @@
1
- require 'capistrano'
2
- require 'instrumental_agent'
3
- require 'etc'
4
-
5
- if Capistrano::Configuration.instance
6
- Capistrano::Configuration.instance.load do
7
- namespace :instrumental do
8
- namespace :util do
9
- desc "marker for beginning of deploy"
10
- task :deploy_start do
11
- set :instrumental_deploy_start, Time.now
12
- end
13
-
14
- desc "marker for end of deploy"
15
- task :deploy_end do
16
- set :instrumental_deploy_end, Time.now
17
- end
18
- end
19
-
20
- desc "send a notice to instrumental about the deploy"
21
- task :record_deploy_notice do
22
- start_at = exists?(:instrumental_deploy_start) ? instrumental_deploy_start : Time.now
23
- end_at = exists?(:instrumental_deploy_end) ? instrumental_deploy_end : start_at
24
- deploy_duration_in_seconds = end_at - start_at
25
- deployer = Etc.getlogin.chomp
26
- agent_options = { :synchronous => true }
27
- agent_options[:collector] = instrumental_host if exists?(:instrumental_host)
28
- agent = Instrumental::Agent.new(instrumental_key, agent_options)
29
- message = if exists?(:deploy_message)
30
- deploy_message
31
- else
32
- "#{deployer} deployed #{current_revision}"
33
- end
34
- agent.notice(message,
35
- start_at,
36
- deploy_duration_in_seconds)
37
- logger.info("Notified Instrumental of deployment")
38
- end
39
- end
40
-
41
- before "deploy", "instrumental:util:deploy_start"
42
- after "deploy", "instrumental:util:deploy_end"
43
- before "deploy:migrations", "instrumental:util:deploy_start"
44
- after "deploy:migrations", "instrumental:util:deploy_end"
45
- after "instrumental:util:deploy_end", "instrumental:record_deploy_notice"
46
- end
1
+ if Gem::Specification.find_by_name("capistrano").version >= Gem::Version.new("3.0.0")
2
+ load File.expand_path("../capistrano/capistrano3.rake", __FILE__)
3
+ else
4
+ require_relative "capistrano/capistrano2"
47
5
  end
@@ -0,0 +1,47 @@
1
+ require "etc"
2
+ require "instrumental_agent"
3
+
4
+ Capistrano::Configuration.instance.load do
5
+ _cset(:instrumental_hooks) { true }
6
+ _cset(:instrumental_key) { nil }
7
+ _cset(:deployer) { Etc.getlogin.chomp }
8
+
9
+ if fetch(:instrumental_hooks)
10
+ before "deploy", "instrumental:util:deploy_start"
11
+ after "deploy", "instrumental:util:deploy_end"
12
+ before "deploy:migrations", "instrumental:util:deploy_start"
13
+ after "deploy:migrations", "instrumental:util:deploy_end"
14
+ after "instrumental:util:deploy_end", "instrumental:record_deploy_notice"
15
+ end
16
+
17
+ namespace :instrumental do
18
+ namespace :util do
19
+ desc "marker for beginning of deploy"
20
+ task :deploy_start do
21
+ set :instrumental_deploy_start, Time.now
22
+ end
23
+
24
+ desc "marker for end of deploy"
25
+ task :deploy_end do
26
+ set :instrumental_deploy_end, Time.now
27
+ end
28
+ end
29
+
30
+ desc "send a notice to instrumental about the deploy"
31
+ task :record_deploy_notice do
32
+ start_at = fetch(:instrumental_deploy_start, Time.now)
33
+ end_at = fetch(:instrumental_deploy_end, start_at)
34
+ deploy_duration_in_seconds = end_at - start_at
35
+ deployer = fetch(:deployer)
36
+ agent_options = { :synchronous => true }
37
+ agent_options[:collector] = instrumental_host if fetch(:instrumental_host, false)
38
+ agent = Instrumental::Agent.new(fetch(:instrumental_key), agent_options)
39
+ message = fetch(:deploy_message, "#{deployer} deployed #{current_revision}")
40
+
41
+ agent.notice(message,
42
+ start_at,
43
+ deploy_duration_in_seconds)
44
+ logger.info("Notified Instrumental of deployment")
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,56 @@
1
+ require "etc"
2
+ require "instrumental_agent"
3
+
4
+ namespace :load do
5
+ task :defaults do
6
+ set :instrumental_hooks, true
7
+ set :instrumental_key, nil
8
+ set :deployer, Etc.getlogin.chomp
9
+ end
10
+ end
11
+
12
+ namespace :deploy do
13
+ before :starting, :check_instrumental_hooks do
14
+ invoke "instrumental:util:add_hooks" if fetch(:instrumental_hooks)
15
+ end
16
+ end
17
+
18
+ namespace :instrumental do
19
+ namespace :util do
20
+ desc "add instrumental hooks to deploy"
21
+ task :add_hooks do
22
+ before "deploy", "instrumental:util:deploy_start"
23
+ after "deploy", "instrumental:util:deploy_end"
24
+ after "instrumental:util:deploy_end", "instrumental:record_deploy_notice"
25
+ end
26
+
27
+ desc "marker for beginning of deploy"
28
+ task :deploy_start do
29
+ set :instrumental_deploy_start, Time.now
30
+ end
31
+
32
+ desc "marker for end of deploy"
33
+ task :deploy_end do
34
+ set :instrumental_deploy_end, Time.now
35
+ end
36
+ end
37
+
38
+ desc "send a notice to instrumental about the deploy"
39
+ task :record_deploy_notice do
40
+ start_at = fetch(:instrumental_deploy_start, Time.now)
41
+ end_at = fetch(:instrumental_deploy_end, start_at)
42
+ deploy_duration_in_seconds = end_at - start_at
43
+ deployer = fetch(:deployer)
44
+ agent_options = { :synchronous => true }
45
+ agent_options[:collector] = instrumental_host if fetch(:instrumental_host, false)
46
+ message = fetch(:deploy_message, "#{deployer} deployed #{fetch(:current_revision)}".strip)
47
+
48
+ if fetch(:instrumental_key)
49
+ agent = Instrumental::Agent.new(fetch(:instrumental_key), agent_options)
50
+ agent.notice(message,
51
+ start_at,
52
+ deploy_duration_in_seconds)
53
+ puts "Notified Instrumental of deployment"
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,32 @@
1
+ module Instrumental
2
+ METRIC_TYPES = ["increment".freeze, "gauge".freeze].freeze
3
+
4
+ Command = Struct.new(:command, :metric, :value, :time, :count) do
5
+ def initialize(command, metric, value, time, count)
6
+ super(command, metric, value, time.to_i, count.to_i)
7
+ end
8
+
9
+ def to_s
10
+ [command, metric, value, time, count].map(&:to_s).join(" ")
11
+ end
12
+
13
+ def metadata
14
+ "#{metric}:#{time}".freeze
15
+ end
16
+
17
+ def +(other_command)
18
+ return self if other_command.nil?
19
+ Command.new(command, metric, value + other_command.value, time, count + other_command.count)
20
+ end
21
+ end
22
+
23
+ Notice = Struct.new(:note, :time, :duration) do
24
+ def initialize(note, time, duration)
25
+ super(note, time.to_i, duration.to_i)
26
+ end
27
+
28
+ def to_s
29
+ ["notice".freeze, time, duration, note].map(&:to_s).join(" ")
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,26 @@
1
+ class EventAggregator
2
+ attr_accessor :counts, :values, :received_at, :frequency
3
+
4
+ def initialize(frequency:)
5
+ @values = Hash.new
6
+ @frequency = frequency
7
+ end
8
+
9
+ def put(command)
10
+ command_at = command.time
11
+ unless(command_at % frequency == 0)
12
+ command.time = (command_at - (command_at % frequency))
13
+ end
14
+ metadata = command.metadata
15
+ @values[metadata] = (command + @values[metadata])
16
+ end
17
+
18
+ def size
19
+ @values.size
20
+ end
21
+
22
+ def coerce_time(time)
23
+ itime = time.to_i
24
+ (itime - (itime % frequency)).to_i
25
+ end
26
+ end
@@ -1,3 +1,3 @@
1
1
  module Instrumental
2
- VERSION = "1.0.1"
2
+ VERSION = "3.0.0.beta"
3
3
  end
@@ -2,7 +2,25 @@
2
2
  set -e
3
3
  cd "$(dirname "$0")/.."
4
4
 
5
- type rbenv || $(brew install rbenv; brew install ruby-build)
6
- rbenv which ruby || $(brew upgrade ruby-build || true; rbenv install)
7
- gem list -i bundler || gem install bundler
8
- bundle install
5
+ rbenv which ruby >/dev/null 2>&1 || (brew upgrade ruby-build || true; rbenv install)
6
+
7
+ # Setup rbenv so we can switch rubies below
8
+ eval "$(rbenv init - --no-rehash)"
9
+
10
+ for ruby_version in `ruby -ryaml -e 'puts YAML.load(File.read(".travis.yml"))["rvm"].join(" ")'`; do
11
+ rbenv versions --bare | grep "^${ruby_version}$" || rbenv install $ruby_version
12
+ rbenv shell $ruby_version
13
+
14
+ gem list -i bundler >/dev/null || gem install bundler
15
+
16
+ bundle install
17
+ done
18
+
19
+ tput bold # bold text
20
+ tput setaf 2 # green text
21
+ echo "****************************************************************"
22
+ echo "* *"
23
+ echo "* Good to go! *"
24
+ echo "* *"
25
+ echo "****************************************************************"
26
+ tput sgr0 # reset to default text
@@ -2,5 +2,37 @@
2
2
  set -e
3
3
  cd "$(dirname "$0")/.."
4
4
 
5
- script/setup
6
- bundle exec rspec
5
+ eval "$(rbenv init - --no-rehash)"
6
+
7
+ rspec_file_line="$1"
8
+ if [[ "$rspec_file_line" != "" ]]; then
9
+ rspec_file_line="[${rspec_file_line}]"
10
+ fi
11
+
12
+ success="true"
13
+
14
+ for ruby_version in `ruby -ryaml -e 'puts YAML.load(File.read(".travis.yml"))["rvm"].join(" ")'`; do
15
+ {
16
+ echo "testing ruby version $ruby_version" &&
17
+ rbenv shell $ruby_version &&
18
+ bundle exec rspec
19
+ } || success="false"
20
+ done
21
+
22
+ if [ $success == "true" ]; then
23
+ tput bold # bold text
24
+ tput setaf 2 # green text
25
+ echo "======================================"
26
+ echo "= Passed ="
27
+ echo "======================================"
28
+ tput sgr0 # reset to default text
29
+ exit 0
30
+ else
31
+ tput bold # bold text
32
+ tput setaf 1 # red text
33
+ echo "======================================"
34
+ echo "= FAILED ="
35
+ echo "======================================"
36
+ tput sgr0 # reset to default text
37
+ exit 1
38
+ fi
@@ -1,7 +1,21 @@
1
1
  require 'spec_helper'
2
2
 
3
- def wait(n=0.2)
4
- sleep n # FIXME: hack
3
+ def wait(n=0.2, &block)
4
+ start = Time.now
5
+ if block_given?
6
+ begin
7
+ yield
8
+ rescue Exception => ex
9
+ if (Time.now - start) < 5
10
+ sleep n
11
+ retry
12
+ else
13
+ raise ex
14
+ end
15
+ end
16
+ else
17
+ sleep n
18
+ end
5
19
  end
6
20
 
7
21
  FORK_SUPPORTED = begin
@@ -24,7 +38,9 @@ shared_examples "Instrumental Agent" do
24
38
  let(:synchronous) { false }
25
39
  let(:token) { 'test_token' }
26
40
  let(:address) { server.host_and_port }
27
- let(:agent) { Instrumental::Agent.new(token, :collector => address, :synchronous => synchronous, :enabled => enabled, :secure => secure?, :verify_cert => verify_cert?) }
41
+ let(:metrician) { false }
42
+ let(:frequency) { 0 }
43
+ let(:agent) { Instrumental::Agent.new(token, :collector => address, :synchronous => synchronous, :enabled => enabled, :secure => secure?, :verify_cert => verify_cert?, :metrician => metrician, :frequency => frequency) }
28
44
 
29
45
  # Server options
30
46
  let(:listen) { true }
@@ -32,6 +48,12 @@ shared_examples "Instrumental Agent" do
32
48
  let(:authenticate) { true }
33
49
  let(:server) { TestServer.new(:listen => listen, :authenticate => authenticate, :response => response, :secure => secure?) }
34
50
 
51
+ # Time Travel Options
52
+ let(:start_of_minute) do
53
+ now = Time.now.to_i
54
+ Time.at(now - (now % 60))
55
+ end
56
+
35
57
  before do
36
58
  Instrumental::Agent.logger.level = Logger::UNKNOWN
37
59
  @server = server
@@ -52,31 +74,36 @@ shared_examples "Instrumental Agent" do
52
74
 
53
75
  it "should not connect to the server after receiving a metric" do
54
76
  agent.gauge('disabled_test', 1)
55
- wait
56
- expect(server.connect_count).to eq(0)
77
+ wait do
78
+ expect(server.connect_count).to eq(0)
79
+ end
57
80
  end
58
81
 
59
82
  it "should no op on flush without reconnect" do
60
83
  1.upto(100) { agent.gauge('disabled_test', 1) }
61
84
  agent.flush(false)
62
- wait
63
- expect(server.commands).to be_empty
85
+ wait do
86
+ expect(server.commands).to be_empty
87
+ end
64
88
  end
65
89
 
66
90
  it "should no op on flush with reconnect" do
67
91
  1.upto(100) { agent.gauge('disabled_test', 1) }
68
92
  agent.flush(true)
69
- wait
70
- expect(server.commands).to be_empty
93
+ wait do
94
+ expect(server.commands).to be_empty
95
+ end
71
96
  end
72
97
 
73
98
  it "should no op on an empty flush" do
74
99
  agent.flush(true)
75
- wait
76
- expect(server.commands).to be_empty
100
+ wait do
101
+ expect(server.commands).to be_empty
102
+ end
77
103
  end
78
104
 
79
105
  it "should send metrics to logger" do
106
+ Timecop.freeze
80
107
  now = Time.now
81
108
  expect(agent.logger).to receive(:debug).with("gauge metric 1 #{now.to_i} 1")
82
109
  agent.gauge("metric", 1)
@@ -91,14 +118,16 @@ shared_examples "Instrumental Agent" do
91
118
 
92
119
  it "should connect to the server after sending a metric" do
93
120
  agent.increment("test.foo")
94
- wait
95
- expect(server.connect_count).to eq(1)
121
+ wait do
122
+ expect(server.connect_count).to eq(1)
123
+ end
96
124
  end
97
125
 
98
126
  it "should announce itself, and include version" do
99
127
  agent.increment("test.foo")
100
- wait
101
- expect(server.commands[0]).to match(/hello .*/)
128
+ wait do
129
+ expect(server.commands[0]).to match(/hello .*/)
130
+ end
102
131
  expect(server.commands[0]).to match(/ version /)
103
132
  expect(server.commands[0]).to match(/ hostname /)
104
133
  expect(server.commands[0]).to match(/ pid /)
@@ -108,15 +137,18 @@ shared_examples "Instrumental Agent" do
108
137
 
109
138
  it "should authenticate using the token" do
110
139
  agent.increment("test.foo")
111
- wait
112
- expect(server.commands[1]).to eq("authenticate test_token")
140
+ wait do
141
+ expect(server.commands[1]).to eq("authenticate test_token")
142
+ end
113
143
  end
114
144
 
115
145
  it "should report a gauge" do
146
+ Timecop.freeze
116
147
  now = Time.now
117
148
  agent.gauge('gauge_test', 123)
118
- wait
119
- expect(server.commands.last).to eq("gauge gauge_test 123 #{now.to_i} 1")
149
+ wait do
150
+ expect(server.commands.last).to eq("gauge gauge_test 123 #{now.to_i} 1")
151
+ end
120
152
  end
121
153
 
122
154
  it "should report a time as gauge and return the block result" do
@@ -125,8 +157,9 @@ shared_examples "Instrumental Agent" do
125
157
  1 + 1
126
158
  end
127
159
  expect(return_value).to eq(2)
128
- wait
129
- expect(server.commands.last).to match(/gauge time_value_test .* #{now.to_i}/)
160
+ wait do
161
+ expect(server.commands.last).to match(/gauge time_value_test .* #{now.to_i}/)
162
+ end
130
163
  end
131
164
 
132
165
  it "should report a time_ms as gauge and return the block result" do
@@ -136,8 +169,9 @@ shared_examples "Instrumental Agent" do
136
169
  1 + 1
137
170
  end
138
171
  expect(return_value).to eq(2)
139
- wait
140
- expect(server.commands.last).to match(/gauge time_value_test 1000/)
172
+ wait do
173
+ expect(server.commands.last).to match(/gauge time_value_test 1000/)
174
+ end
141
175
  end
142
176
 
143
177
  it "should return the value gauged" do
@@ -147,21 +181,24 @@ shared_examples "Instrumental Agent" do
147
181
 
148
182
  it "should report a gauge with a set time" do
149
183
  agent.gauge('gauge_test', 123, 555)
150
- wait
151
- expect(server.commands.last).to eq("gauge gauge_test 123 555 1")
184
+ wait do
185
+ expect(server.commands.last).to eq("gauge gauge_test 123 555 1")
186
+ end
152
187
  end
153
188
 
154
189
  it "should report a gauge with a set time and count" do
155
190
  agent.gauge('gauge_test', 123, 555, 111)
156
- wait
157
- expect(server.commands.last).to eq("gauge gauge_test 123 555 111")
191
+ wait do
192
+ expect(server.commands.last).to eq("gauge gauge_test 123 555 111")
193
+ end
158
194
  end
159
195
 
160
196
  it "should report an increment" do
161
197
  now = Time.now
162
198
  agent.increment("increment_test")
163
- wait
164
- expect(server.commands.last).to eq("increment increment_test 1 #{now.to_i} 1")
199
+ wait do
200
+ expect(server.commands.last).to eq("increment increment_test 1 #{now.to_i} 1")
201
+ end
165
202
  end
166
203
 
167
204
  it "should return the value incremented by" do
@@ -172,33 +209,43 @@ shared_examples "Instrumental Agent" do
172
209
  it "should report an increment a value" do
173
210
  now = Time.now
174
211
  agent.increment("increment_test", 2)
175
- wait
176
- expect(server.commands.last).to eq("increment increment_test 2 #{now.to_i} 1")
212
+ wait do
213
+ expect(server.commands.last).to eq("increment increment_test 2 #{now.to_i} 1")
214
+ end
177
215
  end
178
216
 
179
217
  it "should report an increment with a set time" do
180
218
  agent.increment('increment_test', 1, 555)
181
- wait
182
- expect(server.commands.last).to eq("increment increment_test 1 555 1")
219
+ wait do
220
+ expect(server.commands.last).to eq("increment increment_test 1 555 1")
221
+ end
183
222
  end
184
223
 
185
224
  it "should report an increment with a set time and count" do
186
225
  agent.increment('increment_test', 1, 555, 111)
187
- wait
188
- expect(server.commands.last).to eq("increment increment_test 1 555 111")
226
+ wait do
227
+ expect(server.commands.last).to eq("increment increment_test 1 555 111")
228
+ end
189
229
  end
190
230
 
191
- it "should discard data that overflows the buffer" do
192
- with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
193
- 5.times do |i|
194
- agent.increment('overflow_test', i + 1, 300)
231
+ context do
232
+ let(:listen) { false }
233
+ it "should discard data that overflows the buffer" do
234
+ with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
235
+ allow(agent.logger).to receive(:debug)
236
+ expect(agent.logger).to receive(:debug).with("Dropping command, queue full(3): increment overflow_test 4 300 1")
237
+ expect(agent.logger).to receive(:debug).with("Dropping command, queue full(3): increment overflow_test 5 300 1")
238
+ 1.upto(5) do |i|
239
+ agent.increment('overflow_test', i, 300)
240
+ end
241
+
242
+ wait
243
+ expect(agent.sender_queue.size).to eq(3)
244
+ expect(agent.sender_queue.pop.first.to_s).to start_with("increment overflow_test 1 300 1")
245
+ expect(agent.sender_queue.pop.first.to_s).to start_with("increment overflow_test 2 300 1")
246
+ expect(agent.sender_queue.pop.first.to_s).to start_with("increment overflow_test 3 300 1")
247
+ expect(agent.sender_queue.size).to eq(0)
195
248
  end
196
- wait
197
- expect(server.commands).to include("increment overflow_test 1 300 1")
198
- expect(server.commands).to include("increment overflow_test 2 300 1")
199
- expect(server.commands).to include("increment overflow_test 3 300 1")
200
- expect(server.commands).to_not include("increment overflow_test 4 300 1")
201
- expect(server.commands).to_not include("increment overflow_test 5 300 1")
202
249
  end
203
250
  end
204
251
 
@@ -208,7 +255,7 @@ shared_examples "Instrumental Agent" do
208
255
  5.times do |i|
209
256
  agent.increment('overflow_test', i + 1, 300)
210
257
  end
211
- expect(agent.instance_variable_get(:@queue).size).to eq(0)
258
+ expect(agent.instance_variable_get(:@sender_queue).size).to eq(0)
212
259
  wait # let the server receive the commands
213
260
  expect(server.commands).to include("increment overflow_test 1 300 1")
214
261
  expect(server.commands).to include("increment overflow_test 2 300 1")
@@ -224,8 +271,10 @@ shared_examples "Instrumental Agent" do
224
271
  fork do
225
272
  agent.increment('fork_reconnect_test', 1, 3) # triggers reconnect
226
273
  end
274
+
227
275
  wait(1)
228
276
  agent.increment('fork_reconnect_test', 1, 4) # triggers reconnect
277
+
229
278
  wait(1)
230
279
  expect(server.connect_count).to eq(2)
231
280
 
@@ -241,24 +290,25 @@ shared_examples "Instrumental Agent" do
241
290
  sleep 1
242
291
  }
243
292
 
244
- run_worker_loop_calls = 0
245
- allow(agent).to receive(:run_worker_loop) {
246
- run_worker_loop_calls += 1
293
+ run_sender_loop_calls = 0
294
+ allow(agent).to receive(:run_sender_loop) {
295
+ run_sender_loop_calls += 1
247
296
  sleep 3 # keep the worker thread alive
248
297
  }
249
298
 
250
299
  t = Thread.new { agent.increment("race") }
251
300
  agent.increment("race")
252
301
  wait(2)
253
- expect(run_worker_loop_calls).to eq(1)
254
- expect(agent.queue.size).to eq(2)
302
+ expect(run_sender_loop_calls).to eq(1)
303
+ expect(agent.sender_queue.size).to eq(2)
255
304
  end
256
305
 
257
306
  it "should never let an exception reach the user" do
258
307
  expect(agent).to receive(:send_command).twice { raise(Exception.new("Test Exception")) }
259
308
  expect(agent.increment('throws_exception', 2)).to eq(nil)
260
- wait
261
- expect(agent.increment('throws_exception', 234)).to eq(nil)
309
+ wait do
310
+ expect(agent.increment('throws_exception', 234)).to eq(nil)
311
+ end
262
312
  end
263
313
 
264
314
  it "should let exceptions in time bubble up" do
@@ -273,27 +323,22 @@ shared_examples "Instrumental Agent" do
273
323
  expect(agent.increment("test")).to eq(nil)
274
324
  end
275
325
 
276
- it "should track invalid metrics" do
277
- expect(agent.logger).to receive(:warn).with(/%%/)
278
- agent.increment(' %% .!#@$%^&*', 1, 1)
279
- wait
280
- expect(server.commands.join("\n")).to include("increment agent.invalid_metric")
281
- end
282
-
283
326
  it "should allow reasonable metric names" do
284
327
  agent.increment('a')
285
328
  agent.increment('a.b')
286
329
  agent.increment('hello.world')
287
330
  agent.increment('ThisIsATest.Of.The.Emergency.Broadcast.System.12345')
288
- wait
289
- expect(server.commands.join("\n")).to_not include("increment agent.invalid_metric")
331
+ wait do
332
+ expect(server.commands.join("\n")).to_not include("increment agent.invalid_metric")
333
+ end
290
334
  end
291
335
 
292
336
  it "should track invalid values" do
293
337
  expect(agent.logger).to receive(:warn).with(/hello.*testington/)
294
338
  agent.increment('testington', 'hello')
295
- wait
296
- expect(server.commands.join("\n")).to include("increment agent.invalid_value")
339
+ wait do
340
+ expect(server.commands.join("\n")).to include("increment agent.invalid_value")
341
+ end
297
342
  end
298
343
 
299
344
  it "should allow reasonable values" do
@@ -305,61 +350,86 @@ shared_examples "Instrumental Agent" do
305
350
  agent.increment('a', 2.2)
306
351
  agent.increment('a', 333.333)
307
352
  agent.increment('a', Float::EPSILON)
308
- wait
309
- expect(server.commands.join("\n")).to_not include("increment agent.invalid_value")
353
+ wait do
354
+ expect(server.commands.join("\n")).to_not include("increment agent.invalid_value")
355
+ end
310
356
  end
311
357
 
312
358
  it "should send notices to the server" do
359
+ Timecop.freeze
313
360
  tm = Time.now
314
361
  agent.notice("Test note", tm)
315
- wait
316
- expect(server.commands.join("\n")).to include("notice #{tm.to_i} 0 Test note")
362
+ wait do
363
+ expect(server.commands.join("\n")).to include("notice #{tm.to_i} 0 Test note")
364
+ end
317
365
  end
318
366
 
319
367
  it "should prevent a note w/ newline characters from being sent to the server" do
320
- expect(agent.notice("Test note\n")).to eq(nil)
321
- wait
322
- expect(server.commands.join("\n")).to_not include("notice Test note")
368
+ expect(agent.notice("Test_bad_note\n")).to eq(nil)
369
+
370
+ # Send a note that make it through so we're sure the bad note would have
371
+ # arrived if it was going to.
372
+ Timecop.freeze
373
+ tm = Time.now
374
+ agent.notice("Test_good_note", tm)
375
+ wait do
376
+ expect(server.commands.join("\n")).to include("Test_good_note")
377
+ end
378
+
379
+ expect(server.commands.join("\n")).to_not include("Test_bad_note")
323
380
  end
324
381
 
325
382
  it "should allow outgoing metrics to be stopped" do
326
383
  tm = Time.now
327
384
  agent.increment("foo.bar", 1, tm)
328
385
  agent.stop
386
+
387
+ # In Java the test server hangs sometimes when the agent disconnects so
388
+ # this cleans up the server.
389
+ server.stop
329
390
  wait
330
- agent.increment("foo.baz", 1, tm)
391
+ server.listen
392
+
331
393
  wait
332
- expect(server.commands.join("\n")).to include("increment foo.baz 1 #{tm.to_i}")
394
+ agent.increment("foo.baz", 1, tm)
395
+ wait do
396
+ expect(server.commands.join("\n")).to include("increment foo.baz 1 #{tm.to_i}")
397
+ end
333
398
  expect(server.commands.join("\n")).to_not include("increment foo.bar 1 #{tm.to_i}")
334
399
  end
335
400
 
336
401
  it "should allow flushing pending values to the server" do
337
402
  1.upto(100) { agent.gauge('a', rand(50)) }
338
- expect(agent.instance_variable_get(:@queue).size).to be > 0
403
+ expect(agent.instance_variable_get(:@sender_queue).size).to be > 0
339
404
  agent.flush
340
- expect(agent.instance_variable_get(:@queue).size).to eq(0)
341
- wait
342
- expect(server.commands.grep(/^gauge a /).size).to eq(100)
405
+ expect(agent.instance_variable_get(:@sender_queue).size).to eq(0)
406
+ wait do
407
+ expect(server.commands.grep(/^gauge a /).size).to eq(100)
408
+ end
343
409
  end
344
410
 
345
411
  it "should no op on an empty flush" do
346
412
  agent.flush(true)
347
- wait
348
- expect(server.commands).to be_empty
413
+ wait do
414
+ expect(server.commands).to be_empty
415
+ end
349
416
  end
350
417
  end
351
418
 
352
419
  describe Instrumental::Agent, "connection problems" do
353
420
  it "should automatically reconnect on disconnect" do
354
- agent.increment("reconnect_test", 1, 1234)
355
- wait
421
+ agent.increment("reconnect_test1", 1, 1234)
422
+ wait do
423
+ expect(server.commands.grep(/reconnect_test1/).size).to eq(1)
424
+ end
356
425
  server.disconnect_all
357
426
  wait(1)
358
427
  agent.increment('reconnect_test', 1, 5678) # triggers reconnect
359
- wait(1)
360
- expect(server.connect_count).to eq(2)
361
- # Ensure the last command sent has been received after the reconnect attempt
362
- expect(server.commands.last).to eq("increment reconnect_test 1 5678 1")
428
+ wait do
429
+ expect(server.connect_count).to eq(2)
430
+ # Ensure the last command sent has been received after the reconnect attempt
431
+ expect(server.commands.last).to eq("increment reconnect_test 1 5678 1")
432
+ end
363
433
  end
364
434
 
365
435
  context 'not listening' do
@@ -370,7 +440,7 @@ shared_examples "Instrumental Agent" do
370
440
  agent.increment('reconnect_test', 1, 1234)
371
441
  wait
372
442
  # The agent should not have sent the metric yet, the server is not responding
373
- expect(agent.queue.pop(true)).to include("increment reconnect_test 1 1234 1\n")
443
+ expect(agent.sender_queue.pop(true).first.to_s).to eq("increment reconnect_test 1 1234 1")
374
444
  end
375
445
 
376
446
  it "should warn once when buffer is full" do
@@ -405,55 +475,124 @@ shared_examples "Instrumental Agent" do
405
475
  agent.increment('reconnect_test', 1, 1234)
406
476
  wait
407
477
  # Since server hasn't responded to hello or authenticate, worker thread will not send data
408
- expect(agent.queue.pop(true)).to include("increment reconnect_test 1 1234 1\n")
478
+ expect(agent.sender_queue.pop(true).first.to_s).to eq("increment reconnect_test 1 1234 1")
409
479
  end
410
480
  end
411
481
 
412
482
  context 'server hangup' do
413
483
  it "should cancel the worker thread when the host has hung up" do
414
484
  # Start the background agent thread and let it send one metric successfully
415
- agent.gauge('connection_failure', 1, 1234)
416
- wait
485
+ agent.gauge('connection_failure1', 1, 1234)
486
+ wait do
487
+ expect(server.commands.grep(/connection_failure/).size).to eq(1)
488
+ end
417
489
  # Stop the server
418
490
  server.stop
419
491
  wait
420
492
  # Send one metric to the stopped server
421
- agent.gauge('connection_failure', 1, 1234)
422
- wait
493
+ agent.gauge('connection_failure2', 1, 1234)
423
494
  # The agent thread should have stopped running since the network write would
424
495
  # have failed. The queue will still contain the metric that has yet to be sent
425
- expect(agent.send(:running?)).to eq(false)
426
- expect(agent.queue.size).to eq(1)
496
+ wait do
497
+ expect(agent.send(:running?)).to eq(false)
498
+ end
499
+ expect(agent.sender_queue.size).to eq(1)
427
500
  end
428
501
 
429
502
  it "should restart the worker thread after hanging it up during an unreachable host event" do
430
503
  # Start the background agent thread and let it send one metric successfully
431
504
  agent.gauge('connection_failure', 1, 1234)
432
- wait
505
+ wait do
506
+ expect(server.commands.grep(/connection_failure/).size).to eq(1)
507
+ end
433
508
  # Stop the server
434
509
  server.stop
435
510
  wait
436
511
  # Send one metric to the stopped server
437
512
  agent.gauge('connection_failure', 1, 1234)
438
- wait
439
513
  # The agent thread should have stopped running since the network write would
440
514
  # have failed. The queue will still contain the metric that has yet to be sent
441
- expect(agent.send(:running?)).to eq(false)
442
- expect(agent.queue.size).to eq(1)
443
- wait
515
+ wait do
516
+ expect(agent.send(:running?)).to eq(false)
517
+ end
518
+ expect(agent.sender_queue.size).to eq(1)
444
519
  # Start the server back up again
445
520
  server.listen
446
- wait
447
521
  # Sending another metric should kickstart the background worker thread
448
522
  agent.gauge('connection_failure', 1, 1234)
449
- wait
450
523
  # The agent should now be running the background thread, and the queue should be empty
451
- expect(agent.send(:running?)).to eq(true)
452
- expect(agent.queue.size).to eq(0)
524
+ wait do
525
+ expect(agent.send(:running?)).to eq(true)
526
+ expect(agent.sender_queue.size).to eq(0)
527
+ end
453
528
  end
454
529
 
455
- end
530
+ it "should restart the worker thread after hanging it up during a bad ssl handshake event" do
531
+ # Start the background agent thread and let it send one metric successfully
532
+ agent.gauge('connection_failure', 1, 1234)
533
+ wait do
534
+ expect(server.commands.grep(/connection_failure/).size).to eq(1)
535
+ end
536
+ # Make the agent return the relevant exception on the next connection test
537
+ test_connection_fail = true
538
+ tc = agent.method(:test_connection)
539
+ allow(agent).to receive(:test_connection) do |*args, &block|
540
+ test_connection_fail ? raise(OpenSSL::SSL::SSLError.new) : tc.call(*args)
541
+ end
456
542
 
543
+ # Send one metric to the agent
544
+ agent.gauge('connection_failure', 1, 1234)
545
+ # The agent thread should have stopped running since the network write would
546
+ # have failed.
547
+ wait do
548
+ expect(agent.send(:running?)).to eq(false)
549
+ end
550
+ # The command is not in the queue
551
+ expect(agent.sender_queue.size).to eq(0)
552
+ # allow the agent to behave normally
553
+ test_connection_fail = false
554
+ # Sending another metric should kickstart the background worker thread
555
+ agent.gauge('connection_failure', 1, 1234)
556
+ # The agent should now be running the background thread, and the queue should be empty
557
+ wait do
558
+ expect(agent.send(:running?)).to eq(true)
559
+ expect(agent.sender_queue.size).to eq(0)
560
+ expect(server.commands.grep(/connection_failure/).size).to eq(2)
561
+ end
562
+ end
563
+
564
+ it "should accurately count failures so that backoff can work as intended" do
565
+ # Start the background agent thread and let it send one metric successfully
566
+ agent.gauge('connection_failure', 1, 1234)
567
+ wait do
568
+ expect(server.commands.grep(/connection_failure/).size).to eq(1)
569
+ end
570
+
571
+ # configure test_connection to fail in a way that won't kill the inner loop
572
+ test_connection_fail = true
573
+ tc = agent.method(:test_connection)
574
+ allow(agent).to receive(:test_connection) do |*args, &block|
575
+ test_connection_fail ? raise("test_connection_fail") : tc.call(*args)
576
+ end
577
+
578
+ # send some metrics
579
+ agent.gauge('connection_failure_1', 1, 1234)
580
+ agent.gauge('connection_failure_2', 1, 1234)
581
+ agent.gauge('connection_failure_3', 1, 1234)
582
+ wait do
583
+ expect(agent.instance_variable_get(:@failures)).to be > 0
584
+ expect(agent.sender_queue.size).to be > 0
585
+ end
586
+
587
+ # let the loop proceed
588
+ test_connection_fail = false
589
+
590
+ wait do
591
+ expect(agent.send(:running?)).to eq(true)
592
+ expect(agent.sender_queue.size).to eq(0)
593
+ end
594
+ end
595
+ end
457
596
 
458
597
  context 'not authenticating' do
459
598
  # Server will fail all authentication attempts
@@ -463,7 +602,7 @@ shared_examples "Instrumental Agent" do
463
602
  agent.increment('reconnect_test', 1, 1234)
464
603
  wait
465
604
  # Metrics should not have been sent since all authentication failed
466
- expect(agent.queue.pop(true)).to include("increment reconnect_test 1 1234 1\n")
605
+ expect(agent.sender_queue.pop(true).first.to_s).to eq("increment reconnect_test 1 1234 1")
467
606
  end
468
607
  end
469
608
 
@@ -500,7 +639,7 @@ shared_examples "Instrumental Agent" do
500
639
  it "should not wait to exit a process if there are no commands queued" do
501
640
  allow(agent).to receive(:open_socket) { |*args, &block| sleep(5) && block.call }
502
641
  with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
503
- if (pid = fork { agent.increment('foo', 1); agent.queue.clear })
642
+ if (pid = fork { agent.increment('foo', 1); agent.sender_queue.clear })
504
643
  tm = Time.now.to_f
505
644
  Process.wait(pid)
506
645
  diff = Time.now.to_f - tm
@@ -512,11 +651,19 @@ shared_examples "Instrumental Agent" do
512
651
 
513
652
  it "should not wait longer than EXIT_FLUSH_TIMEOUT to attempt flushing the socket when disconnecting" do
514
653
  agent.increment('foo', 1)
515
- wait
654
+ wait do
655
+ expect(server.commands.grep(/foo/).size).to eq(1)
656
+ end
516
657
  expect(agent).to receive(:flush_socket) do
517
658
  r, w = IO.pipe
518
- Thread.new do
519
- IO.select([r]) # mimic an endless blocking select poll
659
+ Thread.new do # JRuby requires extra thread here according to e9bb707e
660
+ begin
661
+ IO.select([r]) # mimic an endless blocking select poll
662
+ rescue Object => ex
663
+ # This rescue-raise prevents JRuby from printing a backtrace at
664
+ # the end of the run complaining about an exception in this thread.
665
+ raise
666
+ end
520
667
  end.join
521
668
  end
522
669
  with_constants('Instrumental::Agent::EXIT_FLUSH_TIMEOUT' => 3) do
@@ -578,7 +725,7 @@ shared_examples "Instrumental Agent" do
578
725
  expect(agent.send(:running?)).to eq(true)
579
726
 
580
727
  # Setup a failure for the next command so we'll break out of the inner
581
- # loop in run_worker_loop causing another call to open_socket
728
+ # loop in run_sender_loop causing another call to open_socket
582
729
  test_connection_fail = true
583
730
  tc = agent.method(:test_connection)
584
731
  allow(agent).to receive(:test_connection) { |*args, &block| test_connection_fail ? raise("fail") : tc.call(*args) }
@@ -629,6 +776,329 @@ shared_examples "Instrumental Agent" do
629
776
  end
630
777
  end
631
778
  end
779
+
780
+ describe Instrumental::Agent, "metrician" do
781
+ context "enabled" do
782
+ let(:metrician) { true }
783
+
784
+ it "is enabled by default" do
785
+ a = agent
786
+ expect(Metrician.agent).to eq(a)
787
+ end
788
+
789
+ it "uses agent logger" do
790
+ new_logger = double
791
+ agent.logger = new_logger
792
+ expect(Metrician.logger).to eq(new_logger)
793
+ end
794
+ end
795
+
796
+ context "disabled" do
797
+ let(:metrician) { false }
798
+
799
+ it "can be disbaled" do
800
+ expect(Metrician).to_not receive(:activate)
801
+ agent = Instrumental::Agent.new('test-token', :metrician => false)
802
+ end
803
+ end
804
+ end
805
+
806
+ describe Instrumental::Agent, "aggregation" do
807
+ context "aggregation enabled" do
808
+ let(:frequency) { 2 }
809
+
810
+ it "can be enabled at Agent.new time" do
811
+ expect(agent.frequency).to eq(2)
812
+ end
813
+
814
+ it "can be modified by setting the agent frequency" do
815
+ agent.frequency = 15
816
+ expect(agent.frequency).to eq(15)
817
+ end
818
+
819
+ it "is disabled by default" do
820
+ agent = Instrumental::Agent.new('test_token')
821
+ expect(agent.frequency.to_f).to eq(0)
822
+ end
823
+
824
+ it "should only allow frequencies that align with minutes" do
825
+ (-5..100).each do |freq|
826
+ agent.frequency = freq
827
+ expect(Instrumental::Agent::VALID_FREQUENCIES).to include(agent.frequency)
828
+ end
829
+ end
830
+
831
+ it "bypasses aggregator queue entirely for most commands when frequency == 0" do
832
+ agent.frequency = 0 # this is red - 0 for green
833
+ expect(EventAggregator).not_to receive(:new)
834
+ agent.increment('a_metric')
835
+ end
836
+
837
+ it "adds data to the event aggregator and does not immediately send it" do
838
+ Timecop.travel start_of_minute
839
+ agent.increment('test')
840
+ wait do
841
+ expect(agent.instance_variable_get(:@event_aggregator).size).to eq(1)
842
+ expect(agent.instance_variable_get(:@event_aggregator).values.values.first.metric).to eq('test')
843
+ end
844
+ end
845
+
846
+ it "batches data before sending" do
847
+ Timecop.freeze do
848
+ agent.increment('a_metric')
849
+ agent.increment('a_metric')
850
+ agent.increment('another_metric')
851
+ end
852
+ agent.flush(true)
853
+ wait do
854
+ expect(server.commands.grep(/_metric/).size).to eq(2)
855
+ aggregated_metric = server.commands.grep(/a_metric/).first.split(" ")
856
+ expect(aggregated_metric[2].to_i).to eq(2) # value
857
+ expect(aggregated_metric[4].to_i).to eq(2) # count
858
+ end
859
+ end
860
+
861
+ it "aggregates to the specified frequency within the aggregator" do
862
+ Timecop.travel(start_of_minute)
863
+ agent.frequency = 15
864
+ expect(agent.frequency).not_to be(Instrumental::Agent::DEFAULT_FREQUENCY)
865
+ agent.increment('metric', 1, Time.at(0))
866
+
867
+ # will get aligned to the closest frequency (15)
868
+ agent.increment('metric', 1, Time.at(20))
869
+ wait do
870
+ expect(agent.instance_variable_get(:@event_aggregator).values.keys).to eq(["metric:0", "metric:15"])
871
+ end
872
+ agent.flush
873
+ wait do
874
+ expect(server.commands.grep(/metric 1 0/).size).to eq(1)
875
+ expect(server.commands.grep(/metric 1 15/).size).to eq(1)
876
+ end
877
+ end
878
+
879
+ it "flushes data from both queues before sending" do
880
+ Timecop.freeze do
881
+ 100.times do |i|
882
+ agent.increment("test_metric_#{i}")
883
+ agent.increment("other_metric")
884
+ end
885
+ end
886
+
887
+ expect(agent.instance_variable_get(:@aggregator_queue).size).to be > 0
888
+ agent.flush
889
+ expect(agent.instance_variable_get(:@sender_queue).size).to eq(0)
890
+ expect(agent.instance_variable_get(:@aggregator_queue).size).to eq(0)
891
+
892
+ wait do
893
+ expect(server.commands.grep(/test_metric/).size).to eq(100)
894
+ expect(server.commands.grep(/other_metric/).size).to eq(1)
895
+ end
896
+ end
897
+
898
+ it "does not batch notices" do
899
+ agent.frequency = 60
900
+ agent.notice "things are happening", 0, 100
901
+ agent.notice "things are happening", 0, 100
902
+ agent.notice "things are happening", 0, 100
903
+ wait do
904
+ expect(server.commands.grep(/things are happening/).size).to eq(3)
905
+ end
906
+ end
907
+
908
+ it "can be disabled by setting frequency to nil" do
909
+ agent.frequency = nil
910
+ expect(EventAggregator).not_to receive(:new)
911
+ agent.increment('metric')
912
+ wait do
913
+ expect(server.commands.grep(/metric/).size).to eq(1)
914
+ end
915
+ end
916
+
917
+ it "can be disabled by setting frequency to 0" do
918
+ agent.frequency = 0
919
+ expect(EventAggregator).not_to receive(:new)
920
+ agent.increment('metric')
921
+ wait do
922
+ expect(server.commands.grep(/metric/).size).to eq(1)
923
+ end
924
+ end
925
+
926
+ it "automatically uses the highest-without-going-over frequency for a bad frequency" do
927
+ agent.frequency = 17
928
+ expect(agent.frequency).to eq(15)
929
+ agent.frequency = 69420
930
+ expect(agent.frequency).to eq(60)
931
+ agent.frequency = 0
932
+ expect(agent.frequency).to eq(0)
933
+ agent.frequency = -1
934
+ expect(agent.frequency).to eq(0)
935
+ end
936
+
937
+ it "can take strings as frequency" do
938
+ agent = Instrumental::Agent.new('test_token', :frequency => "15")
939
+ expect(agent.frequency).to eq(15)
940
+ end
941
+
942
+ it "should not be enabled at the same time as synchronous" do
943
+ expect(Instrumental::Agent.logger).to receive(:warn).with(/Synchronous and Frequency should not be enabled at the same time! Defaulting to synchronous mode./)
944
+ agent = Instrumental::Agent.new('test_token', :synchronous => true, :frequency => 6)
945
+ expect(agent.synchronous).to eq(true)
946
+ expect(agent.frequency).to eq(0)
947
+ end
948
+
949
+ it "should use synchronous mode if it is enabled, even if turned on after frequency set at start" do
950
+ agent.increment('metric')
951
+ agent.increment('metric')
952
+ agent.synchronous = true
953
+ agent.increment('metric')
954
+ wait do
955
+ expect(server.commands.grep(/metric 1/).size).to eq(1)
956
+ end
957
+ agent.flush
958
+ wait do
959
+ expect(server.commands.grep(/metric 1/).size).to eq(1)
960
+ expect(server.commands.grep(/metric 2/).size).to eq(1)
961
+ end
962
+ end
963
+
964
+ it "sends aggregated metrics after specified frequency, even if no flush is sent" do
965
+ agent.frequency = 1
966
+ Timecop.travel(start_of_minute)
967
+ agent.increment('metric')
968
+ agent.increment('metric')
969
+ agent.gauge('other', 1)
970
+ agent.gauge('other', 1)
971
+ agent.gauge('other', 1)
972
+ sleep (0.5)
973
+ wait { expect(server.commands.grep(/metric/).size).to eq(0) }
974
+ sleep (0.51) # total sleep > 1 frequency
975
+
976
+ expect(server.commands.grep(/metric 2/).size).to eq(1)
977
+ expect(server.commands.grep(/other 3/).size).to eq(1)
978
+ end
979
+
980
+ # this test really relies on the worker threads not working unexpectedly
981
+ it "will overflow if the aggregator queue is full" do
982
+ Timecop.travel(start_of_minute)
983
+ with_constants('Instrumental::Agent::MAX_BUFFER' => 3) do
984
+ allow(agent.logger).to receive(:debug)
985
+ expect(agent.logger).to receive(:debug).with("Dropping command, queue full(3): increment overflow_test 4 300 1")
986
+ agent.increment('overflow_test', 4, 300, 1)
987
+ agent.increment('overflow_test', 4, 300, 1)
988
+ agent.increment('overflow_test', 4, 300, 1)
989
+ agent.increment('overflow_test', 4, 300, 1)
990
+
991
+ expect(agent.instance_variable_get(:@aggregator_queue).size).to eq(3)
992
+ agent.flush
993
+ expect(agent.instance_variable_get(:@aggregator_queue).size).to eq(0)
994
+ end
995
+ end
996
+
997
+ it "if aggregator is at max size, next command will force a forward to the sender thread" do
998
+ Timecop.travel(start_of_minute)
999
+ with_constants('Instrumental::Agent::MAX_AGGREGATOR_SIZE' => 3) do
1000
+ agent.increment('overflow_test1')
1001
+ agent.increment('overflow_test2')
1002
+ agent.increment('overflow_test3')
1003
+ agent.increment('overflow_test4')
1004
+ agent.increment('overflow_test5')
1005
+
1006
+ # only 1 because the 5th command triggers a forward of the first 4
1007
+ wait do
1008
+ expect(agent.instance_variable_get(:@event_aggregator).size).to eq(1)
1009
+ end
1010
+ agent.flush
1011
+ wait do
1012
+ expect(server.commands.grep(/overflow_test/).size).to eq(5)
1013
+ end
1014
+ end
1015
+ end
1016
+
1017
+ context do
1018
+ let(:listen) { false }
1019
+ it "will not send aggregators to the sender queue if the sender thread is not ready" do
1020
+ Timecop.travel(start_of_minute)
1021
+ agent.frequency = 1
1022
+
1023
+ with_constants('Instrumental::Agent::MAX_BUFFER' => 3,
1024
+ 'Instrumental::Agent::MAX_AGGREGATOR_SIZE' => 4) do
1025
+
1026
+ # fill the queue
1027
+ agent.increment('overflow_test1')
1028
+ agent.increment('overflow_test2')
1029
+ agent.increment('overflow_test3')
1030
+
1031
+ # wait until they are all in the aggregator
1032
+ wait do
1033
+ expect(agent.instance_variable_get(:@aggregator_queue).size).to eq(0)
1034
+ expect(agent.instance_variable_get(:@event_aggregator).size).to eq(3)
1035
+ expect(agent.instance_variable_get(:@sender_queue).size).to eq(0)
1036
+ end
1037
+
1038
+ # fill the queue again
1039
+ agent.increment('overflow_test1')
1040
+ agent.increment('overflow_test2')
1041
+ agent.increment('overflow_test3')
1042
+
1043
+ # wait until they are all in the aggregator
1044
+ wait do
1045
+ expect(agent.instance_variable_get(:@aggregator_queue).size).to eq(0)
1046
+ expect(agent.instance_variable_get(:@event_aggregator).size).to eq(3)
1047
+ expect(agent.instance_variable_get(:@sender_queue).size).to eq(0)
1048
+ end
1049
+
1050
+ # wait for the aggregator to get forwarded and popped by the sender
1051
+ wait do
1052
+ expect(agent.instance_variable_get(:@aggregator_queue).size).to eq(0)
1053
+ expect(agent.instance_variable_get(:@event_aggregator)).to eq(nil)
1054
+ expect(agent.instance_variable_get(:@sender_queue).size).to eq(1)
1055
+ end
1056
+
1057
+ # fill the queue again
1058
+ agent.increment('overflow_test4')
1059
+ agent.increment('overflow_test5')
1060
+ agent.increment('overflow_test6')
1061
+
1062
+ # wait for them all to be in the aggregator
1063
+ wait do
1064
+ expect(agent.instance_variable_get(:@aggregator_queue).size).to eq(0)
1065
+ expect(agent.instance_variable_get(:@event_aggregator).size).to eq(3)
1066
+ expect(agent.instance_variable_get(:@sender_queue).size).to eq(1)
1067
+ end
1068
+
1069
+ # sleep until the next forward is done
1070
+ sleep(agent.frequency + 0.1)
1071
+
1072
+ # fill the queue again
1073
+ agent.increment('overflow_test7')
1074
+ agent.increment('overflow_test8')
1075
+ agent.increment('overflow_test9')
1076
+
1077
+ # because sending is blocked, the prevous aggregator never sent
1078
+ # when it hits max size, the aggregator queue starts backing up
1079
+ wait do
1080
+ expect(agent.instance_variable_get(:@aggregator_queue).size).to eq(1)
1081
+ expect(agent.instance_variable_get(:@event_aggregator).size).to eq(5)
1082
+ expect(agent.instance_variable_get(:@sender_queue).size).to eq(1)
1083
+ end
1084
+
1085
+ # send 3 more items, to overflow the aggregator queue
1086
+ allow(agent.logger).to receive(:debug)
1087
+ expect(agent.logger).to receive(:debug).with("Dropping command, queue full(3): increment overflow_testc 4 300 1")
1088
+ agent.increment('overflow_testa')
1089
+ agent.increment('overflow_testb')
1090
+ agent.increment('overflow_testc', 4, 300, 1) # will get dropped
1091
+
1092
+ wait do
1093
+ expect(agent.instance_variable_get(:@aggregator_queue).size).to eq(3)
1094
+ expect(agent.instance_variable_get(:@event_aggregator).size).to eq(5)
1095
+ expect(agent.instance_variable_get(:@sender_queue).size).to eq(1)
1096
+ end
1097
+ end
1098
+ end
1099
+ end
1100
+ end
1101
+ end
632
1102
  end
633
1103
  end
634
1104