seamless_database_pool 1.0.1 → 1.0.2

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.
data/Rakefile CHANGED
@@ -22,7 +22,7 @@ end
22
22
 
23
23
  spec = Gem::Specification.new do |s|
24
24
  s.name = "seamless_database_pool"
25
- s.version = "1.0.1"
25
+ s.version = "1.0.2"
26
26
  s.author = "Brian Durand"
27
27
  s.platform = Gem::Platform::RUBY
28
28
  s.summary = "Support for master/slave database clusters in ActiveRecord"
@@ -31,7 +31,9 @@ module ActiveRecord
31
31
  end
32
32
  end if config[:read_pool]
33
33
 
34
- ActiveRecord::ConnectionAdapters::SeamlessDatabasePoolAdapter.new(nil, logger, master_connection, read_connections, pool_weights)
34
+
35
+ klass = ActiveRecord::ConnectionAdapters::SeamlessDatabasePoolAdapter.adapter_class(master_connection)
36
+ klass.new(nil, logger, master_connection, read_connections, pool_weights)
35
37
  end
36
38
 
37
39
  def self.establish_adapter (adapter)
@@ -79,12 +81,60 @@ module ActiveRecord
79
81
 
80
82
  module ConnectionAdapters
81
83
  class SeamlessDatabasePoolAdapter < AbstractAdapter
82
-
83
- READ_ONLY_METHODS = [:select_one, :select_all, :select_value, :select_values, :select_rows, :select]
84
- CONNECTION_METHODS = [:adapter_name, :active?, :reconnect!, :disconnect!, :verify!, :reset_runtime]
85
84
 
86
85
  attr_reader :read_connections, :master_connection
87
86
 
87
+ # Create an anonymous class that extends this one and proxies methods to the pool connections.
88
+ def self.adapter_class (master_connection)
89
+ # Define methods to proxy to the appropriate pool
90
+ read_only_methods = [:select_one, :select_all, :select_value, :select_values, :select_rows, :select]
91
+ connection_methods = [:adapter_name, :active?, :reconnect!, :disconnect!, :reset!, :verify!, :reset_runtime]
92
+ master_methods = master_connection.public_methods(false) + master_connection.protected_methods(false) + master_connection.private_methods(false)
93
+ master_methods -= public_methods(false) + protected_methods(false) + private_methods(false)
94
+ master_methods = master_methods.collect{|m| m.to_sym}
95
+ master_methods -= read_only_methods
96
+ master_methods -= connection_methods
97
+ master_methods.delete(:transaction)
98
+
99
+ klass = Class.new(self)
100
+ master_methods.each do |method_name|
101
+ klass.class_eval(%Q(
102
+ def #{method_name}(*args, &block)
103
+ begin
104
+ proxy_connection_method(master_connection, :#{method_name}, :master, *args, &block)
105
+ rescue DatabaseConnectionError => e
106
+ raise e.wrapped_exception
107
+ end
108
+ end
109
+ ))
110
+ end
111
+
112
+ read_only_methods.each do |method_name|
113
+ klass.class_eval(%Q(
114
+ def #{method_name}(*args, &block)
115
+ connection = current_read_connection
116
+ begin
117
+ proxy_connection_method(connection, :#{method_name}, :read, *args, &block)
118
+ rescue DatabaseConnectionError => e
119
+ unless block or using_master_connection?
120
+ # Try again with a different connection if needed unless it could have a side effect
121
+ unless connection.active?
122
+ suppress_read_connection(connection, 30)
123
+ connection = current_read_connection
124
+ SeamlessDatabasePool.set_persistent_read_connection(self, connection)
125
+ end
126
+ proxy_connection_method(connection, :#{method_name}, :retry, *args, &block)
127
+ else
128
+ raise e.wrapped_exception
129
+ end
130
+ end
131
+ end
132
+ ))
133
+ end
134
+
135
+ return klass
136
+ end
137
+
88
138
  def initialize (connection, logger, master_connection, read_connections, pool_weights)
89
139
  super(connection, logger)
90
140
 
@@ -96,8 +146,6 @@ module ActiveRecord
96
146
  weight.times{@weighted_read_connections << conn}
97
147
  end
98
148
  @available_read_connections = [AvailableConnections.new(@weighted_read_connections)]
99
-
100
- define_proxy_methods
101
149
  end
102
150
 
103
151
  def adapter_name #:nodoc:
@@ -128,8 +176,12 @@ module ActiveRecord
128
176
  all_connections.each{|conn| conn.disconnect!}
129
177
  end
130
178
 
131
- def verify!(timeout)
132
- all_connections.each{|conn| conn.verify!(timeout)}
179
+ def reset!
180
+ all_connections.each{|conn| conn.reset!}
181
+ end
182
+
183
+ def verify!(*ignored)
184
+ all_connections.each{|conn| conn.verify!(*ignored)}
133
185
  end
134
186
 
135
187
  def reset_runtime
@@ -254,49 +306,6 @@ module ActiveRecord
254
306
  end
255
307
  end
256
308
 
257
- # Define proxy methods to handle actual database work.
258
- def define_proxy_methods
259
- master_methods = master_connection.public_methods(false) + master_connection.protected_methods(false) + master_connection.private_methods(false)
260
- master_methods -= READ_ONLY_METHODS
261
- master_methods -= CONNECTION_METHODS
262
- master_methods.delete(:transaction)
263
-
264
- master_methods.each do |method_name|
265
- instance_eval(%Q(
266
- def #{method_name}(*args, &block)
267
- begin
268
- proxy_connection_method(master_connection, :#{method_name}, :master, *args, &block)
269
- rescue DatabaseConnectionError => e
270
- raise e.wrapped_exception
271
- end
272
- end
273
- ))
274
- end
275
-
276
- READ_ONLY_METHODS.each do |method_name|
277
- instance_eval(%Q(
278
- def #{method_name}(*args, &block)
279
- connection = current_read_connection
280
- begin
281
- proxy_connection_method(connection, :#{method_name}, :read, *args, &block)
282
- rescue DatabaseConnectionError => e
283
- unless block or using_master_connection?
284
- # Try again with a different connection if needed unless it could have a side effect
285
- unless connection.active?
286
- suppress_read_connection(connection, 30)
287
- connection = current_read_connection
288
- SeamlessDatabasePool.set_persistent_read_connection(self, connection)
289
- end
290
- proxy_connection_method(connection, :#{method_name}, :retry, *args, &block)
291
- else
292
- raise e.wrapped_exception
293
- end
294
- end
295
- end
296
- ))
297
- end
298
- end
299
-
300
309
  end
301
310
  end
302
311
  end
@@ -8,7 +8,7 @@ module SeamlessDatabasePool
8
8
  # Example:
9
9
  #
10
10
  # ApplicationController < ActionController::Base
11
- # include SeamlessDatabasePool
11
+ # include SeamlessDatabasePool::ControllerFilter
12
12
  # use_database_pool :all => :persistent, [:save, :delete] => :master
13
13
  # ...
14
14
 
@@ -9,7 +9,7 @@ describe "SeamlessDatabasePool::ControllerFilter" do
9
9
 
10
10
  def initialize(action, session = nil)
11
11
  @action_name = action
12
- session ||= CGI::Session.new(CGI.new)
12
+ session ||= {}
13
13
  @session = session
14
14
  end
15
15
 
@@ -89,7 +89,7 @@ describe "SeamlessDatabasePool::ControllerFilter" do
89
89
  end
90
90
 
91
91
  it "should be able to force using the master connection on the next request" do
92
- session = CGI::Session.new(CGI.new)
92
+ session = {}
93
93
 
94
94
  # First request
95
95
  controller = SeamlessDatabasePool::TestOtherController.new('read', session)
@@ -116,7 +116,7 @@ describe "SeamlessDatabasePool::ControllerFilter" do
116
116
  end
117
117
 
118
118
  it "should force the master connection on the next request for a redirect in master connection block" do
119
- session = CGI::Session.new(CGI.new)
119
+ session = {}
120
120
  controller = SeamlessDatabasePool::TestOtherController.new('redirect_master_action', session)
121
121
  controller.perform_action.should == {:action => :read}
122
122
 
@@ -125,7 +125,7 @@ describe "SeamlessDatabasePool::ControllerFilter" do
125
125
  end
126
126
 
127
127
  it "should not force the master connection on the next request for a redirect not in master connection block" do
128
- session = CGI::Session.new(CGI.new)
128
+ session = {}
129
129
  controller = SeamlessDatabasePool::TestOtherController.new('redirect_read_action', session)
130
130
  controller.perform_action.should == {:action => :read}
131
131
 
@@ -52,7 +52,9 @@ describe "SeamlessDatabasePoolAdapter ActiveRecord::Base extension" do
52
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
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
54
 
55
- ActiveRecord::ConnectionAdapters::SeamlessDatabasePoolAdapter.should_receive(:new).with(nil, logger, master_connection, [read_connection_1, read_connection_2], weights).and_return(pool_connection)
55
+ klass = mock(:class)
56
+ ActiveRecord::ConnectionAdapters::SeamlessDatabasePoolAdapter.should_receive(:adapter_class).with(master_connection).and_return(klass)
57
+ klass.should_receive(:new).with(nil, logger, master_connection, [read_connection_1, read_connection_2], weights).and_return(pool_connection)
56
58
 
57
59
  ActiveRecord::Base.should_receive(:establish_adapter).with('writer')
58
60
  ActiveRecord::Base.should_receive(:establish_adapter).with('reader').twice
@@ -72,7 +74,8 @@ describe "SeamlessDatabasePoolAdapter" do
72
74
  @read_connection_1 = SeamlessDatabasePool::MockConnection.new("read_1")
73
75
  @read_connection_2 = SeamlessDatabasePool::MockConnection.new("read_2")
74
76
  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)
77
+ connection_class = ActiveRecord::ConnectionAdapters::SeamlessDatabasePoolAdapter.adapter_class(@master_connection)
78
+ @pool_connection = connection_class.new(nil, mock(:logger), @master_connection, [@read_connection_1, @read_connection_2], weights)
76
79
  end
77
80
 
78
81
  it "should initialize the connection pool" do
@@ -102,7 +105,8 @@ describe "SeamlessDatabasePoolAdapter" do
102
105
  end
103
106
 
104
107
  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})
108
+ connection_class = ActiveRecord::ConnectionAdapters::SeamlessDatabasePoolAdapter.adapter_class(@master_connection)
109
+ connection = connection_class.new(nil, mock(:logger), @master_connection, [@read_connection_1], {@read_connection_1 => 1})
106
110
  connection.random_read_connection.should == @read_connection_1
107
111
  connection.use_master_connection do
108
112
  connection.random_read_connection.should == @master_connection
@@ -111,7 +115,8 @@ describe "SeamlessDatabasePoolAdapter" do
111
115
  end
112
116
 
113
117
  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})
118
+ connection_class = ActiveRecord::ConnectionAdapters::SeamlessDatabasePoolAdapter.adapter_class(@master_connection)
119
+ connection = connection_class.new(nil, mock(:logger), @master_connection, [@read_connection_1], {@read_connection_1 => 1})
115
120
  @master_connection.should_receive(:transaction).with(true).and_yield
116
121
  @master_connection.should_receive(:select_one).with('Transaction SQL')
117
122
  @read_connection_1.should_receive(:select_one).with('SQL 1')
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: seamless_database_pool
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Durand
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-07-25 00:00:00 -05:00
12
+ date: 2009-05-06 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -61,7 +61,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
61
61
  requirements:
62
62
  - rspec 1.0.8 or higher is needed to run the tests
63
63
  rubyforge_project: seamlessdbpool
64
- rubygems_version: 1.2.0
64
+ rubygems_version: 1.3.1
65
65
  signing_key:
66
66
  specification_version: 2
67
67
  summary: Support for master/slave database clusters in ActiveRecord