seamless_database_pool 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,432 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ module SeamlessDatabasePool
4
+ class MockConnection < ActiveRecord::ConnectionAdapters::AbstractAdapter
5
+ def initialize (name)
6
+ @name = name
7
+ end
8
+
9
+ def inspect
10
+ "#{@name} connection"
11
+ end
12
+
13
+ def reconnect!
14
+ sleep(0.25)
15
+ end
16
+ end
17
+
18
+ class MockMasterConnection < MockConnection
19
+ def insert (sql, name = nil); end
20
+ def update (sql, name = nil); end
21
+ def execute (sql, name = nil); end
22
+ def columns (table_name, name = nil); end
23
+ end
24
+ end
25
+
26
+ describe "SeamlessDatabasePoolAdapter ActiveRecord::Base extension" do
27
+
28
+ it "should establish the connections in the pool merging global options into the connection options" do
29
+ options = {
30
+ :adapter => 'seamless_database_pool',
31
+ :pool_adapter => 'reader',
32
+ :username => 'user',
33
+ :master => {
34
+ 'adapter' => 'writer',
35
+ 'host' => 'master_host'
36
+ },
37
+ :read_pool => [
38
+ {'host' => 'read_host_1'},
39
+ {'host' => 'read_host_2', 'pool_weight' => '2'},
40
+ {'host' => 'read_host_3', 'pool_weight' => '0'}
41
+ ]
42
+ }
43
+
44
+ pool_connection = mock(:connection)
45
+ master_connection = SeamlessDatabasePool::MockConnection.new("master")
46
+ read_connection_1 = SeamlessDatabasePool::MockConnection.new("read_1")
47
+ read_connection_2 = SeamlessDatabasePool::MockConnection.new("read_2")
48
+ logger = ActiveRecord::Base.logger
49
+ weights = {master_connection => 1, read_connection_1 => 1, read_connection_2 => 2}
50
+
51
+ ActiveRecord::Base.should_receive(:writer_connection).with(:adapter => 'writer', :host => 'master_host', :username => 'user', :pool_weight => 1).and_return(master_connection)
52
+ ActiveRecord::Base.should_receive(:reader_connection).with(:adapter => 'reader', :host => 'read_host_1', :username => 'user', :pool_weight => 1).and_return(read_connection_1)
53
+ ActiveRecord::Base.should_receive(:reader_connection).with(:adapter => 'reader', :host => 'read_host_2', :username => 'user', :pool_weight => 2).and_return(read_connection_2)
54
+
55
+ ActiveRecord::ConnectionAdapters::SeamlessDatabasePoolAdapter.should_receive(:new).with(nil, logger, master_connection, [read_connection_1, read_connection_2], weights).and_return(pool_connection)
56
+
57
+ ActiveRecord::Base.should_receive(:establish_adapter).with('writer')
58
+ ActiveRecord::Base.should_receive(:establish_adapter).with('reader').twice
59
+
60
+ ActiveRecord::Base.seamless_database_pool_connection(options).should == pool_connection
61
+ end
62
+
63
+ it "should raise an error if the adapter would be recursive" do
64
+ lambda{ActiveRecord::Base.seamless_database_pool_connection('seamless_database_pool').should_raise(ActiveRecord::AdapterNotFound)}
65
+ end
66
+ end
67
+
68
+ describe "SeamlessDatabasePoolAdapter" do
69
+
70
+ before(:each) do
71
+ @master_connection = SeamlessDatabasePool::MockMasterConnection.new("master")
72
+ @read_connection_1 = SeamlessDatabasePool::MockConnection.new("read_1")
73
+ @read_connection_2 = SeamlessDatabasePool::MockConnection.new("read_2")
74
+ weights = {@master_connection => 1, @read_connection_1 => 1, @read_connection_2 => 2}
75
+ @pool_connection = ActiveRecord::ConnectionAdapters::SeamlessDatabasePoolAdapter.new(nil, mock(:logger), @master_connection, [@read_connection_1, @read_connection_2], weights)
76
+ end
77
+
78
+ it "should initialize the connection pool" do
79
+ @pool_connection.master_connection.should == @master_connection
80
+ @pool_connection.read_connections.should == [@read_connection_1, @read_connection_2]
81
+ @pool_connection.all_connections.should == [@master_connection, @read_connection_1, @read_connection_2]
82
+ @pool_connection.pool_weight(@master_connection).should == 1
83
+ @pool_connection.pool_weight(@read_connection_1).should == 1
84
+ @pool_connection.pool_weight(@read_connection_2).should == 2
85
+ end
86
+
87
+ it "should return the current read connection" do
88
+ SeamlessDatabasePool.should_receive(:read_only_connection).with(@pool_connection).and_return(:current)
89
+ @pool_connection.current_read_connection.should == :current
90
+ end
91
+
92
+ it "should select a random read connection" do
93
+ mock_connection = stub(:connection, :active? => true)
94
+ @pool_connection.should_receive(:available_read_connections).and_return([:fake1, :fake2, mock_connection])
95
+ @pool_connection.should_receive(:rand).with(3).and_return(2)
96
+ @pool_connection.random_read_connection.should == mock_connection
97
+ end
98
+
99
+ it "should select the master connection if the read pool is empty" do
100
+ @pool_connection.should_receive(:available_read_connections).and_return([])
101
+ @pool_connection.random_read_connection.should == @master_connection
102
+ end
103
+
104
+ it "should use the master connection in a block" do
105
+ connection = ActiveRecord::ConnectionAdapters::SeamlessDatabasePoolAdapter.new(nil, mock(:logger), @master_connection, [@read_connection_1], {@read_connection_1 => 1})
106
+ connection.random_read_connection.should == @read_connection_1
107
+ connection.use_master_connection do
108
+ connection.random_read_connection.should == @master_connection
109
+ end
110
+ connection.random_read_connection.should == @read_connection_1
111
+ end
112
+
113
+ it "should use the master connection inside a transaction" do
114
+ connection = ActiveRecord::ConnectionAdapters::SeamlessDatabasePoolAdapter.new(nil, mock(:logger), @master_connection, [@read_connection_1], {@read_connection_1 => 1})
115
+ @master_connection.should_receive(:transaction).with(true).and_yield
116
+ @master_connection.should_receive(:select_one).with('Transaction SQL')
117
+ @read_connection_1.should_receive(:select_one).with('SQL 1')
118
+ @read_connection_1.should_receive(:select_one).with('SQL 2')
119
+
120
+ SeamlessDatabasePool.use_persistent_read_connection do
121
+ connection.select_one('SQL 1')
122
+ connection.transaction do
123
+ connection.select_one('Transaction SQL')
124
+ end
125
+ connection.select_one('SQL 2')
126
+ end
127
+ end
128
+
129
+ # Read methods
130
+
131
+ it "should proxy select methods to a read connection" do
132
+ @pool_connection.should_receive(:current_read_connection).and_return(@read_connection_1)
133
+ @read_connection_1.should_receive(:select_one).with('SQL').and_return(:retval)
134
+ @pool_connection.select_one('SQL').should == :retval
135
+ end
136
+
137
+ it "should proxy select methods to a read connection" do
138
+ @pool_connection.should_receive(:current_read_connection).and_return(@read_connection_1)
139
+ @read_connection_1.should_receive(:select_all).with('SQL').and_return(:retval)
140
+ @pool_connection.select_all('SQL').should == :retval
141
+ end
142
+
143
+ it "should proxy select methods to a read connection" do
144
+ @pool_connection.should_receive(:current_read_connection).and_return(@read_connection_1)
145
+ @read_connection_1.should_receive(:select_value).with('SQL').and_return(:retval)
146
+ @pool_connection.select_value('SQL').should == :retval
147
+ end
148
+
149
+ it "should proxy select methods to a read connection" do
150
+ @pool_connection.should_receive(:current_read_connection).and_return(@read_connection_1)
151
+ @read_connection_1.should_receive(:select_values).with('SQL').and_return(:retval)
152
+ @pool_connection.select_values('SQL').should == :retval
153
+ end
154
+
155
+ it "should proxy select methods to a read connection" do
156
+ @pool_connection.should_receive(:current_read_connection).and_return(@read_connection_1)
157
+ @read_connection_1.should_receive(:select_rows).with('SQL').and_return(:retval)
158
+ @pool_connection.select_rows('SQL').should == :retval
159
+ end
160
+
161
+ it "should proxy select methods to a read connection" do
162
+ @pool_connection.should_receive(:current_read_connection).and_return(@read_connection_1)
163
+ @read_connection_1.should_receive(:select).with('SQL', 'name').and_return(:retval)
164
+ @pool_connection.send(:select, 'SQL', 'name').should == :retval
165
+ end
166
+
167
+ # Master connection methods
168
+
169
+ it "should proxy quote method to the master connection" do
170
+ @master_connection.should_receive(:insert).with('SQL').and_return(:retval)
171
+ @pool_connection.insert('SQL').should == :retval
172
+ end
173
+
174
+ it "should proxy quote method to the master connection" do
175
+ @master_connection.should_receive(:update).with('SQL').and_return(:retval)
176
+ @pool_connection.update('SQL').should == :retval
177
+ end
178
+
179
+ it "should proxy quote method to the master connection" do
180
+ @master_connection.should_receive(:execute).with('SQL').and_return(:retval)
181
+ @pool_connection.execute('SQL').should == :retval
182
+ end
183
+
184
+ it "should proxy quote method to the master connection" do
185
+ @master_connection.should_receive(:columns).with(:table).and_return(:retval)
186
+ @pool_connection.columns(:table).should == :retval
187
+ end
188
+
189
+ # Fork to all connection methods
190
+
191
+ it "should fork active? to all connections and return true if all are up" do
192
+ @master_connection.should_receive(:active?).and_return(true)
193
+ @read_connection_1.should_receive(:active?).and_return(true)
194
+ @read_connection_2.should_receive(:active?).and_return(true)
195
+ @pool_connection.active?.should == true
196
+ end
197
+
198
+ it "should fork active? to all connections and return false if one is down" do
199
+ @master_connection.should_receive(:active?).and_return(true)
200
+ @read_connection_1.should_receive(:active?).and_return(true)
201
+ @read_connection_2.should_receive(:active?).and_return(false)
202
+ @pool_connection.active?.should == false
203
+ end
204
+
205
+ it "should fork verify! to all connections" do
206
+ @master_connection.should_receive(:verify!).with(5)
207
+ @read_connection_1.should_receive(:verify!).with(5)
208
+ @read_connection_2.should_receive(:verify!).with(5)
209
+ @pool_connection.verify!(5)
210
+ end
211
+
212
+ it "should fork disconnect! to all connections" do
213
+ @master_connection.should_receive(:disconnect!)
214
+ @read_connection_1.should_receive(:disconnect!)
215
+ @read_connection_2.should_receive(:disconnect!)
216
+ @pool_connection.disconnect!
217
+ end
218
+
219
+ it "should fork reconnect! to all connections" do
220
+ @master_connection.should_receive(:reconnect!)
221
+ @read_connection_1.should_receive(:reconnect!)
222
+ @read_connection_2.should_receive(:reconnect!)
223
+ @pool_connection.reconnect!
224
+ end
225
+
226
+ it "should timeout reconnect! calls to dead servers" do
227
+ @read_connection_1.connect_timeout = 0.1
228
+ lambda{@pool_connection.reconnect!}.should raise_error("reconnect timed out")
229
+ end
230
+
231
+ it "should fork reset_runtime to all connections" do
232
+ @master_connection.should_receive(:reset_runtime).and_return(1)
233
+ @read_connection_1.should_receive(:reset_runtime).and_return(2)
234
+ @read_connection_2.should_receive(:reset_runtime).and_return(3)
235
+ @pool_connection.reset_runtime.should == 6
236
+ end
237
+
238
+ # Reconnection logic
239
+
240
+ it "should proxy requests to a connection" do
241
+ args = [:arg1, :arg2]
242
+ block = Proc.new{}
243
+ @master_connection.should_receive(:select_value).with(*args, &block)
244
+ @master_connection.should_not_receive(:active?)
245
+ @master_connection.should_not_receive(:reconnect!)
246
+ @pool_connection.send(:proxy_connection_method, @master_connection, :select_value, :master, *args, &block)
247
+ end
248
+
249
+ it "should try to reconnect dead connections" do
250
+ args = [:arg1, :arg2]
251
+ block = Proc.new{}
252
+ @master_connection.should_receive(:select_value).with(*args, &block).and_raise("SQL ERROR")
253
+ @master_connection.should_receive(:active?).and_return(false)
254
+ @master_connection.should_receive(:reconnect!)
255
+ lambda{@pool_connection.send(:proxy_connection_method, @master_connection, :select_value, :master, *args, &block)}.should raise_error(ActiveRecord::ConnectionAdapters::SeamlessDatabasePoolAdapter::DatabaseConnectionError)
256
+ end
257
+
258
+ it "should not try to reconnect live connections" do
259
+ args = [:arg1, :arg2]
260
+ block = Proc.new{}
261
+ @master_connection.should_receive(:select_value).with(*args, &block).and_raise("SQL ERROR")
262
+ @master_connection.should_receive(:active?).and_return(true)
263
+ @master_connection.should_not_receive(:reconnect!)
264
+ lambda{@pool_connection.send(:proxy_connection_method, @master_connection, :select_value, :master, *args, &block)}.should raise_error("SQL ERROR")
265
+ end
266
+
267
+ it "should not try to reconnect a connection during a retry" do
268
+ args = [:arg1, :arg2]
269
+ block = Proc.new{}
270
+ @master_connection.should_receive(:select_value).with(*args, &block).and_raise("SQL ERROR")
271
+ @master_connection.should_not_receive(:active?)
272
+ @master_connection.should_not_receive(:reconnect!)
273
+ lambda{@pool_connection.send(:proxy_connection_method, @master_connection, :select_value, :retry, *args, &block)}.should raise_error("SQL ERROR")
274
+ end
275
+
276
+ it "should try to execute a read statement again after a connection error" do
277
+ connection_error = ActiveRecord::ConnectionAdapters::SeamlessDatabasePoolAdapter::DatabaseConnectionError.new
278
+ connection_error.wrapped_exception = StandardError.new("Error")
279
+ @pool_connection.should_receive(:current_read_connection).and_return(@read_connection_1)
280
+ @pool_connection.should_receive(:proxy_connection_method).with(@read_connection_1, :select_value, :read, 'SQL').and_raise(connection_error)
281
+ @read_connection_1.should_receive(:active?).and_return(true)
282
+ @pool_connection.should_not_receive(:suppress_read_connection)
283
+ SeamlessDatabasePool.should_not_receive(:set_persistent_read_connection)
284
+ @pool_connection.should_receive(:proxy_connection_method).with(@read_connection_1, :select_value, :retry, 'SQL').and_return(:results)
285
+ @pool_connection.select_value('SQL').should == :results
286
+ end
287
+
288
+ it "should not try to execute a read statement again after a connection error if the master connection must be used" do
289
+ connection_error = ActiveRecord::ConnectionAdapters::SeamlessDatabasePoolAdapter::DatabaseConnectionError.new
290
+ connection_error.wrapped_exception = StandardError.new("Error")
291
+ @pool_connection.should_receive(:current_read_connection).and_return(@read_connection_1)
292
+ @pool_connection.should_receive(:proxy_connection_method).with(@read_connection_1, :select_value, :read, 'SQL').and_raise(connection_error)
293
+ @pool_connection.use_master_connection do
294
+ lambda{@pool_connection.select_value('SQL')}.should raise_error("Error")
295
+ end
296
+ end
297
+
298
+ it "should not try to execute a read statement again after a connection error if there is a block" do
299
+ connection_error = ActiveRecord::ConnectionAdapters::SeamlessDatabasePoolAdapter::DatabaseConnectionError.new
300
+ connection_error.wrapped_exception = StandardError.new("Error")
301
+ @pool_connection.should_receive(:current_read_connection).and_return(@read_connection_1)
302
+ @pool_connection.should_receive(:proxy_connection_method).with(@read_connection_1, :select_value, :read, 'SQL').and_raise(connection_error)
303
+ lambda{@pool_connection.select_value('SQL'){}}.should raise_error("Error")
304
+ end
305
+
306
+ it "should not try to execute a read statement again after a non-connection error" do
307
+ @pool_connection.should_receive(:current_read_connection).and_return(@read_connection_1)
308
+ @pool_connection.should_receive(:proxy_connection_method).with(@read_connection_1, :select_value, :read, 'SQL').and_raise("SQL Error")
309
+ lambda{@pool_connection.select_value('SQL')}.should raise_error("SQL Error")
310
+ end
311
+
312
+ it "should use a different connection on a retry if the original connection could not be reconnected" do
313
+ connection_error = ActiveRecord::ConnectionAdapters::SeamlessDatabasePoolAdapter::DatabaseConnectionError.new
314
+ connection_error.wrapped_exception = StandardError.new("Error")
315
+ @pool_connection.should_receive(:current_read_connection).and_return(@read_connection_1, @read_connection_2)
316
+ @pool_connection.should_receive(:proxy_connection_method).with(@read_connection_1, :select_value, :read, 'SQL').and_raise(connection_error)
317
+ @read_connection_1.should_receive(:active?).and_return(false)
318
+ @pool_connection.should_receive(:suppress_read_connection).with(@read_connection_1, 30)
319
+ SeamlessDatabasePool.should_receive(:set_persistent_read_connection).with(@pool_connection, @read_connection_2)
320
+ @pool_connection.should_receive(:proxy_connection_method).with(@read_connection_2, :select_value, :retry, 'SQL').and_return(:results)
321
+ @pool_connection.select_value('SQL').should == :results
322
+ end
323
+
324
+ it "should keep track of read connections that can't be reconnected for a set period" do
325
+ @pool_connection.available_read_connections.should include(@read_connection_1)
326
+ @pool_connection.suppress_read_connection(@read_connection_1, 30)
327
+ @pool_connection.available_read_connections.should_not include(@read_connection_1)
328
+ end
329
+
330
+ it "should return dead connections to the pool after the timeout has expired" do
331
+ @pool_connection.available_read_connections.should include(@read_connection_1)
332
+ @pool_connection.suppress_read_connection(@read_connection_1, 0.2)
333
+ @pool_connection.available_read_connections.should_not include(@read_connection_1)
334
+ sleep(0.3)
335
+ @pool_connection.available_read_connections.should include(@read_connection_1)
336
+ end
337
+
338
+ it "should not return a connection to the pool until it can be reconnected" do
339
+ @pool_connection.available_read_connections.should include(@read_connection_1)
340
+ @pool_connection.suppress_read_connection(@read_connection_1, 0.2)
341
+ @pool_connection.available_read_connections.should_not include(@read_connection_1)
342
+ sleep(0.3)
343
+ @read_connection_1.should_receive(:reconnect!)
344
+ @read_connection_1.should_receive(:active?).and_return(false)
345
+ @pool_connection.available_read_connections.should_not include(@read_connection_1)
346
+ end
347
+
348
+ it "should try all connections again if none of them can be reconnected" do
349
+ stack = @pool_connection.instance_variable_get(:@available_read_connections)
350
+
351
+ available = @pool_connection.available_read_connections
352
+ available.should include(@read_connection_1)
353
+ available.should include(@read_connection_2)
354
+ available.should include(@master_connection)
355
+ stack.size.should == 1
356
+
357
+ @pool_connection.suppress_read_connection(@read_connection_1, 30)
358
+ available = @pool_connection.available_read_connections
359
+ available.should_not include(@read_connection_1)
360
+ available.should include(@read_connection_2)
361
+ available.should include(@master_connection)
362
+ stack.size.should == 2
363
+
364
+ @pool_connection.suppress_read_connection(@master_connection, 30)
365
+ available = @pool_connection.available_read_connections
366
+ available.should_not include(@read_connection_1)
367
+ available.should include(@read_connection_2)
368
+ available.should_not include(@master_connection)
369
+ stack.size.should == 3
370
+
371
+ @pool_connection.suppress_read_connection(@read_connection_2, 30)
372
+ available = @pool_connection.available_read_connections
373
+ available.should include(@read_connection_1)
374
+ available.should include(@read_connection_2)
375
+ available.should include(@master_connection)
376
+ stack.size.should == 1
377
+ end
378
+
379
+ it "should not try to suppress a read connection that wasn't available in the read pool" do
380
+ stack = @pool_connection.instance_variable_get(:@available_read_connections)
381
+ stack.size.should == 1
382
+ @pool_connection.suppress_read_connection(@read_connection_1, 30)
383
+ stack.size.should == 2
384
+ @pool_connection.suppress_read_connection(@read_connection_1, 30)
385
+ stack.size.should == 2
386
+ end
387
+
388
+ end
389
+
390
+ describe "Reload extensions" do
391
+ before(:all) do
392
+ ActiveRecord::Migration.suppress_messages do
393
+ class SeamlessDatabasePoolTestThing < ActiveRecord::Base
394
+ ActiveRecord::Migration.create_table(:seamless_database_pool_test_things) do |t|
395
+ t.column :name, :string
396
+ t.column :seamless_database_pool_test_model_id, :integer
397
+ end unless table_exists?
398
+ belongs_to :seamless_database_pool_test_model
399
+ end
400
+
401
+ class SeamlessDatabasePoolTestModel < ActiveRecord::Base
402
+ ActiveRecord::Migration.create_table(:seamless_database_pool_test_models) do |t|
403
+ t.column :name, :string
404
+ end unless table_exists?
405
+ has_many :seamless_database_pool_test_things
406
+ end
407
+ end
408
+
409
+ @record = SeamlessDatabasePoolTestModel.create(:name => 'test')
410
+ @thing = SeamlessDatabasePoolTestThing.create(:name => 'thing', :seamless_database_pool_test_model_id => @record.id)
411
+ end
412
+
413
+ after(:all) do
414
+ ActiveRecord::Migration.suppress_messages do
415
+ ActiveRecord::Migration.drop_table(:seamless_database_pool_test_models) if SeamlessDatabasePoolTestModel.table_exists?
416
+ ActiveRecord::Migration.drop_table(:seamless_database_pool_test_things) if SeamlessDatabasePoolTestThing.table_exists?
417
+ end
418
+ end
419
+
420
+ it "should force the master connection on reload" do
421
+ SeamlessDatabasePool.should_receive(:use_master_connection).and_yield
422
+ @record.should_receive(:reload_without_seamless_database_pool).with(:options)
423
+ @record.reload(:options)
424
+ end
425
+
426
+ it "should force the master connection on reload of an association" do
427
+ association = @record.seamless_database_pool_test_things
428
+ SeamlessDatabasePool.should_receive(:use_master_connection).and_yield
429
+ association.should_receive(:reload_without_seamless_database_pool)
430
+ @record.seamless_database_pool_test_things(true)
431
+ end
432
+ end
@@ -0,0 +1,134 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe "SeamlessDatabasePool" do
4
+
5
+ before(:each) do
6
+ SeamlessDatabasePool.clear_read_only_connection
7
+ end
8
+
9
+ after(:each) do
10
+ SeamlessDatabasePool.clear_read_only_connection
11
+ end
12
+
13
+ it "should use the master connection by default" do
14
+ connection = stub(:connection, :master_connection => :master_db_connection)
15
+ connection.stub!(:using_master_connection?).and_return(false)
16
+ SeamlessDatabasePool.read_only_connection_type.should == :master
17
+ SeamlessDatabasePool.read_only_connection(connection).should == :master_db_connection
18
+ end
19
+
20
+ it "should be able to set using persistent read connections" do
21
+ connection = mock(:connection)
22
+ connection.should_receive(:random_read_connection).once.and_return(:read_db_connection)
23
+ connection.stub!(:using_master_connection?).and_return(false)
24
+ SeamlessDatabasePool.use_persistent_read_connection
25
+ SeamlessDatabasePool.read_only_connection_type.should == :persistent
26
+ SeamlessDatabasePool.read_only_connection(connection).should == :read_db_connection
27
+ SeamlessDatabasePool.read_only_connection(connection).should == :read_db_connection
28
+ end
29
+
30
+ it "should be able to set using random read connections" do
31
+ connection = mock(:connection)
32
+ connection.should_receive(:random_read_connection).and_return(:read_db_connection_1, :read_db_connection_2)
33
+ connection.stub!(:using_master_connection?).and_return(false)
34
+ SeamlessDatabasePool.use_random_read_connection
35
+ SeamlessDatabasePool.read_only_connection_type.should == :random
36
+ SeamlessDatabasePool.read_only_connection(connection).should == :read_db_connection_1
37
+ SeamlessDatabasePool.read_only_connection(connection).should == :read_db_connection_2
38
+ end
39
+
40
+ it "should use the master connection if the connection is forcing it" do
41
+ connection = stub(:connection, :master_connection => :master_db_connection)
42
+ connection.should_receive(:using_master_connection?).and_return(true)
43
+ SeamlessDatabasePool.use_persistent_read_connection
44
+ SeamlessDatabasePool.read_only_connection(connection).should == :master_db_connection
45
+ end
46
+
47
+ it "should be able to set using the master connection" do
48
+ connection = stub(:connection, :master_connection => :master_db_connection)
49
+ connection.stub!(:using_master_connection?).and_return(false)
50
+ SeamlessDatabasePool.use_master_connection
51
+ SeamlessDatabasePool.read_only_connection(connection).should == :master_db_connection
52
+ end
53
+
54
+ it "should be able to use persistent read connections within a block" do
55
+ connection = stub(:connection, :master_connection => :master_db_connection)
56
+ connection.should_receive(:random_read_connection).once.and_return(:read_db_connection)
57
+ connection.stub!(:using_master_connection?).and_return(false)
58
+ SeamlessDatabasePool.read_only_connection(connection).should == :master_db_connection
59
+ SeamlessDatabasePool.use_persistent_read_connection do
60
+ SeamlessDatabasePool.read_only_connection(connection).should == :read_db_connection
61
+ SeamlessDatabasePool.read_only_connection(connection).should == :read_db_connection
62
+ :test_val
63
+ end.should == :test_val
64
+ SeamlessDatabasePool.read_only_connection(connection).should == :master_db_connection
65
+ end
66
+
67
+ it "should be able to use random read connections within a block" do
68
+ connection = stub(:connection, :master_connection => :master_db_connection)
69
+ connection.should_receive(:random_read_connection).and_return(:read_db_connection_1, :read_db_connection_2)
70
+ connection.stub!(:using_master_connection?).and_return(false)
71
+ SeamlessDatabasePool.read_only_connection(connection).should == :master_db_connection
72
+ SeamlessDatabasePool.use_random_read_connection do
73
+ SeamlessDatabasePool.read_only_connection(connection).should == :read_db_connection_1
74
+ SeamlessDatabasePool.read_only_connection(connection).should == :read_db_connection_2
75
+ :test_val
76
+ end.should == :test_val
77
+ SeamlessDatabasePool.read_only_connection(connection).should == :master_db_connection
78
+ end
79
+
80
+ it "should be able to use the master connection within a block" do
81
+ connection = stub(:connection, :master_connection => :master_db_connection)
82
+ connection.should_receive(:random_read_connection).once.and_return(:read_db_connection)
83
+ connection.stub!(:using_master_connection?).and_return(false)
84
+ SeamlessDatabasePool.use_persistent_read_connection
85
+ SeamlessDatabasePool.read_only_connection(connection).should == :read_db_connection
86
+ SeamlessDatabasePool.use_master_connection do
87
+ SeamlessDatabasePool.read_only_connection(connection).should == :master_db_connection
88
+ :test_val
89
+ end.should == :test_val
90
+ SeamlessDatabasePool.read_only_connection(connection).should == :read_db_connection
91
+ SeamlessDatabasePool.clear_read_only_connection
92
+ end
93
+
94
+ it "should be able to use connection blocks within connection blocks" do
95
+ connection = stub(:connection, :master_connection => :master_db_connection)
96
+ connection.should_receive(:random_read_connection).any_number_of_times.and_return(:read_db_connection)
97
+ connection.stub!(:using_master_connection?).and_return(false)
98
+ SeamlessDatabasePool.use_persistent_read_connection do
99
+ SeamlessDatabasePool.read_only_connection(connection).should == :read_db_connection
100
+ SeamlessDatabasePool.use_master_connection do
101
+ SeamlessDatabasePool.read_only_connection(connection).should == :master_db_connection
102
+ SeamlessDatabasePool.use_random_read_connection do
103
+ SeamlessDatabasePool.read_only_connection(connection).should == :read_db_connection
104
+ end
105
+ SeamlessDatabasePool.read_only_connection(connection).should == :master_db_connection
106
+ end
107
+ end
108
+ SeamlessDatabasePool.clear_read_only_connection
109
+ end
110
+
111
+ it "should be able to change the persistent connection" do
112
+ connection = mock(:connection)
113
+ connection.stub!(:random_read_connection).and_return(:read_db_connection)
114
+ connection.stub!(:using_master_connection?).and_return(false)
115
+
116
+ SeamlessDatabasePool.use_persistent_read_connection
117
+ SeamlessDatabasePool.read_only_connection_type.should == :persistent
118
+ SeamlessDatabasePool.read_only_connection(connection).should == :read_db_connection
119
+ SeamlessDatabasePool.set_persistent_read_connection(connection, :another_db_connection)
120
+ SeamlessDatabasePool.read_only_connection(connection).should == :another_db_connection
121
+
122
+ SeamlessDatabasePool.use_random_read_connection
123
+ SeamlessDatabasePool.read_only_connection_type.should == :random
124
+ SeamlessDatabasePool.read_only_connection(connection).should == :read_db_connection
125
+ SeamlessDatabasePool.set_persistent_read_connection(connection, :another_db_connection)
126
+ SeamlessDatabasePool.read_only_connection(connection).should == :read_db_connection
127
+ end
128
+
129
+ it "should be able to specify a default read connection type instead of :master" do
130
+ SeamlessDatabasePool.read_only_connection_type.should == :master
131
+ SeamlessDatabasePool.read_only_connection_type(nil).should == nil
132
+ end
133
+
134
+ end