From 74bd98dc4247f13f3203bb0de23ab20f3082ea0b Mon Sep 17 00:00:00 2001 From: Benjamin Quorning Date: Wed, 11 Oct 2023 17:40:02 +0200 Subject: [PATCH] tmp: work around issues in Rails 7.1 --- .../clear_query_cache_patch.rb | 18 ++++++++++-- .../connection_adapter_mixin.rb | 29 +++++++++++++++---- lib/active_record_host_pool/pool_proxy_6_1.rb | 3 +- test/test_arhp.rb | 6 ++-- test/test_thread_safety.rb | 18 ++++++++++-- 5 files changed, 60 insertions(+), 14 deletions(-) diff --git a/lib/active_record_host_pool/clear_query_cache_patch.rb b/lib/active_record_host_pool/clear_query_cache_patch.rb index ec4d698a..84e7a203 100644 --- a/lib/active_record_host_pool/clear_query_cache_patch.rb +++ b/lib/active_record_host_pool/clear_query_cache_patch.rb @@ -17,9 +17,20 @@ # This is a private Rails API and may change in future releases as they're # actively working on sharding in Rails 6 and above. module ActiveRecordHostPool + # For Rails 7.1. + module NewClearQueryCachePatch + def clear_query_caches_for_current_thread + ActiveRecord::Base.connection_handler.all_connection_pools.each do |pool| + pool._unproxied_connection.clear_query_cache if pool.active_connection? + end + end + end + # For Rails 6.1 & 7.0. module ClearOnHandlerPatch - def clear_on_handler(handler) + # def clear_on_handler(handler) # reinstate when 7.1 works + # Work around https://github.com/rails/rails/commit/4a41b758208e3ea817cc9b756d35cb6c53a884db + def clear_on_handler(handler, _owner = nil) handler.all_connection_pools.each do |pool| pool._unproxied_connection.clear_query_cache if pool.active_connection? end @@ -39,7 +50,10 @@ def clear_query_caches_for_current_thread end case "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}" -when '6.1', '7.0', '7.1' +when '7.1' + # Fix https://github.com/rails/rails/commit/401e2f24161ed6047ae33c322aaf6584b7728ab9 + ActiveRecord::Base.singleton_class.prepend(ActiveRecordHostPool::NewClearQueryCachePatch) +when '6.1', '7.0' ActiveRecord::Base.singleton_class.prepend(ActiveRecordHostPool::ClearOnHandlerPatch) when '6.0' ActiveRecord::Base.singleton_class.prepend(ActiveRecordHostPool::ClearQueryCachePatch) diff --git a/lib/active_record_host_pool/connection_adapter_mixin.rb b/lib/active_record_host_pool/connection_adapter_mixin.rb index 915ea621..27567f4e 100644 --- a/lib/active_record_host_pool/connection_adapter_mixin.rb +++ b/lib/active_record_host_pool/connection_adapter_mixin.rb @@ -22,8 +22,16 @@ def self.included(base) base.class_eval do attr_reader(:_host_pool_current_database) - alias_method :execute_without_switching, :execute - alias_method :execute, :execute_with_switching + # raw_execute instead of execute after + # https://github.com/rails/rails/commit/f69bbcbc0752ca5d5af327d55922614a26f5c7e9 + case "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}" + when '7.1' + alias_method :raw_execute_without_switching, :raw_execute + alias_method :raw_execute, :raw_execute_with_switching + else + alias_method :execute_without_switching, :execute + alias_method :execute, :execute_with_switching + end alias_method :drop_database_without_no_switching, :drop_database alias_method :drop_database, :drop_database_with_no_switching @@ -50,11 +58,20 @@ def self.ruby2_keywords(*); end unless respond_to?(:ruby2_keywords, true) # This one really does need ruby2_keywords; in Rails 6.0 the method does not take # any keyword arguments, but in Rails 7.0 it does. So, we don't know whether or not # what we're delegating to takes kwargs, so ruby2_keywords is needed. - ruby2_keywords def execute_with_switching(*args) - if _host_pool_current_database && !_no_switch - _switch_connection + if ActiveRecord.version >= Gem::Version.new('7.1') + ruby2_keywords def raw_execute_with_switching(*args) + if _host_pool_current_database && !_no_switch + _switch_connection + end + raw_execute_without_switching(*args) + end + else + ruby2_keywords def execute_with_switching(*args) + if _host_pool_current_database && !_no_switch + _switch_connection + end + execute_without_switching(*args) end - execute_without_switching(*args) end def drop_database_with_no_switching(*args) diff --git a/lib/active_record_host_pool/pool_proxy_6_1.rb b/lib/active_record_host_pool/pool_proxy_6_1.rb index 9ea06797..e3fbd751 100644 --- a/lib/active_record_host_pool/pool_proxy_6_1.rb +++ b/lib/active_record_host_pool/pool_proxy_6_1.rb @@ -150,7 +150,8 @@ def _connection_proxy_for(connection, database) @connection_proxy_cache[key] ||= begin cx = ActiveRecordHostPool::ConnectionProxy.new(connection, database) - cx.execute('select 1') + # Work around https://github.com/rails/rails/pull/48061/commits/63c0d6b31bcd0fc33745ec6fd278b2d1aab9be54 + # cx.raw_execute('SELECT 1') cx end end diff --git a/test/test_arhp.rb b/test/test_arhp.rb index 700b19f5..de84dd5b 100644 --- a/test/test_arhp.rb +++ b/test/test_arhp.rb @@ -122,7 +122,8 @@ def test_checkout def test_no_switch_when_creating_db conn = Pool1DbA.connection - assert_called(conn, :execute_without_switching) do + meth = ActiveRecord.version >= Gem::Version.new('7.1') ? :raw_execute_without_switching : :execute_without_switching + assert_called(conn, meth) do refute_called(conn, :_switch_connection) do assert conn._host_pool_current_database conn.create_database(:some_args) @@ -132,7 +133,8 @@ def test_no_switch_when_creating_db def test_no_switch_when_dropping_db conn = Pool1DbA.connection - assert_called(conn, :execute_without_switching) do + meth = ActiveRecord.version >= Gem::Version.new('7.1') ? :raw_execute_without_switching : :execute_without_switching + assert_called(conn, meth) do refute_called(conn, :_switch_connection) do assert conn._host_pool_current_database conn.drop_database(:some_args) diff --git a/test/test_thread_safety.rb b/test/test_thread_safety.rb index b393c20a..1e614082 100644 --- a/test/test_thread_safety.rb +++ b/test/test_thread_safety.rb @@ -153,17 +153,29 @@ def test_each_thread_has_its_own_connection_and_can_switch end def assert_query_host_1_db_a(sleep_time: 0) - result = Pool1DbA.connection.execute("SELECT val, SLEEP(#{sleep_time}) from tests") + if ActiveRecord.version >= Gem::Version.new('7.1') + result = Pool1DbA.connection.raw_execute("SELECT val, SLEEP(#{sleep_time}) from tests", nil) + else + result = Pool1DbA.connection.execute("SELECT val, SLEEP(#{sleep_time}) from tests") + end assert_equal('test_Pool1DbA_value', result.first.first) end def assert_query_host_1_db_b(sleep_time: 0) - result = Pool1DbB.connection.execute("SELECT val, SLEEP(#{sleep_time}) from tests") + if ActiveRecord.version >= Gem::Version.new('7.1') + result = Pool1DbB.connection.raw_execute("SELECT val, SLEEP(#{sleep_time}) from tests", nil) + else + result = Pool1DbB.connection.execute("SELECT val, SLEEP(#{sleep_time}) from tests") + end assert_equal('test_Pool1DbB_value', result.first.first) end def assert_query_host_2_db_d(sleep_time: 0) - result = Pool2DbD.connection.execute("SELECT val, SLEEP(#{sleep_time}) from tests") + if ActiveRecord.version >= Gem::Version.new('7.1') + result = Pool2DbD.connection.raw_execute("SELECT val, SLEEP(#{sleep_time}) from tests", nil) + else + result = Pool2DbD.connection.execute("SELECT val, SLEEP(#{sleep_time}) from tests") + end assert_equal('test_Pool2DbD_value', result.first.first) end