active_record_host_pool 0.11.0 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '018bdc07dc5428ae890589b96fbf3748d46ef5abc2625c61278ac50ffa079923'
4
- data.tar.gz: 101a032eba4e5628106fd4ae8a83c66f52bb68d89f0baad5180ec41c83a932ab
3
+ metadata.gz: 5eb359cbe2626874bf6836ee5b5b1826ccf02fef7b474f81f5eec273435232d0
4
+ data.tar.gz: 40383e9f54da681dd340b7dba75aac65613a34fd083862bf96a905f766c9f7ea
5
5
  SHA512:
6
- metadata.gz: 1ea8bf1c7704be6d72c97be06dfc084a2a66aa0e64848fcabcbbbcb6c73b360517f9737eac8d0e6459e8b6c85d236d36ea7f2a8bf8ed697a46c9d2eb0eca5d3a
7
- data.tar.gz: 6eeb13fbd13582106e01bd8fc8a560eeef690d4c375926d17ebccae96b385ca8d4d3e2a75372fc99efad09e7e1bc90e06fc4417d21b3e5deada1ee940f8e347a
6
+ metadata.gz: 84d8eaafd2a6ebd88a42e89c8a52113b1ae3654123bf6d6735256e4787563862024af288b9755c254d445151c43d35502f52608e468beed6daad309225fb4480
7
+ data.tar.gz: 9a6493969b78849cea22220f562371e25a8215015ee747c5bf8ca533dfca4db21a637336c127c7abc50e6eee7d31843dd75b14fcbe7f329e461e2a24dc0fbd35
data/Readme.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Build Status](https://travis-ci.org/zendesk/active_record_host_pool.svg?branch=master)](https://travis-ci.org/zendesk/active_record_host_pool)
1
+ [![Build Status](https://github.com/zendesk/active_record_host_pool/workflows/CI/badge.svg)](https://github.com/zendesk/active_record_host_pool/actions?query=workflow%3ACI)
2
2
 
3
3
  # ActiveRecord host pooling
4
4
 
@@ -17,13 +17,21 @@ Postgres, from an informal reading of the docs, will never support the concept o
17
17
  and make sure to require 'active\_record\_host\_pool' in some way.
18
18
 
19
19
  ## Testing
20
- You need a local user called 'travis'.
20
+ You need a local user called 'john-doe'.
21
21
 
22
22
  mysql -uroot
23
- CREATE USER 'travis'@'localhost';
24
- GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,INDEX ON *.* TO 'travis'@'localhost';
23
+ CREATE USER 'john-doe'@'localhost';
24
+ GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,INDEX ON *.* TO 'john-doe'@'localhost';
25
25
  FLUSH PRIVILEGES;
26
26
 
27
+ With mysql running locally, run
28
+
29
+ BUNDLE_GEMFILE=gemfiles/rails5.2.gemfile bundle exec rake test
30
+
31
+ Or
32
+
33
+ BUNDLE_GEMFILE=gemfiles/rails5.2.gemfile ruby test/test_arhp.rb --seed 19911 --verbose
34
+
27
35
  ## Copyright
28
36
 
29
37
  Copyright (c) 2011 Zendesk. See MIT-LICENSE for details.
@@ -4,6 +4,7 @@ require 'active_record'
4
4
  require 'active_record/base'
5
5
  require 'active_record/connection_adapters/abstract_adapter'
6
6
 
7
+ require 'active_record_host_pool/clear_query_cache_patch'
7
8
  require 'active_record_host_pool/connection_proxy'
8
9
  require 'active_record_host_pool/pool_proxy'
9
10
  require 'active_record_host_pool/connection_adapter_mixin'
@@ -85,46 +85,36 @@ module ActiveRecordHostPool
85
85
  end
86
86
  end
87
87
 
88
+ # rubocop:disable Lint/DuplicateMethods
88
89
  module ActiveRecord
89
90
  module ConnectionAdapters
90
91
  class ConnectionHandler
91
- if ActiveRecord::VERSION::MAJOR == 5
92
- if ActiveRecord::VERSION::MINOR == 0
93
- def establish_connection(spec)
94
- owner_to_pool[spec.name] = ActiveRecordHostPool::PoolProxy.new(spec)
95
- end
96
- else
97
- def establish_connection(spec)
98
- resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(Base.configurations)
99
- spec = resolver.spec(spec)
100
-
101
- owner_to_pool[spec.name] = ActiveRecordHostPool::PoolProxy.new(spec)
102
- end
92
+ case "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
93
+ when '5.1', '5.2', '6.0'
94
+
95
+ def establish_connection(spec)
96
+ resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(Base.configurations)
97
+ spec = resolver.spec(spec)
98
+
99
+ owner_to_pool[spec.name] = ActiveRecordHostPool::PoolProxy.new(spec)
103
100
  end
104
101
 
105
- elsif ActiveRecord::VERSION::MAJOR == 4
102
+ when '4.2'
106
103
 
107
104
  def establish_connection(owner, spec)
108
105
  @class_to_pool.clear
109
106
  raise "Anonymous class is not allowed." unless owner.name
110
- owner_to_pool[owner.name] = ActiveRecordHostPool::PoolProxy.new(spec)
111
- end
112
-
113
- elsif ActiveRecord::VERSION::MAJOR == 3
114
107
 
115
- def establish_connection(owner, spec)
116
- @connection_pools[spec] ||= ActiveRecordHostPool::PoolProxy.new(spec)
117
- @class_to_pool[owner] = @connection_pools[spec]
108
+ owner_to_pool[owner.name] = ActiveRecordHostPool::PoolProxy.new(spec)
118
109
  end
119
110
 
120
111
  else
121
112
 
122
- def establish_connection(owner, spec)
123
- @connection_pools[owner] = ActiveRecordHostPool::PoolProxy.new(spec)
124
- end
113
+ raise "Unsupported version of Rails (v#{ActiveRecord::VERSION::STRING})"
125
114
  end
126
115
  end
127
116
  end
128
117
  end
118
+ # rubocop:enable Lint/DuplicateMethods
129
119
 
130
120
  ActiveRecord::ConnectionAdapters::Mysql2Adapter.include(ActiveRecordHostPool::DatabaseSwitch)
@@ -66,6 +66,7 @@ module ActiveRecordHostPool
66
66
  def disconnect!
67
67
  p = _connection_pool(false)
68
68
  return unless p
69
+
69
70
  p.disconnect!
70
71
  p.automatic_reconnect = true if p.respond_to?(:automatic_reconnect=)
71
72
  _clear_connection_proxy_cache
@@ -74,6 +75,7 @@ module ActiveRecordHostPool
74
75
  def automatic_reconnect=(value)
75
76
  p = _connection_pool(false)
76
77
  return unless p
78
+
77
79
  p.automatic_reconnect = value if p.respond_to?(:automatic_reconnect=)
78
80
  end
79
81
 
@@ -82,6 +84,34 @@ module ActiveRecordHostPool
82
84
  _clear_connection_proxy_cache
83
85
  end
84
86
 
87
+ def release_connection(*args)
88
+ p = _connection_pool(false)
89
+ return unless p
90
+
91
+ p.release_connection(*args)
92
+ end
93
+
94
+ def flush!
95
+ p = _connection_pool(false)
96
+ return unless p
97
+
98
+ p.flush!
99
+ end
100
+
101
+ def discard!
102
+ p = _connection_pool(false)
103
+ return unless p
104
+
105
+ p.discard!
106
+
107
+ # All connections in the pool (even if they're currently
108
+ # leased!) have just been discarded, along with the pool itself.
109
+ # Any further interaction with the pool (except #spec and #schema_cache)
110
+ # is undefined.
111
+ # Remove the connection for the given key so a new one can be created in its place
112
+ _connection_pools.delete(_pool_key)
113
+ end
114
+
85
115
  private
86
116
 
87
117
  def rescuable_errors
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecordHostPool
4
- VERSION = "0.11.0"
4
+ VERSION = "1.0.2"
5
5
  end
data/test/database.yml CHANGED
@@ -52,7 +52,7 @@ test_host_2_db_5:
52
52
  adapter: mysql2
53
53
  encoding: utf8
54
54
  database: arhp_test_4
55
- username: travis
55
+ username: john-doe
56
56
  password:
57
57
  host: <%= mysql.host %>
58
58
  port: <%= mysql.port %>
@@ -66,3 +66,12 @@ test_host_1_db_not_there:
66
66
  password: <%= mysql.password %>
67
67
  host: <%= mysql.host %>
68
68
  reconnect: true
69
+
70
+ test_host_1_db_shard:
71
+ adapter: mysql2
72
+ encoding: utf8
73
+ database: arhp_test_1_shard
74
+ username: <%= mysql.user %>
75
+ password: <%= mysql.password %>
76
+ host: <%= mysql.host %>
77
+ reconnect: true
data/test/helper.rb CHANGED
@@ -15,15 +15,23 @@ Minitest::Test = MiniTest::Unit::TestCase unless defined?(::Minitest::Test)
15
15
  ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + '/test.log')
16
16
 
17
17
  Phenix.configure do |config|
18
- config.skip_database = ->(name, conf) { name =~ /not_there/ || conf['username'] == 'travis' }
18
+ config.skip_database = ->(name, conf) { name =~ /not_there/ || conf['username'] == 'john-doe' }
19
19
  end
20
20
 
21
21
  module ARHPTestSetup
22
22
  private
23
23
 
24
24
  def arhp_create_models
25
- return if Object.const_defined?('Test1')
25
+ return if ARHPTestSetup.const_defined?('Test1')
26
+
26
27
  eval <<-RUBY
28
+ # The placement of the Test1Shard class is important so that its
29
+ # connection will not be the most recent connection established
30
+ # for test_host_1.
31
+ class Test1Shard < ::ActiveRecord::Base
32
+ establish_connection(:test_host_1_db_shard)
33
+ end
34
+
27
35
  class Test1 < ActiveRecord::Base
28
36
  self.table_name = "tests"
29
37
  establish_connection(:test_host_1_db_1)
@@ -59,4 +67,15 @@ module ARHPTestSetup
59
67
  def current_database(klass)
60
68
  klass.connection.select_value('select DATABASE()')
61
69
  end
70
+
71
+ # Remove a method from a given module that fixes something.
72
+ # Execute the passed in block.
73
+ # Re-add the method back to the module.
74
+ def without_module_patch(mod, method_name)
75
+ method_body = mod.instance_method(method_name)
76
+ mod.remove_method(method_name)
77
+ yield if block_given?
78
+ ensure
79
+ mod.define_method(method_name, method_body)
80
+ end
62
81
  end
data/test/schema.rb CHANGED
@@ -2,7 +2,15 @@
2
2
 
3
3
  require_relative 'helper'
4
4
  ActiveRecord::Schema.define(version: 1) do
5
- create_table 'tests' do |t|
5
+ create_table 'tests', force: true do |t|
6
6
  t.string 'val'
7
7
  end
8
+
9
+ # Add a table only the shard database will have. Conditional
10
+ # exists since Phenix loads the schema file for every database.
11
+ if ActiveRecord::Base.connection.current_database == 'arhp_test_1_shard'
12
+ create_table 'test1_shards' do |t|
13
+ t.string 'name'
14
+ end
15
+ end
8
16
  end
data/test/test_arhp.rb CHANGED
@@ -13,6 +13,23 @@ class ActiveRecordHostPoolTest < Minitest::Test
13
13
  Phenix.burn!
14
14
  end
15
15
 
16
+ def test_process_forking_with_connections
17
+ # Ensure we have a connection already
18
+ assert_equal(true, ActiveRecord::Base.connected?)
19
+
20
+ # Verify that when we fork, the process doesn't crash
21
+ pid = Process.fork do
22
+ if ActiveRecord.version >= Gem::Version.new('5.2')
23
+ assert_equal(false, ActiveRecord::Base.connected?) # New to Rails 5.2
24
+ else
25
+ assert_equal(true, ActiveRecord::Base.connected?)
26
+ end
27
+ end
28
+ Process.wait(pid)
29
+ # Cleanup any connections we may have left around
30
+ ActiveRecord::Base.connection_handler.clear_all_connections!
31
+ end
32
+
16
33
  def test_models_with_matching_hosts_should_share_a_connection
17
34
  assert_equal(Test1.connection.raw_connection, Test2.connection.raw_connection)
18
35
  assert_equal(Test3.connection.raw_connection, Test4.connection.raw_connection)
@@ -38,6 +55,38 @@ class ActiveRecordHostPoolTest < Minitest::Test
38
55
  assert_action_uses_correct_database(:insert, "insert into tests values(NULL, 'foo')")
39
56
  end
40
57
 
58
+ def test_models_with_matching_hosts_and_non_matching_databases_should_share_a_connection
59
+ simulate_rails_app_active_record_railties
60
+ assert_equal(Test1.connection.raw_connection, Test1Shard.connection.raw_connection)
61
+ end
62
+
63
+ if ActiveRecord.version >= Gem::Version.new('6.0')
64
+ def test_models_with_matching_hosts_and_non_matching_databases_issue_exists_without_arhp_patch
65
+ simulate_rails_app_active_record_railties
66
+
67
+ # Remove patch that fixes an issue in Rails 6+ to ensure it still
68
+ # exists. If this begins to fail then it may mean that Rails has fixed
69
+ # the issue so that it no longer occurs.
70
+ without_module_patch(ActiveRecordHostPool::ClearQueryCachePatch, :clear_query_caches_for_current_thread) do
71
+ exception = assert_raises(ActiveRecord::StatementInvalid) do
72
+ ActiveRecord::Base.cache { Test1Shard.create! }
73
+ end
74
+
75
+ assert_equal("Mysql2::Error: Table 'arhp_test_2.test1_shards' doesn't exist", exception.message)
76
+ end
77
+ end
78
+
79
+ def test_models_with_matching_hosts_and_non_matching_databases_do_not_mix_up_underlying_database
80
+ simulate_rails_app_active_record_railties
81
+
82
+ # ActiveRecord 6.0 introduced a change that surfaced a problematic code
83
+ # path in active_record_host_pool when clearing caches across connection
84
+ # handlers which can cause the database to change.
85
+ # See ActiveRecordHostPool::ClearQueryCachePatch
86
+ ActiveRecord::Base.cache { Test1Shard.create! }
87
+ end
88
+ end
89
+
41
90
  def test_connection_returns_a_proxy
42
91
  assert_kind_of ActiveRecordHostPool::ConnectionProxy, Test1.connection
43
92
  end
@@ -146,14 +195,33 @@ class ActiveRecordHostPoolTest < Minitest::Test
146
195
  assert_equal expected_database, current_database(switch_to_klass)
147
196
  end
148
197
 
198
+ def test_release_connection
199
+ pool = ActiveRecord::Base.connection_pool
200
+ conn = pool.connection
201
+ pool.expects(:checkin).with(conn)
202
+ pool.release_connection
203
+ end
204
+
149
205
  private
150
206
 
151
207
  def assert_action_uses_correct_database(action, sql)
152
208
  (1..4).each do |i|
153
- klass = eval "Test#{i}"
209
+ klass = ARHPTestSetup.const_get("Test#{i}")
154
210
  desired_db = "arhp_test_#{i}"
155
211
  klass.connection.send(action, sql)
156
212
  assert_equal desired_db, current_database(klass)
157
213
  end
158
214
  end
215
+
216
+ def simulate_rails_app_active_record_railties
217
+ if ActiveRecord.version >= Gem::Version.new('6.0')
218
+ # Necessary for testing ActiveRecord 6.0 which uses the connection
219
+ # handlers when clearing query caches across all handlers when
220
+ # an operation that dirties the cache is involved (e.g. create/insert,
221
+ # update, delete/destroy, truncate, etc.)
222
+ ActiveRecord::Base.connection_handlers = {
223
+ ActiveRecord::Base.writing_role => ActiveRecord::Base.default_connection_handler
224
+ }
225
+ end
226
+ end
159
227
  end
metadata CHANGED
@@ -1,14 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record_host_pool
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
+ - Benjamin Quorning
8
+ - Gabe Martin-Dempesy
9
+ - Pierre Schambacher
7
10
  - Ben Osheroff
8
- autorequire:
11
+ autorequire:
9
12
  bindir: bin
10
13
  cert_chain: []
11
- date: 2018-04-24 00:00:00.000000000 Z
14
+ date: 2021-02-09 00:00:00.000000000 Z
12
15
  dependencies:
13
16
  - !ruby/object:Gem::Dependency
14
17
  name: activerecord
@@ -16,20 +19,20 @@ dependencies:
16
19
  requirements:
17
20
  - - ">="
18
21
  - !ruby/object:Gem::Version
19
- version: 3.2.0
22
+ version: 4.2.0
20
23
  - - "<"
21
24
  - !ruby/object:Gem::Version
22
- version: '6.0'
25
+ version: '6.1'
23
26
  type: :runtime
24
27
  prerelease: false
25
28
  version_requirements: !ruby/object:Gem::Requirement
26
29
  requirements:
27
30
  - - ">="
28
31
  - !ruby/object:Gem::Version
29
- version: 3.2.0
32
+ version: 4.2.0
30
33
  - - "<"
31
34
  - !ruby/object:Gem::Version
32
- version: '6.0'
35
+ version: '6.1'
33
36
  - !ruby/object:Gem::Dependency
34
37
  name: mysql2
35
38
  requirement: !ruby/object:Gem::Requirement
@@ -104,16 +107,16 @@ dependencies:
104
107
  name: rubocop
105
108
  requirement: !ruby/object:Gem::Requirement
106
109
  requirements:
107
- - - ">="
110
+ - - "~>"
108
111
  - !ruby/object:Gem::Version
109
- version: 0.55.0
112
+ version: 0.80.0
110
113
  type: :development
111
114
  prerelease: false
112
115
  version_requirements: !ruby/object:Gem::Requirement
113
116
  requirements:
114
- - - ">="
117
+ - - "~>"
115
118
  - !ruby/object:Gem::Version
116
- version: 0.55.0
119
+ version: 0.80.0
117
120
  - !ruby/object:Gem::Dependency
118
121
  name: shoulda
119
122
  requirement: !ruby/object:Gem::Requirement
@@ -128,23 +131,11 @@ dependencies:
128
131
  - - ">="
129
132
  - !ruby/object:Gem::Version
130
133
  version: '0'
131
- - !ruby/object:Gem::Dependency
132
- name: wwtd
133
- requirement: !ruby/object:Gem::Requirement
134
- requirements:
135
- - - ">="
136
- - !ruby/object:Gem::Version
137
- version: '0'
138
- type: :development
139
- prerelease: false
140
- version_requirements: !ruby/object:Gem::Requirement
141
- requirements:
142
- - - ">="
143
- - !ruby/object:Gem::Version
144
- version: '0'
145
134
  description: ''
146
135
  email:
147
- - ben@gimbo.net
136
+ - bquorning@zendesk.com
137
+ - gabe@zendesk.com
138
+ - pschambacher@zendesk.com
148
139
  executables: []
149
140
  extensions: []
150
141
  extra_rdoc_files:
@@ -166,7 +157,7 @@ homepage: https://github.com/zendesk/active_record_host_pool
166
157
  licenses:
167
158
  - MIT
168
159
  metadata: {}
169
- post_install_message:
160
+ post_install_message:
170
161
  rdoc_options: []
171
162
  require_paths:
172
163
  - lib
@@ -181,9 +172,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
181
172
  - !ruby/object:Gem::Version
182
173
  version: '0'
183
174
  requirements: []
184
- rubyforge_project:
185
- rubygems_version: 2.7.6
186
- signing_key:
175
+ rubygems_version: 3.2.2
176
+ signing_key:
187
177
  specification_version: 4
188
178
  summary: Allow ActiveRecord to share a connection to multiple databases on the same
189
179
  host