Skip to content
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

fix: Use Active Support Lazy Load Hooks to avoid prematurely initializing ActiveRecord::Base and ActiveJob::Base #1104

Merged

Conversation

bensheldon
Copy link
Contributor

This PR ensures that requiring OpenTelemetry Rails will not prematurely initialize ActiveRecord::Base and ActiveJob::Base classes, which are autoloaded by Rails:

https://api.rubyonrails.org/v7.0.0/classes/ActiveSupport/LazyLoadHooks.html

The benefit is that Rails (tests / development) can potentially boot faster because these constants aren't being initialized unnecessarily.

Copy link
Collaborator

@arielvalentin arielvalentin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non blocking comments. If there are any takeaways we can address those in separate PRs.

Thank you again for this fix.

require_relative 'patches/base'
require_relative 'handlers'
end

def patch_activejob
::ActiveJob::Base.prepend(Patches::Base) unless ::ActiveJob::Base <= Patches::Base

Handlers.subscribe
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also defer these subscriptions until an explicit point in the Rails initialization process? Or are we assuming folks are already registering this in config/initializers themselves?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not certain this is the right approach, but when New Relic detects the presence of Rails, it will load instrumentation within a Rails::Configuration.after_initialize block. It would be the equivalent of wrapping OpenTelemetry::SDK::Configurator#install_instrumentation in an after_initialize block.

This could probably be done individually in instrumentation if we didn't want to tie anything about Rails into the main SDK.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's right that Instrumentation subscriptions should happen in an after_initialize block. I was trying to avoid adding a full-blown Railtie, but that's the fully correct way to do it.

Though from poking around Rails, it seems like simply require "activesupport/notifications" might be totally load-order independent. e.g.

https://github.com/rails/rails/blob/e412839f9d2e0133a50769db16709a09f87c1b7d/activerecord/lib/active_record/explain_subscriber.rb#L3-L3

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think then we should have a follow up PR and use a Railtie. I am ok with incremental improvements.

# module that they are defined in as they are included into ActiveRecord::Base
# Example: Patches::PersistenceClassMethods refers to https://github.com/rails/rails/blob/v6.1.0/activerecord/lib/active_record/persistence.rb#L10
# which is included into ActiveRecord::Base in https://github.com/rails/rails/blob/914caca2d31bd753f47f9168f2a375921d9e91cc/activerecord/lib/active_record/base.rb#L283
::ActiveRecord::Base.prepend(Patches::Querying)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It occurs to me now that none of these use active support concerns.

Is there any issue related to code reloading that would be alleviated by using concerns?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of the concerns have lazy-load-hooks, so they end up just being loaded at the same time.

I had to read the original comment like 5 times, but I think what it means is that the Rails code being patched here has been extracted into a concern by Rails, and OTel is patching it, with a similar naming convention, when it's included back into ActiveRecord::Base.

Also Concern is waaaay too overloaded as a word cause it can both mean "includes ActiveSupport::Concern which has some nice helper methods for extraction/inclusion" but also just like "a module with some behavior that's been extracted that's intended to be included back". ¯\_(ツ)_/¯

end

def require_dependencies
require 'active_support/lazy_load_hooks'
require_relative 'patches/querying'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though the hooks are lazy, shouldn't the modules or references be loaded closer to where they are used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was pattern-matching the structure 😁 They could just as easily all live together right where they're called. I'll do some cleanup.

Copy link
Contributor

@kaylareopelle kaylareopelle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @bensheldon!

@kaylareopelle kaylareopelle enabled auto-merge (squash) August 12, 2024 16:25
@kaylareopelle kaylareopelle merged commit a9e6e1a into open-telemetry:main Aug 12, 2024
55 of 57 checks passed
@github-actions github-actions bot mentioned this pull request Aug 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants