mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge pull request #10156 from wangjohn/grouping_thread_locals
Grouping thread locals in ActiveRecord
This commit is contained in:
commit
e0af93dd3a
9 changed files with 92 additions and 19 deletions
|
@ -50,6 +50,7 @@ module ActiveRecord
|
|||
autoload :Querying
|
||||
autoload :ReadonlyAttributes
|
||||
autoload :Reflection
|
||||
autoload :RuntimeRegistry
|
||||
autoload :Sanitization
|
||||
autoload :Schema
|
||||
autoload :SchemaDumper
|
||||
|
|
|
@ -54,11 +54,11 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def connection_id
|
||||
Thread.current['ActiveRecord::Base.connection_id']
|
||||
ActiveRecord::RuntimeRegistry.instance.connection_id
|
||||
end
|
||||
|
||||
def connection_id=(connection_id)
|
||||
Thread.current['ActiveRecord::Base.connection_id'] = connection_id
|
||||
ActiveRecord::RuntimeRegistry.instance.connection_id = connection_id
|
||||
end
|
||||
|
||||
# Returns the configuration of the associated connection as a hash:
|
||||
|
|
|
@ -80,11 +80,11 @@ module ActiveRecord
|
|||
class_attribute :default_connection_handler, instance_writer: false
|
||||
|
||||
def self.connection_handler
|
||||
Thread.current[:active_record_connection_handler] || self.default_connection_handler
|
||||
ActiveRecord::RuntimeRegistry.instance.connection_handler || self.default_connection_handler
|
||||
end
|
||||
|
||||
def self.connection_handler=(handler)
|
||||
Thread.current[:active_record_connection_handler] = handler
|
||||
ActiveRecord::RuntimeRegistry.instance.connection_handler = handler
|
||||
end
|
||||
|
||||
self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
|
||||
|
|
|
@ -3,11 +3,11 @@ module ActiveRecord
|
|||
IGNORE_PAYLOAD_NAMES = ["SCHEMA", "EXPLAIN"]
|
||||
|
||||
def self.runtime=(value)
|
||||
Thread.current[:active_record_sql_runtime] = value
|
||||
ActiveRecord::RuntimeRegistry.instance.sql_runtime = value
|
||||
end
|
||||
|
||||
def self.runtime
|
||||
Thread.current[:active_record_sql_runtime] ||= 0
|
||||
ActiveRecord::RuntimeRegistry.instance.sql_runtime ||= 0
|
||||
end
|
||||
|
||||
def self.reset_runtime
|
||||
|
|
32
activerecord/lib/active_record/runtime_registry.rb
Normal file
32
activerecord/lib/active_record/runtime_registry.rb
Normal file
|
@ -0,0 +1,32 @@
|
|||
require 'active_support/per_thread_registry'
|
||||
|
||||
module ActiveRecord
|
||||
# This is a registry class for storing local variables in active record. The
|
||||
# class allows you to access variables that are local to the current thread.
|
||||
# These thread local variables are stored as attributes in the
|
||||
# +RuntimeRegistry+ class.
|
||||
#
|
||||
# You can access the thread local variables by calling a variable's name on
|
||||
# the +RuntimeRegistry+ class. For example, if you wanted to obtain the
|
||||
# connection handler for the current thread, you would invoke:
|
||||
#
|
||||
# ActiveRecord::RuntimeRegistry.instance.connection_handler
|
||||
#
|
||||
# The +PerThreadRegistry+ module will make a new +RuntimeRegistry+ instance
|
||||
# and store it in +Thread.current+. Whenever you make a call for an attribute
|
||||
# on the +RuntimeRegistry+ class, the call will be sent to the instance that
|
||||
# is stored in +Thread.current+.
|
||||
#
|
||||
# Note that you can also make the following call which would provide an
|
||||
# equivalent result as the previous code:
|
||||
#
|
||||
# ActiveRecord::RuntimeRegistry.connection_handler
|
||||
#
|
||||
# However, this is less performant because it makes a call to +method_missing+
|
||||
# before it sends the method call to the +instance+.
|
||||
class RuntimeRegistry
|
||||
extend ActiveSupport::PerThreadRegistry
|
||||
|
||||
attr_accessor :connection_handler, :sql_runtime, :connection_id
|
||||
end
|
||||
end
|
|
@ -1,3 +1,5 @@
|
|||
require 'active_support/per_thread_registry'
|
||||
|
||||
module ActiveRecord
|
||||
module Scoping
|
||||
extend ActiveSupport::Concern
|
||||
|
@ -34,7 +36,7 @@ module ActiveRecord
|
|||
# to get the current_scope for the +Board+ model, then you would use the
|
||||
# following code:
|
||||
#
|
||||
# registry = ActiveRecord::Scoping::ScopeRegistry.current
|
||||
# registry = ActiveRecord::Scoping::ScopeRegistry.instance
|
||||
# registry.set_value_for(:current_scope, "Board", some_new_scope)
|
||||
#
|
||||
# Now when you run:
|
||||
|
@ -48,12 +50,10 @@ module ActiveRecord
|
|||
# ActiveRecord::Scoping::ScopeRegistry.set_value_for(:current_scope,
|
||||
# "Board", some_new_scope)
|
||||
class ScopeRegistry # :nodoc:
|
||||
class << self
|
||||
delegate :value_for, :set_value_for, to: :current
|
||||
extend ActiveSupport::PerThreadRegistry
|
||||
|
||||
def current
|
||||
Thread.current["scope_registry"] ||= new
|
||||
end
|
||||
class << self
|
||||
delegate :value_for, :set_value_for, to: :instance
|
||||
end
|
||||
|
||||
VALID_SCOPE_TYPES = [:current_scope, :ignore_default_scope]
|
||||
|
|
|
@ -1377,9 +1377,9 @@ class BasicsTest < ActiveRecord::TestCase
|
|||
UnloadablePost.send(:current_scope=, UnloadablePost.all)
|
||||
|
||||
UnloadablePost.unloadable
|
||||
assert_not_nil ActiveRecord::Scoping::ScopeRegistry.current.value_for(:current_scope, "UnloadablePost")
|
||||
assert_not_nil ActiveRecord::Scoping::ScopeRegistry.instance.value_for(:current_scope, "UnloadablePost")
|
||||
ActiveSupport::Dependencies.remove_unloadable_constants!
|
||||
assert_nil ActiveRecord::Scoping::ScopeRegistry.current.value_for(:current_scope, "UnloadablePost")
|
||||
assert_nil ActiveRecord::Scoping::ScopeRegistry.instance.value_for(:current_scope, "UnloadablePost")
|
||||
ensure
|
||||
Object.class_eval{ remove_const :UnloadablePost } if defined?(UnloadablePost)
|
||||
end
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
require 'active_support/notifications/instrumenter'
|
||||
require 'active_support/notifications/fanout'
|
||||
require 'active_support/per_thread_registry'
|
||||
|
||||
module ActiveSupport
|
||||
# = Notifications
|
||||
|
@ -190,12 +191,10 @@ module ActiveSupport
|
|||
# The instrumenters for multiple notifiers are held in a single instance of
|
||||
# this class.
|
||||
class InstrumentationRegistry # :nodoc:
|
||||
class << self
|
||||
delegate :instrumenter_for, to: :current
|
||||
extend ActiveSupport::PerThreadRegistry
|
||||
|
||||
def current
|
||||
Thread.current[:instrumentation_registry] ||= new
|
||||
end
|
||||
class << self
|
||||
delegate :instrumenter_for, to: :instance
|
||||
end
|
||||
|
||||
def initialize
|
||||
|
|
41
activesupport/lib/active_support/per_thread_registry.rb
Normal file
41
activesupport/lib/active_support/per_thread_registry.rb
Normal file
|
@ -0,0 +1,41 @@
|
|||
module ActiveSupport
|
||||
# This module creates a local registry class inside each thread. It provides
|
||||
# basic methods which will store thread locals in a single class. This
|
||||
# prevents the proliferation of too many thread locals and allows you to
|
||||
# explicitly keep track of each of the variables you're using as thread
|
||||
# locals in a class which includes this module.
|
||||
#
|
||||
# For example, instead of using a bunch of different thread locals to keep
|
||||
# track of some variables like so:
|
||||
#
|
||||
# Thread.current[:active_record_connection_handler] = connection_handler
|
||||
# Thread.current[:active_record_sql_runtime] = sql_runtime
|
||||
#
|
||||
# You could use the following class which implements the +PerThreadRegistry+
|
||||
# module:
|
||||
#
|
||||
# class NewRegistry
|
||||
# extend ActiveSupport::PerThreadRegistry
|
||||
#
|
||||
# attr_accessor :connection_handler, :sql_runtime
|
||||
# end
|
||||
#
|
||||
# NewRegistry.instance.connection_handler = connection_handler
|
||||
# NewRegistry.instance.sql_runtime = sql_runtime
|
||||
#
|
||||
# The new way of keeping track of the thread locals will create a new local
|
||||
# inside of +Thread.current+ with a key which is the name of the extended
|
||||
# class. Now you can easily access per thread variables by just calling the
|
||||
# variable name on the registry.
|
||||
module PerThreadRegistry
|
||||
def instance
|
||||
Thread.current[self.name] ||= new
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def method_missing(*args, &block)
|
||||
instance.send(*args, &block)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue