diff options
Diffstat (limited to 'activerecord')
7 files changed, 50 insertions, 51 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 8d76b4145b..b5dd19988d 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,10 @@ +* Dont enroll records in the transaction if they dont have commit callbacks. + That was causing a memory grow problem when creating a lot of records inside a transaction. + + Fixes #15549. + + *Will Bryant*, *Aaron Patterson* + * Correctly create through records when created on a has many through association when using `where`. diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index 3a1e4a4a88..ebb4a1523f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -1,13 +1,10 @@ module ActiveRecord module ConnectionAdapters class TransactionState - attr_reader :parent - VALID_STATES = Set.new([:committed, :rolledback, nil]) def initialize(state = nil) @state = state - @parent = nil end def finalized? diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb index 984af79642..24f5849e45 100644 --- a/activerecord/lib/active_record/connection_handling.rb +++ b/activerecord/lib/active_record/connection_handling.rb @@ -5,7 +5,7 @@ module ActiveRecord # Establishes the connection to the database. Accepts a hash as input where # the <tt>:adapter</tt> key must be specified with the name of a database adapter (in lower-case) - # example for regular databases (MySQL, Postgresql, etc): + # example for regular databases (MySQL, PostgreSQL, etc): # # ActiveRecord::Base.establish_connection( # adapter: "mysql", diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index d41872d767..97ce4642aa 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -483,51 +483,6 @@ module ActiveRecord private - def set_transaction_state(state) # :nodoc: - @transaction_state = state - end - - def has_transactional_callbacks? # :nodoc: - !_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_before_commit_callbacks.empty? - end - - # Updates the attributes on this particular ActiveRecord object so that - # if it is associated with a transaction, then the state of the AR object - # will be updated to reflect the current state of the transaction - # - # The @transaction_state variable stores the states of the associated - # transaction. This relies on the fact that a transaction can only be in - # one rollback or commit (otherwise a list of states would be required) - # Each AR object inside of a transaction carries that transaction's - # TransactionState. - # - # This method checks to see if the ActiveRecord object's state reflects - # the TransactionState, and rolls back or commits the ActiveRecord object - # as appropriate. - # - # Since ActiveRecord objects can be inside multiple transactions, this - # method recursively goes through the parent of the TransactionState and - # checks if the ActiveRecord object reflects the state of the object. - def sync_with_transaction_state - update_attributes_from_transaction_state(@transaction_state, 0) - end - - def update_attributes_from_transaction_state(transaction_state, depth) - @reflects_state = [false] if depth == 0 - - if transaction_state && transaction_state.finalized? && !has_transactional_callbacks? - unless @reflects_state[depth] - restore_transaction_record_state if transaction_state.rolledback? - clear_transaction_record_state - @reflects_state[depth] = true - end - - if transaction_state.parent && !@reflects_state[depth+1] - update_attributes_from_transaction_state(transaction_state.parent, depth+1) - end - end - end - # Under Ruby 1.9, Array#flatten will call #to_ary (recursively) on each of the elements # of the array, and then rescues from the possible NoMethodError. If those elements are # ActiveRecord::Base's, then this triggers the various method_missing's that we have, diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 4a4de86d48..7f27e7b463 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -222,7 +222,7 @@ module ActiveRecord end def execute_simple_calculation(operation, column_name, distinct) #:nodoc: - # Postgresql doesn't like ORDER BY when there are no GROUP BY + # PostgreSQL doesn't like ORDER BY when there are no GROUP BY relation = unscope(:order) column_alias = column_name diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index fb47d915ff..7bd091b66c 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -1,4 +1,3 @@ -require 'active_support/deprecation' require 'active_support/core_ext/string/filters' module ActiveRecord diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index 598defce50..b620233c3a 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -442,5 +442,46 @@ module ActiveRecord end end end + + def set_transaction_state(state) # :nodoc: + @transaction_state = state + end + + def has_transactional_callbacks? # :nodoc: + !_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_before_commit_callbacks.empty? + end + + # Updates the attributes on this particular ActiveRecord object so that + # if it is associated with a transaction, then the state of the AR object + # will be updated to reflect the current state of the transaction + # + # The @transaction_state variable stores the states of the associated + # transaction. This relies on the fact that a transaction can only be in + # one rollback or commit (otherwise a list of states would be required) + # Each AR object inside of a transaction carries that transaction's + # TransactionState. + # + # This method checks to see if the ActiveRecord object's state reflects + # the TransactionState, and rolls back or commits the ActiveRecord object + # as appropriate. + # + # Since ActiveRecord objects can be inside multiple transactions, this + # method recursively goes through the parent of the TransactionState and + # checks if the ActiveRecord object reflects the state of the object. + def sync_with_transaction_state + update_attributes_from_transaction_state(@transaction_state, 0) + end + + def update_attributes_from_transaction_state(transaction_state, depth) + @reflects_state = [false] if depth == 0 + + if transaction_state && transaction_state.finalized? && !has_transactional_callbacks? + unless @reflects_state[depth] + restore_transaction_record_state if transaction_state.rolledback? + clear_transaction_record_state + @reflects_state[depth] = true + end + end + end end end |