-
Notifications
You must be signed in to change notification settings - Fork 600
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve ActiveRecord connection resolution performance #296
Conversation
Looping through connection handlers and connections can be a performance hit due to duping involved on the underlying thread safe call from `connection_pool_list` to `ThreadSafe::Cache#values`. Underneath `#values` dupes the connection list per each call which can add up. This backports in a patch that was introduced in Rails 6 that passes through the connection in the notification payload. StackProf: ``` NewRelic::Agent::Instrumentation::ActiveRecordSubscriber#active_record_config (/usr/local/bundle/gems/newrelic_rpm-4.6.0.338/lib/new_relic/agent/instrumentation/active_record_subscriber.rb:78) samples: 9 self (0.1%) / 167 total (1.4%) callers: 167 ( 100.0%) NewRelic::Agent::Instrumentation::ActiveRecordSubscriber#start 7 ( 4.2%) NewRelic::Agent::Instrumentation::ActiveRecordSubscriber#active_record_config callees (158 total): 147 ( 93.0%) ActiveRecord::ConnectionAdapters::ConnectionHandler#connection_pool_list 10 ( 6.3%) ActiveRecord::Base.connection_handler 7 ( 4.4%) NewRelic::Agent::Instrumentation::ActiveRecordSubscriber#active_record_config 1 ( 0.6%) AutoReplica::ConnectionHandler#method_missing code: | 78 | def active_record_config(payload) | 79 | return unless payload[:connection_id] | 80 | | 81 | connection = nil | 82 | connection_id = payload[:connection_id] | 83 | 162 (1.3%) | 84 | ::ActiveRecord::Base.connection_handler.connection_pool_list.each do |handler| 3 (0.0%) | 85 | connection = handler.connections.detect do |conn| 3 (0.0%) / 3 (0.0%) | 86 | conn.object_id == connection_id | 87 | end | 88 | 1 (0.0%) / 1 (0.0%) | 89 | break if connection | 90 | end | 91 | 5 (0.0%) / 5 (0.0%) | 92 | connection.instance_variable_get(:@config) if connection | 93 | end ```
@callumj: Thanks for this work. We have internal issue around improving this same code path, but in a different way. On MRI I haven't tested this code, but this is what I think it'd look like: active_record_subscriber.rb ...
def active_record_config(payload)
connection = nil
if payload[:connection]
connection = payload[:connection]
elsif payload[:connection_id]
connection = active_record_connection_from_id payload[:connection_id]
end
connection.instance_variable_get(:@config) if connection
end
if RUBY_ENGINE == 'jruby'
def active_record_connection_from_id connection_id
::ActiveRecord::Base.connection_handler.connection_pool_list.each do |handler|
connection = handler.connections.detect do |conn|
conn.object_id == connection_id
end
return connection if connection
end
end
else
require 'objspace'
def active_record_connection_from_id connection_id
ObjectSpace._id2ref connection_id
end
end
... We do something similar in TransactionTimeAggregator. What do you think about this approach? |
@mwear That sounds like a great approach since rpm already uses Originally this was inspired by the Datadog work around this: DataDog/dd-trace-rb#649 |
@callumj Thanks for bringing that Ruby issue to my attention. While I was aware the use of ObjectSpace on JRuby was not recommended, I was not aware that it was being considered for deprecation from MRI. Given that information, I don't think it makes sense to use We can probably accept the backport approach so long as there is a way to opt-out if a monkey patch causes issues for a user. Would you consider adding an option called |
Good call @mwear, I should be able to get this to you by end of week April 8. |
@mwear Added setting control |
This looks great @callumj. Thanks for your work and for improving the Ruby Agent! We're going to pull this into our development branch and it should become part of our next release. We'll let you know when it goes out! |
Thank you @mwear for your review and guidance on getting this merged in. |
This has been included in version 6.3.0 of the New Relic ruby agent! |
Looping through connection handlers and connections can be a performance hit due to duping involved on the underlying thread safe call from
connection_pool_list
toThreadSafe::Cache#values
. Underneath#values
dupes the connection list per each call which can add up.This backports in an improvement that was introduced in Rails 6 that passes through the connection in the notification payload.
StackProf:
Happy to take pointers on where best to add tests.