newrelic_rpm 3.5.7.59 → 3.5.8.64.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. data.tar.gz.sig +3 -2
  2. data/CHANGELOG +34 -3
  3. data/LICENSE +23 -0
  4. data/lib/new_relic/agent.rb +50 -3
  5. data/lib/new_relic/agent/agent.rb +40 -60
  6. data/lib/new_relic/agent/configuration/defaults.rb +9 -3
  7. data/lib/new_relic/agent/configuration/server_source.rb +4 -0
  8. data/lib/new_relic/agent/cross_app_monitor.rb +230 -0
  9. data/lib/new_relic/agent/cross_app_tracing.rb +274 -0
  10. data/lib/new_relic/agent/database.rb +28 -10
  11. data/lib/new_relic/agent/error_collector.rb +5 -0
  12. data/lib/new_relic/agent/event_listener.rb +4 -0
  13. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +53 -34
  14. data/lib/new_relic/agent/instrumentation/metric_frame.rb +16 -3
  15. data/lib/new_relic/agent/instrumentation/net.rb +13 -11
  16. data/lib/new_relic/agent/instrumentation/resque.rb +10 -10
  17. data/lib/new_relic/agent/instrumentation/sinatra.rb +19 -9
  18. data/lib/new_relic/agent/new_relic_service.rb +63 -9
  19. data/lib/new_relic/agent/pipe_service.rb +8 -12
  20. data/lib/new_relic/agent/rules_engine.rb +72 -0
  21. data/lib/new_relic/agent/shim_agent.rb +0 -1
  22. data/lib/new_relic/agent/sql_sampler.rb +3 -2
  23. data/lib/new_relic/agent/stats.rb +149 -0
  24. data/lib/new_relic/agent/stats_engine.rb +9 -0
  25. data/lib/new_relic/agent/stats_engine/gc_profiler.rb +1 -24
  26. data/lib/new_relic/agent/stats_engine/metric_stats.rb +84 -185
  27. data/lib/new_relic/agent/stats_engine/stats_hash.rb +58 -0
  28. data/lib/new_relic/agent/stats_engine/transactions.rb +10 -2
  29. data/lib/new_relic/agent/transaction_info.rb +31 -6
  30. data/lib/new_relic/agent/transaction_sample_builder.rb +19 -8
  31. data/lib/new_relic/agent/transaction_sampler.rb +17 -10
  32. data/lib/new_relic/helper.rb +32 -0
  33. data/lib/new_relic/local_environment.rb +24 -32
  34. data/lib/new_relic/okjson.rb +599 -0
  35. data/lib/new_relic/transaction_sample.rb +2 -1
  36. data/lib/new_relic/transaction_sample/segment.rb +2 -1
  37. data/lib/new_relic/version.rb +1 -1
  38. data/newrelic.yml +27 -41
  39. data/test/multiverse/suites/agent_only/audit_log_test.rb +2 -4
  40. data/test/multiverse/suites/agent_only/config/newrelic.yml +1 -2
  41. data/test/multiverse/suites/agent_only/{cross_process_test.rb → cross_application_tracing_test.rb} +3 -3
  42. data/test/multiverse/suites/agent_only/key_transactions_test.rb +66 -0
  43. data/test/multiverse/suites/agent_only/marshaling_test.rb +9 -22
  44. data/test/multiverse/suites/agent_only/rename_rule_test.rb +57 -0
  45. data/test/multiverse/suites/agent_only/start_up_test.rb +1 -1
  46. data/test/multiverse/suites/agent_only/thread_profiling_test.rb +17 -6
  47. data/test/multiverse/suites/rails/error_tracing_test.rb +20 -8
  48. data/test/multiverse/suites/resque/instrumentation_test.rb +2 -2
  49. data/test/multiverse/suites/sinatra/Envfile +2 -0
  50. data/test/multiverse/suites/sinatra/config/newrelic.yml +1 -0
  51. data/test/multiverse/suites/sinatra/sinatra_metric_explosion_test.rb +5 -5
  52. data/test/multiverse/suites/sinatra/sinatra_test.rb +75 -4
  53. data/test/new_relic/agent/agent/connect_test.rb +45 -1
  54. data/test/new_relic/agent/agent/start_worker_thread_test.rb +0 -3
  55. data/test/new_relic/agent/agent_test.rb +20 -40
  56. data/test/new_relic/agent/agent_test_controller_test.rb +24 -19
  57. data/test/new_relic/agent/busy_calculator_test.rb +1 -1
  58. data/test/new_relic/agent/configuration/server_source_test.rb +8 -3
  59. data/test/new_relic/agent/cross_app_monitor_test.rb +237 -0
  60. data/test/new_relic/agent/database_test.rb +60 -16
  61. data/test/new_relic/agent/error_collector_test.rb +28 -4
  62. data/test/new_relic/agent/event_listener_test.rb +23 -2
  63. data/test/new_relic/agent/instrumentation/controller_instrumentation_test.rb +53 -0
  64. data/test/new_relic/agent/instrumentation/metric_frame_test.rb +95 -0
  65. data/test/new_relic/agent/instrumentation/net_instrumentation_test.rb +414 -59
  66. data/test/new_relic/agent/instrumentation/task_instrumentation_test.rb +2 -5
  67. data/test/new_relic/agent/method_tracer_test.rb +4 -2
  68. data/test/new_relic/agent/new_relic_service_test.rb +108 -6
  69. data/test/new_relic/agent/pipe_channel_manager_test.rb +1 -1
  70. data/test/new_relic/agent/pipe_service_test.rb +9 -9
  71. data/test/new_relic/agent/rpm_agent_test.rb +0 -11
  72. data/test/new_relic/agent/rules_engine_test.rb +82 -0
  73. data/test/new_relic/agent/shim_agent_test.rb +0 -4
  74. data/test/new_relic/agent/sql_sampler_test.rb +7 -0
  75. data/test/new_relic/agent/stats_engine/gc_profiler_test.rb +85 -0
  76. data/test/new_relic/agent/stats_engine/metric_stats_test.rb +110 -23
  77. data/test/new_relic/agent/stats_engine_test.rb +1 -46
  78. data/test/new_relic/agent/stats_hash_test.rb +93 -0
  79. data/test/new_relic/agent/stats_test.rb +197 -0
  80. data/test/new_relic/agent/transaction_info_test.rb +63 -11
  81. data/test/new_relic/agent/transaction_sample_builder_test.rb +10 -3
  82. data/test/new_relic/agent/transaction_sampler_test.rb +92 -80
  83. data/test/new_relic/agent_test.rb +35 -5
  84. data/test/new_relic/control_test.rb +1 -1
  85. data/test/new_relic/fake_collector.rb +87 -9
  86. data/test/new_relic/helper_test.rb +24 -0
  87. data/test/new_relic/metric_data_test.rb +11 -11
  88. data/test/new_relic/metric_spec_test.rb +1 -1
  89. data/test/script/ci.sh +1 -1
  90. data/test/test_contexts.rb +0 -1
  91. data/test/test_helper.rb +21 -3
  92. metadata +32 -16
  93. metadata.gz.sig +0 -0
  94. data/lib/new_relic/agent/cross_process_monitoring.rb +0 -187
  95. data/lib/new_relic/stats.rb +0 -337
  96. data/test/new_relic/agent/cross_process_monitoring_test.rb +0 -190
  97. data/test/new_relic/agent/stats_engine/metric_stats/harvest_test.rb +0 -133
  98. data/test/new_relic/fakes_sending_data.rb +0 -30
  99. data/test/new_relic/stats_test.rb +0 -421
@@ -16,6 +16,6 @@ class StartUpTest < Test::Unit::TestCase
16
16
 
17
17
  jruby_noise.each {|noise| output.gsub!(noise, "")}
18
18
 
19
- assert_equal '', output
19
+ assert_equal '', output.chomp
20
20
  end
21
21
  end
@@ -13,6 +13,7 @@ class ThreadProfilingTest < Test::Unit::TestCase
13
13
 
14
14
  NewRelic::Agent::Agent.instance_variable_set(:@instance, nil)
15
15
  NewRelic::Agent.manual_start(:'thread_profiler.enabled' => true)
16
+ NewRelic::Agent.instance.service.request_timeout = 0.5
16
17
 
17
18
  @agent = NewRelic::Agent.instance
18
19
  @thread_profiler = @agent.thread_profiler
@@ -51,11 +52,11 @@ class ThreadProfilingTest < Test::Unit::TestCase
51
52
 
52
53
  def test_thread_profiling
53
54
  @agent.send(:check_for_agent_commands)
54
- sleep(1)
55
- NewRelic::Agent.shutdown
55
+
56
+ let_it_finish
56
57
 
57
58
  profile_data = $collector.calls_for('profile_data')[0]
58
- assert_equal(666, profile_data[0])
59
+ assert_equal('666', profile_data.run_id)
59
60
 
60
61
  poll_count = profile_data[1][0][3]
61
62
  assert poll_count > 25, "Expected poll_count > 25, but was #{poll_count}"
@@ -67,15 +68,25 @@ class ThreadProfilingTest < Test::Unit::TestCase
67
68
  $collector.mock['get_agent_commands'] = [200, {'return_value' => STOP_COMMAND}]
68
69
  @agent.send(:check_for_agent_commands)
69
70
 
70
- sleep(0.1)
71
- NewRelic::Agent.shutdown
71
+ let_it_finish
72
72
 
73
73
  profile_data = $collector.calls_for('profile_data')[0]
74
- assert_equal(666, profile_data[0])
74
+ assert_equal('666', profile_data.run_id)
75
75
 
76
76
  poll_count = profile_data[1][0][3]
77
77
  assert poll_count < 10, "Expected poll_count < 10, but was #{poll_count}"
78
78
  end
79
+
80
+
81
+ def let_it_finish
82
+ Timeout.timeout(2) do
83
+ until @thread_profiler.finished?
84
+ sleep(0.1)
85
+ end
86
+ end
87
+
88
+ NewRelic::Agent.shutdown
89
+ end
79
90
  end
80
91
  end
81
92
 
@@ -85,22 +85,26 @@ class ErrorsWithoutSSCTest < ActionDispatch::IntegrationTest
85
85
 
86
86
  def test_should_capture_error_raised_in_view
87
87
  get '/error/view_error'
88
- assert_error_reported_once('this is an uncaught view error')
88
+ assert_error_reported_once('this is an uncaught view error',
89
+ 'Controller/error/view_error')
89
90
  end
90
91
 
91
92
  def test_should_capture_error_raised_in_controller
92
93
  get '/error/controller_error'
93
- assert_error_reported_once('this is an uncaught controller error')
94
+ assert_error_reported_once('this is an uncaught controller error',
95
+ 'Controller/error/controller_error')
94
96
  end
95
97
 
96
98
  def test_should_capture_error_raised_in_model
97
99
  get '/error/model_error'
98
- assert_error_reported_once('this is an uncaught model error')
100
+ assert_error_reported_once('this is an uncaught model error',
101
+ 'Controller/error/model_error')
99
102
  end
100
103
 
101
104
  def test_should_capture_noticed_error_in_controller
102
105
  get '/error/noticed_error'
103
- assert_error_reported_once('this error should be noticed')
106
+ assert_error_reported_once('this error should be noticed',
107
+ 'Controller/error/noticed_error')
104
108
  end
105
109
 
106
110
  # Important choice of controllor_error, since this goes through both the
@@ -160,17 +164,25 @@ class ErrorsWithoutSSCTest < ActionDispatch::IntegrationTest
160
164
 
161
165
  protected
162
166
 
163
- def assert_errors_reported(message, queued_count, total_count=queued_count)
167
+ def assert_errors_reported(message, queued_count, total_count=queued_count, txn_name=nil)
164
168
  error_count = NewRelic::Agent::Agent.instance.stats_engine.get_stats("Errors/all")
165
- assert_equal total_count, error_count.call_count
169
+ assert_equal(total_count, error_count.call_count,
170
+ 'Incorrect call count on Errors/all')
171
+
172
+ if txn_name
173
+ error_count = NewRelic::Agent::Agent.instance.stats_engine \
174
+ .get_stats("Errors/#{txn_name}")
175
+ assert_equal(total_count, error_count.call_count,
176
+ "Incorrect call count on Errors/#{txn_name}")
177
+ end
166
178
 
167
179
  assert_equal(queued_count,
168
180
  @error_collector.errors.select{|error| error.message == message}.size,
169
181
  "Wrong number of errors with message '#{message} found'")
170
182
  end
171
183
 
172
- def assert_error_reported_once(message)
173
- assert_errors_reported(message, 1)
184
+ def assert_error_reported_once(message, txn_name=nil)
185
+ assert_errors_reported(message, 1, 1, txn_name)
174
186
  end
175
187
  end
176
188
 
@@ -40,7 +40,7 @@ class ResqueTest < Test::Unit::TestCase
40
40
  end
41
41
 
42
42
  def start_worker_child(env_vars=nil)
43
- worker_cmd = "NEWRELIC_DISPATCHER=resque #{env_vars} QUEUE=* bundle exec rake resque:work"
43
+ worker_cmd = "#{env_vars} QUEUE=* bundle exec rake resque:work"
44
44
  @worker_pid = Process.fork
45
45
  Process.exec(worker_cmd) if @worker_pid.nil?
46
46
  end
@@ -52,7 +52,7 @@ class ResqueTest < Test::Unit::TestCase
52
52
 
53
53
  def start_worker_background(env_vars=nil)
54
54
  worker_cmd = "PIDFILE=#{@pidfile} TERM_CHILD=1 RESQUE_TERM_TIMEOUT=1 BACKGROUND=1 " +
55
- "NEWRELIC_DISPATCHER=resque #{env_vars} QUEUE=* bundle exec rake resque:work"
55
+ "#{env_vars} QUEUE=* bundle exec rake resque:work"
56
56
  system(worker_cmd)
57
57
  end
58
58
 
@@ -5,9 +5,11 @@ end
5
5
  gemfile <<-RB
6
6
  gem 'sinatra', '1.3.3'
7
7
  gem 'rack-test', :require => 'rack/test'
8
+ gem 'mocha'
8
9
  RB
9
10
 
10
11
  gemfile <<-RB
11
12
  gem 'sinatra', '1.2.8'
12
13
  gem 'rack-test', :require => 'rack/test'
14
+ gem 'mocha'
13
15
  RB
@@ -10,6 +10,7 @@ development:
10
10
  license_key: bootstrap_newrelic_admin_license_key_000
11
11
  developer_mode: false
12
12
  app_name: test
13
+ sync_startup: true
13
14
  host: 127.0.0.1
14
15
  api_host: 127.0.0.1
15
16
  port: <%= 30_000 + ($$ % 10_000) %>
@@ -42,14 +42,14 @@ class SinatraMetricExplosionTest < Test::Unit::TestCase
42
42
 
43
43
  def test_transaction_name_from_route
44
44
  get '/hello/world'
45
- metric_names = ::NewRelic::Agent.agent.stats_engine.stats_hash.keys.map{|k| k.name}
45
+ metric_names = ::NewRelic::Agent.agent.stats_engine.metrics
46
46
  assert metric_names.include?('Controller/Sinatra/SinatraTestApp/GET hello/([^/?#]+)')
47
47
  assert metric_names.include?('Apdex/Sinatra/SinatraTestApp/GET hello/([^/?#]+)')
48
48
  end
49
49
 
50
50
  def test_transaction_name_from_path
51
51
  get '/wrong'
52
- metric_names = ::NewRelic::Agent.agent.stats_engine.stats_hash.keys.map{|k| k.name}
52
+ metric_names = ::NewRelic::Agent.agent.stats_engine.metrics
53
53
  assert metric_names.include?('Controller/Sinatra/SinatraTestApp/GET (unknown)')
54
54
  assert metric_names.include?('Apdex/Sinatra/SinatraTestApp/GET (unknown)')
55
55
  end
@@ -61,8 +61,8 @@ class SinatraMetricExplosionTest < Test::Unit::TestCase
61
61
  get '/hello/isitmeyourelookingfor?'
62
62
  get '/another_controller'
63
63
 
64
- metric_names = ::NewRelic::Agent.agent.stats_engine.stats_hash.keys.
65
- map{|k| k.name} - ['CPU/User Time', "Middleware/all", "WebFrontend/QueueTime", "WebFrontend/WebServer/all"]
64
+ metric_names = ::NewRelic::Agent.agent.stats_engine.metrics
65
+ metric_names -= ['CPU/User Time', "Middleware/all", "WebFrontend/QueueTime", "WebFrontend/WebServer/all"]
66
66
  assert_equal 6, metric_names.size, "Explosion detected in: #{metric_names.inspect}"
67
67
  end
68
68
 
@@ -70,7 +70,7 @@ class SinatraMetricExplosionTest < Test::Unit::TestCase
70
70
  assert_nothing_raised do
71
71
  post '/some/garbage'
72
72
  end
73
- metric_names = ::NewRelic::Agent.agent.stats_engine.stats_hash.keys.map{|k| k.name}
73
+ metric_names = ::NewRelic::Agent.agent.stats_engine.metrics
74
74
  assert metric_names.include?('Controller/Sinatra/SinatraTestApp/POST (unknown)')
75
75
  assert metric_names.include?('Apdex/Sinatra/SinatraTestApp/POST (unknown)')
76
76
  end
@@ -1,6 +1,10 @@
1
+ require 'mocha'
1
2
 
2
3
  class SinatraRouteTestApp < Sinatra::Base
3
4
  configure do
5
+ # display exceptions so we see what's going on
6
+ disable :show_exceptions
7
+
4
8
  # create a condition (sintra's version of a before_filter) that returns the
5
9
  # value that was passed into it.
6
10
  set :my_condition do |boolean|
@@ -8,9 +12,6 @@ class SinatraRouteTestApp < Sinatra::Base
8
12
  halt 404 unless boolean
9
13
  end
10
14
  end
11
-
12
- # treat errors like production for testing purposes
13
- set :show_exceptions, false
14
15
  end
15
16
 
16
17
  get '/user/login' do
@@ -22,10 +23,37 @@ class SinatraRouteTestApp < Sinatra::Base
22
23
  "Welcome #{id}"
23
24
  end
24
25
 
25
- get '/error' do
26
+ get '/raise' do
26
27
  raise "Uh-oh"
27
28
  end
28
29
 
30
+ # check that pass works properly
31
+ condition { pass { halt 418, "I'm a teapot." } }
32
+ get('/pass') { }
33
+
34
+ get '/pass' do
35
+ "I'm not a teapot."
36
+ end
37
+
38
+ class Error < StandardError; end
39
+ error(Error) { halt 200, 'nothing happened' }
40
+ condition { raise Error }
41
+ get('/error') { }
42
+
43
+ def perform_action_with_newrelic_trace(options)
44
+ $last_sinatra_route = options[:name]
45
+ super
46
+ end
47
+
48
+ get '/route/:name' do |name|
49
+ # usually this would be a db test or something
50
+ pass if name != 'match'
51
+ 'first route'
52
+ end
53
+
54
+ get '/route/no_match' do
55
+ 'second route'
56
+ end
29
57
  end
30
58
 
31
59
  class SinatraTest < Test::Unit::TestCase
@@ -62,7 +90,50 @@ class SinatraTest < Test::Unit::TestCase
62
90
  end
63
91
 
64
92
  def test_shown_errors_get_caught
93
+ get '/raise'
94
+ assert_equal 1, ::NewRelic::Agent.agent.error_collector.errors.size
95
+ end
96
+
97
+ def test_does_not_break_pass
98
+ get '/pass'
99
+ assert_equal 200, last_response.status
100
+ assert_equal "I'm not a teapot.", last_response.body
101
+ end
102
+
103
+ def test_does_not_break_error_handling
104
+ get '/error'
105
+ assert_equal 200, last_response.status
106
+ assert_equal "nothing happened", last_response.body
107
+ end
108
+
109
+ def test_sees_handled_error
65
110
  get '/error'
66
111
  assert_equal 1, ::NewRelic::Agent.agent.error_collector.errors.size
67
112
  end
113
+
114
+ def test_correct_pattern
115
+ get '/route/match'
116
+ assert_equal 'first route', last_response.body
117
+ assert_equal 'GET route/([^/?#]+)', $last_sinatra_route
118
+
119
+ get '/route/no_match'
120
+ assert_equal 'second route', last_response.body
121
+
122
+ # Ideally we could handle this assert, but we can't rename transactions
123
+ # in flight at this point. Once we get that ability, consider patching
124
+ # process_route to notify of route name changes.
125
+
126
+ # assert_equal 'GET route/no_match', $last_sinatra_route
127
+ end
128
+
129
+ def test_set_unknown_transaction_name_if_error_in_routing
130
+ ::NewRelic::Agent::Instrumentation::Sinatra::NewRelic \
131
+ .stubs(:http_verb).raises(StandardError.new('madness'))
132
+
133
+ get '/user/login'
134
+
135
+ metric_names = ::NewRelic::Agent.agent.stats_engine.metrics
136
+ assert(metric_names.include?('Controller/Sinatra/SinatraRouteTestApp/(unknown)'),
137
+ "#{metric_names} should include 'Controller/Sinatra/SinatraRouteTestApp/(unknown)'")
138
+ end
68
139
  end
@@ -181,7 +181,7 @@ class NewRelic::Agent::Agent::ConnectTest < Test::Unit::TestCase
181
181
  query_server_for_configuration
182
182
  end
183
183
 
184
- def test_connect_to_server_gets_config_from_collector
184
+ def test_connect_gets_config
185
185
  NewRelic::Agent.manual_start
186
186
  NewRelic::Agent::Agent.instance.service = default_service(
187
187
  :connect => {'agent_run_id' => 23, 'config' => 'a lot'})
@@ -194,6 +194,50 @@ class NewRelic::Agent::Agent::ConnectTest < Test::Unit::TestCase
194
194
  NewRelic::Agent.shutdown
195
195
  end
196
196
 
197
+ def test_finish_setup_saves_transaction_name_rules
198
+ NewRelic::Agent.instance.instance_variable_set(:@transaction_rules,
199
+ NewRelic::Agent::RulesEngine.new)
200
+ config = {
201
+ 'transaction_name_rules' => [ { 'match_expression' => '88',
202
+ 'replacement' => '**' },
203
+ { 'match_expression' => 'xx',
204
+ 'replacement' => 'XX' } ]
205
+ }
206
+ finish_setup(config)
207
+
208
+ rules = NewRelic::Agent.instance.transaction_rules
209
+ assert_equal 2, rules.size
210
+ assert(rules.find{|r| r.match_expression == /88/ && r.replacement == '**' },
211
+ "rule not found among #{rules}")
212
+ assert(rules.find{|r| r.match_expression == /xx/ && r.replacement == 'XX' },
213
+ "rule not found among #{rules}")
214
+ ensure
215
+ NewRelic::Agent.instance.instance_variable_set(:@transaction_rules,
216
+ NewRelic::Agent::RulesEngine.new)
217
+ end
218
+
219
+ def test_finish_setup_saves_metric_name_rules
220
+ NewRelic::Agent.instance.instance_variable_set(:@metric_rules,
221
+ NewRelic::Agent::RulesEngine.new)
222
+ config = {
223
+ 'metric_name_rules' => [ { 'match_expression' => '77',
224
+ 'replacement' => '&&' },
225
+ { 'match_expression' => 'yy',
226
+ 'replacement' => 'YY' }]
227
+ }
228
+ finish_setup(config)
229
+
230
+ rules = NewRelic::Agent.instance.metric_rules
231
+ assert_equal 2, rules.size
232
+ assert(rules.find{|r| r.match_expression == /77/ && r.replacement == '&&' },
233
+ "rule not found among #{rules}")
234
+ assert(rules.find{|r| r.match_expression == /yy/ && r.replacement == 'YY' },
235
+ "rule not found among #{rules}")
236
+ ensure
237
+ NewRelic::Agent.instance.instance_variable_set(:@metric_rules,
238
+ NewRelic::Agent::RulesEngine.new)
239
+ end
240
+
197
241
  def test_finish_setup
198
242
  config = {
199
243
  'agent_run_id' => 'fishsticks',
@@ -36,13 +36,10 @@ class NewRelic::Agent::Agent::StartWorkerThreadTest < Test::Unit::TestCase
36
36
 
37
37
  self.expects(:reset_stats)
38
38
  self.expects(:sleep).with(30)
39
-
40
- @metric_ids = 'this is not an empty hash'
41
39
  @connected = true
42
40
 
43
41
  handle_force_restart(error)
44
42
 
45
- assert_equal({}, @metric_ids)
46
43
  assert_equal(:pending, @connect_state)
47
44
  end
48
45
 
@@ -70,10 +70,6 @@ module NewRelic
70
70
  @agent.instance_eval { transmit_data }
71
71
  end
72
72
 
73
- def test_serialize
74
- assert_equal([{}, [], []], @agent.send(:serialize), "should return nil when shut down")
75
- end
76
-
77
73
  def test_harvest_transaction_traces
78
74
  assert_equal([], @agent.send(:harvest_transaction_traces), 'should return transaction traces')
79
75
  end
@@ -121,23 +117,29 @@ module NewRelic
121
117
  'should return timeslice data')
122
118
  end
123
119
 
124
- def test_harvest_timelice_data_should_be_thread_safe
125
- 2000.times do |i|
126
- @agent.stats_engine.stats_hash[i.to_s] = NewRelic::StatsBase.new
127
- end
120
+ # This test asserts nothing about correctness of logging data from multiple
121
+ # threads, since the get_stats + record_data_point combo is not designed
122
+ # to be thread-safe, but it does ensure that writes to the stats hash
123
+ # via this path that happen concurrently with harvests will not cause
124
+ # 'hash modified during iteration' errors.
125
+ def test_harvest_timeslice_data_should_be_thread_safe
126
+ threads = []
127
+ nthreads = 10
128
+ nmetrics = 100
128
129
 
129
- harvest = Thread.new("Harvesting Test run timeslices") do
130
- @agent.send(:harvest_timeslice_data)
131
- end
132
-
133
- app = Thread.new("Harvesting Test Modify stats_hash") do
134
- 200.times do |i|
135
- @agent.stats_engine.stats_hash["a#{i}"] = NewRelic::StatsBase.new
130
+ assert_nothing_raised do
131
+ nthreads.times do |tid|
132
+ t = Thread.new do
133
+ nmetrics.times do |mid|
134
+ @agent.stats_engine.get_stats("m#{mid}").record_data_point(1)
135
+ end
136
+ end
137
+ t.abort_on_exception = true
138
+ threads << t
136
139
  end
137
- end
138
140
 
139
- assert_nothing_raised do
140
- [app, harvest].each{|t| t.join}
141
+ 100.times { @agent.send(:harvest_timeslice_data) }
142
+ threads.each { |t| t.join }
141
143
  end
142
144
  end
143
145
 
@@ -228,28 +230,6 @@ module NewRelic
228
230
  assert_equal 0, NewRelic::Agent.get_stats("Errors/all").call_count
229
231
  end
230
232
 
231
- def test_fill_metric_id_cache_from_collect_response
232
- response = [[{"scope"=>"Controller/blogs/index", "name"=>"Database/SQL/other"}, 1328],
233
- [{"scope"=>"", "name"=>"WebFrontend/QueueTime"}, 10],
234
- [{"scope"=>"", "name"=>"ActiveRecord/Blog/find"}, 1017]]
235
-
236
- @agent.send(:fill_metric_id_cache, response)
237
- assert_equal 1328, @agent.metric_ids[MetricSpec.new('Database/SQL/other', 'Controller/blogs/index')]
238
- assert_equal 10, @agent.metric_ids[MetricSpec.new('WebFrontend/QueueTime')]
239
- assert_equal 1017, @agent.metric_ids[MetricSpec.new('ActiveRecord/Blog/find')]
240
- end
241
-
242
- def test_fill_metric_id_cache_from_collect_response
243
- response = [[{"scope"=>"Controller/blogs/index", "name"=>"Database/SQL/other"}, 1328],
244
- [{"scope"=>"", "name"=>"WebFrontend/QueueTime"}, 10],
245
- [{"scope"=>"", "name"=>"ActiveRecord/Blog/find"}, 1017]]
246
-
247
- @agent.send(:fill_metric_id_cache, response)
248
- assert_equal 1328, @agent.metric_ids[MetricSpec.new('Database/SQL/other', 'Controller/blogs/index')]
249
- assert_equal 10, @agent.metric_ids[MetricSpec.new('WebFrontend/QueueTime')]
250
- assert_equal 1017, @agent.metric_ids[MetricSpec.new('ActiveRecord/Blog/find')]
251
- end
252
-
253
233
  def test_connect_retries_on_timeout
254
234
  service = @agent.service
255
235
  def service.connect(opts={})