diff options
Diffstat (limited to 'activerecord')
-rw-r--r-- | activerecord/CHANGELOG | 2 | ||||
-rwxr-xr-x | activerecord/lib/active_record/base.rb | 25 | ||||
-rw-r--r-- | activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb | 66 | ||||
-rw-r--r-- | activerecord/test/method_scoping_test.rb | 6 | ||||
-rw-r--r-- | activerecord/test/threaded_connections_test.rb | 10 |
5 files changed, 78 insertions, 31 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 5cde7f465b..aee47c652e 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *SVN* +* Dynamically set allow_concurrency. #4044 [Stefan Kaes] + * Added Base#to_xml that'll turn the current record into a XML representation [DHH]. Example: topic.to_xml diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 70d88e561b..9e3c54c7d4 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -310,7 +310,7 @@ module ActiveRecord #:nodoc: # Determines whether or not to use a connection for each thread, or a single shared connection for all threads. # Defaults to false. Set to true if you're writing a threaded application. cattr_accessor :allow_concurrency - @@allow_concurrency = true + @@allow_concurrency = false # Determines whether to speed up access by generating optimized reader # methods to avoid expensive calls to method_missing when accessing @@ -1140,15 +1140,22 @@ module ActiveRecord #:nodoc: end end - def scoped_methods - if allow_concurrency - Thread.current[:scoped_methods] ||= {} - Thread.current[:scoped_methods][self] ||= [] - else - @scoped_methods ||= [] - end + def thread_safe_scoped_methods #:nodoc: + scoped_methods = (Thread.current[:scoped_methods] ||= {}) + scoped_methods[self] ||= [] end - + + def single_threaded_scoped_methods #:nodoc: + @scoped_methods ||= [] + end + + # pick up the correct scoped_methods version from @@allow_concurrency + if @@allow_concurrency + alias_method :scoped_methods, :thread_safe_scoped_methods + else + alias_method :scoped_methods, :single_threaded_scoped_methods + end + def current_scoped_methods scoped_methods.last end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb index fe581a14b5..8b70b2f047 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb @@ -22,15 +22,35 @@ module ActiveRecord class << self # Retrieve the connection cache. - def active_connections - if @@allow_concurrency - @@active_connections[Thread.current.object_id] ||= {} - else - @@active_connections + def thread_safe_active_connections #:nodoc: + @@active_connections[Thread.current.object_id] ||= {} + end + + def single_threaded_active_connections #:nodoc: + @@active_connections + end + + # pick up the right active_connection method from @@allow_concurrency + if @@allow_concurrency + alias_method :active_connections, :thread_safe_active_connections + else + alias_method :active_connections, :single_threaded_active_connections + end + + # set concurrency support flag (not thread safe, like most of the methods in this file) + def allow_concurrency=(threaded) + logger.debug "allow_concurrency=#{threaded}" if logger + return if @@allow_concurrency == threaded + clear_all_cached_connections! + @@allow_concurrency = threaded + method_prefix = threaded ? "thread_safe" : "single_threaded" + sing = (class << self; self; end) + [:active_connections, :scoped_methods].each do |method| + sing.send(:alias_method, method, "#{method_prefix}_#{method}") end + log_connections if logger end - - @active_connection_name = nil + def active_connection_name @active_connection_name ||= if active_connections[name] || @@defined_connections[name] @@ -62,15 +82,19 @@ module ActiveRecord # Clears the cache which maps classes to connections. def clear_active_connections! - clear_cache!(@@active_connections) + clear_cache!(@@active_connections) do |name, conn| + conn.disconnect! + end end # Verify active connections. def verify_active_connections! - remove_stale_cached_threads!(@@active_connections) do |name, conn| - conn.disconnect! + if @@allow_concurrency + remove_stale_cached_threads!(@@active_connections) do |name, conn| + conn.disconnect! + end end - + active_connections.each_value do |connection| connection.verify!(@@verification_timeout) end @@ -106,6 +130,18 @@ module ActiveRecord clear_cache!(cache, thread_id, &block) end end + + def clear_all_cached_connections! + if @@allow_concurrency + @@active_connections.each_value do |connection_hash_for_thread| + connection_hash_for_thread.each_value {|conn| conn.disconnect! } + connection_hash_for_thread.clear + end + else + @@active_connections.each_value {|conn| conn.disconnect! } + end + @@active_connections.clear + end end # Returns the connection currently associated with the class. This can @@ -167,14 +203,6 @@ module ActiveRecord end end - def self.active_connections #:nodoc: - if @@allow_concurrency - Thread.current['active_connections'] ||= {} - else - @@active_connections ||= {} - end - end - # Locate the connection of the nearest super class. This can be an # active or defined connections: if it is the latter, it will be # opened and set as the active connection for the class it was defined diff --git a/activerecord/test/method_scoping_test.rb b/activerecord/test/method_scoping_test.rb index 551d7c1dd3..c01aab6a36 100644 --- a/activerecord/test/method_scoping_test.rb +++ b/activerecord/test/method_scoping_test.rb @@ -9,7 +9,7 @@ class MethodScopingTest < Test::Unit::TestCase def test_set_conditions Developer.with_scope(:find => { :conditions => 'just a test...' }) do - assert_equal 'just a test...', Thread.current[:scoped_methods][Developer][-1][:find][:conditions] + assert_equal 'just a test...', Developer.send(:current_scoped_methods)[:find][:conditions] end end @@ -64,7 +64,7 @@ class MethodScopingTest < Test::Unit::TestCase new_comment = nil VerySpecialComment.with_scope(:create => { :post_id => 1 }) do - assert_equal({ :post_id => 1 }, Thread.current[:scoped_methods][VerySpecialComment][-1][:create]) + assert_equal({ :post_id => 1 }, VerySpecialComment.send(:current_scoped_methods)[:create]) new_comment = VerySpecialComment.create :body => "Wonderful world" end @@ -123,7 +123,7 @@ class NestedScopingTest < Test::Unit::TestCase Developer.with_scope(:find => { :conditions => "name = 'David'" }) do Developer.with_exclusive_scope(:find => { :conditions => "name = 'Jamis'" }) do assert_equal({:find => { :conditions => "name = 'Jamis'" }}, Developer.instance_eval('current_scoped_methods')) - assert_equal({:find => { :conditions => "name = 'Jamis'" }}, Thread.current[:scoped_methods][Developer][-1]) + assert_equal({:find => { :conditions => "name = 'Jamis'" }}, Developer.send(:scoped_methods)[-1]) end end end diff --git a/activerecord/test/threaded_connections_test.rb b/activerecord/test/threaded_connections_test.rb index 177c6c7e13..d9cc47eef3 100644 --- a/activerecord/test/threaded_connections_test.rb +++ b/activerecord/test/threaded_connections_test.rb @@ -9,6 +9,16 @@ class ThreadedConnectionsTest < Test::Unit::TestCase def setup @connection = ActiveRecord::Base.remove_connection @connections = [] + @allow_concurrency = ActiveRecord::Base.allow_concurrency + end + + def teardown + # clear the connection cache + ActiveRecord::Base.send(:clear_all_cached_connections!) + # set allow_concurrency to saved value + ActiveRecord::Base.allow_concurrency = @allow_concurrency + # reestablish old connection + ActiveRecord::Base.establish_connection(@connection) end def gather_connections(use_threaded_connections) |