1
0
Fork 0
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:
Jeremy Kemper 2013-04-09 21:27:48 -07:00
commit e0af93dd3a
9 changed files with 92 additions and 19 deletions

View file

@ -50,6 +50,7 @@ module ActiveRecord
autoload :Querying
autoload :ReadonlyAttributes
autoload :Reflection
autoload :RuntimeRegistry
autoload :Sanitization
autoload :Schema
autoload :SchemaDumper

View file

@ -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:

View file

@ -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

View file

@ -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

View 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

View file

@ -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]

View file

@ -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

View file

@ -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

View 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