slave_pools 0.1.2 → 1.0.0.rc1

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.
@@ -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