active_record_host_pool 1.2.5 → 2.0.0
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.
- checksums.yaml +4 -4
- data/Changelog.md +115 -0
- data/Readme.md +3 -3
- data/lib/active_record_host_pool/clear_query_cache_patch.rb +13 -14
- data/lib/active_record_host_pool/connection_adapter_mixin.rb +49 -43
- data/lib/active_record_host_pool/connection_proxy.rb +14 -1
- data/lib/active_record_host_pool/pool_proxy.rb +182 -5
- data/lib/active_record_host_pool/version.rb +1 -1
- data/lib/active_record_host_pool.rb +21 -9
- metadata +8 -31
- data/lib/active_record_host_pool/pool_proxy_6_1.rb +0 -159
- data/lib/active_record_host_pool/pool_proxy_legacy.rb +0 -156
- data/test/database.yml +0 -108
- data/test/helper.rb +0 -198
- data/test/schema.rb +0 -23
- data/test/test_arhp.rb +0 -186
@@ -1,159 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'delegate'
|
4
|
-
require 'active_record'
|
5
|
-
require 'active_record_host_pool/connection_adapter_mixin'
|
6
|
-
require 'mutex_m'
|
7
|
-
|
8
|
-
# this module sits in between ConnectionHandler and a bunch of different ConnectionPools (one per host).
|
9
|
-
# when a connection is requested, it goes like:
|
10
|
-
# ActiveRecordClass -> ConnectionHandler#connection
|
11
|
-
# ConnectionHandler#connection -> (find or create PoolProxy)
|
12
|
-
# PoolProxy -> shared list of Pools
|
13
|
-
# Pool actually gives back a connection, then PoolProxy turns this
|
14
|
-
# into a ConnectionProxy that can inform (on execute) which db we should be on.
|
15
|
-
|
16
|
-
module ActiveRecordHostPool
|
17
|
-
# Sits between ConnectionHandler and a bunch of different ConnectionPools (one per host).
|
18
|
-
class PoolProxy < Delegator
|
19
|
-
include Mutex_m
|
20
|
-
|
21
|
-
def initialize(pool_config)
|
22
|
-
super(pool_config)
|
23
|
-
@pool_config = pool_config
|
24
|
-
@config = pool_config.db_config.configuration_hash
|
25
|
-
end
|
26
|
-
|
27
|
-
def __getobj__
|
28
|
-
_connection_pool
|
29
|
-
end
|
30
|
-
|
31
|
-
def __setobj__(pool_config)
|
32
|
-
@pool_config = pool_config
|
33
|
-
@config = pool_config.db_config.configuration_hash
|
34
|
-
@_pool_key = nil
|
35
|
-
end
|
36
|
-
|
37
|
-
attr_reader :pool_config
|
38
|
-
|
39
|
-
def connection(*args)
|
40
|
-
real_connection = _unproxied_connection(*args)
|
41
|
-
_connection_proxy_for(real_connection, @config[:database])
|
42
|
-
rescue Mysql2::Error, ActiveRecord::NoDatabaseError
|
43
|
-
_connection_pools.delete(_pool_key)
|
44
|
-
Kernel.raise
|
45
|
-
end
|
46
|
-
|
47
|
-
def _unproxied_connection(*args)
|
48
|
-
_connection_pool.connection(*args)
|
49
|
-
end
|
50
|
-
|
51
|
-
# by the time we are patched into ActiveRecord, the current thread has already established
|
52
|
-
# a connection. thus we need to patch both connection and checkout/checkin
|
53
|
-
def checkout(*args, &block)
|
54
|
-
cx = _connection_pool.checkout(*args, &block)
|
55
|
-
_connection_proxy_for(cx, @config[:database])
|
56
|
-
end
|
57
|
-
|
58
|
-
def checkin(cx)
|
59
|
-
cx = cx.unproxied
|
60
|
-
_connection_pool.checkin(cx)
|
61
|
-
end
|
62
|
-
|
63
|
-
def with_connection
|
64
|
-
cx = checkout
|
65
|
-
yield cx
|
66
|
-
ensure
|
67
|
-
checkin cx
|
68
|
-
end
|
69
|
-
|
70
|
-
def disconnect!
|
71
|
-
p = _connection_pool(false)
|
72
|
-
return unless p
|
73
|
-
|
74
|
-
synchronize do
|
75
|
-
p.disconnect!
|
76
|
-
p.automatic_reconnect = true
|
77
|
-
_clear_connection_proxy_cache
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def automatic_reconnect=(value)
|
82
|
-
p = _connection_pool(false)
|
83
|
-
return unless p
|
84
|
-
|
85
|
-
p.automatic_reconnect = value
|
86
|
-
end
|
87
|
-
|
88
|
-
def clear_reloadable_connections!
|
89
|
-
_connection_pool.clear_reloadable_connections!
|
90
|
-
_clear_connection_proxy_cache
|
91
|
-
end
|
92
|
-
|
93
|
-
def release_connection(*args)
|
94
|
-
p = _connection_pool(false)
|
95
|
-
return unless p
|
96
|
-
|
97
|
-
p.release_connection(*args)
|
98
|
-
end
|
99
|
-
|
100
|
-
def flush!
|
101
|
-
p = _connection_pool(false)
|
102
|
-
return unless p
|
103
|
-
|
104
|
-
p.flush!
|
105
|
-
end
|
106
|
-
|
107
|
-
def discard!
|
108
|
-
p = _connection_pool(false)
|
109
|
-
return unless p
|
110
|
-
|
111
|
-
p.discard!
|
112
|
-
|
113
|
-
# All connections in the pool (even if they're currently
|
114
|
-
# leased!) have just been discarded, along with the pool itself.
|
115
|
-
# Any further interaction with the pool (except #pool_config and #schema_cache)
|
116
|
-
# is undefined.
|
117
|
-
# Remove the connection for the given key so a new one can be created in its place
|
118
|
-
_connection_pools.delete(_pool_key)
|
119
|
-
end
|
120
|
-
|
121
|
-
private
|
122
|
-
|
123
|
-
def _connection_pools
|
124
|
-
@@_connection_pools ||= {}
|
125
|
-
end
|
126
|
-
|
127
|
-
def _pool_key
|
128
|
-
@_pool_key ||= "#{@config[:host]}/#{@config[:port]}/#{@config[:socket]}/" \
|
129
|
-
"#{@config[:username]}/#{replica_configuration? && 'replica'}"
|
130
|
-
end
|
131
|
-
|
132
|
-
def _connection_pool(auto_create = true)
|
133
|
-
pool = _connection_pools[_pool_key]
|
134
|
-
if pool.nil? && auto_create
|
135
|
-
pool = _connection_pools[_pool_key] = ActiveRecord::ConnectionAdapters::ConnectionPool.new(@pool_config)
|
136
|
-
end
|
137
|
-
pool
|
138
|
-
end
|
139
|
-
|
140
|
-
def _connection_proxy_for(connection, database)
|
141
|
-
@connection_proxy_cache ||= {}
|
142
|
-
key = [connection, database]
|
143
|
-
|
144
|
-
@connection_proxy_cache[key] ||= begin
|
145
|
-
cx = ActiveRecordHostPool::ConnectionProxy.new(connection, database)
|
146
|
-
cx.execute('select 1')
|
147
|
-
cx
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
def _clear_connection_proxy_cache
|
152
|
-
@connection_proxy_cache = {}
|
153
|
-
end
|
154
|
-
|
155
|
-
def replica_configuration?
|
156
|
-
@config[:replica] || @config[:slave]
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
@@ -1,156 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# For versions of Rails < 6.1
|
4
|
-
|
5
|
-
require 'delegate'
|
6
|
-
require 'active_record'
|
7
|
-
require 'active_record_host_pool/connection_adapter_mixin'
|
8
|
-
|
9
|
-
# this module sits in between ConnectionHandler and a bunch of different ConnectionPools (one per host).
|
10
|
-
# when a connection is requested, it goes like:
|
11
|
-
# ActiveRecordClass -> ConnectionHandler#connection
|
12
|
-
# ConnectionHandler#connection -> (find or create PoolProxy)
|
13
|
-
# PoolProxy -> shared list of Pools
|
14
|
-
# Pool actually gives back a connection, then PoolProxy turns this
|
15
|
-
# into a ConnectionProxy that can inform (on execute) which db we should be on.
|
16
|
-
|
17
|
-
module ActiveRecordHostPool
|
18
|
-
# Sits between ConnectionHandler and a bunch of different ConnectionPools (one per host).
|
19
|
-
class PoolProxy < Delegator
|
20
|
-
def initialize(spec)
|
21
|
-
super(spec)
|
22
|
-
@spec = spec
|
23
|
-
@config = spec.config
|
24
|
-
end
|
25
|
-
|
26
|
-
def __getobj__
|
27
|
-
_connection_pool
|
28
|
-
end
|
29
|
-
|
30
|
-
def __setobj__(spec)
|
31
|
-
@spec = spec
|
32
|
-
@config = spec.config
|
33
|
-
@_pool_key = nil
|
34
|
-
end
|
35
|
-
|
36
|
-
attr_reader :spec
|
37
|
-
|
38
|
-
def connection(*args)
|
39
|
-
real_connection = _unproxied_connection(*args)
|
40
|
-
_connection_proxy_for(real_connection, @config[:database])
|
41
|
-
rescue Mysql2::Error, ActiveRecord::NoDatabaseError
|
42
|
-
_connection_pools.delete(_pool_key)
|
43
|
-
Kernel.raise
|
44
|
-
end
|
45
|
-
|
46
|
-
def _unproxied_connection(*args)
|
47
|
-
_connection_pool.connection(*args)
|
48
|
-
end
|
49
|
-
|
50
|
-
# by the time we are patched into ActiveRecord, the current thread has already established
|
51
|
-
# a connection. thus we need to patch both connection and checkout/checkin
|
52
|
-
def checkout(*args, &block)
|
53
|
-
cx = _connection_pool.checkout(*args, &block)
|
54
|
-
_connection_proxy_for(cx, @config[:database])
|
55
|
-
end
|
56
|
-
|
57
|
-
def checkin(cx)
|
58
|
-
cx = cx.unproxied
|
59
|
-
_connection_pool.checkin(cx)
|
60
|
-
end
|
61
|
-
|
62
|
-
def with_connection
|
63
|
-
cx = checkout
|
64
|
-
yield cx
|
65
|
-
ensure
|
66
|
-
checkin cx
|
67
|
-
end
|
68
|
-
|
69
|
-
def disconnect!
|
70
|
-
p = _connection_pool(false)
|
71
|
-
return unless p
|
72
|
-
|
73
|
-
p.disconnect!
|
74
|
-
p.automatic_reconnect = true
|
75
|
-
_clear_connection_proxy_cache
|
76
|
-
end
|
77
|
-
|
78
|
-
def automatic_reconnect=(value)
|
79
|
-
p = _connection_pool(false)
|
80
|
-
return unless p
|
81
|
-
|
82
|
-
p.automatic_reconnect = value if p.respond_to?(:automatic_reconnect=)
|
83
|
-
end
|
84
|
-
|
85
|
-
def clear_reloadable_connections!
|
86
|
-
_connection_pool.clear_reloadable_connections!
|
87
|
-
_clear_connection_proxy_cache
|
88
|
-
end
|
89
|
-
|
90
|
-
def release_connection(*args)
|
91
|
-
p = _connection_pool(false)
|
92
|
-
return unless p
|
93
|
-
|
94
|
-
p.release_connection(*args)
|
95
|
-
end
|
96
|
-
|
97
|
-
def flush!
|
98
|
-
p = _connection_pool(false)
|
99
|
-
return unless p
|
100
|
-
|
101
|
-
p.flush!
|
102
|
-
end
|
103
|
-
|
104
|
-
def discard!
|
105
|
-
p = _connection_pool(false)
|
106
|
-
return unless p
|
107
|
-
|
108
|
-
p.discard!
|
109
|
-
|
110
|
-
# All connections in the pool (even if they're currently
|
111
|
-
# leased!) have just been discarded, along with the pool itself.
|
112
|
-
# Any further interaction with the pool (except #spec and #schema_cache)
|
113
|
-
# is undefined.
|
114
|
-
# Remove the connection for the given key so a new one can be created in its place
|
115
|
-
_connection_pools.delete(_pool_key)
|
116
|
-
end
|
117
|
-
|
118
|
-
private
|
119
|
-
|
120
|
-
def _connection_pools
|
121
|
-
@@_connection_pools ||= {}
|
122
|
-
end
|
123
|
-
|
124
|
-
def _pool_key
|
125
|
-
@_pool_key ||= "#{@config[:host]}/#{@config[:port]}/#{@config[:socket]}/" \
|
126
|
-
"#{@config[:username]}/#{replica_configuration? && 'replica'}"
|
127
|
-
end
|
128
|
-
|
129
|
-
def _connection_pool(auto_create = true)
|
130
|
-
pool = _connection_pools[_pool_key]
|
131
|
-
if pool.nil? && auto_create
|
132
|
-
pool = _connection_pools[_pool_key] = ActiveRecord::ConnectionAdapters::ConnectionPool.new(@spec)
|
133
|
-
end
|
134
|
-
pool
|
135
|
-
end
|
136
|
-
|
137
|
-
def _connection_proxy_for(connection, database)
|
138
|
-
@connection_proxy_cache ||= {}
|
139
|
-
key = [connection, database]
|
140
|
-
|
141
|
-
@connection_proxy_cache[key] ||= begin
|
142
|
-
cx = ActiveRecordHostPool::ConnectionProxy.new(connection, database)
|
143
|
-
cx.execute('select 1')
|
144
|
-
cx
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
def _clear_connection_proxy_cache
|
149
|
-
@connection_proxy_cache = {}
|
150
|
-
end
|
151
|
-
|
152
|
-
def replica_configuration?
|
153
|
-
@config[:replica] || @config[:slave]
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
data/test/database.yml
DELETED
@@ -1,108 +0,0 @@
|
|
1
|
-
<% mysql = URI(ENV['MYSQL_URL'] || 'mysql://root@127.0.0.1:3306') %>
|
2
|
-
# ARHP creates separate connection pools based on the pool key.
|
3
|
-
# The pool key is defined as:
|
4
|
-
# host / port / socket / username / replica
|
5
|
-
#
|
6
|
-
# Therefore two databases with identical host, port, socket, username, and replica status will share a connection pool.
|
7
|
-
# If any part (host, port, etc.) of the pool key differ, two databases will _not_ share a connection pool.
|
8
|
-
#
|
9
|
-
# Below, "test_pool_1..." and "test_pool_2..." have identical host, username, socket, and replica status but the port information differs.
|
10
|
-
# Here the yml configurations are reformatted as a table to give a visual example:
|
11
|
-
#
|
12
|
-
# |----------+----------------+----------------|
|
13
|
-
# | | test_pool_1 | test_pool_2 |
|
14
|
-
# |----------+----------------+----------------+
|
15
|
-
# | host | 127.0.0.1 | 127.0.0.1 |
|
16
|
-
# | port | | 3306 |
|
17
|
-
# | socket | | |
|
18
|
-
# | username | root | root |
|
19
|
-
# | replica | false | false |
|
20
|
-
# |----------+----------------+----------------|
|
21
|
-
#
|
22
|
-
# Note: The configuration items must be explicitly defined or will be blank in the pool key.
|
23
|
-
# Configurations with matching _implicit_ items but differing _explicit_ items will create separate pools.
|
24
|
-
# e.g. "test_pool_1" will default to port 3306 but because it is not explicitly defined it will not share a pool with test_pool_2
|
25
|
-
#
|
26
|
-
# ARHP will therefore create the following pool keys:
|
27
|
-
# test_pool_1 => 127.0.0.1///root/false
|
28
|
-
# test_pool_2 => 127.0.0.1/3306//root/false
|
29
|
-
|
30
|
-
test_pool_1_db_a:
|
31
|
-
adapter: mysql2
|
32
|
-
encoding: utf8
|
33
|
-
database: arhp_test_db_a
|
34
|
-
username: <%= mysql.user %>
|
35
|
-
password: <%= mysql.password %>
|
36
|
-
host: <%= mysql.host %>
|
37
|
-
reconnect: true
|
38
|
-
|
39
|
-
# Mimic configurations as read by active_record_shards/ar_flexmaster
|
40
|
-
test_pool_1_db_a_replica:
|
41
|
-
adapter: mysql2
|
42
|
-
encoding: utf8
|
43
|
-
database: arhp_test_db_a_replica
|
44
|
-
username: <%= mysql.user %>
|
45
|
-
password: <%= mysql.password %>
|
46
|
-
host: <%= mysql.host %>
|
47
|
-
reconnect: true
|
48
|
-
slave: true
|
49
|
-
|
50
|
-
test_pool_1_db_b:
|
51
|
-
adapter: mysql2
|
52
|
-
encoding: utf8
|
53
|
-
database: arhp_test_db_b
|
54
|
-
username: <%= mysql.user %>
|
55
|
-
password: <%= mysql.password %>
|
56
|
-
host: <%= mysql.host %>
|
57
|
-
reconnect: true
|
58
|
-
|
59
|
-
test_pool_1_db_not_there:
|
60
|
-
adapter: mysql2
|
61
|
-
encoding: utf8
|
62
|
-
database: arhp_test_db_not_there
|
63
|
-
username: <%= mysql.user %>
|
64
|
-
password: <%= mysql.password %>
|
65
|
-
host: <%= mysql.host %>
|
66
|
-
reconnect: true
|
67
|
-
|
68
|
-
test_pool_2_db_d:
|
69
|
-
adapter: mysql2
|
70
|
-
encoding: utf8
|
71
|
-
database: arhp_test_db_d
|
72
|
-
username: <%= mysql.user %>
|
73
|
-
password: <%= mysql.password %>
|
74
|
-
host: <%= mysql.host %>
|
75
|
-
port: <%= mysql.port %>
|
76
|
-
reconnect: true
|
77
|
-
|
78
|
-
test_pool_2_db_e:
|
79
|
-
adapter: mysql2
|
80
|
-
encoding: utf8
|
81
|
-
database: arhp_test_db_e
|
82
|
-
username: <%= mysql.user %>
|
83
|
-
password: <%= mysql.password %>
|
84
|
-
host: <%= mysql.host %>
|
85
|
-
port: <%= mysql.port %>
|
86
|
-
reconnect: true
|
87
|
-
|
88
|
-
test_pool_3_db_e:
|
89
|
-
adapter: mysql2
|
90
|
-
encoding: utf8
|
91
|
-
database: arhp_test_db_e
|
92
|
-
username: john-doe
|
93
|
-
password:
|
94
|
-
host: <%= mysql.host %>
|
95
|
-
port: <%= mysql.port %>
|
96
|
-
reconnect: true
|
97
|
-
|
98
|
-
# test_pool_1_db_c needs to be the last database defined in the file
|
99
|
-
# otherwise the test_models_with_matching_hosts_and_non_matching_databases_issue_exists_without_arhp_patch
|
100
|
-
# test fails
|
101
|
-
test_pool_1_db_c:
|
102
|
-
adapter: mysql2
|
103
|
-
encoding: utf8
|
104
|
-
database: arhp_test_db_c
|
105
|
-
username: <%= mysql.user %>
|
106
|
-
password: <%= mysql.password %>
|
107
|
-
host: <%= mysql.host %>
|
108
|
-
reconnect: true
|
data/test/helper.rb
DELETED
@@ -1,198 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'bundler/setup'
|
4
|
-
require 'minitest/autorun'
|
5
|
-
require 'pry-byebug'
|
6
|
-
|
7
|
-
require 'active_record_host_pool'
|
8
|
-
require 'logger'
|
9
|
-
require 'minitest/mock_expectations'
|
10
|
-
require 'phenix'
|
11
|
-
|
12
|
-
ENV['RAILS_ENV'] = 'test'
|
13
|
-
ENV['LEGACY_CONNECTION_HANDLING'] = 'true' if ENV['LEGACY_CONNECTION_HANDLING'].nil?
|
14
|
-
|
15
|
-
if ActiveRecord.version >= Gem::Version.new('6.1')
|
16
|
-
ActiveRecord::Base.legacy_connection_handling = (ENV['LEGACY_CONNECTION_HANDLING'] == 'true')
|
17
|
-
end
|
18
|
-
|
19
|
-
RAILS_6_1_WITH_NON_LEGACY_CONNECTION_HANDLING =
|
20
|
-
ActiveRecord.version >= Gem::Version.new('6.1') && !ActiveRecord::Base.legacy_connection_handling
|
21
|
-
|
22
|
-
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + '/test.log')
|
23
|
-
|
24
|
-
Thread.abort_on_exception = true
|
25
|
-
|
26
|
-
# BEGIN preventing_writes? patch
|
27
|
-
## Rails 6.1 by default does not allow writing to replica databases which prevents
|
28
|
-
## us from properly setting up the test databases. This patch is used in test/schema.rb
|
29
|
-
## to allow us to write to the replicas but only during migrations
|
30
|
-
module ActiveRecordHostPool
|
31
|
-
cattr_accessor :allowing_writes
|
32
|
-
module PreventWritesPatch
|
33
|
-
def preventing_writes?
|
34
|
-
return false if ActiveRecordHostPool.allowing_writes && replica?
|
35
|
-
|
36
|
-
super
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
if ActiveRecord.version >= Gem::Version.new('6.1')
|
42
|
-
ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend ActiveRecordHostPool::PreventWritesPatch
|
43
|
-
end
|
44
|
-
# END preventing_writes? patch
|
45
|
-
|
46
|
-
Phenix.configure do |config|
|
47
|
-
config.skip_database = ->(name, conf) { name =~ /not_there/ || conf['username'] == 'john-doe' }
|
48
|
-
end
|
49
|
-
|
50
|
-
module ARHPTestSetup
|
51
|
-
private
|
52
|
-
|
53
|
-
def arhp_create_models
|
54
|
-
return if ARHPTestSetup.const_defined?('Pool1DbA')
|
55
|
-
|
56
|
-
if RAILS_6_1_WITH_NON_LEGACY_CONNECTION_HANDLING
|
57
|
-
eval <<-RUBY
|
58
|
-
class AbstractPool1DbC < ActiveRecord::Base
|
59
|
-
self.abstract_class = true
|
60
|
-
connects_to database: { writing: :test_pool_1_db_c }
|
61
|
-
end
|
62
|
-
|
63
|
-
# The placement of the Pool1DbC class is important so that its
|
64
|
-
# connection will not be the most recent connection established
|
65
|
-
# for test_pool_1.
|
66
|
-
class Pool1DbC < AbstractPool1DbC
|
67
|
-
end
|
68
|
-
|
69
|
-
class AbstractPool1DbA < ActiveRecord::Base
|
70
|
-
self.abstract_class = true
|
71
|
-
connects_to database: { writing: :test_pool_1_db_a, reading: :test_pool_1_db_a_replica }
|
72
|
-
end
|
73
|
-
|
74
|
-
class Pool1DbA < AbstractPool1DbA
|
75
|
-
self.table_name = "tests"
|
76
|
-
end
|
77
|
-
|
78
|
-
class AbstractPool1DbB < ActiveRecord::Base
|
79
|
-
self.abstract_class = true
|
80
|
-
connects_to database: { writing: :test_pool_1_db_b }
|
81
|
-
end
|
82
|
-
|
83
|
-
class Pool1DbB < AbstractPool1DbB
|
84
|
-
self.table_name = "tests"
|
85
|
-
end
|
86
|
-
|
87
|
-
class AbstractPool2DbD < ActiveRecord::Base
|
88
|
-
self.abstract_class = true
|
89
|
-
connects_to database: { writing: :test_pool_2_db_d }
|
90
|
-
end
|
91
|
-
|
92
|
-
class Pool2DbD < AbstractPool2DbD
|
93
|
-
self.table_name = "tests"
|
94
|
-
end
|
95
|
-
|
96
|
-
class AbstractPool2DbE < ActiveRecord::Base
|
97
|
-
self.abstract_class = true
|
98
|
-
connects_to database: { writing: :test_pool_2_db_e }
|
99
|
-
end
|
100
|
-
|
101
|
-
class Pool2DbE < AbstractPool2DbE
|
102
|
-
self.table_name = "tests"
|
103
|
-
end
|
104
|
-
|
105
|
-
class AbstractPool3DbE < ActiveRecord::Base
|
106
|
-
self.abstract_class = true
|
107
|
-
connects_to database: { writing: :test_pool_3_db_e }
|
108
|
-
end
|
109
|
-
|
110
|
-
class Pool3DbE < AbstractPool3DbE
|
111
|
-
self.table_name = "tests"
|
112
|
-
end
|
113
|
-
|
114
|
-
# Test ARHP with Rails 6.1+ horizontal sharding functionality
|
115
|
-
class AbstractShardedModel < ActiveRecord::Base
|
116
|
-
self.abstract_class = true
|
117
|
-
connects_to shards: {
|
118
|
-
default: { writing: :test_pool_1_db_shard_a },
|
119
|
-
shard_b: { writing: :test_pool_1_db_shard_b, reading: :test_pool_1_db_shard_b_replica },
|
120
|
-
shard_c: { writing: :test_pool_1_db_shard_c, reading: :test_pool_1_db_shard_c_replica },
|
121
|
-
shard_d: { writing: :test_pool_2_db_shard_d, reading: :test_pool_2_db_shard_d_replica }
|
122
|
-
}
|
123
|
-
end
|
124
|
-
|
125
|
-
class ShardedModel < AbstractShardedModel
|
126
|
-
self.table_name = "tests"
|
127
|
-
end
|
128
|
-
RUBY
|
129
|
-
else
|
130
|
-
eval <<-RUBY
|
131
|
-
# The placement of the Pool1DbC class is important so that its
|
132
|
-
# connection will not be the most recent connection established
|
133
|
-
# for test_pool_1.
|
134
|
-
class Pool1DbC < ActiveRecord::Base
|
135
|
-
establish_connection(:test_pool_1_db_c)
|
136
|
-
end
|
137
|
-
|
138
|
-
class Pool1DbA < ActiveRecord::Base
|
139
|
-
self.table_name = "tests"
|
140
|
-
establish_connection(:test_pool_1_db_a)
|
141
|
-
end
|
142
|
-
|
143
|
-
class Pool1DbAReplica < ActiveRecord::Base
|
144
|
-
self.table_name = "tests"
|
145
|
-
establish_connection(:test_pool_1_db_a_replica)
|
146
|
-
end
|
147
|
-
|
148
|
-
class Pool1DbB < ActiveRecord::Base
|
149
|
-
self.table_name = "tests"
|
150
|
-
establish_connection(:test_pool_1_db_b)
|
151
|
-
end
|
152
|
-
|
153
|
-
class Pool2DbD < ActiveRecord::Base
|
154
|
-
self.table_name = "tests"
|
155
|
-
establish_connection(:test_pool_2_db_d)
|
156
|
-
end
|
157
|
-
|
158
|
-
class Pool2DbE < ActiveRecord::Base
|
159
|
-
self.table_name = "tests"
|
160
|
-
establish_connection(:test_pool_2_db_e)
|
161
|
-
end
|
162
|
-
|
163
|
-
class Pool3DbE < ActiveRecord::Base
|
164
|
-
self.table_name = "tests"
|
165
|
-
establish_connection(:test_pool_3_db_e)
|
166
|
-
end
|
167
|
-
RUBY
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
def current_database(klass)
|
172
|
-
klass.connection.select_value('select DATABASE()')
|
173
|
-
end
|
174
|
-
|
175
|
-
# Remove a method from a given module that fixes something.
|
176
|
-
# Execute the passed in block.
|
177
|
-
# Re-add the method back to the module.
|
178
|
-
def without_module_patch(mod, method_name)
|
179
|
-
method_body = mod.instance_method(method_name)
|
180
|
-
mod.remove_method(method_name)
|
181
|
-
yield if block_given?
|
182
|
-
ensure
|
183
|
-
mod.define_method(method_name, method_body)
|
184
|
-
end
|
185
|
-
|
186
|
-
def simulate_rails_app_active_record_railties
|
187
|
-
if ActiveRecord.version >= Gem::Version.new('6.0') && !RAILS_6_1_WITH_NON_LEGACY_CONNECTION_HANDLING
|
188
|
-
# Necessary for testing ActiveRecord 6.0 which uses the connection
|
189
|
-
# handlers when clearing query caches across all handlers when
|
190
|
-
# an operation that dirties the cache is involved (e.g. create/insert,
|
191
|
-
# update, delete/destroy, truncate, etc.)
|
192
|
-
# In Rails 6.1 this is only present when legacy_connection_handling=true
|
193
|
-
ActiveRecord::Base.connection_handlers = {
|
194
|
-
ActiveRecord::Base.writing_role => ActiveRecord::Base.default_connection_handler
|
195
|
-
}
|
196
|
-
end
|
197
|
-
end
|
198
|
-
end
|
data/test/schema.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'helper'
|
4
|
-
|
5
|
-
begin
|
6
|
-
ActiveRecordHostPool.allowing_writes = true
|
7
|
-
|
8
|
-
ActiveRecord::Schema.define(version: 1) do
|
9
|
-
create_table 'tests', force: true do |t|
|
10
|
-
t.string 'val'
|
11
|
-
end
|
12
|
-
|
13
|
-
# Add a table only the shard database will have. Conditional
|
14
|
-
# exists since Phenix loads the schema file for every database.
|
15
|
-
if ActiveRecord::Base.connection.current_database == 'arhp_test_db_c'
|
16
|
-
create_table 'pool1_db_cs' do |t|
|
17
|
-
t.string 'name'
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
ensure
|
22
|
-
ActiveRecordHostPool.allowing_writes = false
|
23
|
-
end
|