slave_pools 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +20 -0
- data/README.rdoc +259 -0
- data/lib/slave_pools.rb +43 -0
- data/lib/slave_pools/active_record_extensions.rb +54 -0
- data/lib/slave_pools/connection_proxy.rb +280 -0
- data/lib/slave_pools/observer_extensions.rb +19 -0
- data/lib/slave_pools/query_cache_compat.rb +45 -0
- data/lib/slave_pools/slave_pool.rb +29 -0
- data/slave_pools.gemspec +24 -0
- data/spec/config/database.yml +43 -0
- data/spec/connection_proxy_spec.rb +330 -0
- data/spec/slave_pool_spec.rb +43 -0
- data/spec/slave_pools_spec.rb +69 -0
- data/spec/spec_helper.rb +17 -0
- metadata +132 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
module SlavePoolsModule
|
2
|
+
module ObserverExtensions
|
3
|
+
def self.included(base)
|
4
|
+
base.alias_method_chain :update, :masterdb
|
5
|
+
end
|
6
|
+
|
7
|
+
# Send observed_method(object) if the method exists.
|
8
|
+
# currently replicating the update method instead of using the aliased method call to update_without_master
|
9
|
+
def update_with_masterdb(observed_method, object, &block) #:nodoc:
|
10
|
+
if object.class.connection.respond_to?(:with_master)
|
11
|
+
object.class.connection.with_master do
|
12
|
+
send(observed_method, object, &block) if respond_to?(observed_method) && !disabled_for?(object)
|
13
|
+
end
|
14
|
+
else
|
15
|
+
send(observed_method, object, &block) if respond_to?(observed_method) && !disabled_for?(object)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module SlavePoolsModule
|
2
|
+
# Implements the methods expected by the QueryCache module
|
3
|
+
module QueryCacheCompat
|
4
|
+
|
5
|
+
def select_all(*a, &b)
|
6
|
+
arel, name, binds = a
|
7
|
+
if query_cache_enabled && !locked?(arel)
|
8
|
+
# FIXME this still hits the +select_all+ method in AR connection's
|
9
|
+
# query_cache.rb. It'd be nice if we could avoid it somehow so
|
10
|
+
# +select_all+ and then +to_sql+ aren't called redundantly.
|
11
|
+
sql = to_sql(arel, binds)
|
12
|
+
@master.connection.send(:cache_sql, sql, binds) {send_to_current(:select_all, *[sql, name, binds], &b)}
|
13
|
+
else
|
14
|
+
send_to_current(:select_all, *a, &b)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def insert(*a, &b)
|
19
|
+
@master.connection.clear_query_cache if query_cache_enabled
|
20
|
+
send_to_master(:insert, *a, &b)
|
21
|
+
end
|
22
|
+
|
23
|
+
def update(*a, &b)
|
24
|
+
@master.connection.clear_query_cache if query_cache_enabled
|
25
|
+
send_to_master(:update, *a, &b)
|
26
|
+
end
|
27
|
+
|
28
|
+
def delete(*a, &b)
|
29
|
+
@master.connection.clear_query_cache if query_cache_enabled
|
30
|
+
send_to_master(:delete, *a, &b)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Rails 3.2 changed query cacheing a little and affected slave_pools like this:
|
34
|
+
#
|
35
|
+
# * ActiveRecord::Base.cache sets @query_cache_enabled for current connection
|
36
|
+
# * ActiveRecord::QueryCache middleware (in call()) that Rails uses sets
|
37
|
+
# @query_cache_enabled directly on ActiveRecord::Base.connection
|
38
|
+
# (which could be master at that point)
|
39
|
+
#
|
40
|
+
# :`( So, let's just use the master connection for all query cacheing.
|
41
|
+
def query_cache_enabled
|
42
|
+
@master.connection.query_cache_enabled
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module SlavePoolsModule
|
2
|
+
class SlavePool
|
3
|
+
|
4
|
+
attr_accessor :name, :slaves,:pool_size, :current_index
|
5
|
+
|
6
|
+
def initialize(name, slaves)
|
7
|
+
@name = name
|
8
|
+
@slaves = slaves
|
9
|
+
@pool_size = @slaves.length
|
10
|
+
@current_index = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def current
|
14
|
+
@slaves[@current_index]
|
15
|
+
end
|
16
|
+
|
17
|
+
def next
|
18
|
+
next_index! if @pool_size != 1
|
19
|
+
current
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def next_index!
|
25
|
+
@current_index = (@current_index + 1) % @pool_size
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
data/slave_pools.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{slave_pools}
|
5
|
+
s.version = "0.1.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Dan Drabik"]
|
9
|
+
s.description = "Connection proxy for ActiveRecord for single master / multiple slave database groups"
|
10
|
+
s.email = "dan@kickstarter.com"
|
11
|
+
s.extra_rdoc_files = ["LICENSE", "README.rdoc"]
|
12
|
+
s.files = ["lib/slave_pools.rb", "lib/slave_pools/active_record_extensions.rb", "lib/slave_pools/connection_proxy.rb", "lib/slave_pools/observer_extensions.rb", "lib/slave_pools/query_cache_compat.rb", "lib/slave_pools/slave_pool.rb", "LICENSE", "README.rdoc", "spec/config/database.yml", "spec/connection_proxy_spec.rb", "spec/slave_pool_spec.rb","spec/slave_pools_spec.rb", "spec/spec_helper.rb", "slave_pools.gemspec"]
|
13
|
+
s.has_rdoc = true
|
14
|
+
s.homepage = "https://github.com/kickstarter/slave_pools"
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "slave_pools", "--main", "README.rdoc"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubygems_version = %q{1.3.1}
|
18
|
+
s.summary = "Connection proxy for ActiveRecord for single master / multiple slave database groups"
|
19
|
+
|
20
|
+
s.add_dependency('activerecord', ["~> 3.2.12"])
|
21
|
+
s.add_development_dependency('mysql2', ["~> 0.3.11"])
|
22
|
+
s.add_development_dependency('rspec')
|
23
|
+
s.add_development_dependency('rake')
|
24
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
login: &login
|
2
|
+
adapter: mysql2
|
3
|
+
username: root
|
4
|
+
password:
|
5
|
+
host: localhost
|
6
|
+
encoding: utf8
|
7
|
+
read_timeout: 1
|
8
|
+
|
9
|
+
readonly_login: &readonly_login
|
10
|
+
adapter: mysql2
|
11
|
+
username: read_only
|
12
|
+
password: readme
|
13
|
+
host: localhost
|
14
|
+
encoding: utf8
|
15
|
+
read_timeout: 1
|
16
|
+
|
17
|
+
test:
|
18
|
+
database: test_db
|
19
|
+
<<: *login
|
20
|
+
|
21
|
+
test_pool_secondary_name_db1:
|
22
|
+
database: test_db
|
23
|
+
<<: *readonly_login
|
24
|
+
|
25
|
+
test_pool_secondary_name_db2:
|
26
|
+
database: test_db
|
27
|
+
<<: *readonly_login
|
28
|
+
|
29
|
+
test_pool_secondary_name_db3:
|
30
|
+
database: test_db
|
31
|
+
<<: *readonly_login
|
32
|
+
|
33
|
+
test_pool_default_name_db1:
|
34
|
+
database: test_db
|
35
|
+
<<: *readonly_login
|
36
|
+
|
37
|
+
test_pool_default_name_db2:
|
38
|
+
database: test_db
|
39
|
+
<<: *readonly_login
|
40
|
+
|
41
|
+
test_pool_default_name_fake_db:
|
42
|
+
database: fake_db_that_doesnt_exist #do not create this db, used to test the connection
|
43
|
+
<<: *readonly_login
|
@@ -0,0 +1,330 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe SlavePools do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
ActiveRecord::Base.configurations = SLAVE_POOLS_SPEC_CONFIG
|
7
|
+
ActiveRecord::Base.establish_connection :test
|
8
|
+
ActiveRecord::Migration.verbose = false
|
9
|
+
ActiveRecord::Migration.create_table(:master_models, :force => true) {}
|
10
|
+
class MasterModel < ActiveRecord::Base; end
|
11
|
+
ActiveRecord::Migration.create_table(:foo_models, :force => true) {|t| t.string :bar}
|
12
|
+
class FooModel < ActiveRecord::Base; end
|
13
|
+
@sql = 'SELECT NOW()'
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "standard setup" do
|
17
|
+
before(:each) do
|
18
|
+
SlavePoolsModule::ConnectionProxy.master_models = ['MasterModel']
|
19
|
+
SlavePoolsModule::ConnectionProxy.setup!
|
20
|
+
@proxy = ActiveRecord::Base.connection_proxy
|
21
|
+
@slave_pool_hash = @proxy.slave_pools
|
22
|
+
@slave_pool_array = @slave_pool_hash.values
|
23
|
+
@master = @proxy.master.retrieve_connection
|
24
|
+
# creates instance variables (@default_slave1, etc.) for each slave based on the order they appear in the slave_pool
|
25
|
+
# ruby 1.8.7 doesn't support ordered hashes, so we assign numbers to the slaves this way, and not the order in the yml file
|
26
|
+
# to prevent @default_slave1 from being different on different systems
|
27
|
+
['default', 'secondary'].each do |pool_name|
|
28
|
+
@slave_pool_hash[pool_name.to_sym].slaves.each_with_index do |slave, i|
|
29
|
+
instance_variable_set("@#{pool_name}_slave#{i + 1}", slave.retrieve_connection)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'AR::B should respond to #connection_proxy' do
|
35
|
+
ActiveRecord::Base.connection_proxy.should be_kind_of(SlavePoolsModule::ConnectionProxy)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'FooModel#connection should return an instance of SlavePools::ConnectionProxy' do
|
39
|
+
FooModel.connection.should be_kind_of(SlavePoolsModule::ConnectionProxy)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'MasterModel#connection should not return an instance of SlavePools::ConnectionProxy' do
|
43
|
+
MasterModel.connection.should_not be_kind_of(SlavePoolsModule::ConnectionProxy)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should generate classes for each entry in the database.yml" do
|
47
|
+
defined?(SlavePoolsModule::DefaultDb1).should_not be_nil
|
48
|
+
defined?(SlavePoolsModule::DefaultDb2).should_not be_nil
|
49
|
+
defined?(SlavePoolsModule::SecondaryDb1).should_not be_nil
|
50
|
+
defined?(SlavePoolsModule::SecondaryDb2).should_not be_nil
|
51
|
+
defined?(SlavePoolsModule::SecondaryDb3).should_not be_nil
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should not generate classes for an invalid DB in the database.yml" do
|
55
|
+
defined?(SlavePoolsModule::DefaultFakeDb).should be_nil
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should handle nested with_master-blocks correctly' do
|
59
|
+
@proxy.current.should_not == @proxy.master
|
60
|
+
@proxy.with_master do
|
61
|
+
@proxy.current.should == @proxy.master
|
62
|
+
@proxy.with_master do
|
63
|
+
@proxy.current.should == @proxy.master
|
64
|
+
@proxy.with_master do
|
65
|
+
@proxy.current.should == @proxy.master
|
66
|
+
end
|
67
|
+
@proxy.current.should == @proxy.master
|
68
|
+
end
|
69
|
+
@proxy.current.should == @proxy.master
|
70
|
+
end
|
71
|
+
@proxy.current.should_not == @proxy.master
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should perform transactions on the master' do
|
75
|
+
@master.should_receive(:select_all).exactly(5)
|
76
|
+
@default_slave1.should_receive(:select_all).exactly(0)
|
77
|
+
ActiveRecord::Base.transaction({}) do
|
78
|
+
5.times {@proxy.select_all(@sql)}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should perform transactions on the master, and selects outside of transaction on the slave' do
|
83
|
+
@default_slave1.should_receive(:select_all).exactly(2) # before and after the transaction go to slaves
|
84
|
+
@master.should_receive(:select_all).exactly(5)
|
85
|
+
@proxy.select_all(@sql)
|
86
|
+
ActiveRecord::Base.transaction do
|
87
|
+
5.times {@proxy.select_all(@sql)}
|
88
|
+
end
|
89
|
+
@proxy.select_all(@sql)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should not switch to the next reader on selects' do
|
93
|
+
@default_slave1.should_receive(:select_one).exactly(6)
|
94
|
+
@default_slave2.should_receive(:select_one).exactly(0)
|
95
|
+
6.times { @proxy.select_one(@sql) }
|
96
|
+
end
|
97
|
+
|
98
|
+
it '#next_slave! should switch to the next slave' do
|
99
|
+
@default_slave1.should_receive(:select_one).exactly(3)
|
100
|
+
@default_slave2.should_receive(:select_one).exactly(7)
|
101
|
+
3.times { @proxy.select_one(@sql) }
|
102
|
+
@proxy.next_slave!
|
103
|
+
7.times { @proxy.select_one(@sql) }
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'should switch if next reader is explicitly called' do
|
107
|
+
@default_slave1.should_receive(:select_one).exactly(3)
|
108
|
+
@default_slave2.should_receive(:select_one).exactly(3)
|
109
|
+
6.times do
|
110
|
+
@proxy.select_one(@sql)
|
111
|
+
@proxy.next_slave!
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'should not switch to the next reader when whithin a with_master-block' do
|
116
|
+
@master.should_receive(:select_one).twice
|
117
|
+
@default_slave1.should_not_receive(:select_one)
|
118
|
+
@default_slave2.should_not_receive(:select_one)
|
119
|
+
@proxy.with_master do
|
120
|
+
2.times { @proxy.select_one(@sql) }
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'should send dangerous methods to the master' do
|
125
|
+
meths = [:insert, :update, :delete, :execute]
|
126
|
+
meths.each do |meth|
|
127
|
+
@default_slave1.stub!(meth).and_raise(RuntimeError)
|
128
|
+
@master.should_receive(meth).and_return(true)
|
129
|
+
@proxy.send(meth, @sql)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should send writes to the master, even if current gets called for a write" do
|
134
|
+
@proxy.instance_variable_set("@master_depth", 0)
|
135
|
+
@master.should_receive(:update).and_return(true)
|
136
|
+
@proxy.send(:send_to_current, :update, @sql, {})
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should not allow master depth to get below 0" do
|
140
|
+
@proxy.instance_variable_set("@master_depth", -500)
|
141
|
+
@proxy.instance_variable_get("@master_depth").should == -500
|
142
|
+
@proxy.with_master {@sql}
|
143
|
+
@proxy.instance_variable_get("@master_depth").should == 0
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'should dynamically generate safe methods' do
|
147
|
+
@proxy.should_not respond_to(:select_value)
|
148
|
+
@proxy.select_value(@sql)
|
149
|
+
@proxy.should respond_to(:select_value)
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should cache queries using select_all' do
|
153
|
+
ActiveRecord::Base.cache do
|
154
|
+
# next_slave will be called and switch to the SlaveDatabase2
|
155
|
+
@default_slave1.should_receive(:select_all).exactly(1).and_return([])
|
156
|
+
@default_slave2.should_not_receive(:select_all)
|
157
|
+
@master.should_not_receive(:select_all)
|
158
|
+
3.times { @proxy.select_all(@sql) }
|
159
|
+
@master.query_cache.keys.size.should == 1
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'should invalidate the cache on insert, delete and update' do
|
164
|
+
ActiveRecord::Base.cache do
|
165
|
+
meths = [:insert, :update, :delete, :insert, :update]
|
166
|
+
meths.each do |meth|
|
167
|
+
@master.should_receive(meth).and_return(true)
|
168
|
+
end
|
169
|
+
@default_slave1.should_receive(:select_all).exactly(5).and_return([])
|
170
|
+
@default_slave2.should_receive(:select_all).exactly(0)
|
171
|
+
5.times do |i|
|
172
|
+
@proxy.select_all(@sql)
|
173
|
+
@proxy.select_all(@sql)
|
174
|
+
@master.query_cache.keys.size.should == 1
|
175
|
+
@proxy.send(meths[i])
|
176
|
+
@master.query_cache.keys.size.should == 0
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
describe "using querycache middleware" do
|
182
|
+
it 'should cache queries using select_all' do
|
183
|
+
mw = ActiveRecord::QueryCache.new lambda { |env|
|
184
|
+
@default_slave1.should_receive(:select_all).exactly(1).and_return([])
|
185
|
+
@default_slave2.should_not_receive(:select_all)
|
186
|
+
@master.should_not_receive(:select_all)
|
187
|
+
3.times { @proxy.select_all(@sql) }
|
188
|
+
@proxy.next_slave!
|
189
|
+
3.times { @proxy.select_all(@sql) }
|
190
|
+
@proxy.next_slave!
|
191
|
+
3.times { @proxy.select_all(@sql)}
|
192
|
+
@master.query_cache.keys.size.should == 1
|
193
|
+
}
|
194
|
+
mw.call({})
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'should invalidate the cache on insert, delete and update' do
|
198
|
+
mw = ActiveRecord::QueryCache.new lambda { |env|
|
199
|
+
meths = [:insert, :update, :delete, :insert, :update]
|
200
|
+
meths.each do |meth|
|
201
|
+
@master.should_receive(meth).and_return(true)
|
202
|
+
end
|
203
|
+
@default_slave1.should_receive(:select_all).exactly(5).and_return([])
|
204
|
+
@default_slave2.should_receive(:select_all).exactly(0)
|
205
|
+
5.times do |i|
|
206
|
+
@proxy.select_all(@sql)
|
207
|
+
@proxy.select_all(@sql)
|
208
|
+
@master.query_cache.keys.size.should == 1
|
209
|
+
@proxy.send(meths[i])
|
210
|
+
@master.query_cache.keys.size.should == 0
|
211
|
+
end
|
212
|
+
}
|
213
|
+
mw.call({})
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'should NOT rescue a non Mysql2::Error' do
|
218
|
+
@default_slave1.should_receive(:select_all).once.and_raise(RuntimeError.new('some error'))
|
219
|
+
@default_slave2.should_not_receive(:select_all)
|
220
|
+
@master.should_not_receive(:select_all)
|
221
|
+
lambda { @proxy.select_all(@sql) }.should raise_error
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'should rescue a Mysql::Error fall back to the master' do
|
225
|
+
@default_slave1.should_receive(:select_all).once.and_raise(Mysql2::Error.new('connection error'))
|
226
|
+
@default_slave2.should_not_receive(:select_all)
|
227
|
+
@master.should_receive(:select_all).and_return(true)
|
228
|
+
lambda { @proxy.select_all(@sql) }.should_not raise_error
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'should re-raise a Mysql::Error from a query timeout and not fall back to master' do
|
232
|
+
@default_slave1.should_receive(:select_all).once.and_raise(Mysql2::Error.new('Timeout waiting for a response from the last query. (waited 5 seconds)'))
|
233
|
+
@default_slave2.should_not_receive(:select_all)
|
234
|
+
@master.should_not_receive(:select_all)
|
235
|
+
lambda { @proxy.select_all(@sql) }.should raise_error
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'should try to reconnect the master connection after the master has failed' do
|
239
|
+
@master.should_receive(:update).and_raise(RuntimeError)
|
240
|
+
lambda { @proxy.update(@sql) }.should raise_error
|
241
|
+
@master.should_receive(:reconnect!).and_return(true)
|
242
|
+
@master.should_receive(:insert).and_return(1)
|
243
|
+
@proxy.insert(@sql)
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'should reload models from the master' do
|
247
|
+
foo = FooModel.create!(:bar => 'baz')
|
248
|
+
foo.bar = "not_saved"
|
249
|
+
@default_slave1.should_not_receive(:select_all)
|
250
|
+
@default_slave2.should_not_receive(:select_all)
|
251
|
+
foo.reload
|
252
|
+
# we didn't stub @master#select_all here, check that we actually hit the db
|
253
|
+
foo.bar.should == 'baz'
|
254
|
+
end
|
255
|
+
|
256
|
+
context "Using with_pool call" do
|
257
|
+
|
258
|
+
it "should switch to default pool if an invalid pool is specified" do
|
259
|
+
@default_slave1.should_receive(:select_one).exactly(3)
|
260
|
+
@secondary_slave1.should_not_receive(:select_one)
|
261
|
+
@secondary_slave2.should_not_receive(:select_one)
|
262
|
+
@secondary_slave3.should_not_receive(:select_one)
|
263
|
+
@proxy.with_pool('sfdsfsdf') do
|
264
|
+
3.times {@proxy.select_one(@sql)}
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
it "should switch to default pool if an no pool is specified" do
|
269
|
+
@default_slave1.should_receive(:select_one).exactly(1)
|
270
|
+
@proxy.with_pool do
|
271
|
+
@proxy.select_one(@sql)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
it "should use a different pool if specified" do
|
276
|
+
@default_slave1.should_not_receive(:select_one)
|
277
|
+
@secondary_slave1.should_receive(:select_one).exactly(3)
|
278
|
+
@secondary_slave2.should_not_receive(:select_one)
|
279
|
+
@secondary_slave3.should_not_receive(:select_one)
|
280
|
+
@proxy.with_pool('secondary') do
|
281
|
+
3.times {@proxy.select_one(@sql)}
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
it "should different pool should use next_slave! to advance to the next DB" do
|
286
|
+
@default_slave1.should_not_receive(:select_one)
|
287
|
+
@secondary_slave1.should_receive(:select_one).exactly(2)
|
288
|
+
@secondary_slave2.should_receive(:select_one).exactly(1)
|
289
|
+
@secondary_slave3.should_receive(:select_one).exactly(1)
|
290
|
+
@proxy.with_pool('secondary') do
|
291
|
+
4.times do
|
292
|
+
@proxy.select_one(@sql)
|
293
|
+
@proxy.next_slave!
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
it "should switch to master if with_master is specified in an inner block" do
|
299
|
+
@master.should_receive(:select_one).exactly(5)
|
300
|
+
@default_slave1.should_receive(:select_one).exactly(0)
|
301
|
+
@secondary_slave1.should_receive(:select_one).exactly(0)
|
302
|
+
@secondary_slave2.should_receive(:select_one).exactly(0)
|
303
|
+
@secondary_slave3.should_receive(:select_one).exactly(0)
|
304
|
+
@proxy.with_pool('secondary') do
|
305
|
+
@proxy.with_master do
|
306
|
+
5.times do
|
307
|
+
@proxy.select_one(@sql)
|
308
|
+
@proxy.next_slave!
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
it "should switch to master if with_master is specified in an outer block (with master needs to trump with_pool)" do
|
315
|
+
@secondary_slave1.should_receive(:select_one).exactly(0)
|
316
|
+
@secondary_slave2.should_receive(:select_one).exactly(0)
|
317
|
+
@secondary_slave3.should_receive(:select_one).exactly(0)
|
318
|
+
@proxy.with_master do
|
319
|
+
@proxy.with_pool('secondary') do
|
320
|
+
5.times do
|
321
|
+
@proxy.select_one(@sql)
|
322
|
+
@proxy.next_slave!
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|