zendesk / active_record_shards

Support for sharded databases and replicas for ActiveRecord
MIT License
250 stars 26 forks source link

"where" doesn't work when on_shard (Rails 3.2) #6

Open lukkry opened 10 years ago

lukkry commented 10 years ago

ActiveRecordShards tries to read from not sharded database when calling

ActiveRecord::Base.on_shard(1){ Project.where(:id => 1) }

Steps to reproduce:

development:
  adapter: mysql2
  encoding: utf8
  database: mango
  pool: 5
  host: 127.0.0.1
  username: root
  password:
  slave:
    database: mango_slave
  shards:
    1:
      database: mango_shard0
      slave:
        database: mango_shard0_slave
    2:
      database: mango_shard1
      slave:
        database: mango_shard1_slave
class CreateProjects < ActiveRecord::Migration
  shard :all

  def change
    create_table :projects do |t|
      t.string :name

      t.timestamps
    end
  end
end
1.9.3-p429 :008 > ActiveRecord::Base.on_shard(1){ Project.connection.current_database }
   (2.5ms)  SELECT DATABASE() as db
 => "mango_shard0"
1.9.3-p429 :006 > ActiveRecord::Base.on_shard(1){ Project.all }
  Project Load (0.3ms)  SELECT `projects`.* FROM `projects`
 => []
1.9.3-p429 :001 > ActiveRecord::Base.on_shard(1){ Project.where(:id => 1) }
  Project Load (0.3ms)  SELECT `projects`.* FROM `projects` WHERE `projects`.`id` = 1
ActiveRecord::StatementInvalid: Mysql2::Error: Table 'mango.projects' doesn't exist: SELECT `projects`.* FROM `projects`  WHERE `projects`.`id` = 1

Am I missing something obvious here?

lukkry commented 10 years ago

It works if I set default_shard earlier

$ be rails c
Loading development environment (Rails 3.2.14)
1.9.3-p429 :001 > ActiveRecord::Base.default_shard = 1
 => 1
1.9.3-p429 :002 > ActiveRecord::Base.on_shard(1){ Project.where(:id => 1) }
  Project Load (0.3ms)  SELECT `projects`.* FROM `projects` WHERE `projects`.`id` = 1
 => []
osheroff commented 10 years ago

What's probably happening here is that the call to 'where' is returning an active relation object, ie a "promise" that is then evaluated outside the block (probably when irb calls "to_s") on it. Unfortunately, at that point we've already switched off the shard again.

On Sep 2, 2013, at 1:13 PM, Lukasz Krystkowiak notifications@github.com wrote:

ActiveRecordShards tries to read from not sharded database when calling

ActiveRecord::Base.on_shard(1){ Project.where(:id => 1) } Steps to reproduce:

create a new rails 3.2.14 app add active_record_shards to Gemfile create database.yml development: adapter: mysql2 encoding: utf8 database: mango pool: 5 host: 127.0.0.1 username: root password: slave: database: mango_slave shards: 1: database: mango_shard0 slave: database: mango_shard0_slave 2: database: mango_shard1 slave: database: mango_shard1_slave $ bundle exec rails g model Project name:string class CreateProjects < ActiveRecord::Migration shard :all

def change create_table :projects do |t| t.string :name

  t.timestamps
end

end end $ bundle exec rake db:migrate $ bundle exec rails c 1.9.3-p429 :008 > ActiveRecord::Base.on_shard(1){ Project.connection.current_database } (2.5ms) SELECT DATABASE() as db => "mango_shard0" 1.9.3-p429 :006 > ActiveRecord::Base.on_shard(1){ Project.all } Project Load (0.3ms) SELECT projects.* FROM projects => [] 1.9.3-p429 :001 > ActiveRecord::Base.on_shard(1){ Project.where(:id => 1) } Project Load (0.3ms) SELECT projects.* FROM projects WHERE projects.id = 1 ActiveRecord::StatementInvalid: Mysql2::Error: Table 'mango.projects' doesn't exist: SELECT projects.* FROM projects WHERE projects.id = 1 Am I missing something obvious here?

— Reply to this email directly or view it on GitHub.

lukkry commented 10 years ago

You're right, it works as expected when it's evaluated inside the block

1.9.3-p429 :001 > ActiveRecord::Base.on_shard(1){ Project.where(:id => 1).first }
  Project Load (0.3ms)  SELECT `projects`.* FROM `projects` WHERE `projects`.`id` = 1 LIMIT 1
 => nil
osheroff commented 10 years ago

re-opening, since this is very violiating of principle of least surprises -- any ideas on what it would take to stash the shard-selection alongside the activerelation promise object and switch in and out of the shard for the duration of the query?

staugaard commented 10 years ago

Something like what I did for the old ticket archive. RelationFromArchive in https://github.com/zendesk/zendesk/blob/master/lib/archive.rb