thiagopradi / octopus

Database Sharding for ActiveRecord
2.53k stars 505 forks source link

Rails DB and Gem using octopus #362

Open ghost opened 8 years ago

ghost commented 8 years ago

A bit complex, but let me explain

The gem by itself is able to connect and the .using(:shard_name) works perfectly. When I include this gem into rails app the .using(:shard_name) is ignored. Is there anything special and different I have to do when octopus is included in rails?

I do have a config/shards.yml in the rails app.

shared: &shared
  adapter: mysql2
  encoding: utf8
  username: user
  password: pass
  database: legacy_shard_db
  port: 3306

octopus:
  replicated: false
  fully_replicated: false
  shards:
    my_shard:
      <<: *shared
      host: shard1.database.host
    my_shard2:
      <<: *shared
      host: shard2.database.host

I have created an initializer that does the octopus_connection_establish on the base class of my gem

config1 = YAML::load(File.open('config/mygem.yml'))
MyGem::Models::Base.octopus_establish_connection(config1[Rails.env.to_sym])

In a rails console I see the rails models connected to their db and the Gem models connected to the legacy database, but the using statement is ignored

# gem (octopus)
MyGem::Models::MyModel.using(:'my_shard').connection_config
=> {:adapter=>"mysql2", :encoding=>"utf8", :username=>"username", :password=>"password", :database=>"legacy_shard_db", :port=>3306, :secure_auth=>false, :host=>"shard1.database.host"}

# using statement ignored here.. host is still 'shard1.database.host'
MyGem::Models::MyModel.using(:'my_shard2').connection_config
=> {:adapter=>"mysql2", :encoding=>"utf8", :username=>"username", :password=>"password", :database=>"legacy_shard_db", :port=>3306, :secure_auth=>false, :host=>"shard1.database.host"}

# rails db
RailsModel.connection_config
=> {:adapter=>"mysql2", :username=>"u", :password=>"p", :host=>"localhost", :database=>"myapp_development", :flags=>2}
ghost commented 8 years ago

Ok so I experiment more and adding environments to my shards.yml has fixed this issue.

shared: &shared
  adapter: mysql2
  encoding: utf8
  username: user
  password: pass
  database: legacy_shard_db
  port: 3306

octopus:
  replicated: false
  fully_replicated: false
  environments:
    - development
  development:
    my_shard:
      <<: *shared
      host: shard1.database.host
    my_shard2:
      <<: *shared
      host: shard2.database.host

The issue that arises with this setup is migrations no longer work

Mysql2::Error: CREATE command denied to user 'user'@'10.xx.xx.xx' for table 'schema_migrations': CREATE TABLE `schema_migrations` (`version` varchar(255) NOT NULL) ENGINE=InnoDB
vendor/ruby/1.9.1/gems/activerecord-3.2.8/lib/active_record/connection_adapters/abstract_mysql_adapter.rb:245:in `query'
xxx/vendor/ruby/1.9.1/gems/activerecord-3.2.8/lib/active_record/connection_adapters/abstract_mysql_adapter.rb:245:in `block in execute'

are the migrations trying to run on the shards... can I disable this and have it only run on the rails db?

ghost commented 8 years ago

I worked around this problem by overriding the connection for all database tasks; now it will use only my primary rails database and ignore the shard databases that are part of my gem (legacy db that I don't manage from this project).

task :before_db_task => :environment do
  puts "overriding octopus connection; using rails connection - #{Rails.env} "
  require 'active_record'
  ActiveRecord::Base.octopus_establish_connection(Rails.env)
end
tasks_arr = ['db:create', 'db:drop', 'db:fixtures:load', 'db:migrate', 'db:migrate:status', 'db:rollback', 'db:schema:cache:clear', 'db:schema:cache:dump', 'db:schema:dump', 'db:schema:load', 'db:seed', 'db:setup', 'db:structure:dump', 'db:structure:load', 'db:version', 'test:all:db', 'test:db']
tasks_arr.each do |t|
  Rake::Task[t].enhance(['before_db_task'])
end

I do wish there was a octopus supported way to do this?