newrelic_rpm 3.6.7.159 → 3.6.8.164

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/CHANGELOG +14 -0
  2. data/lib/new_relic/agent/agent.rb +38 -35
  3. data/lib/new_relic/agent/agent_logger.rb +6 -47
  4. data/lib/new_relic/agent/beacon_configuration.rb +10 -4
  5. data/lib/new_relic/agent/browser_monitoring.rb +39 -33
  6. data/lib/new_relic/agent/commands/agent_command.rb +4 -4
  7. data/lib/new_relic/agent/commands/agent_command_router.rb +72 -10
  8. data/lib/new_relic/agent/commands/thread_profiler_session.rb +110 -0
  9. data/lib/new_relic/agent/commands/xray_session.rb +55 -0
  10. data/lib/new_relic/agent/commands/xray_session_collection.rb +158 -0
  11. data/lib/new_relic/agent/configuration/default_source.rb +61 -24
  12. data/lib/new_relic/agent/configuration/mask_defaults.rb +2 -2
  13. data/lib/new_relic/agent/configuration/server_source.rb +1 -1
  14. data/lib/new_relic/agent/instrumentation/action_controller_subscriber.rb +2 -0
  15. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +4 -10
  16. data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +10 -11
  17. data/lib/new_relic/agent/memory_logger.rb +52 -0
  18. data/lib/new_relic/agent/new_relic_service.rb +4 -0
  19. data/lib/new_relic/agent/request_sampler.rb +32 -13
  20. data/lib/new_relic/agent/samplers/cpu_sampler.rb +6 -3
  21. data/lib/new_relic/agent/threading/agent_thread.rb +2 -1
  22. data/lib/new_relic/agent/threading/backtrace_node.rb +80 -27
  23. data/lib/new_relic/agent/threading/backtrace_service.rb +264 -0
  24. data/lib/new_relic/agent/threading/thread_profile.rb +79 -118
  25. data/lib/new_relic/agent/transaction/developer_mode_sample_buffer.rb +56 -0
  26. data/lib/new_relic/agent/transaction/force_persist_sample_buffer.rb +25 -0
  27. data/lib/new_relic/agent/transaction/slowest_sample_buffer.rb +25 -0
  28. data/lib/new_relic/agent/transaction/transaction_sample_buffer.rb +86 -0
  29. data/lib/new_relic/agent/transaction/xray_sample_buffer.rb +64 -0
  30. data/lib/new_relic/agent/transaction.rb +25 -4
  31. data/lib/new_relic/agent/transaction_sample_builder.rb +6 -10
  32. data/lib/new_relic/agent/transaction_sampler.rb +47 -202
  33. data/lib/new_relic/agent/worker_loop.rb +47 -39
  34. data/lib/new_relic/agent.rb +1 -1
  35. data/lib/new_relic/build.rb +2 -2
  36. data/lib/new_relic/coerce.rb +8 -0
  37. data/lib/new_relic/control/instance_methods.rb +1 -0
  38. data/lib/new_relic/rack/browser_monitoring.rb +15 -1
  39. data/lib/new_relic/rack/developer_mode.rb +1 -1
  40. data/lib/new_relic/transaction_sample.rb +20 -5
  41. data/lib/new_relic/version.rb +1 -1
  42. data/newrelic.yml +4 -6
  43. data/newrelic_rpm.gemspec +1 -1
  44. data/test/agent_helper.rb +11 -0
  45. data/test/environments/lib/environments/runner.rb +5 -1
  46. data/test/environments/rails21/Gemfile +2 -2
  47. data/test/environments/rails22/Gemfile +2 -2
  48. data/test/environments/rails23/Gemfile +2 -2
  49. data/test/environments/rails31/Gemfile +2 -2
  50. data/test/environments/rails32/Gemfile +2 -2
  51. data/test/multiverse/suites/agent_only/marshaling_test.rb +1 -1
  52. data/test/multiverse/suites/agent_only/testing_app.rb +6 -0
  53. data/test/multiverse/suites/agent_only/thread_profiling_test.rb +5 -5
  54. data/test/multiverse/suites/agent_only/xray_sessions_test.rb +163 -0
  55. data/test/multiverse/suites/rails/request_statistics_test.rb +2 -2
  56. data/test/multiverse/suites/rails/view_instrumentation_test.rb +20 -21
  57. data/test/new_relic/agent/agent/connect_test.rb +0 -10
  58. data/test/new_relic/agent/agent_test.rb +27 -44
  59. data/test/new_relic/agent/browser_monitoring_test.rb +0 -52
  60. data/test/new_relic/agent/commands/agent_command_router_test.rb +150 -12
  61. data/test/new_relic/agent/commands/{thread_profiler_test.rb → thread_profiler_session_test.rb} +58 -19
  62. data/test/new_relic/agent/commands/xray_session_collection_test.rb +332 -0
  63. data/test/new_relic/agent/commands/xray_session_test.rb +42 -0
  64. data/test/new_relic/agent/configuration/manager_test.rb +2 -1
  65. data/test/new_relic/agent/configuration/server_source_test.rb +10 -10
  66. data/test/new_relic/agent/cpu_sampler_test.rb +50 -0
  67. data/test/new_relic/agent/instrumentation/action_controller_subscriber_test.rb +31 -0
  68. data/test/new_relic/agent/instrumentation/queue_time_test.rb +0 -1
  69. data/test/new_relic/agent/instrumentation/sequel_test.rb +1 -1
  70. data/test/new_relic/agent/instrumentation/task_instrumentation_test.rb +0 -1
  71. data/test/new_relic/agent/memory_logger_test.rb +53 -0
  72. data/test/new_relic/agent/new_relic_service_test.rb +1 -1
  73. data/test/new_relic/agent/pipe_channel_manager_test.rb +4 -5
  74. data/test/new_relic/agent/request_sampler_test.rb +70 -20
  75. data/test/new_relic/agent/rules_engine_test.rb +6 -0
  76. data/test/new_relic/agent/threading/agent_thread_test.rb +2 -2
  77. data/test/new_relic/agent/threading/backtrace_node_test.rb +110 -17
  78. data/test/new_relic/agent/threading/backtrace_service_test.rb +567 -0
  79. data/test/new_relic/agent/threading/fake_thread.rb +4 -0
  80. data/test/new_relic/agent/threading/thread_profile_test.rb +141 -217
  81. data/test/new_relic/agent/threading/threaded_test_case.rb +3 -8
  82. data/test/new_relic/agent/transaction/developer_mode_sample_buffer_test.rb +69 -0
  83. data/test/new_relic/agent/transaction/force_persist_sample_buffer_test.rb +52 -0
  84. data/test/new_relic/agent/transaction/slowest_sample_buffer_test.rb +67 -0
  85. data/test/new_relic/agent/transaction/xray_sample_buffer_test.rb +71 -0
  86. data/test/new_relic/agent/transaction_sampler_test.rb +171 -307
  87. data/test/new_relic/agent/transaction_test.rb +33 -5
  88. data/test/new_relic/agent/worker_loop_test.rb +33 -11
  89. data/test/new_relic/coerce_test.rb +13 -0
  90. data/test/new_relic/fake_collector.rb +26 -3
  91. data/test/new_relic/multiverse_helpers.rb +2 -0
  92. data/test/new_relic/rack/browser_monitoring_test.rb +12 -0
  93. data/test/new_relic/rack/developer_mode_test.rb +2 -2
  94. data/test/new_relic/transaction_sample_test.rb +19 -2
  95. data/test/performance/lib/performance/console_reporter.rb +1 -1
  96. data/test/performance/lib/performance/test_case.rb +7 -3
  97. data/test/performance/script/runner +3 -0
  98. data/test/performance/suites/thread_profiling.rb +83 -0
  99. data/test/test_helper.rb +2 -2
  100. data.tar.gz.sig +0 -0
  101. metadata +32 -32
  102. metadata.gz.sig +1 -1
  103. data/lib/new_relic/agent/commands/thread_profiler.rb +0 -80
@@ -119,6 +119,10 @@ module NewRelic
119
119
  end
120
120
  end
121
121
 
122
+ def name_set?
123
+ @name && @name != NewRelic::Agent::UNKNOWN_METRIC
124
+ end
125
+
122
126
  def freeze_name
123
127
  return if name_frozen?
124
128
  @name = NewRelic::Agent.instance.transaction_rules.rename(@name)
@@ -144,6 +148,8 @@ module NewRelic
144
148
  # Indicate that we are entering a measured controller action or task.
145
149
  # Make sure you unwind every push with a pop call.
146
150
  def start(transaction_type)
151
+ @name = NewRelic::Agent::UNKNOWN_METRIC
152
+
147
153
  transaction_sampler.notice_first_scope_push(start_time)
148
154
  sql_sampler.notice_first_scope_push(start_time)
149
155
 
@@ -177,8 +183,8 @@ module NewRelic
177
183
 
178
184
  # Unwind one stack level. It knows if it's back at the outermost caller and
179
185
  # does the appropriate wrapup of the context.
180
- def stop(metric=::NewRelic::Agent::UNKNOWN_METRIC, end_time=Time.now)
181
- @name ||= metric unless name_frozen?
186
+ def stop(fallback_name=::NewRelic::Agent::UNKNOWN_METRIC, end_time=Time.now)
187
+ @name = fallback_name unless name_set? || name_frozen?
182
188
  freeze_name
183
189
  log_underflow if @type.nil?
184
190
 
@@ -200,11 +206,22 @@ module NewRelic
200
206
 
201
207
  # these tear everything down so need to be done after merging stats
202
208
  if self.root?
203
- agent.events.notify(:transaction_finished, @name, start_time.to_f, end_time.to_f - start_time.to_f, overview_metrics)
209
+ send_transaction_finished_event(start_time, end_time, overview_metrics)
204
210
  agent.stats_engine.end_transaction
205
211
  end
206
212
  end
207
213
 
214
+ def send_transaction_finished_event(start_time, end_time, overview_metrics)
215
+ payload = {
216
+ :name => @name,
217
+ :type => @type,
218
+ :start_timestamp => start_time.to_f,
219
+ :duration => end_time.to_f - start_time.to_f,
220
+ :overview_metrics => overview_metrics
221
+ }
222
+ agent.events.notify(:transaction_finished, payload)
223
+ end
224
+
208
225
  def merge_stats_hash
209
226
  stats_hash.resolve_scopes!(@name)
210
227
  NewRelic::Agent.instance.stats_engine.merge!(stats_hash)
@@ -364,8 +381,12 @@ module NewRelic
364
381
  self.current && self.current.recording_web_transaction?
365
382
  end
366
383
 
384
+ def self.transaction_type_is_web?(type)
385
+ [:controller, :uri, :rack, :sinatra].include?(type)
386
+ end
387
+
367
388
  def recording_web_transaction?
368
- [:controller, :uri, :rack, :sinatra].include?(@type)
389
+ self.class.transaction_type_is_web?(@type)
369
390
  end
370
391
 
371
392
  # Make a safe attempt to get the referer from a request object, generally successful when
@@ -100,10 +100,10 @@ module NewRelic
100
100
  end
101
101
 
102
102
  def finish_trace(time=Time.now.to_f, custom_params={})
103
- # This should never get called twice, but in a rare case that we can't reproduce in house it does.
104
- # log forensics and return gracefully
105
- if @sample.frozen?
106
- ::NewRelic::Agent.logger.error "Unexpected double-freeze of Transaction Trace Object: \n#{@sample.to_s}"
103
+ # Should never get called twice, but in a rare case that we can't
104
+ # reproduce in house it does. log forensics and return gracefully
105
+ if @sample.finished
106
+ ::NewRelic::Agent.logger.error "Unexpected double-finish_trace of Transaction Trace Object: \n#{@sample.to_s}"
107
107
  return
108
108
  end
109
109
  @sample.root_segment.end_trace(time.to_f - @sample_start)
@@ -112,7 +112,7 @@ module NewRelic
112
112
 
113
113
  @sample.force_persist = sample.force_persist_sample?
114
114
  @sample.threshold = transaction_trace_threshold
115
- @sample.freeze
115
+ @sample.finished = true
116
116
  @current_segment = nil
117
117
  end
118
118
 
@@ -139,10 +139,6 @@ module NewRelic
139
139
  depth
140
140
  end
141
141
 
142
- def freeze
143
- @sample.freeze unless sample.frozen?
144
- end
145
-
146
142
  def set_profile(profile)
147
143
  @sample.profile = profile
148
144
  end
@@ -159,7 +155,7 @@ module NewRelic
159
155
  end
160
156
 
161
157
  def set_transaction_name(name)
162
- @sample.params[:path] = name
158
+ @sample.transaction_name = name
163
159
  end
164
160
 
165
161
  def set_transaction_cpu_time(cpu_time)
@@ -5,6 +5,10 @@
5
5
  require 'new_relic/agent'
6
6
  require 'new_relic/control'
7
7
  require 'new_relic/agent/transaction_sample_builder'
8
+ require 'new_relic/agent/transaction/developer_mode_sample_buffer'
9
+ require 'new_relic/agent/transaction/force_persist_sample_buffer'
10
+ require 'new_relic/agent/transaction/slowest_sample_buffer'
11
+ require 'new_relic/agent/transaction/xray_sample_buffer'
8
12
 
9
13
  module NewRelic
10
14
  module Agent
@@ -22,23 +26,17 @@ module NewRelic
22
26
  def notice_scope_empty(*args); end
23
27
  end
24
28
 
25
- attr_accessor :random_sampling, :sampling_rate
26
- attr_accessor :slow_capture_threshold
27
- attr_reader :samples, :last_sample, :disabled
29
+ attr_reader :last_sample, :dev_mode_sample_buffer, :xray_sample_buffer
28
30
 
29
31
  def initialize
32
+ @dev_mode_sample_buffer = NewRelic::Agent::Transaction::DeveloperModeSampleBuffer.new
33
+ @xray_sample_buffer = NewRelic::Agent::Transaction::XraySampleBuffer.new
30
34
 
31
- # @samples is an array of recent samples up to @max_samples in
32
- # size - it's only used by developer mode
33
- @samples = []
34
- @force_persist = []
35
- @max_samples = 100
36
-
37
- # @harvest_count is a count of harvests used for random
38
- # sampling - we pull 1 @random_sample in every @sampling_rate harvests
39
- @harvest_count = 0
40
- @random_sample = nil
41
- @sampling_rate = Agent.config[:sample_rate]
35
+ @sample_buffers = []
36
+ @sample_buffers << @dev_mode_sample_buffer
37
+ @sample_buffers << @xray_sample_buffer
38
+ @sample_buffers << NewRelic::Agent::Transaction::SlowestSampleBuffer.new
39
+ @sample_buffers << NewRelic::Agent::Transaction::ForcePersistSampleBuffer.new
42
40
 
43
41
  # This lock is used to synchronize access to the @last_sample
44
42
  # and related variables. It can become necessary on JRuby or
@@ -61,25 +59,10 @@ module NewRelic
61
59
  end
62
60
  end
63
61
 
64
- # Returns the current sample id, delegated from `builder`
65
- def current_sample_id
66
- b=builder
67
- b and b.sample_id
68
- end
69
-
70
62
  def enabled?
71
63
  Agent.config[:'transaction_tracer.enabled'] || Agent.config[:developer_mode]
72
64
  end
73
65
 
74
- # Set with an integer value n, this takes one in every n
75
- # harvested samples. It also resets the harvest count to a
76
- # random integer between 0 and (n-1)
77
- def sampling_rate=(val)
78
- @sampling_rate = val.to_i
79
- @harvest_count = rand(val.to_i).to_i
80
- end
81
-
82
-
83
66
  # Creates a new transaction sample builder, unless the
84
67
  # transaction sampler is disabled. Takes a time parameter for
85
68
  # the start of the transaction sample
@@ -97,43 +80,15 @@ module NewRelic
97
80
  return unless builder
98
81
 
99
82
  segment = builder.trace_entry(time.to_f)
100
-
101
- capture_segment_trace if Agent.config[:developer_mode]
102
-
83
+ @sample_buffers.each { |sample_buffer| sample_buffer.visit_segment(segment) }
103
84
  return segment
104
85
  end
105
86
 
106
- # in developer mode, capture the stack trace with the segment.
107
- # this is cpu and memory expensive and therefore should not be
108
- # turned on in production mode
109
- def capture_segment_trace
110
- return unless Agent.config[:developer_mode]
111
- segment = builder.current_segment
112
- if segment
113
- # Strip stack frames off the top that match /new_relic/agent/
114
- trace = caller
115
- while trace.first =~/\/lib\/new_relic\/agent\//
116
- trace.shift
117
- end
118
-
119
- trace = trace[0..39] if trace.length > 40
120
- segment[:backtrace] = trace
121
- end
122
- end
123
-
124
- # Defaults to zero, otherwise delegated to the transaction
125
- # sample builder
126
- def scope_depth
127
- return 0 unless builder
128
-
129
- builder.scope_depth
130
- end
131
-
132
87
  # Informs the transaction sample builder about the end of a
133
88
  # traced scope
134
89
  def notice_pop_scope(scope, time = Time.now)
135
90
  return unless builder
136
- raise "frozen already???" if builder.sample.frozen?
91
+ raise "finished already???" if builder.sample.finished
137
92
  builder.trace_exit(scope, time.to_f)
138
93
  end
139
94
 
@@ -161,72 +116,9 @@ module NewRelic
161
116
  end
162
117
  end
163
118
 
164
- # Samples can be stored in three places: the random sample
165
- # variable, when random sampling is active, the developer mode
166
- # @samples array, and the @slowest_sample variable if it is
167
- # slower than the current occupant of that slot
168
119
  def store_sample(sample)
169
- sampler_methods = [ :store_slowest_sample ]
170
- if Agent.config[:developer_mode]
171
- sampler_methods << :store_sample_for_developer_mode
172
- end
173
- if Agent.config[:'transaction_tracer.random_sample']
174
- sampler_methods << :store_random_sample
175
- end
176
-
177
- sampler_methods.each{|sym| send(sym, sample) }
178
-
179
- if sample.force_persist_sample?
180
- store_force_persist(sample)
181
- end
182
- end
183
-
184
- # Only active when random sampling is true - this is very rarely
185
- # used. Always store the most recent sample so that random
186
- # sampling can pick a few of the samples to store, upon harvest
187
- def store_random_sample(sample)
188
- @random_sample = sample if Agent.config[:'transaction_tracer.random_sample']
189
- end
190
-
191
- def store_force_persist(sample)
192
- @force_persist << sample
193
-
194
- # WARNING - this clamp should be configurable
195
- if @force_persist.length > 15
196
- @force_persist.sort! {|a,b| b.duration <=> a.duration}
197
- @force_persist = @force_persist[0..14]
198
- end
199
- end
200
-
201
- # Samples take up a ton of memory, so we only store a lot of
202
- # them in developer mode - we truncate to @max_samples
203
- def store_sample_for_developer_mode(sample)
204
- return unless Agent.config[:developer_mode]
205
- @samples = [] unless @samples
206
- @samples << sample
207
- truncate_samples
208
- end
209
-
210
- # Sets @slowest_sample to the passed in sample if it is slower
211
- # than the current sample in @slowest_sample
212
- def store_slowest_sample(sample)
213
- if slowest_sample?(@slowest_sample, sample) && sample.threshold &&
214
- sample.duration >= sample.threshold
215
- @slowest_sample = sample
216
- end
217
- end
218
-
219
- # Checks to see if the old sample exists, or if its duration is
220
- # less than the new sample
221
- def slowest_sample?(old_sample, new_sample)
222
- old_sample.nil? || (new_sample.duration > old_sample.duration)
223
- end
224
-
225
- # Smashes the @samples array down to the length of @max_samples
226
- # by taking the last @max_samples elements of the array
227
- def truncate_samples
228
- if @samples.length > @max_samples
229
- @samples = @samples[-@max_samples..-1]
120
+ @sample_buffers.each do |sample_buffer|
121
+ sample_buffer.store(sample)
230
122
  end
231
123
  end
232
124
 
@@ -329,8 +221,7 @@ module NewRelic
329
221
  statement
330
222
  end
331
223
 
332
- # Adds non-sql metadata to a segment - generally the memcached
333
- # key
224
+ # Adds non-sql metadata to a segment - generally the memcached key
334
225
  #
335
226
  # duration is seconds, float value.
336
227
  def notice_nosql(key, duration)
@@ -343,96 +234,50 @@ module NewRelic
343
234
  params.each { |k,v| builder.current_segment[k] = v }
344
235
  end
345
236
 
346
- # Every 1/n harvests, adds the most recent sample to the harvest
347
- # array if it exists. Makes sure that the random sample is not
348
- # also the slowest sample for this harvest by `uniq!`ing the
349
- # result array
350
- #
351
- # random sampling is very, very seldom used
352
- def add_random_sample_to(result)
353
- return unless @random_sample &&
354
- Agent.config[:sample_rate] && Agent.config[:sample_rate].to_i > 0
355
- @harvest_count += 1
356
- if (@harvest_count.to_i % Agent.config[:sample_rate].to_i) == 0
357
- result << @random_sample if @random_sample
358
- @harvest_count = 0
359
- end
360
- nil # don't assume this method returns anything
361
- end
362
-
363
- def add_force_persist_to(result)
364
- result.concat(@force_persist)
365
- @force_persist = []
366
- end
367
-
368
- # Returns an array of slow samples, with either one or two
369
- # elements - one element unless random sampling is enabled. The
370
- # sample returned will be the slowest sample among those
371
- # available during this harvest
372
- def add_samples_to(result)
373
- # pull out force persist
374
- force_persist = result.select {|sample| sample.force_persist} || []
375
- result.reject! {|sample| sample.force_persist}
376
-
377
- force_persist.each {|sample| store_force_persist(sample)}
378
-
379
- result << @slowest_sample if @slowest_sample
380
-
381
- result.compact!
382
- result = result.sort_by { |x| x.duration }
383
- result = result[-1..-1] || [] # take the slowest sample
384
-
385
- add_random_sample_to(result)
386
- add_force_persist_to(result)
387
-
388
- result.uniq
389
- end
390
-
391
- # get the set of collected samples, merging into previous samples,
392
- # and clear the collected sample list. Truncates samples to a
393
- # specified segment_limit to save memory and bandwith
394
- # transmitting samples to the server.
237
+ # Gather transaction traces that we'd like to transmit to the server.
238
+ # choose_samples is responsible for determining the contents of that
239
+ # transmission, along with limits and ordering.
395
240
  def harvest(previous=[])
396
241
  return [] if !enabled?
397
- result = Array(previous)
398
242
 
399
- @samples_lock.synchronize do
400
- result = add_samples_to(result)
243
+ # If no unsent transactions from last time, we explicitly pass nil!
244
+ previous ||= []
401
245
 
402
- # clear previous transaction samples
403
- @slowest_sample = nil
404
- @random_sample = nil
246
+ @samples_lock.synchronize do
405
247
  @last_sample = nil
248
+ choose_samples(previous)
406
249
  end
250
+ end
407
251
 
408
- # Clamp the number of TTs we'll keep in memory and send
409
- result = clamp_number_tts(result, 20) if result.length > 20
410
- result
252
+ # Runs previously untransmitted samples into buffers, then chooses what
253
+ # to send based on each buffer's internal logic
254
+ def choose_samples(previous_samples)
255
+ append_previous_samples_to_buffers(previous_samples)
256
+ harvest_from_sample_buffers
411
257
  end
412
258
 
413
- # JON - THIS CODE NEEDS A UNIT TEST
414
- def clamp_number_tts(tts, limit)
415
- tts.sort! do |a,b|
416
- if a.force_persist && b.force_persist
417
- b.duration <=> a.duration
418
- elsif a.force_persist
419
- -1
420
- elsif b.force_persist
421
- 1
422
- else
423
- b.duration <=> a.duration
424
- end
259
+ # Previous samples are added to buffers to enforce limiting rules
260
+ def append_previous_samples_to_buffers(previous_samples)
261
+ @sample_buffers.each do |buffer|
262
+ buffer.store_previous(previous_samples)
425
263
  end
264
+ end
426
265
 
427
- tts[0..(limit-1)]
266
+ def harvest_from_sample_buffers
267
+ # map + flatten hit mocking issues calling to_ary on 1.9.2. We only
268
+ # want a single level flatten anyway, but, as you probably already
269
+ # know, Ruby 1.8.6 :/
270
+ result = []
271
+ @sample_buffers.each {|buffer| result.concat(buffer.harvest_samples)}
272
+ result.uniq
428
273
  end
429
274
 
430
- # reset samples without rebooting the web server
275
+ # reset samples without rebooting the web server (used by dev mode)
431
276
  def reset!
432
- @samples = []
433
- @last_sample = nil
434
- @random_sample = nil
435
- @slowest_sample = nil
277
+ @samples_lock.synchronize do
278
+ @last_sample = nil
279
+ @sample_buffers.each { |sample_buffer| sample_buffer.reset! }
280
+ end
436
281
  end
437
282
 
438
283
  # Checks to see if the transaction sampler is disabled, if
@@ -10,6 +10,8 @@ module NewRelic
10
10
  # A task is a proc or block with a specified call period in seconds.
11
11
  class WorkerLoop
12
12
 
13
+ attr_accessor :period, :propagate_errors
14
+
13
15
  # Optional argument :duration (in seconds) for how long the worker loop runs
14
16
  # or :limit (integer) for max number of iterations
15
17
  def initialize(opts={})
@@ -19,45 +21,53 @@ module NewRelic
19
21
  @duration = opts[:duration] if opts[:duration]
20
22
  @limit = opts[:limit] if opts[:limit]
21
23
  @iterations = 0
24
+ @propagate_errors = opts.fetch(:propagate_errors, false)
22
25
  end
23
26
 
24
- # returns a class-level memoized mutex to make sure we don't run overlapping
25
- def lock
26
- @@lock ||= Mutex.new
27
+ # Reset state that is changed by running the worker loop
28
+ def setup(period, task)
29
+ @task = task
30
+ @period = period if period
31
+ @should_run = true
32
+ @iterations = 0
33
+
34
+ now = Time.now
35
+ @deadline = now + @duration if @duration
36
+ @next_invocation_time = (now + @period)
27
37
  end
28
38
 
29
39
  # Run infinitely, calling the registered tasks at their specified
30
40
  # call periods. The caller is responsible for creating the thread
31
41
  # that runs this worker loop. This will run the task immediately.
32
42
  def run(period=nil, &block)
33
- @deadline = Time.now + @duration if @duration
34
- @period = period if period
35
- @next_invocation_time = (Time.now + @period)
36
- @task = block
43
+ setup(period, block)
37
44
  while keep_running? do
38
- while @now < @next_invocation_time
39
- # sleep until this next task's scheduled invocation time
40
- sleep_time = @next_invocation_time - @now
41
- sleep sleep_time if sleep_time > 0
42
- @now = Time.now
43
- end
45
+ sleep_time = schedule_next_invocation
46
+ sleep(sleep_time) if sleep_time > 0
44
47
  run_task if keep_running?
45
- @iterations += 1 if !@limit.nil?
48
+ @iterations += 1
49
+ end
50
+ end
51
+
52
+ def schedule_next_invocation
53
+ now = Time.now
54
+ while @next_invocation_time <= now && @period > 0
55
+ @next_invocation_time += @period
46
56
  end
57
+ @next_invocation_time - Time.now
47
58
  end
48
59
 
49
60
  # a simple accessor for @should_run
50
61
  def keep_running?
51
- @now = Time.now
52
62
  @should_run && under_duration? && under_limit?
53
63
  end
54
64
 
55
65
  def under_duration?
56
- !@deadline || @now < @deadline
66
+ !@deadline || Time.now < @deadline
57
67
  end
58
68
 
59
69
  def under_limit?
60
- !@limit || @iterations < @limit
70
+ @limit.nil? || @iterations < @limit
61
71
  end
62
72
 
63
73
  # Sets @should_run to false. Returns false
@@ -69,30 +79,28 @@ module NewRelic
69
79
  # possible errors. Also updates the execution time so that the
70
80
  # next run occurs on schedule, even if we execute at some odd time
71
81
  def run_task
72
- begin
73
- lock.synchronize do
82
+ if @propagate_errors
83
+ @task.call
84
+ else
85
+ begin
74
86
  @task.call
87
+ rescue ServerError => e
88
+ ::NewRelic::Agent.logger.debug "Server Error:", e
89
+ rescue NewRelic::Agent::ForceRestartException, NewRelic::Agent::ForceDisconnectException
90
+ # blow out the loop
91
+ raise
92
+ rescue RuntimeError => e
93
+ # This is probably a server error which has been logged in the server along
94
+ # with your account name.
95
+ ::NewRelic::Agent.logger.error "Error running task in worker loop, likely a server error:", e
96
+ rescue Timeout::Error, NewRelic::Agent::ServerConnectionException
97
+ # Want to ignore these because they are handled already
98
+ rescue SystemExit, NoMemoryError, SignalException
99
+ raise
100
+ rescue => e
101
+ # Don't blow out the stack for anything that hasn't already propagated
102
+ ::NewRelic::Agent.logger.error "Error running task in Agent Worker Loop:", e
75
103
  end
76
- rescue ServerError => e
77
- ::NewRelic::Agent.logger.debug "Server Error:", e
78
- rescue NewRelic::Agent::ForceRestartException, NewRelic::Agent::ForceDisconnectException
79
- # blow out the loop
80
- raise
81
- rescue RuntimeError => e
82
- # This is probably a server error which has been logged in the server along
83
- # with your account name.
84
- ::NewRelic::Agent.logger.error "Error running task in worker loop, likely a server error:", e
85
- rescue Timeout::Error, NewRelic::Agent::ServerConnectionException
86
- # Want to ignore these because they are handled already
87
- rescue SystemExit, NoMemoryError, SignalException
88
- raise
89
- rescue => e
90
- # Don't blow out the stack for anything that hasn't already propagated
91
- ::NewRelic::Agent.logger.error "Error running task in Agent Worker Loop:", e
92
- end
93
- now = Time.now
94
- while @next_invocation_time <= now && @period > 0
95
- @next_invocation_time += @period
96
104
  end
97
105
  end
98
106
  end