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

Unexpected rake task behavior change in Rails 5.2 #32870

Closed
st0012 opened this issue May 11, 2018 · 18 comments
Closed

Unexpected rake task behavior change in Rails 5.2 #32870

st0012 opened this issue May 11, 2018 · 18 comments
Labels

Comments

@st0012
Copy link
Contributor

st0012 commented May 11, 2018

Steps to reproduce

  1. Initialize a Rails app (5.2)
  2. Add config.after_initialize { raise "foo" }
  3. Run rake db:create

Expected behavior

Before 5.2, the after_initialize block will not be executed so the database can be created successfully

Actual behavior

The app crashed because the after_initialize block

System configuration

Rails version: 5.2

Ruby version: 2.4.1

Notes

I think this is an issue because before 5.2 we might add some database related logics in after_initialize block. And since running tasks like db:create won't initialize the whole app this can work. Like:

module Sample
  class Application < Rails::Application
    config.after_initialize do
      # connect to database
    end
  end
end

But after upgrade to 5.2 the code won't work because the logic will hit the database first before actually execute the rake task (db:create). And we can't find this behavior change in changelog or documents.

Also, I found this issue is cause by this commit. Before this commit some of the rake tasks will only run load_config as prerequisite. But now load_config has the environment prerequisite so the app will be fully initialized.

So I think either we change this behavior back, or we should document about it.

@y-yagi
Copy link
Member

y-yagi commented May 13, 2018

Your point is right.
However, some tasks originally loaded env(e.g. db:drop), and the behavior was not unified.
Because the behavior was unified, I think that the current behavior is more appropriate.

I think it is good to add documents. Can you create a PR?

@st0012
Copy link
Contributor Author

st0012 commented May 13, 2018

Sure! I will

@st0012
Copy link
Contributor Author

st0012 commented May 17, 2018

@y-yagi I can't find a proper place for adding it, any suggestion?

@y-yagi
Copy link
Member

y-yagi commented May 17, 2018

How about adding doc to Configuring Guide?

@st0012
Copy link
Contributor Author

st0012 commented May 18, 2018

@y-yagi I was thinking there as well. but there's already documents for this

config.after_initialize takes a block which will be run after Rails has finished initializing the application. ...... Note that this block will be run for rake tasks. Useful for configuring values set up by other initializers

So the problem is actually that the old documents don't provide correct infos (not all rake tasks will run the block)

@aried3r
Copy link
Contributor

aried3r commented May 30, 2018

Hey! I just ran into this and found this issue. What is the recommended way to solve this?

In our codebase there's something like if Product.table_exists? in an initializer, which now fails because it seems the initializers are run during db:create as reported by @st0012.

What is the correct construct, I'd love to have that in the docs as well. :)

@aried3r
Copy link
Contributor

aried3r commented May 30, 2018

Oh yeah, I also run into #32994, maybe it's related or just coincidence?

$ rails db:create
ActiveRecord::NoDatabaseError: FATAL:  database "development" does not exist
config/initializers/do_something.rb:3:in `<main>'

Caused by:
PG::ConnectionBad: FATAL:  database "development" does not exist
config/initializers/do_something.rb:3:in `<main>'

@y-yagi
Copy link
Member

y-yagi commented May 30, 2018

@aried3r

Thanks for your report.
Although it is a little tricky approach, I think that it can be solved by doing as follows.

# config/application.rb  
class Application < Rails::Application
  config.after_initialize do
    next if ARGV.include?("db:create")

    p User.table_exists?
  end 
end

I would like to think about a more appropriate approach for this.
By the way, just interested, why do need to use model with initializers?

@tgxworld
Copy link
Contributor

tgxworld commented Jun 7, 2018

By the way, just interested, why do need to use model with initializers?

I think the question here should be whether the use of models is not allowed in after_initialize blocks. With this change it means that all after_initialize blocks should not contain any code that attempts to connect to the database otherwise, rake db:create will not work.

@aried3r
Copy link
Contributor

aried3r commented Jun 11, 2018

By the way, just interested, why do need to use model with initializers?

I took another look, it's a very old initializer. I think it's correct to assume that this is not the right way to do it. I think it was placed there to ensure some data exists. I'll have a look at our code, I think a DB seed or even a PORO model would be a better place for it.

@bwillis
Copy link

bwillis commented Jul 30, 2018

We also encountered this change of behavior and I wanted to share our situation. Running rake db:create in our test environment was throwing a ActiveRecord::NoDatabaseError. We traced it down to factory_bot_rails loading our factory definitions in the after_initialize callback. This began loading our models and triggered another gem track_history to start loading. This specific gem checks the database for what columns are loaded on the model and that causes our exception.

The way we solved this for now was to avoid our factories from being called in after_initialize. I'm torn as to whether this gem (or others) should have to lazily load those hooks to the database. I'd be curious if anyone else encountered this situation.

@infinityrobot
Copy link

infinityrobot commented Aug 7, 2018

Hey team, we've also hit this problem as we have a few cases where we reference constants in our initialisers and our factories (which are being loaded as a part of after_initialize). @bwillis – how did you end up going about avoiding loading factories in after_initialize? @aried3r – have you found a solution for your if Product.table_exists? example? Thanks in advance!

@infinityrobot
Copy link

Should quickly add we've managed to solve our issue by guarding the areas where classes were being loaded with ActiveRecord::Base.connected? – we had a few guards of ActiveRecord::Base.connection.data_source_exists?("model") but these seemed to no longer behave the way they did in Rails 4.x (we have only recently upgraded to 5.2).

@bwillis
Copy link

bwillis commented Aug 16, 2018

@bwillis – how did you end up going about avoiding loading factories in after_initialize?

gem 'factory_bot_rails', require: false

Then loading it where we needed it explicitly, which, for us, was in our spec helper:

require 'factory_bot_rails'

@aried3r
Copy link
Contributor

aried3r commented Aug 16, 2018

@aried3r – have you found a solution for your if Product.table_exists? example? Thanks in advance!

Yes we have – we now lazily load/create this record on demand. It's the Rpush::Apns::App of Rpush, I refactored it so that our PushNotifier has a def app which either creates the App on the very first call or just loads it. Depending on your use case you could use #find_or_create_by I guess.

(There's also now a #create_or_find_by)

chopraanmol1 added a commit to chopraanmol1/public_activity that referenced this issue Aug 30, 2018
This fixes rake db:create task for rails 5.2

Related Information

rails/rails#32870 (comment)
jondkinney added a commit to headwayio/canard that referenced this issue Sep 21, 2018
jondkinney added a commit to headwayio/canard that referenced this issue Sep 21, 2018
rails/rails#32870

Rescue on `table_exists?` call

Without this rescue in place you can’t run `rails db:create`. It’ll fail spectacularly with the following error:

rails db:create
rake aborted!
ActiveRecord::NoDatabaseError: FATAL:  database "headway_rails_template_development" does not exist
/Users/jon/.rvm/gems/ruby-2.5.1@headway_rails_template/gems/activerecord-5.2.1/lib/active_record/connection_adapters/postgresql_adapter.rb:688:in `rescue in connect'
/Users/jon/.rvm/gems/ruby-2.5.1@headway_rails_template/gems/activerecord-5.2.1/lib/active_record/connection_adapters/postgresql_adapter.rb:683:in `connect'
/Users/jon/.rvm/gems/ruby-2.5.1@headway_rails_template/gems/activerecord-5.2.1/lib/active_record/connection_adapters/postgresql_adapter.rb:215:in `initialize'
/Users/jon/.rvm/gems/ruby-2.5.1@headway_rails_template/gems/activerecord-5.2.1/lib/active_record/connection_adapters/postgresql_adapter.rb:40:in `new'
/Users/jon/.rvm/gems/ruby-2.5.1@headway_rails_template/gems/activerecord-5.2.1/lib/active_record/connection_adapters/postgresql_adapter.rb:40:in `postgresql_connection'
/Users/jon/.rvm/gems/ruby-2.5.1@headway_rails_template/gems/activerecord-5.2.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:809:in `new_connection'
/Users/jon/.rvm/gems/ruby-2.5.1@headway_rails_template/gems/activerecord-5.2.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:853:in `checkout_new_connection'
/Users/jon/.rvm/gems/ruby-2.5.1@headway_rails_template/gems/activerecord-5.2.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:832:in `try_to_checkout_new_connection'
/Users/jon/.rvm/gems/ruby-2.5.1@headway_rails_template/gems/activerecord-5.2.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:793:in `acquire_connection'
/Users/jon/.rvm/gems/ruby-2.5.1@headway_rails_template/gems/activerecord-5.2.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:521:in `checkout'
/Users/jon/.rvm/gems/ruby-2.5.1@headway_rails_template/gems/activerecord-5.2.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:380:in `connection'
/Users/jon/.rvm/gems/ruby-2.5.1@headway_rails_template/gems/activerecord-5.2.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:1008:in `retrieve_connection'
/Users/jon/.rvm/gems/ruby-2.5.1@headway_rails_template/gems/activerecord-5.2.1/lib/active_record/connection_handling.rb:118:in `retrieve_connection'
/Users/jon/.rvm/gems/ruby-2.5.1@headway_rails_template/gems/activerecord-5.2.1/lib/active_record/connection_handling.rb:90:in `connection'
/Users/jon/.rvm/gems/ruby-2.5.1@headway_rails_template/gems/activerecord-5.2.1/lib/active_record/model_schema.rb:324:in `table_exists?'
/Users/jon/Sites/Rails/github/canard/lib/canard/adapters/active_record.rb:31:in `active_record_table?'
/Users/jon/Sites/Rails/github/canard/lib/canard/adapters/active_record.rb:36:in `has_roles_mask_accessors?'
/Users/jon/Sites/Rails/github/canard/lib/canard/user_model.rb:74:in `acts_as_user'
/Users/jon/Sites/Rails/headway/headway_rails_template/app/models/user.rb:31:in `<class:User>'
/Users/jon/Sites/Rails/headway/headway_rails_template/app/models/user.rb:1:in `<main>'
@rails-bot
Copy link

rails-bot bot commented Nov 14, 2018

This issue has been automatically marked as stale because it has not been commented on for at least three months.
The resources of the Rails team are limited, and so we are asking for your help.
If you can still reproduce this error on the 5-2-stable branch or on master, please reply with all of the information you have about it in order to keep the issue open.
Thank you for all your contributions.

@rails-bot rails-bot bot added the stale label Nov 14, 2018
@rails-bot rails-bot bot closed this as completed Nov 21, 2018
arvados-bot pushed a commit to arvados/arvados that referenced this issue Aug 17, 2020
There was a behaviour change on rake tasks that avoided the creation of the
test database. All initializers are now called from rake tasks, so those
initializers with code accessing the database will fail when the database
isn't created.

See: rails/rails#32870

Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas@di-pentima.com.ar>
arvados-bot pushed a commit to arvados/arvados that referenced this issue Aug 17, 2020
The 'preload_all_models' initializer is now run even when db:create is
called, so it avoids the database to be created on arvbox, for example,
because it attempts to access the database before being created.

This initializer was added 7 years ago and it now seems to not be needed
anymore, so I think it's the cleaner way to resolve this issue.

See: rails/rails#32870

Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas@di-pentima.com.ar>
@subelsky
Copy link
Contributor

subelsky commented Jan 12, 2021

In case anyone lands here w/ the same issue, I adapted the NullDB fallback fix referenced by @ulferts in opf/openproject#7114 when updating an app to Rails v5.2. Worked well!

@alecernas
Copy link

I have some initializers that are needed for my app, but not for my rake db tasks, has anyone find a solution other than avoiding values form ARGV ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants