From 29e749df108b363a9697e0a3ae94b2e6a8f7fa98 Mon Sep 17 00:00:00 2001 From: Kayla Reopelle Date: Wed, 30 Nov 2022 12:03:28 -0800 Subject: [PATCH] Split agent startup for Rails into two initializers The central argument around loading the agent before other initializers is to allow initializers to reference the add_method_tracer API. Including the modules before other initializers are run allows this to happen. If the second initializer doesn't specify when it should be run, it will run immediately after our first initializer. By specifying after: :load_config_initializers, the agent will not execute init_plugin until after initializers defined in config/initializers are run. This stops the agent from forcing ActiveRecord to load before customer-defined initializers. --- lib/new_relic/control/instance_methods.rb | 8 +++- lib/newrelic_rpm.rb | 7 ++- .../rails40/config/initializers/test.rb | 20 ++++++++ .../rails41/config/initializers/test.rb | 19 ++++++++ .../rails42/config/initializers/test.rb | 20 ++++++++ .../rails50/config/initializers/test.rb | 20 ++++++++ .../rails51/config/initializers/test.rb | 20 ++++++++ .../rails52/config/initializers/test.rb | 20 ++++++++ .../rails60/config/initializers/test.rb | 20 ++++++++ .../rails61/config/initializers/test.rb | 20 ++++++++ .../rails70/config/initializers/test.rb | 20 ++++++++ .../railsedge/config/initializers/test.rb | 20 ++++++++ test/new_relic/newrelic_rpm_test.rb | 47 +++++++++++++++++++ 13 files changed, 258 insertions(+), 3 deletions(-) create mode 100644 test/environments/rails40/config/initializers/test.rb create mode 100644 test/environments/rails41/config/initializers/test.rb create mode 100644 test/environments/rails42/config/initializers/test.rb create mode 100644 test/environments/rails50/config/initializers/test.rb create mode 100644 test/environments/rails51/config/initializers/test.rb create mode 100644 test/environments/rails52/config/initializers/test.rb create mode 100644 test/environments/rails60/config/initializers/test.rb create mode 100644 test/environments/rails61/config/initializers/test.rb create mode 100644 test/environments/rails70/config/initializers/test.rb create mode 100644 test/environments/railsedge/config/initializers/test.rb create mode 100644 test/new_relic/newrelic_rpm_test.rb diff --git a/lib/new_relic/control/instance_methods.rb b/lib/new_relic/control/instance_methods.rb index ee3dd91dfa..6d48b0d2a7 100644 --- a/lib/new_relic/control/instance_methods.rb +++ b/lib/new_relic/control/instance_methods.rb @@ -64,8 +64,12 @@ def init_plugin(options = {}) # An artifact of earlier implementation, we put both #add_method_tracer and #trace_execution # methods in the module methods. - Module.send(:include, NewRelic::Agent::MethodTracer::ClassMethods) - Module.send(:include, NewRelic::Agent::MethodTracer) + # Rails applications load the next two lines before any other initializers are run + unless defined?(Rails::VERSION) + Module.send(:include, NewRelic::Agent::MethodTracer::ClassMethods) + Module.send(:include, NewRelic::Agent::MethodTracer) + end + init_config(options) NewRelic::Agent.agent = NewRelic::Agent::Agent.instance init_instrumentation diff --git a/lib/newrelic_rpm.rb b/lib/newrelic_rpm.rb index 0b118b1d39..37e6e1854e 100644 --- a/lib/newrelic_rpm.rb +++ b/lib/newrelic_rpm.rb @@ -20,7 +20,12 @@ if defined?(Rails::VERSION) module NewRelic class Railtie < Rails::Railtie - initializer "newrelic_rpm.start_plugin", before: :load_config_initializers do |app| + initializer "newrelic_rpm.include_method_tracers", before: :load_config_initializers do |app| + Module.send(:include, NewRelic::Agent::MethodTracer::ClassMethods) + Module.send(:include, NewRelic::Agent::MethodTracer) + end + + initializer "newrelic_rpm.start_plugin", after: :load_config_initializers do |app| NewRelic::Control.instance.init_plugin(config: app.config) end end diff --git a/test/environments/rails40/config/initializers/test.rb b/test/environments/rails40/config/initializers/test.rb new file mode 100644 index 0000000000..1d88e77b0a --- /dev/null +++ b/test/environments/rails40/config/initializers/test.rb @@ -0,0 +1,20 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +require 'new_relic/agent/method_tracer' + +class Bloodhound < ActiveRecord::Base + include ::NewRelic::Agent::MethodTracer + + def sniff + puts "When a bloodhound sniffs a scent article (a piece of clothing or item touched only by the subject), " \ + "air rushes through its nasal cavity and chemical vapors — or odors — lodge in the mucus and bombard the dog's " \ + "scent receptors. " \ + "Source: https://www.pbs.org/wnet/nature/underdogs-the-bloodhounds-amazing-sense-of-smell/350/" + end + + add_method_tracer :sniff +end + +Rails.application.config.active_record.timestamped_migrations = false diff --git a/test/environments/rails41/config/initializers/test.rb b/test/environments/rails41/config/initializers/test.rb new file mode 100644 index 0000000000..06be89db54 --- /dev/null +++ b/test/environments/rails41/config/initializers/test.rb @@ -0,0 +1,19 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +require 'new_relic/agent/method_tracer' + +class Bloodhound < ActiveRecord::Base + include ::NewRelic::Agent::MethodTracer + + def sniff + puts "When a bloodhound sniffs a scent article (a piece of clothing or item touched only by the subject)," \ + "air rushes through its nasal cavity and chemical vapors — or odors — lodge in the mucus and bombard the dog's scent receptors. " \ + "Source: https://www.pbs.org/wnet/nature/underdogs-the-bloodhounds-amazing-sense-of-smell/350/" + end + + add_method_tracer :sniff +end + +Rails.application.config.active_record.timestamped_migrations = false diff --git a/test/environments/rails42/config/initializers/test.rb b/test/environments/rails42/config/initializers/test.rb new file mode 100644 index 0000000000..1d88e77b0a --- /dev/null +++ b/test/environments/rails42/config/initializers/test.rb @@ -0,0 +1,20 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +require 'new_relic/agent/method_tracer' + +class Bloodhound < ActiveRecord::Base + include ::NewRelic::Agent::MethodTracer + + def sniff + puts "When a bloodhound sniffs a scent article (a piece of clothing or item touched only by the subject), " \ + "air rushes through its nasal cavity and chemical vapors — or odors — lodge in the mucus and bombard the dog's " \ + "scent receptors. " \ + "Source: https://www.pbs.org/wnet/nature/underdogs-the-bloodhounds-amazing-sense-of-smell/350/" + end + + add_method_tracer :sniff +end + +Rails.application.config.active_record.timestamped_migrations = false diff --git a/test/environments/rails50/config/initializers/test.rb b/test/environments/rails50/config/initializers/test.rb new file mode 100644 index 0000000000..1d88e77b0a --- /dev/null +++ b/test/environments/rails50/config/initializers/test.rb @@ -0,0 +1,20 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +require 'new_relic/agent/method_tracer' + +class Bloodhound < ActiveRecord::Base + include ::NewRelic::Agent::MethodTracer + + def sniff + puts "When a bloodhound sniffs a scent article (a piece of clothing or item touched only by the subject), " \ + "air rushes through its nasal cavity and chemical vapors — or odors — lodge in the mucus and bombard the dog's " \ + "scent receptors. " \ + "Source: https://www.pbs.org/wnet/nature/underdogs-the-bloodhounds-amazing-sense-of-smell/350/" + end + + add_method_tracer :sniff +end + +Rails.application.config.active_record.timestamped_migrations = false diff --git a/test/environments/rails51/config/initializers/test.rb b/test/environments/rails51/config/initializers/test.rb new file mode 100644 index 0000000000..1d88e77b0a --- /dev/null +++ b/test/environments/rails51/config/initializers/test.rb @@ -0,0 +1,20 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +require 'new_relic/agent/method_tracer' + +class Bloodhound < ActiveRecord::Base + include ::NewRelic::Agent::MethodTracer + + def sniff + puts "When a bloodhound sniffs a scent article (a piece of clothing or item touched only by the subject), " \ + "air rushes through its nasal cavity and chemical vapors — or odors — lodge in the mucus and bombard the dog's " \ + "scent receptors. " \ + "Source: https://www.pbs.org/wnet/nature/underdogs-the-bloodhounds-amazing-sense-of-smell/350/" + end + + add_method_tracer :sniff +end + +Rails.application.config.active_record.timestamped_migrations = false diff --git a/test/environments/rails52/config/initializers/test.rb b/test/environments/rails52/config/initializers/test.rb new file mode 100644 index 0000000000..1d88e77b0a --- /dev/null +++ b/test/environments/rails52/config/initializers/test.rb @@ -0,0 +1,20 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +require 'new_relic/agent/method_tracer' + +class Bloodhound < ActiveRecord::Base + include ::NewRelic::Agent::MethodTracer + + def sniff + puts "When a bloodhound sniffs a scent article (a piece of clothing or item touched only by the subject), " \ + "air rushes through its nasal cavity and chemical vapors — or odors — lodge in the mucus and bombard the dog's " \ + "scent receptors. " \ + "Source: https://www.pbs.org/wnet/nature/underdogs-the-bloodhounds-amazing-sense-of-smell/350/" + end + + add_method_tracer :sniff +end + +Rails.application.config.active_record.timestamped_migrations = false diff --git a/test/environments/rails60/config/initializers/test.rb b/test/environments/rails60/config/initializers/test.rb new file mode 100644 index 0000000000..1d88e77b0a --- /dev/null +++ b/test/environments/rails60/config/initializers/test.rb @@ -0,0 +1,20 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +require 'new_relic/agent/method_tracer' + +class Bloodhound < ActiveRecord::Base + include ::NewRelic::Agent::MethodTracer + + def sniff + puts "When a bloodhound sniffs a scent article (a piece of clothing or item touched only by the subject), " \ + "air rushes through its nasal cavity and chemical vapors — or odors — lodge in the mucus and bombard the dog's " \ + "scent receptors. " \ + "Source: https://www.pbs.org/wnet/nature/underdogs-the-bloodhounds-amazing-sense-of-smell/350/" + end + + add_method_tracer :sniff +end + +Rails.application.config.active_record.timestamped_migrations = false diff --git a/test/environments/rails61/config/initializers/test.rb b/test/environments/rails61/config/initializers/test.rb new file mode 100644 index 0000000000..1d88e77b0a --- /dev/null +++ b/test/environments/rails61/config/initializers/test.rb @@ -0,0 +1,20 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +require 'new_relic/agent/method_tracer' + +class Bloodhound < ActiveRecord::Base + include ::NewRelic::Agent::MethodTracer + + def sniff + puts "When a bloodhound sniffs a scent article (a piece of clothing or item touched only by the subject), " \ + "air rushes through its nasal cavity and chemical vapors — or odors — lodge in the mucus and bombard the dog's " \ + "scent receptors. " \ + "Source: https://www.pbs.org/wnet/nature/underdogs-the-bloodhounds-amazing-sense-of-smell/350/" + end + + add_method_tracer :sniff +end + +Rails.application.config.active_record.timestamped_migrations = false diff --git a/test/environments/rails70/config/initializers/test.rb b/test/environments/rails70/config/initializers/test.rb new file mode 100644 index 0000000000..1d88e77b0a --- /dev/null +++ b/test/environments/rails70/config/initializers/test.rb @@ -0,0 +1,20 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +require 'new_relic/agent/method_tracer' + +class Bloodhound < ActiveRecord::Base + include ::NewRelic::Agent::MethodTracer + + def sniff + puts "When a bloodhound sniffs a scent article (a piece of clothing or item touched only by the subject), " \ + "air rushes through its nasal cavity and chemical vapors — or odors — lodge in the mucus and bombard the dog's " \ + "scent receptors. " \ + "Source: https://www.pbs.org/wnet/nature/underdogs-the-bloodhounds-amazing-sense-of-smell/350/" + end + + add_method_tracer :sniff +end + +Rails.application.config.active_record.timestamped_migrations = false diff --git a/test/environments/railsedge/config/initializers/test.rb b/test/environments/railsedge/config/initializers/test.rb new file mode 100644 index 0000000000..1d88e77b0a --- /dev/null +++ b/test/environments/railsedge/config/initializers/test.rb @@ -0,0 +1,20 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +require 'new_relic/agent/method_tracer' + +class Bloodhound < ActiveRecord::Base + include ::NewRelic::Agent::MethodTracer + + def sniff + puts "When a bloodhound sniffs a scent article (a piece of clothing or item touched only by the subject), " \ + "air rushes through its nasal cavity and chemical vapors — or odors — lodge in the mucus and bombard the dog's " \ + "scent receptors. " \ + "Source: https://www.pbs.org/wnet/nature/underdogs-the-bloodhounds-amazing-sense-of-smell/350/" + end + + add_method_tracer :sniff +end + +Rails.application.config.active_record.timestamped_migrations = false diff --git a/test/new_relic/newrelic_rpm_test.rb b/test/new_relic/newrelic_rpm_test.rb new file mode 100644 index 0000000000..3698b9bad8 --- /dev/null +++ b/test/new_relic/newrelic_rpm_test.rb @@ -0,0 +1,47 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +require_relative '../test_helper' + +class NewRelicRpmTest < Minitest::Test + # This test examines the behavior of an initializer when the agent is started in a Rails context + # The initializer used for these tests is defined in test/environments/*/config/inititalizers/test.rb + + # We have documentation recommending customers call add_method_tracer + # in their initializers, let's make sure this works + def test_add_method_tracer_in_initializer_gets_traced + skip unless defined?(Rails::VERSION) + skip 'MySQL error for Rails 3.2' if Rails::VERSION::MAJOR == 3 + + assert Bloodhound.newrelic_method_exists?('sniff'), + 'Bloodhound#sniff not found by' \ + 'NewRelic::Agent::MethodTracer::ClassMethods::AddMethodTracer.newrelic_method_exists?' + assert Bloodhound.method_traced?('sniff'), + 'Bloodhound#sniff not found by' \ + 'NewRelic::Agent::MethodTracer::ClassMethods::AddMethodTracer.method_traced?' + end + + # All supported Rails versions have the default value for + # timestamped_migrations as true. Our initializer sets it to false. + # Test to resolve: https://github.com/newrelic/newrelic-ruby-agent/issues/662 + def test_active_record_initializer_config_change_saved + skip unless defined?(Rails::VERSION) + # TODO: This test passes in a Rails console in a playground app and in the customer's environment + # but fails in this unit test context + skip if Gem::Version.new(NewRelic::VERSION::STRING) >= Gem::Version.new('8.13.0') + skip 'MySQL error for Rails 3.2' if Rails::VERSION::MAJOR == 3 + + # Verify the configuration value was set to the initializer value + refute Rails.application.config.active_record.timestamped_migrations, + "Rails.application.config.active_record.timestamped_migrations equals true, expected false" + + # Verify the configuration value was applied to the ActiveRecord class variable + if Gem::Version.new(Rails::VERSION::STRING) >= Gem::Version.new('7.1') + refute ActiveRecord.timestamped_migrations, "ActiveRecord.timestamped_migrations equals true, expected false" + else + refute ActiveRecord::Base.timestamped_migrations, + "ActiveRecord::Base.timestamped_migrations equals true, expected false" + end + end +end