seamless_database_pool 1.0.13 → 1.0.14

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -72,7 +72,7 @@ begin
72
72
  gem.has_rdoc = true
73
73
  gem.extra_rdoc_files = ["README.rdoc", "MIT-LICENSE"]
74
74
 
75
- gem.add_dependency('activerecord', '>= 2.2.2')
75
+ gem.add_dependency('activerecord', '>= 3.0.20')
76
76
  gem.add_development_dependency('rspec', '>= 2.0')
77
77
  gem.add_development_dependency('jeweler')
78
78
  gem.add_development_dependency('sqlite3')
@@ -13,8 +13,6 @@ module ActiveRecord
13
13
  master_config = default_config.merge(config[:master]).with_indifferent_access
14
14
  establish_adapter(master_config[:adapter])
15
15
  master_connection = send("#{master_config[:adapter]}_connection".to_sym, master_config)
16
- master_connection.class.send(:include, SeamlessDatabasePool::ConnectTimeout) unless master_connection.class.include?(SeamlessDatabasePool::ConnectTimeout)
17
- master_connection.connect_timeout = master_config[:connect_timeout]
18
16
  pool_weights[master_connection] = master_config[:pool_weight].to_i if master_config[:pool_weight].to_i > 0
19
17
 
20
18
  read_connections = []
@@ -25,8 +23,6 @@ module ActiveRecord
25
23
  begin
26
24
  establish_adapter(read_config[:adapter])
27
25
  conn = send("#{read_config[:adapter]}_connection".to_sym, read_config)
28
- conn.class.send(:include, SeamlessDatabasePool::ConnectTimeout) unless conn.class.include?(SeamlessDatabasePool::ConnectTimeout)
29
- conn.connect_timeout = read_config[:connect_timeout]
30
26
  read_connections << conn
31
27
  pool_weights[conn] = read_config[:pool_weight]
32
28
  rescue Exception => e
@@ -93,23 +89,22 @@ module ActiveRecord
93
89
  return const_get(adapter_class_name) if const_defined?(adapter_class_name, false)
94
90
 
95
91
  # Define methods to proxy to the appropriate pool
96
- read_only_methods = [:select_one, :select_all, :select_value, :select_values, :select, :select_rows, :execute, :tables, :columns]
92
+ read_only_methods = [:select, :select_rows, :execute, :tables, :columns]
93
+ clear_cache_methods = [:insert, :update, :delete]
94
+
95
+ # Get a list of all methods redefined by the underlying adapter. These will be
96
+ # proxied to the master connection.
97
97
  master_methods = []
98
- master_connection_classes = [AbstractAdapter, Quoting, DatabaseStatements, SchemaStatements]
99
- master_connection_classes << DatabaseLimits if const_defined?(:DatabaseLimits)
100
- master_connection_class = master_connection.class
101
- while ![Object, AbstractAdapter].include?(master_connection_class) do
102
- master_connection_classes << master_connection_class
103
- master_connection_class = master_connection_class.superclass
104
- end
105
- master_connection_classes.each do |connection_class|
98
+ override_classes = (master_connection.class.ancestors - AbstractAdapter.ancestors)
99
+ override_classes.each do |connection_class|
106
100
  master_methods.concat(connection_class.public_instance_methods(false))
107
101
  master_methods.concat(connection_class.protected_instance_methods(false))
108
102
  end
109
- master_methods.uniq!
103
+ master_methods = master_methods.collect{|m| m.to_sym}.uniq
110
104
  master_methods -= public_instance_methods(false) + protected_instance_methods(false) + private_instance_methods(false)
111
- master_methods = master_methods.collect{|m| m.to_sym}
112
105
  master_methods -= read_only_methods
106
+ master_methods -= [:select_all, :select_one, :select_value, :select_values]
107
+ master_methods -= clear_cache_methods
113
108
 
114
109
  klass = Class.new(self)
115
110
  master_methods.each do |method_name|
@@ -121,7 +116,18 @@ module ActiveRecord
121
116
  end
122
117
  EOS
123
118
  end
124
-
119
+
120
+ clear_cache_methods.each do |method_name|
121
+ klass.class_eval <<-EOS, __FILE__, __LINE__ + 1
122
+ def #{method_name}(*args, &block)
123
+ clear_query_cache if query_cache_enabled
124
+ use_master_connection do
125
+ return proxy_connection_method(master_connection, :#{method_name}, :master, *args, &block)
126
+ end
127
+ end
128
+ EOS
129
+ end
130
+
125
131
  read_only_methods.each do |method_name|
126
132
  klass.class_eval <<-EOS, __FILE__, __LINE__ + 1
127
133
  def #{method_name}(*args, &block)
@@ -178,6 +184,12 @@ module ActiveRecord
178
184
  false
179
185
  end
180
186
 
187
+ def transaction(options = {})
188
+ use_master_connection do
189
+ super
190
+ end
191
+ end
192
+
181
193
  def visitor=(visitor)
182
194
  all_connections.each{|conn| conn.visitor = visitor}
183
195
  end
@@ -213,23 +225,6 @@ module ActiveRecord
213
225
  do_to_connections {|conn| total += conn.reset_runtime}
214
226
  total
215
227
  end
216
-
217
- def lease
218
- synchronize do
219
- unless @in_use
220
- @in_use = true
221
- @last_use = Time.now
222
- end
223
- end
224
- end
225
-
226
- def expire
227
- @in_use = false
228
- end
229
-
230
- def close
231
- pool.checkin self
232
- end
233
228
 
234
229
  # Get a random read connection from the pool. If the connection is not active, it will attempt to reconnect
235
230
  # to the database. If that fails, it will be removed from the pool for one minute.
@@ -1,4 +1,3 @@
1
- require File.join(File.dirname(__FILE__), 'seamless_database_pool', 'connect_timeout.rb')
2
1
  require File.join(File.dirname(__FILE__), 'seamless_database_pool', 'connection_statistics.rb')
3
2
  require File.join(File.dirname(__FILE__), 'seamless_database_pool', 'controller_filter.rb')
4
3
  require File.join(File.dirname(__FILE__), 'active_record', 'connection_adapters', 'seamless_database_pool_adapter.rb')
@@ -13,7 +13,9 @@ describe "Test connection adapters" do
13
13
  let(:master_connection){ connection.master_connection }
14
14
 
15
15
  before(:all) do
16
+ ActiveRecord::Base.configurations = {'adapter' => "sqlite3", 'database' => ":memory:"}
16
17
  ActiveRecord::Base.establish_connection('adapter' => "sqlite3", 'database' => ":memory:")
18
+ ActiveRecord::Base.connection
17
19
  SeamlessDatabasePool::TestModel.db_model(adapter).create_tables
18
20
  end
19
21
 
@@ -66,8 +68,18 @@ describe "Test connection adapters" do
66
68
  record = model.find_by_name("test")
67
69
  record.name.should == "test"
68
70
  end
69
-
71
+
70
72
  it "should work with query caching" do
73
+ record_id = model.first.id
74
+ model.cache do
75
+ found = model.find(record_id)
76
+ found.value.should == 1
77
+ connection.master_connection.update("UPDATE #{model.table_name} SET value = 0 WHERE id = #{record_id}")
78
+ model.find(record_id).value.should == 1
79
+ end
80
+ end
81
+
82
+ it "should work bust the query cache on update" do
71
83
  record_id = model.first.id
72
84
  model.cache do
73
85
  found = model.find(record_id)
@@ -76,7 +88,7 @@ describe "Test connection adapters" do
76
88
  model.find(record_id).name.should == "new value"
77
89
  end
78
90
  end
79
-
91
+
80
92
  context "read connection" do
81
93
  let(:sample_sql){"SELECT #{connection.quote_column_name('name')} FROM #{connection.quote_table_name(model.table_name)}"}
82
94
 
@@ -87,43 +99,15 @@ describe "Test connection adapters" do
87
99
 
88
100
  it "should send select to the read connection" do
89
101
  results = connection.send(:select, sample_sql)
90
- results.should == [{"name" => "test"}]
91
- results.should == master_connection.send(:select, sample_sql)
92
- results.should be_read_only
93
- end
94
-
95
- it "should send select_all to the read connection" do
96
- results = connection.select_all(sample_sql)
97
- results.should == [{"name" => "test"}]
98
- results.should == master_connection.select_all(sample_sql)
99
- results.should be_read_only
100
- end
101
-
102
- it "should send select_one to the read connection" do
103
- results = connection.select_one(sample_sql)
104
- results.should == {"name" => "test"}
105
- results.should == master_connection.select_one(sample_sql)
106
- results.should be_read_only
107
- end
108
-
109
- it "should send select_values to the read connection" do
110
- results = connection.select_values(sample_sql)
111
- results.should == ["test"]
112
- results.should == master_connection.select_values(sample_sql)
113
- results.should be_read_only
114
- end
115
-
116
- it "should send select_value to the read connection" do
117
- results = connection.select_value(sample_sql)
118
- results.should == "test"
119
- results.should == master_connection.select_value(sample_sql)
102
+ results.to_a.should == [{"name" => "test"}]
103
+ results.to_a.should == master_connection.send(:select, sample_sql).to_a
120
104
  results.should be_read_only
121
105
  end
122
106
 
123
107
  it "should send select_rows to the read connection" do
124
- results = connection.select_all(sample_sql)
125
- results.should == [{"name" => "test"}]
126
- results.should == master_connection.select_all(sample_sql)
108
+ results = connection.select_rows(sample_sql)
109
+ results.should == [["test"]]
110
+ results.should == master_connection.select_rows(sample_sql)
127
111
  results.should be_read_only
128
112
  end
129
113
 
@@ -155,6 +139,34 @@ describe "Test connection adapters" do
155
139
  read_connection.should be_active
156
140
  end
157
141
  end
142
+
143
+ context "methods not overridden" do
144
+ let(:sample_sql){"SELECT #{connection.quote_column_name('name')} FROM #{connection.quote_table_name(model.table_name)}"}
145
+
146
+ it "should use select_all" do
147
+ results = connection.select_all(sample_sql)
148
+ results.to_a.should == [{"name" => "test"}].to_a
149
+ results.to_a.should == master_connection.select_all(sample_sql).to_a
150
+ end
151
+
152
+ it "should use select_one" do
153
+ results = connection.select_one(sample_sql)
154
+ results.should == {"name" => "test"}
155
+ results.should == master_connection.select_one(sample_sql)
156
+ end
157
+
158
+ it "should use select_values" do
159
+ results = connection.select_values(sample_sql)
160
+ results.should == ["test"]
161
+ results.should == master_connection.select_values(sample_sql)
162
+ end
163
+
164
+ it "should use select_value" do
165
+ results = connection.select_value(sample_sql)
166
+ results.should == "test"
167
+ results.should == master_connection.select_value(sample_sql)
168
+ end
169
+ end
158
170
 
159
171
  context "master connection" do
160
172
  let(:insert_sql){ "INSERT INTO #{connection.quote_table_name(model.table_name)} (#{connection.quote_column_name('name')}) VALUES ('new')" }
@@ -206,20 +218,13 @@ describe "Test connection adapters" do
206
218
  end
207
219
 
208
220
  it "should properly dump the schema" do
209
- schema = <<-EOS
210
- ActiveRecord::Schema.define(:version => 0) do
211
- create_table "#{model.table_name}", :force => true do |t|
212
- t.string "name"
213
- t.integer "value"
214
- end
215
- end
216
- EOS
217
- schema = schema.gsub(/^ +/, '').gsub(/ +/, ' ').strip
221
+ with_driver = StringIO.new
222
+ ActiveRecord::SchemaDumper.dump(connection, with_driver)
223
+
224
+ without_driver = StringIO.new
225
+ ActiveRecord::SchemaDumper.dump(master_connection, without_driver)
218
226
 
219
- io = StringIO.new
220
- ActiveRecord::SchemaDumper.dump(connection, io)
221
- generated_schema = io.string.gsub(/^#.*$/, '').gsub(/\n+/, "\n").gsub(/^ +/, '').gsub(/ +/, ' ').strip
222
- generated_schema.should == schema
227
+ with_driver.string.should == without_driver.string
223
228
  end
224
229
  end
225
230
  end
@@ -13,6 +13,16 @@ module SeamlessDatabasePool
13
13
  def reconnect!
14
14
  sleep(0.1)
15
15
  end
16
+
17
+ def active?
18
+ true
19
+ end
20
+
21
+ def begin_db_transaction
22
+ end
23
+
24
+ def commit_db_transaction
25
+ end
16
26
  end
17
27
 
18
28
  class MockMasterConnection < MockConnection
@@ -118,7 +128,8 @@ describe "SeamlessDatabasePoolAdapter" do
118
128
  it "should use the master connection inside a transaction" do
119
129
  connection_class = ActiveRecord::ConnectionAdapters::SeamlessDatabasePoolAdapter.adapter_class(master_connection)
120
130
  connection = connection_class.new(nil, mock(:logger), master_connection, [read_connection_1], {read_connection_1 => 1})
121
- master_connection.should_receive(:transaction).and_yield
131
+ master_connection.should_receive(:begin_db_transaction)
132
+ master_connection.should_receive(:commit_db_transaction)
122
133
  master_connection.should_receive(:select).with('Transaction SQL', nil)
123
134
  read_connection_1.should_receive(:select).with('SQL 1', nil)
124
135
  read_connection_1.should_receive(:select).with('SQL 2', nil)
@@ -205,12 +216,7 @@ describe "SeamlessDatabasePoolAdapter" do
205
216
  read_connection_2.should_receive(:reconnect!)
206
217
  pool_connection.reconnect!
207
218
  end
208
-
209
- it "should timeout reconnect! calls to dead servers" do
210
- read_connection_1.connect_timeout = 0.01
211
- lambda{read_connection_1.reconnect!}.should raise_error("reconnect timed out")
212
- end
213
-
219
+
214
220
  it "should fork reset_runtime to all connections" do
215
221
  master_connection.should_receive(:reset_runtime).and_return(1)
216
222
  read_connection_1.should_receive(:reset_runtime).and_return(2)
@@ -230,7 +236,7 @@ describe "SeamlessDatabasePoolAdapter" do
230
236
  end
231
237
 
232
238
  it "should try to reconnect dead connections when they become available again" do
233
- master_connection.stub!(:select_value).and_raise("SQL ERROR")
239
+ master_connection.stub!(:select).and_raise("SQL ERROR")
234
240
  master_connection.should_receive(:active?).and_return(false, false, true)
235
241
  master_connection.should_receive(:reconnect!)
236
242
  now = Time.now
@@ -242,19 +248,19 @@ describe "SeamlessDatabasePoolAdapter" do
242
248
  it "should not try to reconnect live connections" do
243
249
  args = [:arg1, :arg2]
244
250
  block = Proc.new{}
245
- master_connection.should_receive(:select_value).with(*args, &block).twice.and_raise("SQL ERROR")
251
+ master_connection.should_receive(:select).with(*args, &block).twice.and_raise("SQL ERROR")
246
252
  master_connection.should_receive(:active?).and_return(true)
247
253
  master_connection.should_not_receive(:reconnect!)
248
- lambda{pool_connection.send(:proxy_connection_method, master_connection, :select_value, :read, *args, &block)}.should raise_error("SQL ERROR")
254
+ lambda{pool_connection.send(:proxy_connection_method, master_connection, :select, :read, *args, &block)}.should raise_error("SQL ERROR")
249
255
  end
250
256
 
251
257
  it "should not try to reconnect a connection during a retry" do
252
258
  args = [:arg1, :arg2]
253
259
  block = Proc.new{}
254
- master_connection.should_receive(:select_value).with(*args, &block).and_raise("SQL ERROR")
260
+ master_connection.should_receive(:select).with(*args, &block).and_raise("SQL ERROR")
255
261
  master_connection.should_not_receive(:active?)
256
262
  master_connection.should_not_receive(:reconnect!)
257
- lambda{pool_connection.send(:proxy_connection_method, master_connection, :select_value, :retry, *args, &block)}.should raise_error("SQL ERROR")
263
+ lambda{pool_connection.send(:proxy_connection_method, master_connection, :select, :retry, *args, &block)}.should raise_error("SQL ERROR")
258
264
  end
259
265
 
260
266
  it "should try to execute a read statement again after a connection error" do
@@ -9,7 +9,7 @@ module ActiveRecord
9
9
 
10
10
  module ConnectionAdapters
11
11
  class ReadOnlyAdapter < AbstractAdapter
12
- %w(select_one select_all select_value select_values select select_rows execute tables columns).each do |read_method|
12
+ %w(select select_rows execute tables columns).each do |read_method|
13
13
  class_eval <<-EOS, __FILE__, __LINE__ + 1
14
14
  def #{read_method} (*args, &block)
15
15
  raise "Not Connected" unless @connected
data/spec/test_model.rb CHANGED
@@ -32,14 +32,14 @@ module SeamlessDatabasePool
32
32
  t.column :name, :string
33
33
  t.column :value, :integer
34
34
  end unless table_exists?
35
- connection.clear_cache!
36
- undefine_attribute_methods
35
+ connection.clear_cache! if connection.respond_to?(:clear_cache!)
36
+ undefine_attribute_methods if respond_to?(:undefine_attribute_methods)
37
37
  end
38
38
 
39
39
  def drop_tables
40
40
  connection.drop_table(table_name)
41
- connection.clear_cache!
42
- undefine_attribute_methods
41
+ connection.clear_cache! if connection.respond_to?(:clear_cache!)
42
+ undefine_attribute_methods if respond_to?(:undefine_attribute_methods)
43
43
  end
44
44
 
45
45
  def cleanup_database!
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.13
4
+ version: 1.0.14
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-06-11 00:00:00.000000000 Z
12
+ date: 2013-07-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - ! '>='
20
20
  - !ruby/object:Gem::Version
21
- version: 2.2.2
21
+ version: 3.0.20
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
- version: 2.2.2
29
+ version: 3.0.20
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: rspec
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -121,7 +121,6 @@ files:
121
121
  - lib/active_record/connection_adapters/seamless_database_pool_adapter.rb
122
122
  - lib/seamless_database_pool.rb
123
123
  - lib/seamless_database_pool/arel_compiler.rb
124
- - lib/seamless_database_pool/connect_timeout.rb
125
124
  - lib/seamless_database_pool/connection_statistics.rb
126
125
  - lib/seamless_database_pool/controller_filter.rb
127
126
  - lib/seamless_database_pool/railtie.rb
@@ -1,24 +0,0 @@
1
- require 'timeout'
2
-
3
- module SeamlessDatabasePool
4
- # This module is mixed into connection adapters to allow the reconnect! method to timeout if the
5
- # IP address becomes unreachable. The default timeout is 1 second, but you can change it by setting
6
- # the connect_timeout parameter in the adapter configuration.
7
- module ConnectTimeout
8
- attr_accessor :connect_timeout
9
-
10
- def self.included(base)
11
- base.alias_method_chain :reconnect!, :connect_timeout
12
- end
13
-
14
- def reconnect_with_connect_timeout!
15
- begin
16
- timeout(connect_timeout || 1) do
17
- reconnect_without_connect_timeout!
18
- end
19
- rescue Timeout::Error
20
- raise ActiveRecord::ConnectionTimeoutError.new("reconnect timed out")
21
- end
22
- end
23
- end
24
- end