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.
- checksums.yaml +7 -0
- data/README.md +196 -0
- data/lib/slave_pools/active_record_extensions.rb +7 -36
- data/lib/slave_pools/config.rb +22 -0
- data/lib/slave_pools/connection_proxy.rb +86 -225
- data/lib/slave_pools/engine.rb +24 -0
- data/lib/slave_pools/hijack.rb +24 -0
- data/lib/slave_pools/observer_extensions.rb +5 -5
- data/lib/slave_pools/pool.rb +21 -0
- data/lib/slave_pools/pools.rb +60 -0
- data/lib/slave_pools/query_cache.rb +35 -0
- data/lib/slave_pools/version.rb +3 -0
- data/lib/slave_pools.rb +55 -27
- data/spec/config/test_model.rb +19 -0
- data/spec/connection_proxy_spec.rb +182 -257
- data/spec/observer_extensions_spec.rb +21 -0
- data/spec/pool_spec.rb +35 -0
- data/spec/query_cache_spec.rb +83 -0
- data/spec/slave_pools_spec.rb +18 -56
- data/spec/spec_helper.rb +34 -6
- metadata +105 -108
- data/README.rdoc +0 -259
- data/lib/slave_pools/query_cache_compat.rb +0 -45
- data/lib/slave_pools/slave_pool.rb +0 -29
- data/slave_pools.gemspec +0 -25
- data/spec/config/database.yml +0 -43
- data/spec/slave_pool_spec.rb +0 -43
@@ -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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
55
|
-
|
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
|
59
|
-
@proxy.
|
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.
|
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.
|
58
|
+
@proxy.send(:within_master_block?).should_not be
|
72
59
|
end
|
60
|
+
end
|
73
61
|
|
74
|
-
|
75
|
-
|
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
|
-
|
78
|
-
|
66
|
+
|
67
|
+
TestModel.transaction do
|
68
|
+
@proxy.select_all(@sql)
|
79
69
|
end
|
80
70
|
end
|
81
71
|
|
82
|
-
it 'should
|
83
|
-
@
|
84
|
-
@
|
85
|
-
|
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
|
-
|
77
|
+
@proxy.select_all(@sql)
|
88
78
|
end
|
89
|
-
@proxy.select_all(@sql)
|
90
79
|
end
|
80
|
+
end
|
91
81
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
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
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
107
|
-
|
108
|
-
@
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
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
|
116
|
-
@master.should_receive(:select_one).
|
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
|
-
|
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
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
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
|
-
|
140
|
-
|
141
|
-
|
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
|
-
|
147
|
-
|
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
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
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
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
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
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
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
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
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
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
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
|
-
|
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
|
232
|
-
@
|
233
|
-
|
234
|
-
|
235
|
-
|
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
|
239
|
-
@
|
240
|
-
|
241
|
-
|
242
|
-
|
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
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
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
|
-
|
257
|
-
|
258
|
-
|
259
|
-
@
|
260
|
-
@
|
261
|
-
@
|
262
|
-
@
|
263
|
-
@proxy.
|
264
|
-
|
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
|
-
|
269
|
-
|
270
|
-
@proxy.
|
271
|
-
|
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
|
-
|
276
|
-
|
277
|
-
|
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
|
-
|
226
|
+
@proxy.current.name.should eq('ActiveRecord::Base')
|
282
227
|
end
|
283
228
|
end
|
229
|
+
end
|
284
230
|
|
285
|
-
|
286
|
-
|
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
|
-
|
299
|
-
|
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
|
-
|
315
|
-
@
|
316
|
-
@
|
317
|
-
|
318
|
-
@proxy.
|
319
|
-
@proxy.
|
320
|
-
|
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
|