octopus
octopus copied to clipboard
Cannot run rake task `db:create` with mysql.
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
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.
@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!
My current workaround:
- 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
- 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 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.
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.
Thank you @evanboho
Can you share your octopus:patch
rake task please?
Hello all, is there any update on this issue? thanks :)
There is simpler solution. You can put
Octopus.environments=[]
in Rakefile and it will do the trick.