seamless_database_pool 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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