newrelic_rpm 3.5.6.46.beta → 3.5.6.48.beta

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,6 +1,14 @@
1
1
 
2
2
  # New Relic Ruby Agent Release Notes #
3
3
 
4
+ ## v3.5.6 ##
5
+
6
+ * Fix two Resque-related issues
7
+
8
+ Fixes a possible hang on exit of an instrumented Resque master process
9
+ (https://github.com/defunkt/resque/issues/578), as well as a file descriptor
10
+ leak that could occur during startup of the Resque master process.
11
+
4
12
  ## v3.5.5 ##
5
13
 
6
14
  * Add thread profiling support
@@ -458,6 +458,14 @@ module NewRelic
458
458
  :info, "Connecting workers after forking.")
459
459
  end
460
460
 
461
+ # Return true if we're using resque and it hasn't had a chance to (potentially)
462
+ # daemonize itself. This avoids hanging when there's a Thread started
463
+ # before Resque calls Process.daemon (Jira RUBY-857)
464
+ def defer_for_resque?
465
+ NewRelic::Agent.config[:dispatcher] == :resque &&
466
+ !NewRelic::Agent::PipeChannelManager.listener.started?
467
+ end
468
+
461
469
  # Sanity-check the agent configuration and start the agent,
462
470
  # setting up the worker thread and the exit handler to shut
463
471
  # down the agent
@@ -475,6 +483,12 @@ module NewRelic
475
483
  # Logs a bunch of data and starts the agent, if needed
476
484
  def start
477
485
  return if already_started? || disabled?
486
+
487
+ if defer_for_resque?
488
+ ::NewRelic::Agent.logger.debug "Deferring startup for Resque in case it daemonizes"
489
+ return
490
+ end
491
+
478
492
  @started = true
479
493
  @local_host = determine_host
480
494
  log_startup
@@ -1,2 +1,2 @@
1
- # GITSHA: 3d7e13a70958fa4cf64f0236dcf1a751da6824f7
2
- module NewRelic; module VERSION; BUILD='46.beta'; end; end
1
+ # GITSHA: e81889c2bce97574ec682dafee12015e13ccb2e1
2
+ module NewRelic; module VERSION; BUILD='48.beta'; end; end
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/ruby
2
+
3
+ module NewRelic
4
+ module LatestChanges
5
+ def self.default_changelog
6
+ File.join(File.dirname(__FILE__), '..', '..', 'CHANGELOG')
7
+ end
8
+
9
+ def self.read(changelog=default_changelog)
10
+ footer = <<'EOS'
11
+ See https://github.com/newrelic/rpm/blob/master/CHANGELOG for a full list of
12
+ changes.
13
+ EOS
14
+
15
+ return footer unless File.exists?(changelog)
16
+
17
+ version_count = 0
18
+ changes = []
19
+ File.read(changelog).each_line do |line|
20
+ if line.match(/##\s+v[\d.]+\s+##/)
21
+ version_count += 1
22
+ end
23
+ break if version_count >= 2
24
+ changes << line.chomp
25
+ end
26
+
27
+ changes << footer
28
+ change_message = changes.join("\n")
29
+ end
30
+ end
31
+ end
data/newrelic_rpm.gemspec CHANGED
@@ -3,12 +3,13 @@
3
3
  lib = File.expand_path('../lib', __FILE__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
  require 'new_relic/version'
6
+ require 'new_relic/latest_changes'
6
7
 
7
8
  Gem::Specification.new do |s|
8
9
  s.name = "newrelic_rpm"
9
10
  s.version = NewRelic::VERSION::STRING
10
11
  s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
11
- s.authors = [ "Jason Clark", "Sam Goldstein", "Jon Guymon", "Ben Weintraub" ]
12
+ s.authors = [ "Jason Clark", "Sam Goldstein", "Michael Granger", "Jon Guymon", "Ben Weintraub" ]
12
13
  s.date = Time.now.strftime('%Y-%m-%d')
13
14
  s.description = <<-EOS
14
15
  New Relic is a performance management system, developed by New Relic,
@@ -38,22 +39,5 @@ EOS
38
39
  s.require_paths = ["lib"]
39
40
  s.rubygems_version = Gem::VERSION
40
41
  s.summary = "New Relic Ruby Agent"
41
-
42
- version_count = 0
43
- changes = []
44
- File.read(File.join(File.dirname(__FILE__), 'CHANGELOG')).each_line do |line|
45
- if line.match(/##\s+v[\d.]+\s+##/)
46
- version_count += 1
47
- end
48
- break if version_count >= 2
49
- changes << line.chomp
50
- end
51
-
52
- post_install_message = changes.join("\n")
53
- post_install_message += <<'EOS'
54
-
55
- See https://github.com/newrelic/rpm/blob/master/CHANGELOG for a full list of
56
- changes.
57
- EOS
58
- s.post_install_message = post_install_message
42
+ s.post_install_message = NewRelic::LatestChanges.read
59
43
  end
@@ -8,3 +8,4 @@ Gemfile.lock
8
8
  .idea
9
9
  tags
10
10
  *.sqlite3
11
+ *.pid
@@ -3,7 +3,7 @@ gemfile <<-RB
3
3
  gem 'activerecord', '2.3.14'
4
4
  elsif RUBY_PLATFORM == 'java'
5
5
  gem 'activerecord-jdbcsqlite3-adapter', '1.2.2.1'
6
- gem 'jdbc-sqlite3'
6
+ gem 'jdbc-sqlite3', '3.7.2'
7
7
  gem 'activerecord'
8
8
  else
9
9
  gem 'activerecord'
@@ -1,10 +1,11 @@
1
- suite_condition("Resque not compatible with 1.8.6") do
2
- RUBY_VERSION != '1.8.6'
1
+ suite_condition("Resque tests not compatible with 1.8.6, JRuby") do
2
+ RUBY_VERSION != '1.8.6' && RUBY_PLATFORM != 'java'
3
3
  end
4
4
 
5
5
  gemfile <<-RB
6
6
  gem 'resque'
7
7
  gem 'json'
8
+ gem 'rake'
8
9
  if (RUBY_PLATFORM == 'java')
9
10
  gem "jruby-openssl"
10
11
  end
@@ -12,6 +13,7 @@ RB
12
13
 
13
14
  before_suite do
14
15
  ENV["NEWRELIC_MULTIVERSE_REDIS_PORT"] = (20_000 + ($$ % 10_000)).to_s
16
+ ENV["NEWRELIC_MULTIVERSE_FAKE_COLLECTOR_PORT"] = (30_000 + ($$ % 10_000)).to_s
15
17
  system("echo 'port #{ENV["NEWRELIC_MULTIVERSE_REDIS_PORT"]}' | redis-server - > /dev/null &")
16
18
  end
17
19
 
@@ -0,0 +1,2 @@
1
+ require File.join(File.dirname(__FILE__), 'resque_setup')
2
+ require 'resque/tasks'
@@ -11,7 +11,7 @@ development:
11
11
  app_name: test
12
12
  host: 127.0.0.1
13
13
  api_host: 127.0.0.1
14
- port: <%= 30_000 + ($$ % 10_000) %>
14
+ port: <%= ENV['NEWRELIC_MULTIVERSE_FAKE_COLLECTOR_PORT'] %>
15
15
  transaction_tracer:
16
16
  record_sql: obfuscated
17
17
  enabled: true
@@ -5,88 +5,133 @@ require 'test/unit'
5
5
  require 'logger'
6
6
  require 'newrelic_rpm'
7
7
  require 'fake_collector'
8
-
9
- REDIS_PORT = ENV["NEWRELIC_MULTIVERSE_REDIS_PORT"]
10
-
11
- class JobForTesting
12
- @queue = :resque_test
13
-
14
- def self.perform(key, val, sleep_duration=0)
15
- sleep sleep_duration
16
- Redis.new(:port => REDIS_PORT).set(key, val)
17
- end
18
- end
8
+ require File.join(File.dirname(__FILE__), 'resque_setup')
19
9
 
20
10
  class ResqueTest < Test::Unit::TestCase
21
11
  JOB_COUNT = 5
12
+ COLLECTOR_PORT = ENV['NEWRELIC_MULTIVERSE_FAKE_COLLECTOR_PORT']
22
13
 
23
14
  def setup
24
- @redis = Redis.new(:port => REDIS_PORT)
25
- Resque.redis = @redis
26
-
27
15
  $collector ||= NewRelic::FakeCollector.new
28
16
  $collector.reset
29
- $collector.run
17
+ $collector.run(COLLECTOR_PORT)
18
+ $redis.del('queue:resque_test')
19
+ $redis.set('index_key', 0)
20
+ @pidfile = "resque_test.#{$$}.pid"
21
+ JOB_COUNT.times do |i|
22
+ Resque.enqueue(JobForTesting, 'index_key', i + 1)
23
+ end
24
+ end
30
25
 
31
- NewRelic::Agent.manual_start
32
- DependencyDetection.detect!
26
+ def teardown
27
+ File.unlink(@pidfile) if File.file?(@pidfile)
28
+ end
33
29
 
34
- JOB_COUNT.times {|i| Resque.enqueue(JobForTesting, 'index_key', i + 1) }
35
- worker = Resque::Worker.new(:resque_test)
36
- Thread.new do
37
- worker.work
38
- end.abort_on_exception = true
30
+ def start_worker(opts={})
31
+ opts[:background] ? start_worker_background : start_worker_child
32
+ end
39
33
 
40
- wait_for_jobs
41
- worker.shutdown
34
+ def stop_worker(opts={})
35
+ opts[:background] ? stop_worker_background : stop_worker_child
36
+ end
42
37
 
43
- NewRelic::Agent.shutdown
38
+ def start_worker_child
39
+ worker_cmd = "NEWRELIC_DISPATCHER=resque QUEUE=* bundle exec rake resque:work"
40
+ @worker_pid = Process.fork
41
+ Process.exec(worker_cmd) if @worker_pid.nil?
44
42
  end
45
43
 
46
- def wait_for_jobs
47
- # JRuby barfs in the timeout on trying to read from Redis.
48
- time_for_jobs = 2
49
- if defined?(JRuby)
50
- sleep time_for_jobs
51
- else
52
- # Give a little time to complete, get out early if we're done....
53
- Timeout::timeout(time_for_jobs) do
54
- until Resque.info[:pending] == 0; end
55
- end
56
- end
44
+ def stop_worker_child
45
+ Process.kill("QUIT", @worker_pid)
46
+ Process.waitpid(@worker_pid)
57
47
  end
58
48
 
59
- def teardown
60
- @redis.set('index_key', 0)
61
- Resque.redis.del('queue:resque_test')
62
- $collector.reset
49
+ def start_worker_background
50
+ worker_cmd = "PIDFILE=#{@pidfile} TERM_CHILD=1 RESQUE_TERM_TIMEOUT=1 BACKGROUND=1 " +
51
+ "NEWRELIC_DISPATCHER=resque QUEUE=* bundle exec rake resque:work"
52
+ system(worker_cmd)
63
53
  end
64
54
 
65
- def test_resque_instrumentation_is_installed
66
- assert DependencyDetection.installed?(:resque)
55
+ def stop_worker_background
56
+ daemon_pid = File.read(@pidfile).to_i
57
+
58
+ tries = 0
59
+ while process_alive?(daemon_pid) && tries < 3
60
+ Process.kill('TERM', daemon_pid)
61
+ sleep 4 # default resque TERM timeout
62
+ tries += 1
63
+ end
64
+
65
+ if process_alive?(daemon_pid)
66
+ $stderr.puts "Oops. Daemon (pid #{daemon_pid}) is still running. Trying to halt it with SIGQUIT"
67
+ Process.kill('QUIT', daemon_pid)
68
+ sleep 1
69
+
70
+ # If it's still alive, someone will likely have to go kill the process manually.
71
+ # Alternatively, we could kill -9 it, but I decided to err on the side of caution
72
+ if process_alive?(daemon_pid)
73
+ raise "Resque is zombified. You might have to clean up process #{daemon_pid} manually."
74
+ end
75
+ end
67
76
  end
68
77
 
69
- def test_all_jobs_ran
70
- assert_equal JOB_COUNT, @redis.get('index_key').to_i
78
+ def process_alive?(pid)
79
+ Process.kill(0, pid)
80
+ return true
81
+ rescue Errno::ESRCH
82
+ return false
71
83
  end
72
84
 
73
- def test_agent_makes_only_one_metric_post
74
- assert_equal(1, $collector.agent_data.select{|x| x.action == 'metric_data'}.size,
75
- "wrong number of metric_data posts in #{$collector.agent_data.inspect}")
85
+ def wait_for_jobs
86
+ time_for_jobs = 5
87
+ begin
88
+ Timeout.timeout(time_for_jobs) { sleep(0.1) until Resque.info[:pending].zero? }
89
+ rescue Timeout::Error => err
90
+ raise err.exception("waiting #{time_for_jobs}s for completion of #{JOB_COUNT} jobs")
91
+ end
76
92
  end
77
93
 
94
+ def run_worker(opts={})
95
+ begin
96
+ start_worker(opts)
97
+ wait_for_jobs
98
+ ensure
99
+ stop_worker(opts)
100
+ end
101
+ end
78
102
 
79
103
  METRIC_VALUES_POSITION = 3
80
104
 
81
- def test_agent_posts_correct_call_count
82
- test_metric = 'OtherTransaction/ResqueJob/all'
83
- metric_data = $collector.calls_for('metric_data').first
105
+ def assert_metric_and_call_count(name, expected_call_count)
106
+ metric_data = $collector.calls_for('metric_data')
107
+ assert_equal(1, metric_data.size, "expected exactly one metric_data post from agent")
84
108
 
85
- metric_names = metric_data[METRIC_VALUES_POSITION].map{|m| m[0]['name']}
86
- assert(metric_names.include?(test_metric),
87
- "#{metric_names.inspect} should include '#{test_metric}'")
109
+ metric = metric_data.first[METRIC_VALUES_POSITION].find { |m| m[0]['name'] == name }
110
+ assert(metric, "could not find metric named #{name}")
88
111
 
89
- call_count = metric_data[METRIC_VALUES_POSITION].find{|m| m[0]['name'] == test_metric}[1][0]
90
- assert_equal JOB_COUNT, call_count
112
+ call_count = metric[1][0]
113
+ assert_equal(expected_call_count, call_count)
114
+ end
115
+
116
+ def test_all_jobs_ran
117
+ run_worker
118
+ assert_equal(JOB_COUNT, $redis.get('index_key').to_i)
119
+ end
120
+
121
+ def test_agent_posts_correct_metric_data
122
+ run_worker
123
+ assert_metric_and_call_count('OtherTransaction/ResqueJob/all', JOB_COUNT)
124
+ end
125
+
126
+ if RUBY_VERSION >= '1.9'
127
+ def test_all_jobs_ran_background
128
+ run_worker(:background => true)
129
+ assert_equal(JOB_COUNT, $redis.get('index_key').to_i)
130
+ end
131
+
132
+ def test_agent_posts_correct_metric_data_background
133
+ run_worker(:background => true)
134
+ assert_metric_and_call_count('OtherTransaction/ResqueJob/all', JOB_COUNT)
135
+ end
91
136
  end
92
137
  end
@@ -0,0 +1,15 @@
1
+ require 'resque'
2
+ require 'newrelic_rpm'
3
+
4
+ redis_port = ENV["NEWRELIC_MULTIVERSE_REDIS_PORT"]
5
+ $redis = Redis.new(:port => redis_port)
6
+ Resque.redis = $redis
7
+
8
+ class JobForTesting
9
+ @queue = :resque_test
10
+
11
+ def self.perform(key, val, sleep_duration=0)
12
+ sleep sleep_duration
13
+ $redis.set(key, val)
14
+ end
15
+ end
@@ -4,12 +4,40 @@ require 'new_relic/agent/thread_profiler'
4
4
  module NewRelic
5
5
  module Agent
6
6
  class AgentTest < Test::Unit::TestCase
7
+
7
8
  def setup
8
9
  super
9
10
  @agent = NewRelic::Agent::Agent.new
10
11
  @agent.service = NewRelic::FakeService.new
11
12
  end
12
13
 
14
+ #
15
+ # Helpers
16
+ #
17
+
18
+ def with_config( options )
19
+ config_source = NewRelic::Agent::Configuration::ManualSource.new( options )
20
+ NewRelic::Agent.config.apply_config( config_source )
21
+
22
+ yield
23
+
24
+ ensure
25
+ NewRelic::Agent.config.remove_config( config_source ) if config_source
26
+ end
27
+
28
+ def with_profile(opts)
29
+ profile = NewRelic::Agent::ThreadProfile.new(-1, 0, 0, true)
30
+ profile.aggregate(["chunky.rb:42:in `bacon'"], profile.traces[:other])
31
+ profile.instance_variable_set(:@finished, opts[:finished])
32
+
33
+ @agent.thread_profiler.instance_variable_set(:@profile, profile)
34
+ profile
35
+ end
36
+
37
+ #
38
+ # Tests
39
+ #
40
+
13
41
  def test_after_fork_reporting_to_channel
14
42
  @agent.stubs(:connected?).returns(true)
15
43
  @agent.after_fork(:report_to_channel => 123)
@@ -100,15 +128,6 @@ module NewRelic
100
128
  .find{|data| data.action == :profile_data}.params)
101
129
  end
102
130
 
103
- def with_profile(opts)
104
- profile = NewRelic::Agent::ThreadProfile.new(-1, 0, 0, true)
105
- profile.aggregate(["chunky.rb:42:in `bacon'"], profile.traces[:other])
106
- profile.instance_variable_set(:@finished, opts[:finished])
107
-
108
- @agent.thread_profiler.instance_variable_set(:@profile, profile)
109
- profile
110
- end
111
-
112
131
  def test_harvest_timeslice_data
113
132
  assert_equal({}, @agent.send(:harvest_timeslice_data),
114
133
  'should return timeslice data')
@@ -278,6 +297,29 @@ module NewRelic
278
297
  @agent.service.expects(:connect)
279
298
  @agent.send(:connect, :force_reconnect => true)
280
299
  end
300
+
301
+ def test_defer_start_if_resque_dispatcher_and_channel_manager_isnt_started
302
+ NewRelic::Agent::PipeChannelManager.listener.expects(:started?).returns(false)
303
+
304
+ # :send_data_on_exit setting to avoid setting an at_exit
305
+ with_config( :send_data_on_exit => false, :dispatcher => :resque ) do
306
+ @agent.start
307
+ end
308
+
309
+ assert !@agent.started?
310
+ end
311
+
312
+ def test_doesnt_defer_start_if_resque_dispatcher_and_channel_manager_started
313
+ NewRelic::Agent::PipeChannelManager.listener.expects(:started?).returns(true)
314
+
315
+ # :send_data_on_exit setting to avoid setting an at_exit
316
+ with_config( :send_data_on_exit => false, :dispatcher => :resque ) do
317
+ @agent.start
318
+ end
319
+
320
+ assert @agent.started?
321
+ end
322
+
281
323
  end
282
324
  end
283
325
  end
metadata CHANGED
@@ -1,18 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: newrelic_rpm
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.5.6.46.beta
4
+ version: 3.5.6.48.beta
5
5
  prerelease: 9
6
6
  platform: ruby
7
7
  authors:
8
8
  - Jason Clark
9
9
  - Sam Goldstein
10
+ - Michael Granger
10
11
  - Jon Guymon
11
12
  - Ben Weintraub
12
13
  autorequire:
13
14
  bindir: bin
14
15
  cert_chain: []
15
- date: 2013-01-22 00:00:00.000000000 Z
16
+ date: 2013-01-24 00:00:00.000000000 Z
16
17
  dependencies: []
17
18
  description: ! 'New Relic is a performance management system, developed by New Relic,
18
19
 
@@ -150,6 +151,7 @@ files:
150
151
  - lib/new_relic/delayed_job_injection.rb
151
152
  - lib/new_relic/helper.rb
152
153
  - lib/new_relic/language_support.rb
154
+ - lib/new_relic/latest_changes.rb
153
155
  - lib/new_relic/local_environment.rb
154
156
  - lib/new_relic/merbtasks.rb
155
157
  - lib/new_relic/metric_data.rb
@@ -236,9 +238,10 @@ files:
236
238
  - test/multiverse/suites/rails/queue_time_test.rb
237
239
  - test/multiverse/suites/rails/view_instrumentation_test.rb
238
240
  - test/multiverse/suites/resque/Envfile
241
+ - test/multiverse/suites/resque/Rakefile
239
242
  - test/multiverse/suites/resque/config/newrelic.yml
240
- - test/multiverse/suites/resque/dump.rdb
241
243
  - test/multiverse/suites/resque/instrumentation_test.rb
244
+ - test/multiverse/suites/resque/resque_setup.rb
242
245
  - test/multiverse/suites/sinatra/Envfile
243
246
  - test/multiverse/suites/sinatra/config/newrelic.yml
244
247
  - test/multiverse/suites/sinatra/sinatra_metric_explosion_test.rb
@@ -435,31 +438,12 @@ files:
435
438
  - lib/new_relic/build.rb
436
439
  homepage: http://www.github.com/newrelic/rpm
437
440
  licenses: []
438
- post_install_message: ! "\n# New Relic Ruby Agent Release Notes #\n\n## v3.5.5 ##\n\n
439
- \ * Add thread profiling support\n\n Thread profiling performs statistical sampling
440
- of backtraces of all threads\n within your Ruby processes. This feature requires
441
- MRI >= 1.9.2, and is\n controlled via the New Relic web UI. JRuby support (in
442
- 1.9.x compat mode) is\n considered experimental, due to issues with JRuby's Thread#backtrace.\n\n
443
- \ * Add audit logging capability\n\n The agent can now log all of the data it
444
- sends to the New Relic servers to\n a special log file for human inspection.
445
- This feature is off by default, and\n can be enabled by setting the audit_log.enabled
446
- configuration key to true.\n You may also control the location of the audit log
447
- with the audit_log.path key. \n\n * Use config system for dispatcher, framework,
448
- and config file detection\n\n Several aspects of the agent's configuration were
449
- not being handled by the\n configuration system. Detection/configuration of
450
- the dispatcher (e.g. passenger,\n unicorn, resque), framework (e.g. rails3, sinatra),
451
- and newrelic.yml\n location are now handled via the Agent environment, manual,
452
- and default\n configuration sources.\n\n * Updates to logging across the agent\n\n
453
- \ We've carefully reviewed the logging messages that the agent outputs, adding\n
454
- \ details in some cases, and removing unnecessary clutter. We've also altered\n
455
- \ the startup sequence to ensure that we don't spam STDOUT with messages\n during
456
- initialization.\n\n * Fix passing environment to manual_start()\n\n Thanks to
457
- Justin Hannus. The :env key, when passed to Agent.manual_start,\n can again
458
- be used to specify which section of newrelic.yml is loaded.\n\n * Rails 4 support\n\n
459
- \ This release includes preliminary support for Rails 4 as of 4.0.0.beta.\n Rails
460
- 4 is still in development, but the agent should work as expected for\n people
461
- who are experimenting with the beta.\n\nSee https://github.com/newrelic/rpm/blob/master/CHANGELOG
462
- for a full list of\nchanges.\n"
441
+ post_install_message: ! "\n# New Relic Ruby Agent Release Notes #\n\n## v3.5.6 ##\n\n
442
+ \ * Fix two Resque-related issues\n\n Fixes a possible hang on exit of an instrumented
443
+ Resque master process\n (https://github.com/defunkt/resque/issues/578), as well
444
+ as a file descriptor\n leak that could occur during startup of the Resque master
445
+ process.\n\nSee https://github.com/newrelic/rpm/blob/master/CHANGELOG for a full
446
+ list of\nchanges.\n"
463
447
  rdoc_options:
464
448
  - --line-numbers
465
449
  - --inline-source
Binary file