octopus icon indicating copy to clipboard operation
octopus copied to clipboard

Cannot run rake task `db:create` with mysql.

Open abMatGit opened this issue 7 years ago • 8 comments

Hey guys! There seems to be an interesting issue related to db:create rake tasks that were failing. This is a synopsis of our research (thanks @camertron):

Issue:

When running bundle exec rake db:create rake task on a fresh rails server, using mysql2 adapters, we get the following error:

Unknown database 'octopus_development'
/Users/amatuszewski/.rbenv/versions/2.1.9/lib/ruby/gems/2.1.0/gems/activerecord-4.2.7.1/lib/active_record/connection_adapters/mysql2_adapter.rb:23:in `rescue in mysql2_connection'
/Users/amatuszewski/.rbenv/versions/2.1.9/lib/ruby/gems/2.1.0/gems/activerecord-4.2.7.1/lib/active_record/connection_adapters/mysql2_adapter.rb:10:in `mysql2_connection'
/Users/amatuszewski/.rbenv/versions/2.1.9/lib/ruby/gems/2.1.0/gems/activerecord-4.2.7.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:438:in `new_connection'
/Users/amatuszewski/.rbenv/versions/2.1.9/lib/ruby/gems/2.1.0/gems/activerecord-4.2.7.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:448:in `checkout_new_connection'
/Users/amatuszewski/.rbenv/versions/2.1.9/lib/ruby/gems/2.1.0/gems/activerecord-4.2.7.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:422:in `acquire_connection'
/Users/amatuszewski/.rbenv/versions/2.1.9/lib/ruby/gems/2.1.0/gems/activerecord-4.2.7.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:349:in `block in checkout'
/Users/amatuszewski/.rbenv/versions/2.1.9/lib/ruby/2.1.0/monitor.rb:211:in `mon_synchronize'
/Users/amatuszewski/.rbenv/versions/2.1.9/lib/ruby/gems/2.1.0/gems/activerecord-4.2.7.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:348:in `checkout'
/Users/amatuszewski/.rbenv/versions/2.1.9/lib/ruby/gems/2.1.0/gems/activerecord-4.2.7.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:263:in `block in connection'
/Users/amatuszewski/.rbenv/versions/2.1.9/lib/ruby/2.1.0/monitor.rb:211:in `mon_synchronize'
/Users/amatuszewski/.rbenv/versions/2.1.9/lib/ruby/gems/2.1.0/gems/activerecord-4.2.7.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:262:in `connection'
/Users/amatuszewski/.rbenv/versions/2.1.9/lib/ruby/gems/2.1.0/bundler/gems/octopus-6b10d719ce0f/lib/octopus/proxy.rb:72:in `safe_connection'
/Users/amatuszewski/.rbenv/versions/2.1.9/lib/ruby/gems/2.1.0/bundler/gems/octopus-6b10d719ce0f/lib/octopus/proxy.rb:79:in `select_connection'
/Users/amatuszewski/.rbenv/versions/2.1.9/lib/ruby/gems/2.1.0/bundler/gems/octopus-6b10d719ce0f/lib/octopus/proxy.rb:199:in `legacy_method_missing_logic'
/Users/amatuszewski/.rbenv/versions/2.1.9/lib/ruby/gems/2.1.0/bundler/gems/octopus-6b10d719ce0f/lib/octopus/proxy.rb:130:in `method_missing'
/Users/amatuszewski/.rbenv/versions/2.1.9/lib/ruby/gems/2.1.0/gems/activerecord-4.2.7.1/lib/active_record/tasks/mysql_database_tasks.rb:16:in `create'
/Users/amatuszewski/.rbenv/versions/2.1.9/lib/ruby/gems/2.1.0/gems/activerecord-4.2.7.1/lib/active_record/tasks/database_tasks.rb:93:in `create'
... <Further stack trace>

What seems to be the issue here is that we are calling the create task: here. The connection is an Octopus::Proxy class which when calling the create_database method, hits Octopus::Proxy#method_missing which eventually delegates to: safe_connection's connection

The problem here is two things: Rails db:create rake task establishes the connection with a database configuration { 'database' => nil }: here The Octopus is overriding this connection with connection_pool.connection which uses a database configuration { 'database' => value in yaml file}. This connection raises the Unknown database issue.

Proposed solution:

Alias the establish_connection method to explicitly and return an establish_connection_without_octopus connection.

This will cause establish_connection configuration_without_database to return a MySQL2::Adapter class instead of a Octopus::Proxy class, from which we can successfully call connection.create_database and bypass Octopus::Proxy#method_missing

Replication stack of issue:

  • Rails 4.2.7
  • Octopus v0.9.0 or master-head
  • MySQL2

Gemfile:

gem 'rails', '4.2.7.1'
...
gem 'ar-octopus', git: '[email protected]:thiagopradi/octopus'
gem 'mysql2'

Database.yml:

development:
  adapter: mysql2
  encoding: utf8
  database: 'octopus_development'
  username: 'root'
  password: 'password'

Shards.yml:

octopus:
  environments:
    - development

  shards: &shards
    master: &master
      adapter: mysql2
      encoding: utf8
      database: 'octopus_development'
      username: 'root'
      password: 'password'

  development:
    <<: *shards

abMatGit avatar Apr 22 '17 02:04 abMatGit

I am running into a similar issue with rake db:reset in development mode, where we are trying to use Octopus to expose write to read only errors before they hit production.

pboling avatar Jun 24 '17 07:06 pboling

@abMatGit @pboling What did you guys end up doing to address this in your apps?

Were you able to come up with a work-around? We are also experiencing this issue and it would help to know how you proceeded.

Thanks to both of you in advance!

swordfish444 avatar Feb 15 '18 19:02 swordfish444

My current workaround:

  1. Add a rake task to enhance db:create db:drop tasks
namespace :octopus do
  task on: :environment do
    ActiveRecord::Base.clear_all_connections!
    Octopus.enable!
  end

  task off: :environment do
    ActiveRecord::Base.clear_all_connections!
    Octopus.disable!
  end
end

Rake::Task['db:create'].enhance(['octopus:off']) do
  Rake::Task['octopus:on'].invoke
end

Rake::Task['db:drop'].enhance(['octopus:off']) do
  Rake::Task['octopus:on'].invoke
end
  1. Patch Octopus module:
module Octopus
   class << self
    def disable!
      @@enabled = false
    end

    def enable!
      @@enabled = true
    end

    def enabled_with_additional_check?
      enabled_without_additional_check? && @@enabled
    end
    alias_method :enabled_without_additional_check?, :enabled?
    alias_method :enabled?, :enabled_with_additional_check?
  end
end
Octopus.enable!

jughead avatar Feb 19 '18 10:02 jughead

@jughead thanks for the workaround.

Didn't you mean to invoke octopus:off after db:drop (instead of octopus:on) ?

This works better for me, otherwise chaining rake db:drop db:create fails, because db:drop would keep Octopus enabled with no existing database.

jvenezia avatar Dec 28 '18 11:12 jvenezia

The problem with this approach is that passing in tasks to the arguments of the enhance method adds them as prerequisites. As such, they only get invoked once, so chaining db:create and db:drop may break.

I was able to get this to work more consistently with this:

Rake::Task['db:create'].actions.unshift Proc.new {
  Rake::Task['octopus:patch'].invoke
  Octopus.disable!
}
Rake::Task['db:create'].enhance do
  Rake::Task['octopus:patch'].invoke
  Octopus.enable!
end
Rake::Task['db:drop'].actions.unshift Proc.new {
  Rake::Task['octopus:patch'].invoke
  Octopus.disable!
}
Rake::Task['db:drop'].enhance do
  Rake::Task['octopus:patch'].invoke
  Octopus.enable!
end

Where there's a task octopus:patch that does the monkey patching. As it's a rake task, it will only ever get called once.

evanboho avatar Jun 17 '19 21:06 evanboho

Thank you @evanboho

Can you share your octopus:patch rake task please?

jvenezia avatar Jun 27 '19 11:06 jvenezia

Hello all, is there any update on this issue? thanks :)

rajat23 avatar Feb 23 '20 15:02 rajat23

There is simpler solution. You can put

Octopus.environments=[]

in Rakefile and it will do the trick.

kvokka avatar Sep 01 '20 01:09 kvokka