slave_pools 0.1.2 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,327 +3,252 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
3
  describe SlavePools do
4
4
 
5
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
6
  @sql = 'SELECT NOW()'
7
+
8
+ @proxy = SlavePools.proxy
9
+ @master = @proxy.master.retrieve_connection
10
+
11
+ reset_proxy(@proxy)
12
+ create_slave_aliases(@proxy)
14
13
  end
15
14
 
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
15
+ it 'AR::B should respond to #connection_proxy' do
16
+ ActiveRecord::Base.should respond_to(:connection_proxy)
17
+ ActiveRecord::Base.connection_proxy.should be_kind_of(SlavePools::ConnectionProxy)
18
+ end
33
19
 
34
- it 'AR::B should respond to #connection_proxy' do
35
- ActiveRecord::Base.connection_proxy.should be_kind_of(SlavePoolsModule::ConnectionProxy)
36
- end
20
+ it 'TestModel#connection should return an instance of SlavePools::ConnectionProxy' do
21
+ TestModel.connection.should be_kind_of(SlavePools::ConnectionProxy)
22
+ end
37
23
 
38
- it 'FooModel#connection should return an instance of SlavePools::ConnectionProxy' do
39
- FooModel.connection.should be_kind_of(SlavePoolsModule::ConnectionProxy)
40
- end
24
+ it "should generate classes for each entry in the database.yml" do
25
+ defined?(SlavePools::DefaultDb1).should_not be_nil
26
+ defined?(SlavePools::DefaultDb2).should_not be_nil
27
+ defined?(SlavePools::SecondaryDb1).should_not be_nil
28
+ defined?(SlavePools::SecondaryDb2).should_not be_nil
29
+ defined?(SlavePools::SecondaryDb3).should_not be_nil
30
+ end
41
31
 
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
32
+ it "should not generate classes for an invalid DB in the database.yml" do
33
+ defined?(SlavePools::DefaultFakeDb).should be_nil
34
+ end
45
35
 
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
36
+ context "with_master" do
37
+ it 'should revert to previous slave connection' do
38
+ @proxy.current = @proxy.current_slave
39
+ @proxy.with_master do
40
+ @proxy.current.should equal(@proxy.master)
41
+ end
42
+ @proxy.current.name.should eq('SlavePools::DefaultDb1')
52
43
  end
53
44
 
54
- it "should not generate classes for an invalid DB in the database.yml" do
55
- defined?(SlavePoolsModule::DefaultFakeDb).should be_nil
45
+ it 'should revert to previous master connection' do
46
+ @proxy.current = @proxy.master
47
+ @proxy.with_master do
48
+ @proxy.current.should equal(@proxy.master)
49
+ end
50
+ @proxy.current.should equal(@proxy.master)
56
51
  end
57
52
 
58
- it 'should handle nested with_master-blocks correctly' do
59
- @proxy.current.should_not == @proxy.master
53
+ it 'should know when in block' do
54
+ @proxy.send(:within_master_block?).should_not be
60
55
  @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
56
+ @proxy.send(:within_master_block?).should be
70
57
  end
71
- @proxy.current.should_not == @proxy.master
58
+ @proxy.send(:within_master_block?).should_not be
72
59
  end
60
+ end
73
61
 
74
- it 'should perform transactions on the master' do
75
- @master.should_receive(:select_all).exactly(5)
62
+ context "transaction" do
63
+ it 'should send all to master' do
64
+ @master.should_receive(:select_all).exactly(1)
76
65
  @default_slave1.should_receive(:select_all).exactly(0)
77
- ActiveRecord::Base.transaction({}) do
78
- 5.times {@proxy.select_all(@sql)}
66
+
67
+ TestModel.transaction do
68
+ @proxy.select_all(@sql)
79
69
  end
80
70
  end
81
71
 
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)
72
+ it 'should send all to master even if transactions begins on AR::Base' do
73
+ @master.should_receive(:select_all).exactly(1)
74
+ @default_slave1.should_receive(:select_all).exactly(0)
75
+
86
76
  ActiveRecord::Base.transaction do
87
- 5.times {@proxy.select_all(@sql)}
77
+ @proxy.select_all(@sql)
88
78
  end
89
- @proxy.select_all(@sql)
90
79
  end
80
+ end
91
81
 
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) }
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)}
96
88
  end
89
+ @proxy.select_all(@sql)
90
+ end
97
91
 
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
92
+ it 'should not switch slaves automatically 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
105
97
 
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
98
+ context "next_slave!" do
99
+ it 'should switch to the next slave' do
100
+ @default_slave1.should_receive(:select_one).exactly(1)
101
+ @default_slave2.should_receive(:select_one).exactly(1)
102
+
103
+ @proxy.select_one(@sql)
104
+ @proxy.next_slave!
105
+ @proxy.select_one(@sql)
113
106
  end
114
107
 
115
- it 'should not switch to the next reader when whithin a with_master-block' do
116
- @master.should_receive(:select_one).twice
108
+ it 'should not switch when in a with_master-block' do
109
+ @master.should_receive(:select_one).exactly(2)
117
110
  @default_slave1.should_not_receive(:select_one)
118
111
  @default_slave2.should_not_receive(:select_one)
112
+
119
113
  @proxy.with_master do
120
- 2.times { @proxy.select_one(@sql) }
114
+ @proxy.select_one(@sql)
115
+ @proxy.next_slave!
116
+ @proxy.select_one(@sql)
121
117
  end
122
118
  end
119
+ end
123
120
 
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
121
+ it 'should send dangerous methods to the master' do
122
+ meths = [:insert, :update, :delete, :execute]
123
+ meths.each do |meth|
124
+ @default_slave1.stub(meth).and_raise(RuntimeError)
125
+ @master.should_receive(meth).and_return(true)
126
+ @proxy.send(meth, @sql)
131
127
  end
128
+ end
132
129
 
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
130
+ it "should not allow master depth to get below 0" do
131
+ @proxy.instance_variable_set("@master_depth", -500)
132
+ @proxy.instance_variable_get("@master_depth").should == -500
133
+ @proxy.with_master {@sql}
134
+ @proxy.instance_variable_get("@master_depth").should == 0
135
+ end
138
136
 
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
137
+ it 'should pre-generate safe methods' do
138
+ @proxy.should respond_to(:select_value)
139
+ end
145
140
 
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
141
+ it 'should dynamically generate unsafe methods' do
142
+ @master.should_receive(:unsafe).and_return(true)
151
143
 
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
144
+ @proxy.should_not respond_to(:unsafe)
145
+ @proxy.unsafe(@sql)
146
+ @proxy.should respond_to(:unsafe)
147
+ end
162
148
 
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
149
+ it 'should NOT rescue a non Mysql2::Error' do
150
+ @default_slave1.should_receive(:select_all).once.and_raise(RuntimeError.new('some error'))
151
+ @default_slave2.should_not_receive(:select_all)
152
+ @master.should_not_receive(:select_all)
153
+ lambda { @proxy.select_all(@sql) }.should raise_error
154
+ end
180
155
 
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
156
+ it 'should rescue a Mysql::Error fall back to the master' do
157
+ @default_slave1.should_receive(:select_all).once.and_raise(Mysql2::Error.new('connection error'))
158
+ @default_slave2.should_not_receive(:select_all)
159
+ @master.should_receive(:select_all).and_return(true)
160
+ lambda { @proxy.select_all(@sql) }.should_not raise_error
161
+ end
196
162
 
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
163
+ it 'should re-raise a Mysql::Error from a query timeout and not fall back to master' do
164
+ @default_slave1.should_receive(:select_all).once.and_raise(Mysql2::Error.new('Timeout waiting for a response from the last query. (waited 5 seconds)'))
165
+ @default_slave2.should_not_receive(:select_all)
166
+ @master.should_not_receive(:select_all)
167
+ lambda { @proxy.select_all(@sql) }.should raise_error
168
+ end
216
169
 
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
170
+ it 'should reload models from the master' do
171
+ foo = TestModel.create!
172
+ @master.should_receive(:select_all).and_return([{'id' => foo.id}])
173
+ @default_slave1.should_not_receive(:select_all)
174
+ @default_slave2.should_not_receive(:select_all)
175
+ foo.reload
176
+ end
223
177
 
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
178
+ context "with_pool" do
230
179
 
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
180
+ it "should switch to the named pool" do
181
+ @proxy.with_pool('secondary') do
182
+ @proxy.current_pool.name.should eq('secondary')
183
+ @proxy.current.name.should eq('SlavePools::SecondaryDb1')
184
+ end
236
185
  end
237
186
 
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)
187
+ it "should switch to default pool if an unknown pool is specified" do
188
+ @proxy.with_pool('unknown') do
189
+ @proxy.current_pool.name.should eq('default')
190
+ @proxy.current.name.should eq('SlavePools::DefaultDb1')
191
+ end
244
192
  end
245
193
 
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'
194
+ it "should switch to default pool if no pool is specified" do
195
+ @proxy.with_pool do
196
+ @proxy.current_pool.name.should eq('default')
197
+ @proxy.current.name.should eq('SlavePools::DefaultDb1')
198
+ end
254
199
  end
255
200
 
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
201
+ it "should cycle replicas only within the pool" do
202
+ @proxy.with_pool('secondary') do
203
+ @proxy.current.name.should eq('SlavePools::SecondaryDb1')
204
+ @proxy.next_slave!
205
+ @proxy.current.name.should eq('SlavePools::SecondaryDb2')
206
+ @proxy.next_slave!
207
+ @proxy.current.name.should eq('SlavePools::SecondaryDb3')
208
+ @proxy.next_slave!
209
+ @proxy.current.name.should eq('SlavePools::SecondaryDb1')
266
210
  end
211
+ end
267
212
 
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)
213
+ it "should allow switching back to master" do
214
+ @proxy.with_pool('secondary') do
215
+ @proxy.current.name.should eq('SlavePools::SecondaryDb1')
216
+ @proxy.with_master do
217
+ @proxy.current.name.should eq('ActiveRecord::Base')
272
218
  end
273
219
  end
220
+ end
274
221
 
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)
222
+ it "should not switch to pool when nested inside with_master" do
223
+ @proxy.current.name.should eq('SlavePools::DefaultDb1')
224
+ @proxy.with_master do
280
225
  @proxy.with_pool('secondary') do
281
- 3.times {@proxy.select_one(@sql)}
226
+ @proxy.current.name.should eq('ActiveRecord::Base')
282
227
  end
283
228
  end
229
+ end
284
230
 
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
231
+ it "should switch back to previous pool and slave" do
232
+ @proxy.next_slave!
297
233
 
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
234
+ @proxy.current_pool.name.should eq('default')
235
+ @proxy.current.name.should eq('SlavePools::DefaultDb2')
313
236
 
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
237
+ @proxy.with_pool('secondary') do
238
+ @proxy.current_pool.name.should eq('secondary')
239
+ @proxy.current.name.should eq('SlavePools::SecondaryDb1')
240
+
241
+ @proxy.with_pool('default') do
242
+ @proxy.current_pool.name.should eq('default')
243
+ @proxy.current.name.should eq('SlavePools::DefaultDb2')
325
244
  end
245
+
246
+ @proxy.current_pool.name.should eq('secondary')
247
+ @proxy.current.name.should eq('SlavePools::SecondaryDb1')
326
248
  end
249
+
250
+ @proxy.current_pool.name.should eq('default')
251
+ @proxy.current.name.should eq('SlavePools::DefaultDb2')
327
252
  end
328
253
  end
329
254
  end
@@ -0,0 +1,21 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/config/test_model')
3
+
4
+ describe SlavePools do
5
+
6
+ before(:each) do
7
+ reset_proxy(SlavePools.proxy)
8
+ @observer = TestModelObserver.instance
9
+
10
+ @test_model = TestModel.create
11
+ end
12
+
13
+ it "should use observers correctly" do
14
+ TestSub.first.test_model_id.should == @test_model.id
15
+ end
16
+
17
+ it "should not throw a stack too deep error" do
18
+ @test_model.should be_valid
19
+ end
20
+ end
21
+
data/spec/pool_spec.rb ADDED
@@ -0,0 +1,35 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe SlavePools::Pool do
4
+
5
+ context "Multiple slaves" do
6
+ before do
7
+ @slaves = ["db1", "db2", "db3"]
8
+ @slave_pool = SlavePools::Pool.new("name", @slaves.clone)
9
+ end
10
+
11
+ specify {@slave_pool.size.should == 3}
12
+
13
+ it "should return items in a round robin fashion" do
14
+ @slave_pool.current.should == @slaves[0]
15
+ @slave_pool.next.should == @slaves[1]
16
+ @slave_pool.next.should == @slaves[2]
17
+ @slave_pool.next.should == @slaves[0]
18
+ end
19
+ end
20
+
21
+ context "Single Slave" do
22
+ before do
23
+ @slaves = ["db1"]
24
+ @slave_pool = SlavePools::Pool.new("name", @slaves.clone)
25
+ end
26
+
27
+ specify {@slave_pool.size.should == 1}
28
+
29
+ it "should return items in a round robin fashion" do
30
+ @slave_pool.current.should == @slaves[0]
31
+ @slave_pool.next.should == @slaves[0]
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,83 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe SlavePools::QueryCache do
4
+ before(:each) do
5
+ @sql = 'SELECT NOW()'
6
+
7
+ @proxy = SlavePools.proxy
8
+ @master = @proxy.master.retrieve_connection
9
+
10
+ @master.clear_query_cache
11
+
12
+ reset_proxy(@proxy)
13
+ create_slave_aliases(@proxy)
14
+ end
15
+
16
+ it 'should cache queries using select_all' do
17
+ ActiveRecord::Base.cache do
18
+ # next_slave will be called and switch to the SlaveDatabase2
19
+ @default_slave1.should_receive(:select_all).exactly(1).and_return([])
20
+ @default_slave2.should_not_receive(:select_all)
21
+ @master.should_not_receive(:select_all)
22
+ 3.times { @proxy.select_all(@sql) }
23
+ @master.query_cache.keys.size.should == 1
24
+ end
25
+ end
26
+
27
+ it 'should invalidate the cache on insert, delete and update' do
28
+ ActiveRecord::Base.cache do
29
+ meths = [:insert, :update, :delete, :insert, :update]
30
+ meths.each do |meth|
31
+ @master.should_receive("exec_#{meth}").and_return(true)
32
+ end
33
+
34
+ @default_slave1.should_receive(:select_all).exactly(5).and_return([])
35
+ @default_slave2.should_receive(:select_all).exactly(0)
36
+ 5.times do |i|
37
+ @proxy.select_all(@sql)
38
+ @proxy.select_all(@sql)
39
+ @master.query_cache.keys.size.should == 1
40
+ @proxy.send(meths[i], '')
41
+ @master.query_cache.keys.size.should == 0
42
+ end
43
+ end
44
+ end
45
+
46
+ describe "using querycache middleware" do
47
+ it 'should cache queries using select_all' do
48
+ mw = ActiveRecord::QueryCache.new lambda { |env|
49
+ @default_slave1.should_receive(:select_all).exactly(1).and_return([])
50
+ @default_slave2.should_not_receive(:select_all)
51
+ @master.should_not_receive(:select_all)
52
+ 3.times { @proxy.select_all(@sql) }
53
+ @proxy.next_slave!
54
+ 3.times { @proxy.select_all(@sql) }
55
+ @proxy.next_slave!
56
+ 3.times { @proxy.select_all(@sql)}
57
+ @master.query_cache.keys.size.should == 1
58
+ }
59
+ mw.call({})
60
+ end
61
+
62
+ it 'should invalidate the cache on insert, delete and update' do
63
+ mw = ActiveRecord::QueryCache.new lambda { |env|
64
+ meths = [:insert, :update, :delete, :insert, :update]
65
+ meths.each do |meth|
66
+ @master.should_receive("exec_#{meth}").and_return(true)
67
+ end
68
+
69
+ @default_slave1.should_receive(:select_all).exactly(5).and_return([])
70
+ @default_slave2.should_receive(:select_all).exactly(0)
71
+ 5.times do |i|
72
+ @proxy.select_all(@sql)
73
+ @proxy.select_all(@sql)
74
+ @master.query_cache.keys.size.should == 1
75
+ @proxy.send(meths[i], '')
76
+ @master.query_cache.keys.size.should == 0
77
+ end
78
+ }
79
+ mw.call({})
80
+ end
81
+ end
82
+
83
+ end