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.
|
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
|
-
|
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
|
132
|
-
all_connections.each{|conn| conn.
|
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
|
|
data/spec/filter_spec.rb
CHANGED
@@ -9,7 +9,7 @@ describe "SeamlessDatabasePool::ControllerFilter" do
|
|
9
9
|
|
10
10
|
def initialize(action, session = nil)
|
11
11
|
@action_name = action
|
12
|
-
session ||=
|
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 =
|
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 =
|
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 =
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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:
|
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.
|
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
|