seamless_database_pool 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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