Skip to content

Commit

Permalink
Decorating the ActiveRecord @config object in a thread-safe manner
Browse files Browse the repository at this point in the history
Not sure if this is needed as @config[:database] doesn't seem to affect any tests.
  • Loading branch information
zdennis committed Jun 11, 2020
1 parent 2ddf249 commit e312b2a
Showing 1 changed file with 47 additions and 9 deletions.
56 changes: 47 additions & 9 deletions lib/active_record_host_pool/connection_adapter_mixin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,54 @@
require "active_record/connection_adapters/mysql2_adapter"

module ActiveRecordHostPool
module PerThreadHashAccess
# PerThreadHashAccess.module_for(...) returns a module which scopes hash accessor methods
# to the given connection_id with thread-local values. It uses `super` as a fallback. This is to
# make access to the config object on a ActiveRecord::ConnectionAdapters::Mysql2Adapter
# thread-safe.
#
# Because AHRP shares the connection across members of its connection pool it is possible for
# multiple threads be simultaneously trying to talk to the database.
def self.module_for(connection_id)
Module.new do
define_method(:[]=) do |key, value|
_ahrp_storage[key] = value
end

define_method(:[]) do |key|
if _ahrp_storage.key?(key)
_ahrp_storage[key]
else
_ahrp_storage[key] = super(key)
end
end

define_method(:fetch) do |*args, &blk|
key, = args
_ahrp_storage.fetch(key) do
_ahrp_storage[key] = super(*args, &blk)
end
end

private

define_method(:_ahrp_storage) do
DatabaseSwitch.thread_local_storage[connection_id]
end
end
end
end

module DatabaseSwitch
def self.included(base)
base.class_eval do
attr_reader :_host_pool_current_database
def _host_pool_current_database
_ahrp_per_thread_per_connection_storage[:_host_pool_current_database]
_ahrp_storage[:_host_pool_current_database]
end

def _host_pool_current_database=(database)
_ahrp_per_thread_per_connection_storage[:_host_pool_current_database] = database
# @config[:database] = _host_pool_current_database if ActiveRecord::VERSION::MAJOR >= 5
_ahrp_storage[:_host_pool_current_database] = database
@config[:database] = _host_pool_current_database if ActiveRecord::VERSION::MAJOR >= 5
end

alias_method :execute_without_switching, :execute
Expand All @@ -31,12 +68,13 @@ def _host_pool_current_database=(database)
end

def self.thread_local_storage
@thread_local_storage = Thread.current[:active_record_host_pool] ||= Hash.new { |h,k| h[k] = {} }
Thread.current[:active_record_host_pool] ||= Hash.new { |h, k| h[k] = {} }
end

def initialize(*)
self._cached_current_database = nil
super

@config.singleton_class.prepend PerThreadHashAccess.module_for(@connection.object_id)
end

def execute_with_switching(*args)
Expand Down Expand Up @@ -71,11 +109,11 @@ def disconnect_with_host_pooling!
attr_accessor :_no_switch

def _cached_current_database
_ahrp_per_thread_per_connection_storage[:_cached_current_database]
_ahrp_storage[:_cached_current_database]
end

def _cached_current_database=(database)
_ahrp_per_thread_per_connection_storage[:_cached_current_database] = database
_ahrp_storage[:_cached_current_database] = database
end

def _cached_connection_object_id
Expand All @@ -101,7 +139,7 @@ def _switch_connection
end
end

def _ahrp_per_thread_per_connection_storage
def _ahrp_storage
DatabaseSwitch.thread_local_storage[_cached_connection_object_id]
end

Expand Down

0 comments on commit e312b2a

Please sign in to comment.