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
Warn if frameworks are loaded too early #46047
base: main
Are you sure you want to change the base?
Conversation
a042631
to
59f1ca8
Compare
59f1ca8
to
72d556d
Compare
Does this work in production? Because |
Thanks for the review @rafaelfranca. I'll try to fix the issues you mentioned. |
3169836
to
f5f53cd
Compare
c07e0ba
to
6c6be18
Compare
I've add |
31df73e
to
72340c3
Compare
|
78f0e23
to
d6d3eed
Compare
Prematurely loading frameworks in a Rails application may slow down boot time and could cause conflicts with load order and boot of the application. This change allows showing a warning in a load hook if a required framework or load hook hasn't been loaded yet: ```ruby initializer "active_record.warn_if_prematurely_loaded" do ActiveSupport.on_load(:active_record) do ActiveSupport.warn_if_prematurely_loaded(:active_record, before: :after_initialize) end end ``` In this case if `:active_record` is loaded before `:after_initialize` has run we show a warning: Load hook :active_record was called before load hook :after_initialize. Prematurely loading frameworks may slow down your boot time and could cause conflicts with load order and boot of your application. Consider wrapping your code with an on_load hook: ActiveSupport.on_load(:active_record) do # your code end Called from: config/initializers/some_initializer.rb:1:in `<main>' ... In production `eager_load` gets called to load add frameworks and we should silence the warning. A `silence_prematurely_loading_warnings` has been added to silence all load hook order warnings. This `silence_prematurely_loading_warnings` is called before `eager_load` gets called. The hook has been implemented for ActiveRecord but other frameworks, like ActionView for example, could use it as well. Some tests include initializers that call ActiveRecord::Base. To not show the warning `silence_prematurely_loading_warnings` has been added to the initializers.
d6d3eed
to
84ce472
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense to be.
@rafaelfranca you good with this?
To clarify, I don't think that's ideal, I'd prefer if we addressed the root cause more rather than to warn that something is wrong, but I can't think of anything better yet and at least it should help people fix their app. |
This feature seems odd to me. If users need Active Record before the application has fully booted, how are they supposed to run the code and get no warnings? |
For example, let's consider this widely used pattern: config.to_prepare do
# Setup an AR model on boot and reload here.
end Which would be the alternative? |
@fxn I think the following works for me (but I don't have much experience in using ActiveSupport::Reloader.to_prepare do
ActiveSupport.on_load(:active_record) do
# Setup an AR model on boot and reload here.
end
end |
@p8 But, if you execute My point is, if I need to do something during boot, I need to. I cannot not do it because not doing it would be faster. |
@fxn We could allow the silencing to accept a block: ActiveSupport::Reloader.to_prepare do
ActiveSupport.silence_prematurely_loading_warnings do
# Setup an AR model on boot and reload here.
end
end |
Smells like a XY solution to me, guess @casperisfine has a similar feeling from his comment. Legit use cases cannot get warnings. Is like saying, don't use a database, it will slow you down! OK, but I need a database! Cool, here's a warning silencer! |
What I'm reading from @casperisfine's comment is that permaturely loading ActiveRecord shouldn't cause a problem. |
Note that while the I buy the "contract" argument, but not so much the performance argument. |
Yes, either that, or that it would be build in such a way that people could hardly make that mistake. But neither seem achievable to me, at least not easily. |
To me, |
@p8 Yes! But they are not about performance, they are about load order, right? Some people comment on passing about booting, but that is just a potential consequence of delaying loading (potential only, as I showed in the Rails commands above). What I believe would make sense is to be able to say: "Hey, you are trying to load AR before Rails!" (that is, a missing I don't think the code is ready to be able to say that easily. |
In other words, Rails is the one who decides when frameworks are loaded. And Rails may decide some have to be loaded at boot time (as it does for AR itself in some commands). Whether things are loaded during boot time or not is out of the control of the user regardless of whether they use What the user has to respect is load order, wait until Rails decides to load. |
And if you think about it, that is aligned with the eager loading case. The same warning works, you don't need special logic, because the point is to wait until frameworks are loaded, sooner or later. |
I'll go even further. Let's imagine all the Rails code in the world uses When should Rails load its components? Now, that question is extremely important because nobody will be able to run anything related before. I'd argue that probably during boot, at some point in time that would belong to the public interface. In the case of AR there are use cases like aborting an application from booting if the roles table lacks an admin. Say. So, in order to support those use cases, AR should be available (ergo loaded) during initialization. Thus, defeating the performance goal. The way I see it, in dev mode Rails has to be lean, but there's a low boundary to that. Being lean means being lazy loading the rest of AR, as it is done with all the autoloads. And users have to play by the rules, which today are not enforced, but would be good to be able to. |
Isn't what this warning is trying to do? If users code, or libraries are trying to load Rails owned frameworks before Rails thinks it should loaded, users will see a warning. |
Not really. This patch is saying: If Active Record was loaded before To accomplish what we are saying we should have something like: Now, I (Rails) am going to load framework F. If at this point F is already loaded, I am going to warn, and the warning won't mention performance. This logic does not depend on F. |
Let me draft one possible way to do this.
Now, who has the responsibility to glue the frameworks? In Rails, the orchestrator is
Ideally, things should be done in a way that the warning is not needed because you simply cannot use the frameworks ealier by design. However, at this point in the game, that seems difficult. Also, there is a balance, because AR and others are independent gems, so other gems extending them do not even have a notion of So, sharing this only for the sake of the discussion, it is not exactly a proposal because I am not sure about its practicality. |
Thanks @fxn for the extensive feedback. I hope to dive into this again later this week. |
It seems to be stalled, so I have created an issue to track this #50133 |
Fixes #50133
Prematurely loading frameworks in a Rails application may slow down boot time and could cause conflicts with load order and boot of the application. It is a common problem in gems:
Delayed::Job
no longer defined in initializers collectiveidea/delayed_job_active_record#185This change allows showing a warning in a load hook if a required
framework or load hook hasn't been loaded yet:
In this case if
:active_record
is loaded before:after_initialize
has run we show a warning:
In production
eager_load
gets called to load add frameworks and weshould silence the warning. A
silence_prematurely_loading_warnings
hasbeen added to silence all load hook order warnings. This
silence_prematurely_loading_warnings
is called beforeeager_load
gets called.
The hook has been implemented for ActiveRecord but other frameworks,
like ActionView for example, could use it as well.
Some tests include initializers that call ActiveRecord::Base. To not
show the warning
silence_prematurely_loading_warnings
has been addedto the initializers.
Additional information
ActiveRecord
should be loaded after:after_initialize
has run, so I've added an initializer to add the load_hook order in the railtie. For other frameworks like ActionView and ActionPack it should probably be added as well.Checklist
Before submitting the PR make sure the following are checked:
[Fix #issue-number]
main
(if not - rebase it).