active_record_host_pool 1.2.4 → 2.0.0.pre.2

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/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
data/test/test_arhp.rb DELETED
@@ -1,186 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'helper'
4
-
5
- class ActiveRecordHostPoolTest < Minitest::Test
6
- include ARHPTestSetup
7
- def setup
8
- if RAILS_6_1_WITH_NON_LEGACY_CONNECTION_HANDLING
9
- Phenix.rise! config_path: 'test/three_tier_database.yml'
10
- else
11
- Phenix.rise!
12
- end
13
- arhp_create_models
14
- end
15
-
16
- def teardown
17
- ActiveRecord::Base.connection.disconnect!
18
- ActiveRecordHostPool::PoolProxy.class_variable_set(:@@_connection_pools, {})
19
- Phenix.burn!
20
- end
21
-
22
- def test_process_forking_with_connections
23
- # Ensure we have a connection already
24
- assert_equal(true, ActiveRecord::Base.connected?)
25
-
26
- # Verify that when we fork, the process doesn't crash
27
- pid = Process.fork do
28
- if ActiveRecord.version >= Gem::Version.new('5.2')
29
- assert_equal(false, ActiveRecord::Base.connected?) # New to Rails 5.2
30
- else
31
- assert_equal(true, ActiveRecord::Base.connected?)
32
- end
33
- end
34
- Process.wait(pid)
35
- # Cleanup any connections we may have left around
36
- ActiveRecord::Base.connection_handler.clear_all_connections!
37
- end
38
-
39
- def test_models_with_matching_hosts_ports_sockets_usernames_and_replica_status_should_share_a_connection
40
- assert_equal(Pool1DbA.connection.raw_connection, Pool1DbB.connection.raw_connection)
41
- assert_equal(Pool2DbD.connection.raw_connection, Pool2DbE.connection.raw_connection)
42
- end
43
-
44
- def test_models_with_different_ports_should_not_share_a_connection
45
- refute_equal(Pool1DbA.connection.raw_connection, Pool2DbD.connection.raw_connection)
46
- end
47
-
48
- def test_models_with_different_usernames_should_not_share_a_connection
49
- refute_equal(Pool2DbE.connection.raw_connection, Pool3DbE.connection.raw_connection)
50
- end
51
-
52
- def test_should_select_on_correct_database
53
- Pool1DbA.connection.send(:select_all, 'select 1')
54
- assert_equal 'arhp_test_db_a', current_database(Pool1DbA)
55
-
56
- Pool2DbD.connection.send(:select_all, 'select 1')
57
- assert_equal 'arhp_test_db_d', current_database(Pool2DbD)
58
-
59
- Pool3DbE.connection.send(:select_all, 'select 1')
60
- assert_equal 'arhp_test_db_e', current_database(Pool3DbE)
61
- end
62
-
63
- def test_should_insert_on_correct_database
64
- Pool1DbA.connection.send(:insert, "insert into tests values(NULL, 'foo')")
65
- assert_equal 'arhp_test_db_a', current_database(Pool1DbA)
66
-
67
- Pool2DbD.connection.send(:insert, "insert into tests values(NULL, 'foo')")
68
- assert_equal 'arhp_test_db_d', current_database(Pool2DbD)
69
-
70
- Pool3DbE.connection.send(:insert, "insert into tests values(NULL, 'foo')")
71
- assert_equal 'arhp_test_db_e', current_database(Pool3DbE)
72
- end
73
-
74
- def test_connection_returns_a_proxy
75
- assert_kind_of ActiveRecordHostPool::ConnectionProxy, Pool1DbA.connection
76
- end
77
-
78
- def test_connection_proxy_handles_private_methods
79
- # Relies on connection.class returning the real class
80
- Pool1DbA.connection.class.class_eval do
81
- private
82
-
83
- def test_private_method
84
- true
85
- end
86
- end
87
- assert Pool1DbA.connection.respond_to?(:test_private_method, true)
88
- refute Pool1DbA.connection.respond_to?(:test_private_method)
89
- assert_includes(Pool1DbA.connection.private_methods, :test_private_method)
90
- assert_equal true, Pool1DbA.connection.send(:test_private_method)
91
- end
92
-
93
- def test_object_creation
94
- Pool1DbA.create(val: 'foo')
95
- assert_equal('arhp_test_db_a', current_database(Pool1DbA))
96
-
97
- Pool2DbD.create(val: 'bar')
98
- assert_equal('arhp_test_db_a', current_database(Pool1DbA))
99
- assert_equal('arhp_test_db_d', current_database(Pool2DbD))
100
-
101
- Pool1DbB.create!(val: 'bar_distinct')
102
- assert_equal('arhp_test_db_b', current_database(Pool1DbB))
103
- assert Pool1DbB.find_by_val('bar_distinct')
104
- refute Pool1DbA.find_by_val('bar_distinct')
105
- end
106
-
107
- def test_disconnect
108
- Pool1DbA.create(val: 'foo')
109
- unproxied = Pool1DbA.connection.unproxied
110
- Pool1DbA.connection_handler.clear_all_connections!
111
- Pool1DbA.create(val: 'foo')
112
- assert(unproxied != Pool1DbA.connection.unproxied)
113
- end
114
-
115
- def test_checkout
116
- connection = ActiveRecord::Base.connection_pool.checkout
117
- assert_kind_of(ActiveRecordHostPool::ConnectionProxy, connection)
118
- ActiveRecord::Base.connection_pool.checkin(connection)
119
- c2 = ActiveRecord::Base.connection_pool.checkout
120
- assert(c2 == connection)
121
- end
122
-
123
- def test_no_switch_when_creating_db
124
- conn = Pool1DbA.connection
125
- assert_called(conn, :execute_without_switching) do
126
- refute_called(conn, :_switch_connection) do
127
- assert conn._host_pool_current_database
128
- conn.create_database(:some_args)
129
- end
130
- end
131
- end
132
-
133
- def test_no_switch_when_dropping_db
134
- conn = Pool1DbA.connection
135
- assert_called(conn, :execute_without_switching) do
136
- refute_called(conn, :_switch_connection) do
137
- assert conn._host_pool_current_database
138
- conn.drop_database(:some_args)
139
- end
140
- end
141
- end
142
-
143
- def test_underlying_assumption_about_test_db
144
- debug_me = false
145
- # ensure connection
146
- Pool1DbA.first
147
-
148
- # which is the "default" DB to connect to?
149
- first_db = Pool1DbA.connection.unproxied.instance_variable_get(:@_cached_current_database)
150
- puts "\nOk, we started on #{first_db}" if debug_me
151
-
152
- switch_to_klass = case first_db
153
- when 'arhp_test_db_b'
154
- Pool1DbA
155
- when 'arhp_test_db_a'
156
- Pool1DbB
157
- else
158
- raise "Expected a database name, got #{first_db.inspect}"
159
- end
160
- expected_database = switch_to_klass.connection.instance_variable_get(:@database)
161
-
162
- # switch to the other database
163
- switch_to_klass.first
164
- puts "\nAnd now we're on #{current_database(switch_to_klass)}" if debug_me
165
-
166
- # get the current thread id so we can shoot ourselves in the head
167
- thread_id = switch_to_klass.connection.select_value('select @@pseudo_thread_id')
168
-
169
- # now, disable our auto-switching and trigger a mysql reconnect
170
- switch_to_klass.connection.unproxied.stub(:_switch_connection, true) do
171
- Pool2DbD.connection.execute("KILL #{thread_id}")
172
- end
173
-
174
- # and finally, did mysql reconnect correctly?
175
- puts "\nAnd now we end up on #{current_database(switch_to_klass)}" if debug_me
176
- assert_equal expected_database, current_database(switch_to_klass)
177
- end
178
-
179
- def test_release_connection
180
- pool = ActiveRecord::Base.connection_pool
181
- conn = pool.connection
182
- assert_called_with(pool, :checkin, [conn]) do
183
- pool.release_connection
184
- end
185
- end
186
- end