seamless_database_pool 1.0.13 → 1.0.14

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
@@ -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