seamless_database_pool 1.0.18 → 1.0.20
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +93 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +3 -2
- data/VERSION +1 -1
- data/lib/active_record/connection_adapters/seamless_database_pool_adapter.rb +17 -11
- data/lib/seamless_database_pool/arel_compiler.rb +1 -1
- data/lib/seamless_database_pool/connection_statistics.rb +15 -22
- data/lib/seamless_database_pool/controller_filter.rb +34 -38
- data/lib/seamless_database_pool/railtie.rb +6 -2
- data/seamless_database_pool.gemspec +2 -1
- data/spec/connection_adapters_spec.rb +14 -1
- data/spec/connection_statistics_spec.rb +13 -14
- data/spec/controller_filter_spec.rb +33 -33
- data/spec/database.yml +2 -0
- data/spec/seamless_database_pool_adapter_spec.rb +46 -19
- data/spec/spec_helper.rb +1 -0
- metadata +8 -8
- data/HISTORY.txt +0 -79
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c628c023a3a48064597f57d3aab3eaa36b843516
|
4
|
+
data.tar.gz: 26d3ae808291738d05f8ed27d52eca11c70c9983
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 04f6f9d7b8f15bec686fab951c1f87f0a36641ee00371a69cb78e9c31b41e21a1053fae5f1690cddcddd2705aa83e290907be0adf10273b944f7181046ce1bf5
|
7
|
+
data.tar.gz: 32bdc669ea87870be6affafd889cb7893e24a50ade3ddf8fbff6b4386c89fa0cb2c0f28aebe7dcbf7b8b949cc404140b7ae4ecb3545584e0e431a02a3714865b
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
## 1.0.20
|
2
|
+
|
3
|
+
* Remove calls to `alias_method_chain` for Rails 5.1 compatibility (thanks )
|
4
|
+
|
5
|
+
* Don't check read only connections on calls to `verify!` and `active?` when not necessary (thanks wjordan)
|
6
|
+
|
7
|
+
## 1.0.19
|
8
|
+
|
9
|
+
* Require ruby 2.0 or greater
|
10
|
+
|
11
|
+
* Eliminate deprecation warning on Rails 5 (thanks wjordan)
|
12
|
+
|
13
|
+
## 1.0.18
|
14
|
+
|
15
|
+
* ActiveRecord 5.0 compatibility (thanks jkowens)
|
16
|
+
|
17
|
+
* End support for ActiveRecord 3.1
|
18
|
+
|
19
|
+
## 1.0.17
|
20
|
+
|
21
|
+
* Do not update the HTTP session if there are no changes.
|
22
|
+
|
23
|
+
## 1.0.16
|
24
|
+
|
25
|
+
* Use shorter to_s output for output on connection.inspect.
|
26
|
+
|
27
|
+
## 1.0.15
|
28
|
+
|
29
|
+
* Implement less wordy connection to string method so logs don't fill up with long messages on connection errors.
|
30
|
+
|
31
|
+
* Update specs to remove deprecation warnings
|
32
|
+
|
33
|
+
* Fix adapter specs to work with ActiveRecord 4.1 configuration changes
|
34
|
+
|
35
|
+
## 1.0.14
|
36
|
+
|
37
|
+
* Remove custom connection timeout logic; Use the underlying driver's timeouts instead.
|
38
|
+
|
39
|
+
* Fix to work with query cache.
|
40
|
+
|
41
|
+
* Make driver less aggressive about overriding methods to proxy to the master connection.
|
42
|
+
|
43
|
+
* End support for ActiveRecord 2.x
|
44
|
+
|
45
|
+
* Add support for ActiveRecord 4.0
|
46
|
+
|
47
|
+
## 1.0.13
|
48
|
+
|
49
|
+
* Fix to work with `rake db:*` Rails tasks by removing the adapter from the configuration when db:* tasks are run.
|
50
|
+
|
51
|
+
* Fix connection pool issues so checkout/checkins don't interact with the underlying connections (thanks afex)
|
52
|
+
|
53
|
+
* Ruby 2.0/Rails 4.0 compatibility (thanks t27duck)
|
54
|
+
|
55
|
+
## 1.0.12
|
56
|
+
|
57
|
+
* Remove excessively long log messages on reconnect attempts.
|
58
|
+
|
59
|
+
## 1.0.11
|
60
|
+
|
61
|
+
* Remove debug code that prevented recovering from errors.
|
62
|
+
|
63
|
+
## 1.0.10
|
64
|
+
|
65
|
+
* Compatibility with ActiveRecord 3.1.0
|
66
|
+
|
67
|
+
## 1.0.9
|
68
|
+
|
69
|
+
* Compatibility with bind variables.
|
70
|
+
|
71
|
+
## 1.0.8
|
72
|
+
|
73
|
+
* Compatibility with ActiveRecord 3.1.0rc4
|
74
|
+
|
75
|
+
## 1.0.7
|
76
|
+
|
77
|
+
* Make compatible with ActionController 3.0
|
78
|
+
|
79
|
+
* Improved handling of down slave instances.
|
80
|
+
|
81
|
+
## 1.0.6
|
82
|
+
|
83
|
+
* Make compatible with ActiveRecord 3.0.
|
84
|
+
|
85
|
+
* Make compatible with database adapters other than MySQL including PostgrSQL.
|
86
|
+
|
87
|
+
* Better test suite to actually hit three different database adapters.
|
88
|
+
|
89
|
+
## 1.0.5
|
90
|
+
|
91
|
+
* Update docs.
|
92
|
+
|
93
|
+
* Remove rake dependency on rspec
|
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -53,6 +53,7 @@ By default, the master connection will be included in the read pool. If you woul
|
|
53
53
|
username: read_user
|
54
54
|
password: abc123
|
55
55
|
pool_adapter: mysql2
|
56
|
+
prepared_statements: false # required for ActiveRecord 5
|
56
57
|
port: 3306
|
57
58
|
master:
|
58
59
|
host: master-db.example.com
|
@@ -66,7 +67,7 @@ By default, the master connection will be included in the read pool. If you woul
|
|
66
67
|
|
67
68
|
In this configuration, the master connection will be a mysql connection to master-db.example.com:6000 using the username master_user and the password 567pass.
|
68
69
|
|
69
|
-
The read pool will use three mysql connections to master-db, read-db-1, and read-db-2. The master connection will use a different port, username, password for the connection. The read connections will use the same values. Further, the connection read-db-1 will get
|
70
|
+
The read pool will use three mysql connections to master-db, read-db-1, and read-db-2. The master connection will use a different port, username, password for the connection. The read connections will use the same values. Further, the connection read-db-1 will get twice as many read requests as each of the other two connections, so presumably it's on a more powerful box.
|
70
71
|
|
71
72
|
You must use compatible database adapters for both the master and the read connections. For example, you cannot use an Oracle server as your master and PostgreSQL servers as you read slaves.
|
72
73
|
|
@@ -80,4 +81,4 @@ This is done with static methods on SeamlessDatabasePool.
|
|
80
81
|
|
81
82
|
To ease integration into a Ruby on Rails application, several controller filters are provided to invoke the above connection methods in a block. These are not implemented as standard controller filters so that the connection methods can be in effect for other filters.
|
82
83
|
|
83
|
-
See SeamlessDatabasePool::ControllerFilter for more details.
|
84
|
+
See SeamlessDatabasePool::ControllerFilter for more details.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.
|
1
|
+
1.0.20
|
@@ -50,7 +50,7 @@ module ActiveRecord
|
|
50
50
|
begin
|
51
51
|
require "active_record/connection_adapters/#{adapter}_adapter"
|
52
52
|
rescue LoadError
|
53
|
-
raise "Please install the #{adapter} adapter: `gem install activerecord-#{adapter}-adapter` (#{$!})"
|
53
|
+
raise LoadError.new("Please install the #{adapter} adapter: `gem install activerecord-#{adapter}-adapter` (#{$!})")
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
@@ -62,19 +62,16 @@ module ActiveRecord
|
|
62
62
|
end
|
63
63
|
|
64
64
|
module SeamlessDatabasePoolBehavior
|
65
|
-
def self.included(base)
|
66
|
-
base.alias_method_chain(:reload, :seamless_database_pool)
|
67
|
-
end
|
68
65
|
|
69
66
|
# Force reload to use the master connection since it's probably being called for a reason.
|
70
|
-
def
|
67
|
+
def reload(*args)
|
71
68
|
SeamlessDatabasePool.use_master_connection do
|
72
|
-
|
69
|
+
super *args
|
73
70
|
end
|
74
71
|
end
|
75
72
|
end
|
76
73
|
|
77
|
-
|
74
|
+
prepend SeamlessDatabasePoolBehavior
|
78
75
|
end
|
79
76
|
|
80
77
|
module ConnectionAdapters
|
@@ -99,6 +96,7 @@ module ActiveRecord
|
|
99
96
|
override_classes.each do |connection_class|
|
100
97
|
master_methods.concat(connection_class.public_instance_methods(false))
|
101
98
|
master_methods.concat(connection_class.protected_instance_methods(false))
|
99
|
+
master_methods.concat(connection_class.private_instance_methods(false))
|
102
100
|
end
|
103
101
|
master_methods = master_methods.collect{|m| m.to_sym}.uniq
|
104
102
|
master_methods -= public_instance_methods(false) + protected_instance_methods(false) + private_instance_methods(false)
|
@@ -199,9 +197,13 @@ module ActiveRecord
|
|
199
197
|
end
|
200
198
|
|
201
199
|
def active?
|
202
|
-
|
203
|
-
|
204
|
-
|
200
|
+
if SeamlessDatabasePool.read_only_connection_type == :master
|
201
|
+
@master_connection.active?
|
202
|
+
else
|
203
|
+
active = true
|
204
|
+
do_to_connections {|conn| active &= conn.active?}
|
205
|
+
active
|
206
|
+
end
|
205
207
|
end
|
206
208
|
|
207
209
|
def reconnect!
|
@@ -217,7 +219,11 @@ module ActiveRecord
|
|
217
219
|
end
|
218
220
|
|
219
221
|
def verify!(*ignored)
|
220
|
-
|
222
|
+
if SeamlessDatabasePool.read_only_connection_type == :master
|
223
|
+
@master_connection.verify!(*ignored)
|
224
|
+
else
|
225
|
+
do_to_connections {|conn| conn.verify!(*ignored)}
|
226
|
+
end
|
221
227
|
end
|
222
228
|
|
223
229
|
def reset_runtime
|
@@ -14,7 +14,7 @@ module Arel
|
|
14
14
|
# try to load an externally defined compiler, in case this adapter has defined the compiler on its own.
|
15
15
|
require "#{master_adapter.downcase}/arel_compiler"
|
16
16
|
rescue LoadError
|
17
|
-
raise "#{master_adapter} is not supported by Arel."
|
17
|
+
raise LoadError.new("#{master_adapter} is not supported by Arel.")
|
18
18
|
end
|
19
19
|
end
|
20
20
|
compiler_class = Arel::SqlCompiler.const_get("#{master_adapter}Compiler")
|
@@ -3,48 +3,41 @@ module SeamlessDatabasePool
|
|
3
3
|
# and it will keep track of how often each connection calls update, insert, execute,
|
4
4
|
# or select.
|
5
5
|
module ConnectionStatistics
|
6
|
-
def self.included(base)
|
7
|
-
base.alias_method_chain(:update, :connection_statistics)
|
8
|
-
base.alias_method_chain(:insert, :connection_statistics)
|
9
|
-
base.alias_method_chain(:execute, :connection_statistics)
|
10
|
-
base.alias_method_chain(:select, :connection_statistics)
|
11
|
-
end
|
12
|
-
|
13
6
|
# Get the connection statistics
|
14
7
|
def connection_statistics
|
15
8
|
@connection_statistics ||= {}
|
16
9
|
end
|
17
|
-
|
10
|
+
|
18
11
|
def reset_connection_statistics
|
19
12
|
@connection_statistics = {}
|
20
13
|
end
|
21
|
-
|
22
|
-
def
|
14
|
+
|
15
|
+
def update(sql, name = nil)
|
23
16
|
increment_connection_statistic(:update) do
|
24
|
-
|
17
|
+
super(sql, name)
|
25
18
|
end
|
26
19
|
end
|
27
|
-
|
28
|
-
def
|
20
|
+
|
21
|
+
def insert(sql, name = nil)
|
29
22
|
increment_connection_statistic(:insert) do
|
30
|
-
|
23
|
+
super(sql, name)
|
31
24
|
end
|
32
25
|
end
|
33
|
-
|
34
|
-
def
|
26
|
+
|
27
|
+
def execute(sql, name = nil)
|
35
28
|
increment_connection_statistic(:execute) do
|
36
|
-
|
29
|
+
super(sql, name)
|
37
30
|
end
|
38
31
|
end
|
39
|
-
|
32
|
+
|
40
33
|
protected
|
41
|
-
|
42
|
-
def
|
34
|
+
|
35
|
+
def select(sql, name = nil, *args)
|
43
36
|
increment_connection_statistic(:select) do
|
44
|
-
|
37
|
+
super(sql, name, *args)
|
45
38
|
end
|
46
39
|
end
|
47
|
-
|
40
|
+
|
48
41
|
def increment_connection_statistic(method)
|
49
42
|
if @counting_pool_statistics
|
50
43
|
yield
|
@@ -6,35 +6,28 @@ module SeamlessDatabasePool
|
|
6
6
|
# when you need different connection types.
|
7
7
|
#
|
8
8
|
# Example:
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# ApplicationController < ActionController::Base
|
11
11
|
# include SeamlessDatabasePool::ControllerFilter
|
12
12
|
# use_database_pool :all => :persistent, [:save, :delete] => :master
|
13
13
|
# ...
|
14
|
-
|
15
|
-
module ControllerFilter
|
14
|
+
module ControllerFilter
|
16
15
|
def self.included(base)
|
17
16
|
unless base.respond_to?(:use_database_pool)
|
18
17
|
base.extend(ClassMethods)
|
19
18
|
base.class_eval do
|
20
|
-
|
21
|
-
alias_method_chain :perform_action, :seamless_database_pool
|
22
|
-
else
|
23
|
-
alias_method_chain :process, :seamless_database_pool
|
24
|
-
end
|
25
|
-
alias_method_chain :redirect_to, :seamless_database_pool
|
19
|
+
send(:prepend, ControllerFilterHooks)
|
26
20
|
end
|
27
21
|
end
|
28
22
|
end
|
29
|
-
|
23
|
+
|
30
24
|
module ClassMethods
|
31
|
-
|
32
25
|
def seamless_database_pool_options
|
33
26
|
return @seamless_database_pool_options if @seamless_database_pool_options
|
34
27
|
@seamless_database_pool_options = superclass.seamless_database_pool_options.dup if superclass.respond_to?(:seamless_database_pool_options)
|
35
28
|
@seamless_database_pool_options ||= {}
|
36
29
|
end
|
37
|
-
|
30
|
+
|
38
31
|
# Call this method to set up the connection types that will be used for your actions.
|
39
32
|
# The configuration is given as a hash where the key is the action name and the value is
|
40
33
|
# the connection type (:master, :persistent, or :random). You can specify :all as the action
|
@@ -58,7 +51,7 @@ module SeamlessDatabasePool
|
|
58
51
|
@seamless_database_pool_options = remapped_options
|
59
52
|
end
|
60
53
|
end
|
61
|
-
|
54
|
+
|
62
55
|
# Force the master connection to be used on the next request. This is very useful for the Post-Redirect pattern
|
63
56
|
# where you post a request to your save action and then redirect the user back to the edit action. By calling
|
64
57
|
# this method, you won't have to worry if the replication engine is slower than the redirect. Normally you
|
@@ -68,34 +61,13 @@ module SeamlessDatabasePool
|
|
68
61
|
def use_master_db_connection_on_next_request
|
69
62
|
session[:next_request_db_connection] = :master if session
|
70
63
|
end
|
71
|
-
|
64
|
+
|
72
65
|
def seamless_database_pool_options
|
73
66
|
self.class.seamless_database_pool_options
|
74
67
|
end
|
75
|
-
|
76
|
-
# Rails 3.x hook for setting the read connection for the request.
|
77
|
-
def process_with_seamless_database_pool(action, *args)
|
78
|
-
set_read_only_connection_for_block(action) do
|
79
|
-
process_without_seamless_database_pool(action, *args)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def redirect_to_with_seamless_database_pool(options = {}, response_status = {})
|
84
|
-
if SeamlessDatabasePool.read_only_connection_type(nil) == :master
|
85
|
-
use_master_db_connection_on_next_request
|
86
|
-
end
|
87
|
-
redirect_to_without_seamless_database_pool(options, response_status)
|
88
|
-
end
|
89
|
-
|
68
|
+
|
90
69
|
private
|
91
|
-
|
92
|
-
# Rails 2.x hook for setting the read connection for the request.
|
93
|
-
def perform_action_with_seamless_database_pool(*args)
|
94
|
-
set_read_only_connection_for_block(action_name) do
|
95
|
-
perform_action_without_seamless_database_pool(*args)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
70
|
+
|
99
71
|
# Set the read only connection for a block. Used to set the connection for a controller action.
|
100
72
|
def set_read_only_connection_for_block(action)
|
101
73
|
read_pool_method = nil
|
@@ -103,7 +75,7 @@ module SeamlessDatabasePool
|
|
103
75
|
read_pool_method = session[:next_request_db_connection]
|
104
76
|
session.delete(:next_request_db_connection) if session[:next_request_db_connection]
|
105
77
|
end
|
106
|
-
|
78
|
+
|
107
79
|
read_pool_method ||= seamless_database_pool_options[action.to_sym] || seamless_database_pool_options[:all]
|
108
80
|
if read_pool_method
|
109
81
|
SeamlessDatabasePool.set_read_only_connection_type(read_pool_method) do
|
@@ -114,4 +86,28 @@ module SeamlessDatabasePool
|
|
114
86
|
end
|
115
87
|
end
|
116
88
|
end
|
89
|
+
|
90
|
+
module ControllerFilterHooks
|
91
|
+
# Rails 3.x hook for setting the read connection for the request.
|
92
|
+
def process(action, *args)
|
93
|
+
set_read_only_connection_for_block(action) do
|
94
|
+
super(action, *args)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Rails 2.x hook for setting the read connection for the request.
|
99
|
+
def perform_action(*args)
|
100
|
+
set_read_only_connection_for_block(action_name) do
|
101
|
+
super
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def redirect_to(options = {}, response_status = {})
|
106
|
+
if SeamlessDatabasePool.read_only_connection_type(nil) == :master
|
107
|
+
use_master_db_connection_on_next_request
|
108
|
+
end
|
109
|
+
|
110
|
+
super(options, response_status)
|
111
|
+
end
|
112
|
+
end
|
117
113
|
end
|
@@ -4,8 +4,12 @@ module SeamlessDatabasePool
|
|
4
4
|
namespace :db do
|
5
5
|
task :load_config do
|
6
6
|
# Override seamless_database_pool configuration so db:* rake tasks work as expected.
|
7
|
-
|
8
|
-
|
7
|
+
module DatabaseConfiguration
|
8
|
+
def configurations
|
9
|
+
SeamlessDatabasePool.master_database_configuration(super.deep_dup)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
ActiveRecord::Base.singleton_class.prepend(DatabaseConfiguration)
|
9
13
|
end
|
10
14
|
end
|
11
15
|
end
|
@@ -12,8 +12,9 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
13
13
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
14
14
|
spec.require_paths = ["lib"]
|
15
|
+
spec.required_ruby_version = '~> 2.0'
|
15
16
|
|
16
|
-
spec.add_runtime_dependency(%q<activerecord>, [">= 3.0
|
17
|
+
spec.add_runtime_dependency(%q<activerecord>, [">= 3.2.0"])
|
17
18
|
spec.add_development_dependency(%q<rspec>, [">= 2.0"])
|
18
19
|
spec.add_development_dependency(%q<sqlite3>, [">= 0"])
|
19
20
|
spec.add_development_dependency(%q<mysql>, [">= 0"])
|
@@ -13,7 +13,7 @@ describe "Test connection adapters" do
|
|
13
13
|
let(:master_connection){ connection.master_connection }
|
14
14
|
|
15
15
|
before(:all) do
|
16
|
-
if ActiveRecord::VERSION::MAJOR <
|
16
|
+
if ActiveRecord::VERSION::MAJOR < 4 || (ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR == 0)
|
17
17
|
ActiveRecord::Base.configurations = {'adapter' => "sqlite3", 'database' => ":memory:"}
|
18
18
|
else
|
19
19
|
ActiveRecord::Base.configurations = {"test" => {'adapter' => "sqlite3", 'database' => ":memory:"}}
|
@@ -230,6 +230,19 @@ describe "Test connection adapters" do
|
|
230
230
|
|
231
231
|
with_driver.string.should == without_driver.string
|
232
232
|
end
|
233
|
+
|
234
|
+
it "should allow for database specific types" do
|
235
|
+
if adapter == "postgresql"
|
236
|
+
SeamlessDatabasePool.use_master_connection do
|
237
|
+
connection.enable_extension "hstore"
|
238
|
+
connection.create_table(:pg) do |t|
|
239
|
+
t.hstore :my_hash
|
240
|
+
end
|
241
|
+
end
|
242
|
+
connection.drop_table(:pg)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
233
246
|
end
|
234
247
|
end
|
235
248
|
end
|
@@ -1,32 +1,31 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe SeamlessDatabasePool::ConnectionStatistics do
|
4
|
-
|
5
4
|
module SeamlessDatabasePool
|
6
5
|
class ConnectionStatisticsTester
|
7
6
|
def insert (sql, name = nil)
|
8
7
|
"INSERT #{sql}/#{name}"
|
9
8
|
end
|
10
|
-
|
9
|
+
|
11
10
|
def update (sql, name = nil)
|
12
11
|
execute(sql)
|
13
12
|
"UPDATE #{sql}/#{name}"
|
14
13
|
end
|
15
|
-
|
14
|
+
|
16
15
|
def execute (sql, name = nil)
|
17
16
|
"EXECUTE #{sql}/#{name}"
|
18
17
|
end
|
19
|
-
|
18
|
+
|
20
19
|
protected
|
21
|
-
|
20
|
+
|
22
21
|
def select (sql, name = nil, binds = [])
|
23
22
|
"SELECT #{sql}/#{name}"
|
24
23
|
end
|
25
|
-
|
26
|
-
|
24
|
+
|
25
|
+
prepend ::SeamlessDatabasePool::ConnectionStatistics
|
27
26
|
end
|
28
27
|
end
|
29
|
-
|
28
|
+
|
30
29
|
it "should increment statistics on update" do
|
31
30
|
connection = SeamlessDatabasePool::ConnectionStatisticsTester.new
|
32
31
|
connection.update('SQL', 'name').should == "UPDATE SQL/name"
|
@@ -34,7 +33,7 @@ describe SeamlessDatabasePool::ConnectionStatistics do
|
|
34
33
|
connection.update('SQL 2').should == "UPDATE SQL 2/"
|
35
34
|
connection.connection_statistics.should == {:update => 2}
|
36
35
|
end
|
37
|
-
|
36
|
+
|
38
37
|
it "should increment statistics on insert" do
|
39
38
|
connection = SeamlessDatabasePool::ConnectionStatisticsTester.new
|
40
39
|
connection.insert('SQL', 'name').should == "INSERT SQL/name"
|
@@ -42,7 +41,7 @@ describe SeamlessDatabasePool::ConnectionStatistics do
|
|
42
41
|
connection.insert('SQL 2').should == "INSERT SQL 2/"
|
43
42
|
connection.connection_statistics.should == {:insert => 2}
|
44
43
|
end
|
45
|
-
|
44
|
+
|
46
45
|
it "should increment statistics on execute" do
|
47
46
|
connection = SeamlessDatabasePool::ConnectionStatisticsTester.new
|
48
47
|
connection.execute('SQL', 'name').should == "EXECUTE SQL/name"
|
@@ -50,7 +49,7 @@ describe SeamlessDatabasePool::ConnectionStatistics do
|
|
50
49
|
connection.execute('SQL 2').should == "EXECUTE SQL 2/"
|
51
50
|
connection.connection_statistics.should == {:execute => 2}
|
52
51
|
end
|
53
|
-
|
52
|
+
|
54
53
|
it "should increment statistics on select" do
|
55
54
|
connection = SeamlessDatabasePool::ConnectionStatisticsTester.new
|
56
55
|
connection.send(:select, 'SQL', 'name').should == "SELECT SQL/name"
|
@@ -58,14 +57,14 @@ describe SeamlessDatabasePool::ConnectionStatistics do
|
|
58
57
|
connection.send(:select, 'SQL 2').should == "SELECT SQL 2/"
|
59
58
|
connection.connection_statistics.should == {:select => 2}
|
60
59
|
end
|
61
|
-
|
60
|
+
|
62
61
|
it "should increment counts only once within a block" do
|
63
62
|
connection = SeamlessDatabasePool::ConnectionStatisticsTester.new
|
64
63
|
expect(connection).to receive(:execute).with('SQL')
|
65
64
|
connection.update('SQL')
|
66
65
|
connection.connection_statistics.should == {:update => 1}
|
67
66
|
end
|
68
|
-
|
67
|
+
|
69
68
|
it "should be able to clear the statistics" do
|
70
69
|
connection = SeamlessDatabasePool::ConnectionStatisticsTester.new
|
71
70
|
connection.update('SQL')
|
@@ -73,5 +72,5 @@ describe SeamlessDatabasePool::ConnectionStatistics do
|
|
73
72
|
connection.reset_connection_statistics
|
74
73
|
connection.connection_statistics.should == {}
|
75
74
|
end
|
76
|
-
|
75
|
+
|
77
76
|
end
|
@@ -1,45 +1,45 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe "SeamlessDatabasePool::ControllerFilter" do
|
4
|
-
|
4
|
+
|
5
5
|
module SeamlessDatabasePool
|
6
6
|
class TestApplicationController
|
7
7
|
attr_reader :session
|
8
|
-
|
8
|
+
|
9
9
|
def initialize(session)
|
10
10
|
@session = session
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
def process(action, *args)
|
14
14
|
send action
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def redirect_to (options = {}, response_status = {})
|
18
18
|
options
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
def base_action
|
22
22
|
::SeamlessDatabasePool.read_only_connection_type
|
23
23
|
end
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
class TestBaseController < TestApplicationController
|
27
27
|
include ::SeamlessDatabasePool::ControllerFilter
|
28
28
|
|
29
29
|
use_database_pool :read => :persistent
|
30
|
-
|
30
|
+
|
31
31
|
def read
|
32
32
|
::SeamlessDatabasePool.read_only_connection_type
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
def other
|
36
36
|
::SeamlessDatabasePool.read_only_connection_type
|
37
37
|
end
|
38
38
|
end
|
39
|
-
|
39
|
+
|
40
40
|
class TestOtherController < TestBaseController
|
41
41
|
use_database_pool :all => :random, [:edit, :save, :redirect_master_action] => :master
|
42
|
-
|
42
|
+
|
43
43
|
def edit
|
44
44
|
::SeamlessDatabasePool.read_only_connection_type
|
45
45
|
end
|
@@ -47,102 +47,102 @@ describe "SeamlessDatabasePool::ControllerFilter" do
|
|
47
47
|
def save
|
48
48
|
::SeamlessDatabasePool.read_only_connection_type
|
49
49
|
end
|
50
|
-
|
50
|
+
|
51
51
|
def redirect_master_action
|
52
52
|
redirect_to(:action => :read)
|
53
53
|
end
|
54
|
-
|
54
|
+
|
55
55
|
def redirect_read_action
|
56
56
|
redirect_to(:action => :read)
|
57
57
|
end
|
58
58
|
end
|
59
|
-
|
59
|
+
|
60
60
|
class TestRails2ApplicationController < TestApplicationController
|
61
61
|
attr_reader :action_name
|
62
|
-
|
62
|
+
|
63
63
|
def process(action, *args)
|
64
64
|
@action_name = action
|
65
65
|
perform_action
|
66
66
|
end
|
67
|
-
|
67
|
+
|
68
68
|
private
|
69
|
-
|
69
|
+
|
70
70
|
def perform_action
|
71
71
|
send action_name
|
72
72
|
end
|
73
73
|
end
|
74
|
-
|
74
|
+
|
75
75
|
class TestRails2BaseController < TestRails2ApplicationController
|
76
76
|
include ::SeamlessDatabasePool::ControllerFilter
|
77
77
|
|
78
78
|
use_database_pool :read => :persistent
|
79
|
-
|
79
|
+
|
80
80
|
def read
|
81
81
|
::SeamlessDatabasePool.read_only_connection_type
|
82
82
|
end
|
83
83
|
end
|
84
84
|
end
|
85
|
-
|
85
|
+
|
86
86
|
let(:session){Hash.new}
|
87
87
|
let(:controller){SeamlessDatabasePool::TestOtherController.new(session)}
|
88
|
-
|
88
|
+
|
89
89
|
it "should work with nothing set" do
|
90
90
|
controller = SeamlessDatabasePool::TestApplicationController.new(session)
|
91
91
|
controller.process('base_action').should == :master
|
92
92
|
end
|
93
|
-
|
93
|
+
|
94
94
|
it "should allow setting a connection type for a single action" do
|
95
95
|
controller = SeamlessDatabasePool::TestBaseController.new(session)
|
96
96
|
controller.process('read').should == :persistent
|
97
97
|
end
|
98
|
-
|
98
|
+
|
99
99
|
it "should allow setting a connection type for actions" do
|
100
100
|
controller.process('edit').should == :master
|
101
101
|
controller.process('save').should == :master
|
102
102
|
end
|
103
|
-
|
103
|
+
|
104
104
|
it "should allow setting a connection type for all actions" do
|
105
105
|
controller.process('other').should == :random
|
106
106
|
end
|
107
|
-
|
107
|
+
|
108
108
|
it "should inherit the superclass' options" do
|
109
109
|
controller.process('read').should == :persistent
|
110
110
|
end
|
111
|
-
|
111
|
+
|
112
112
|
it "should be able to force using the master connection on the next request" do
|
113
113
|
# First request
|
114
114
|
controller.process('read').should == :persistent
|
115
115
|
controller.use_master_db_connection_on_next_request
|
116
|
-
|
116
|
+
|
117
117
|
# Second request
|
118
118
|
controller.process('read').should == :master
|
119
|
-
|
119
|
+
|
120
120
|
# Third request
|
121
121
|
controller.process('read').should == :persistent
|
122
122
|
end
|
123
|
-
|
123
|
+
|
124
124
|
it "should not break trying to force the master connection if sessions are not enabled" do
|
125
125
|
controller.process('read').should == :persistent
|
126
126
|
controller.use_master_db_connection_on_next_request
|
127
|
-
|
127
|
+
|
128
128
|
# Second request
|
129
129
|
session.clear
|
130
130
|
controller.process('read').should == :persistent
|
131
131
|
end
|
132
|
-
|
132
|
+
|
133
133
|
it "should force the master connection on the next request for a redirect in master connection block" do
|
134
134
|
controller = SeamlessDatabasePool::TestOtherController.new(session)
|
135
135
|
controller.process('redirect_master_action').should == {:action => :read}
|
136
|
-
|
136
|
+
|
137
137
|
controller.process('read').should == :master
|
138
138
|
end
|
139
139
|
|
140
140
|
it "should not force the master connection on the next request for a redirect not in master connection block" do
|
141
141
|
controller.process('redirect_read_action').should == {:action => :read}
|
142
|
-
|
142
|
+
|
143
143
|
controller.process('read').should == :persistent
|
144
144
|
end
|
145
|
-
|
145
|
+
|
146
146
|
it "should work with a Rails 2 controller" do
|
147
147
|
controller = SeamlessDatabasePool::TestRails2BaseController.new(session)
|
148
148
|
controller.process('read').should == :persistent
|
data/spec/database.yml
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
sqlite3:
|
6
6
|
adapter: seamless_database_pool
|
7
7
|
database: test.sqlite3
|
8
|
+
prepared_statements: false
|
8
9
|
master:
|
9
10
|
adapter: sqlite3
|
10
11
|
pool_weight: 0
|
@@ -15,6 +16,7 @@ sqlite3:
|
|
15
16
|
postgresql:
|
16
17
|
adapter: seamless_database_pool
|
17
18
|
database: seamless_database_pool_test
|
19
|
+
prepared_statements: false
|
18
20
|
username: postgres
|
19
21
|
password: postgres
|
20
22
|
master:
|
@@ -189,27 +189,54 @@ describe "SeamlessDatabasePoolAdapter" do
|
|
189
189
|
end
|
190
190
|
|
191
191
|
context "fork to all connections" do
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
192
|
+
context "when read-only connection type is master" do
|
193
|
+
it "should fork active? to master connection only" do
|
194
|
+
expect(master_connection).to receive(:active?).and_return(true)
|
195
|
+
expect(read_connection_1).not_to receive(:active?)
|
196
|
+
expect(read_connection_2).not_to receive(:active?)
|
197
|
+
pool_connection.active?.should == true
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should fork verify! to master connection only" do
|
201
|
+
expect(master_connection).to receive(:verify!).with(5)
|
202
|
+
expect(read_connection_1).not_to receive(:verify!)
|
203
|
+
expect(read_connection_2).not_to receive(:verify!)
|
204
|
+
pool_connection.verify!(5)
|
205
|
+
end
|
204
206
|
end
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
207
|
+
|
208
|
+
context "When read-only connection type is persistent or random" do
|
209
|
+
around do |example|
|
210
|
+
SeamlessDatabasePool.set_read_only_connection_type(:persistent) do
|
211
|
+
example.run
|
212
|
+
end
|
213
|
+
SeamlessDatabasePool.set_read_only_connection_type(:random) do
|
214
|
+
example.run
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
it "should fork active? to all connections and return true if all are up" do
|
219
|
+
expect(master_connection).to receive(:active?).and_return(true)
|
220
|
+
expect(read_connection_1).to receive(:active?).and_return(true)
|
221
|
+
expect(read_connection_2).to receive(:active?).and_return(true)
|
222
|
+
pool_connection.active?.should == true
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should fork active? to all connections and return false if one is down" do
|
226
|
+
expect(master_connection).to receive(:active?).and_return(true)
|
227
|
+
expect(read_connection_1).to receive(:active?).and_return(true)
|
228
|
+
expect(read_connection_2).to receive(:active?).and_return(false)
|
229
|
+
pool_connection.active?.should == false
|
230
|
+
end
|
231
|
+
|
232
|
+
it "should fork verify! to all connections" do
|
233
|
+
expect(master_connection).to receive(:verify!).with(5)
|
234
|
+
expect(read_connection_1).to receive(:verify!).with(5)
|
235
|
+
expect(read_connection_2).to receive(:verify!).with(5)
|
236
|
+
pool_connection.verify!(5)
|
237
|
+
end
|
211
238
|
end
|
212
|
-
|
239
|
+
|
213
240
|
it "should fork disconnect! to all connections" do
|
214
241
|
expect(master_connection).to receive(:disconnect!)
|
215
242
|
expect(read_connection_1).to receive(:disconnect!)
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
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.20
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Durand
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-05-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 3.0
|
19
|
+
version: 3.2.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 3.0
|
26
|
+
version: 3.2.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rspec
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -90,7 +90,7 @@ extra_rdoc_files: []
|
|
90
90
|
files:
|
91
91
|
- ".gitignore"
|
92
92
|
- ".travis.yml"
|
93
|
-
-
|
93
|
+
- CHANGELOG.md
|
94
94
|
- MIT-LICENSE
|
95
95
|
- README.rdoc
|
96
96
|
- Rakefile
|
@@ -121,9 +121,9 @@ require_paths:
|
|
121
121
|
- lib
|
122
122
|
required_ruby_version: !ruby/object:Gem::Requirement
|
123
123
|
requirements:
|
124
|
-
- - "
|
124
|
+
- - "~>"
|
125
125
|
- !ruby/object:Gem::Version
|
126
|
-
version: '0'
|
126
|
+
version: '2.0'
|
127
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
129
|
- - ">="
|
@@ -131,7 +131,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
131
|
version: '0'
|
132
132
|
requirements: []
|
133
133
|
rubyforge_project:
|
134
|
-
rubygems_version: 2.
|
134
|
+
rubygems_version: 2.6.11
|
135
135
|
signing_key:
|
136
136
|
specification_version: 4
|
137
137
|
summary: Add support for master/slave database clusters in ActiveRecord to improve
|
data/HISTORY.txt
DELETED
@@ -1,79 +0,0 @@
|
|
1
|
-
1.0.18
|
2
|
-
|
3
|
-
ActiveRecord 5.0 compatibility (thanks jkowens)
|
4
|
-
|
5
|
-
1.0.17
|
6
|
-
|
7
|
-
Do not update the HTTP session if there are no changes.
|
8
|
-
|
9
|
-
1.0.16
|
10
|
-
|
11
|
-
Use shorter to_s output for output on connection.inspect.
|
12
|
-
|
13
|
-
1.0.15
|
14
|
-
|
15
|
-
Implement less wordy connection to string method so logs don't fill up with long messages on connection errors.
|
16
|
-
|
17
|
-
Update specs to remove deprecation warnings
|
18
|
-
|
19
|
-
Fix adapter specs to work with ActiveRecord 4.1 configuration changes
|
20
|
-
|
21
|
-
1.0.14
|
22
|
-
|
23
|
-
Remove custom connection timeout logic; Use the underlying driver's timeouts instead.
|
24
|
-
|
25
|
-
Fix to work with query cache.
|
26
|
-
|
27
|
-
Make driver less aggressive about overriding methods to proxy to the master connection.
|
28
|
-
|
29
|
-
End support for ActiveRecord 2.x
|
30
|
-
|
31
|
-
Add support for ActiveRecord 4.0
|
32
|
-
|
33
|
-
1.0.13
|
34
|
-
|
35
|
-
Fix to work with `rake db:*` Rails tasks by removing the adapter from the configuration when db:* tasks are run.
|
36
|
-
|
37
|
-
Fix connection pool issues so checkout/checkins don't interact with the underlying connections (thanks afex)
|
38
|
-
|
39
|
-
Ruby 2.0/Rails 4.0 compatibility (thanks t27duck)
|
40
|
-
|
41
|
-
1.0.12
|
42
|
-
|
43
|
-
Remove excessively long log messages on reconnect attempts.
|
44
|
-
|
45
|
-
1.0.11
|
46
|
-
|
47
|
-
Remove debug code that prevented recovering from errors.
|
48
|
-
|
49
|
-
1.0.10
|
50
|
-
|
51
|
-
Compatibility with ActiveRecord 3.1.0
|
52
|
-
|
53
|
-
1.0.9
|
54
|
-
|
55
|
-
Compatibility with bind variables.
|
56
|
-
|
57
|
-
1.0.8
|
58
|
-
|
59
|
-
Compatibility with ActiveRecord 3.1.0rc4
|
60
|
-
|
61
|
-
1.0.7
|
62
|
-
|
63
|
-
Make compatible with ActionController 3.0
|
64
|
-
|
65
|
-
Improved handling of down slave instances.
|
66
|
-
|
67
|
-
1.0.6
|
68
|
-
|
69
|
-
Make compatible with ActiveRecord 3.0.
|
70
|
-
|
71
|
-
Make compatible with database adapters other than MySQL including PostgrSQL.
|
72
|
-
|
73
|
-
Better test suite to actually hit three different database adapters.
|
74
|
-
|
75
|
-
1.0.5
|
76
|
-
|
77
|
-
Update docs.
|
78
|
-
|
79
|
-
Remove rake dependency on rspec
|