Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

Do not connect to database if in Rails initialization stage #22

Closed
mehagar opened this issue Feb 12, 2021 · 8 comments
Closed

Do not connect to database if in Rails initialization stage #22

mehagar opened this issue Feb 12, 2021 · 8 comments

Comments

@mehagar
Copy link

mehagar commented Feb 12, 2021

In Rails 5.2, a new behavior described here will cause Rails configs to be loaded for all rake tasks.

In our case, if the database has not yet been created (before rake db:create) has been run, and then we run rake db:create, this is what happens:

we are using the audited gem and using its initializer to configure the class to use for auditing:

Audited.config do |config|
  config.audit_class = ::Audit
end

So that class is loaded, which has this code:

class Audit
  attr_protected :customer_id
end

The attr_protected call connects to the database to determine the primary key of the model to use as a default id. But since the database has not yet been created, it fails. This creates a catch-22 where we cannot create the database, because the rake task to create it fails because there is no database.

Ideally there would be a way to opt out of the behavior that by default connects to the database to determine the primary key.

@westonganger
Copy link
Owner

westonganger commented Feb 12, 2021

Which line of protected_attributes_continued accesses the database in your error stacktrace?

You can also use:

Audited.config do |config|
  if Audit.table_exists? 
    config.audit_class = ::Audit
  end
end

###

class Audit
  if Audit.table_exists?
    attr_protected :customer_id
  end
end

@mehagar
Copy link
Author

mehagar commented Feb 13, 2021

Here is the stack trace:

/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/mysql2-0.5.3/lib/mysql2/client.rb:90:in `connect'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/mysql2-0.5.3/lib/mysql2/client.rb:90:in `initialize'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/connection_adapters/mysql2_adapter.rb:22:in `new'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/connection_adapters/mysql2_adapter.rb:22:in `mysql2_connection'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/connection_adapters/abstract/connection_pool.rb:830:in `new_connection'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/connection_adapters/abstract/connection_pool.rb:874:in `checkout_new_connection'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/connection_adapters/abstract/connection_pool.rb:853:in `try_to_checkout_new_connection'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/connection_adapters/abstract/connection_pool.rb:814:in `acquire_connection'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/connection_adapters/abstract/connection_pool.rb:538:in `checkout'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/connection_adapters/abstract/connection_pool.rb:382:in `connection'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/connection_adapters/abstract/connection_pool.rb:1033:in `retrieve_connection'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/connection_handling.rb:118:in `retrieve_connection'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/connection_handling.rb:90:in `connection'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/model_schema.rb:324:in `table_exists?'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/attribute_methods/primary_key.rb:99:in `get_primary_key'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/attribute_methods/primary_key.rb:87:in `reset_primary_key'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/attribute_methods/primary_key.rb:75:in `primary_key'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/attribute_methods/primary_key.rb:89:in `reset_primary_key'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/attribute_methods/primary_key.rb:75:in `primary_key'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/protected_attributes_continued-1.7.0/lib/active_record/mass_assignment_security/attribute_assignment.rb:15:in `attributes_protected_by_default'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/protected_attributes_continued-1.7.0/lib/active_model/mass_assignment_security.rb:333:in `block in protected_attributes_configs'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/protected_attributes_continued-1.7.0/lib/active_model/mass_assignment_security.rb:222:in `protected_attributes'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/protected_attributes_continued-1.7.0/lib/active_model/mass_assignment_security.rb:126:in `block in attr_protected'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/protected_attributes_continued-1.7.0/lib/active_model/mass_assignment_security.rb:125:in `each'
/Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/protected_attributes_continued-1.7.0/lib/active_model/mass_assignment_security.rb:125:in `attr_protected'

@westonganger
Copy link
Owner

Does the following monkey patch work for you?

ActiveRecord::MassAssignmentSecurity::ClassMethods.module_eval do
  
  def attributes_protected_by_default
    if table_exists?
      default = [ primary_key, inheritance_column ]
      default << 'id' unless primary_key.eql? 'id'
      default
    else
      []
    end
  end

end

@mehagar
Copy link
Author

mehagar commented Feb 13, 2021

It does not, because the call to table_exists? itself tries to connect to the database (shown in this stack trace):

from /Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/connection_adapters/mysql2_adapter.rb:12:in `mysql2_connection'
	from /Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/connection_adapters/abstract/connection_pool.rb:830:in `new_connection'
	from /Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/connection_adapters/abstract/connection_pool.rb:874:in `checkout_new_connection'
	from /Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/connection_adapters/abstract/connection_pool.rb:853:in `try_to_checkout_new_connection'
	from /Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/connection_adapters/abstract/connection_pool.rb:814:in `acquire_connection'
	from /Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/connection_adapters/abstract/connection_pool.rb:538:in `checkout'
	from /Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/connection_adapters/abstract/connection_pool.rb:382:in `connection'
	from /Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/connection_adapters/abstract/connection_pool.rb:1033:in `retrieve_connection'
	from /Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/connection_handling.rb:118:in `retrieve_connection'
	from /Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/connection_handling.rb:90:in `connection'
	from /Users/michaelhagar/.rvm/gems/ruby-2.7.2@sa/gems/activerecord-5.2.4.4/lib/active_record/model_schema.rb:324:in `table_exists?'

@westonganger
Copy link
Owner

How about the following

ActiveRecord::MassAssignmentSecurity::ClassMethods.module_eval do
  
  def attributes_protected_by_default
    begin
      default = [ primary_key, inheritance_column ]
      default << 'id' unless primary_key.eql? 'id'
      default
    rescue ActiveRecord::NoDatabaseError
      []
    end
  end

end

@mehagar
Copy link
Author

mehagar commented Feb 14, 2021

Yes, that works.

@westonganger
Copy link
Owner

Ok this should now be fixed in master.

@mehagar mehagar closed this as completed Feb 15, 2021
@westonganger
Copy link
Owner

westonganger commented Mar 6, 2021

v1.8.0 is now released which contains this fix.

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

No branches or pull requests

2 participants