active_record_host_pool 1.2.4 → 2.0.0.pre.2

Sign up to get free protection for your applications and to get access to all the features.
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