diff options
Diffstat (limited to 'activerecord/lib/active_record/transactions.rb')
-rw-r--r-- | activerecord/lib/active_record/transactions.rb | 81 |
1 files changed, 43 insertions, 38 deletions
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index 6f2def0df1..8de82feae3 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -11,15 +11,16 @@ module ActiveRecord :before_commit_without_transaction_enrollment, :commit_without_transaction_enrollment, :rollback_without_transaction_enrollment, + terminator: deprecated_false_terminator, scope: [:kind, :name] end # = Active Record Transactions # - # Transactions are protective blocks where SQL statements are only permanent + # \Transactions are protective blocks where SQL statements are only permanent # if they can all succeed as one atomic action. The classic example is a # transfer between two accounts where you can only have a deposit if the - # withdrawal succeeded and vice versa. Transactions enforce the integrity of + # withdrawal succeeded and vice versa. \Transactions enforce the integrity of # the database and guard the data against program errors or database # break-downs. So basically you should use transaction blocks whenever you # have a number of statements that must be executed together or not at all. @@ -39,20 +40,20 @@ module ActiveRecord # # == Different Active Record classes in a single transaction # - # Though the transaction class method is called on some Active Record class, + # Though the #transaction class method is called on some Active Record class, # the objects within the transaction block need not all be instances of # that class. This is because transactions are per-database connection, not # per-model. # # In this example a +balance+ record is transactionally saved even - # though +transaction+ is called on the +Account+ class: + # though #transaction is called on the +Account+ class: # # Account.transaction do # balance.save! # account.save! # end # - # The +transaction+ method is also available as a model instance method. + # The #transaction method is also available as a model instance method. # For example, you can also do this: # # balance.transaction do @@ -79,7 +80,8 @@ module ActiveRecord # # == +save+ and +destroy+ are automatically wrapped in a transaction # - # Both +save+ and +destroy+ come wrapped in a transaction that ensures + # Both {#save}[rdoc-ref:Persistence#save] and + # {#destroy}[rdoc-ref:Persistence#destroy] come wrapped in a transaction that ensures # that whatever you do in validations or callbacks will happen under its # protected cover. So you can use validations to check for values that # the transaction depends on or you can raise exceptions in the callbacks @@ -88,7 +90,7 @@ module ActiveRecord # As a consequence changes to the database are not seen outside your connection # until the operation is complete. For example, if you try to update the index # of a search engine in +after_save+ the indexer won't see the updated record. - # The +after_commit+ callback is the only one that is triggered once the update + # The #after_commit callback is the only one that is triggered once the update # is committed. See below. # # == Exception handling and rolling back @@ -97,11 +99,11 @@ module ActiveRecord # be propagated (after triggering the ROLLBACK), so you should be ready to # catch those in your application code. # - # One exception is the <tt>ActiveRecord::Rollback</tt> exception, which will trigger + # One exception is the ActiveRecord::Rollback exception, which will trigger # a ROLLBACK when raised, but not be re-raised by the transaction block. # - # *Warning*: one should not catch <tt>ActiveRecord::StatementInvalid</tt> exceptions - # inside a transaction block. <tt>ActiveRecord::StatementInvalid</tt> exceptions indicate that an + # *Warning*: one should not catch ActiveRecord::StatementInvalid exceptions + # inside a transaction block. ActiveRecord::StatementInvalid exceptions indicate that an # error occurred at the database level, for example when a unique constraint # is violated. On some database systems, such as PostgreSQL, database errors # inside a transaction cause the entire transaction to become unusable @@ -127,11 +129,11 @@ module ActiveRecord # end # # One should restart the entire transaction if an - # <tt>ActiveRecord::StatementInvalid</tt> occurred. + # ActiveRecord::StatementInvalid occurred. # # == Nested transactions # - # +transaction+ calls can be nested. By default, this makes all database + # #transaction calls can be nested. By default, this makes all database # statements in the nested transaction block become part of the parent # transaction. For example, the following behavior may be surprising: # @@ -143,7 +145,7 @@ module ActiveRecord # end # end # - # creates both "Kotori" and "Nemu". Reason is the <tt>ActiveRecord::Rollback</tt> + # creates both "Kotori" and "Nemu". Reason is the ActiveRecord::Rollback # exception in the nested block does not issue a ROLLBACK. Since these exceptions # are captured in transaction blocks, the parent block does not see it and the # real transaction is committed. @@ -167,22 +169,22 @@ module ActiveRecord # writing, the only database that we're aware of that supports true nested # transactions, is MS-SQL. Because of this, Active Record emulates nested # transactions by using savepoints on MySQL and PostgreSQL. See - # http://dev.mysql.com/doc/refman/5.6/en/savepoint.html + # http://dev.mysql.com/doc/refman/5.7/en/savepoint.html # for more information about savepoints. # - # === Callbacks + # === \Callbacks # # There are two types of callbacks associated with committing and rolling back transactions: - # +after_commit+ and +after_rollback+. + # #after_commit and #after_rollback. # - # +after_commit+ callbacks are called on every record saved or destroyed within a - # transaction immediately after the transaction is committed. +after_rollback+ callbacks + # #after_commit callbacks are called on every record saved or destroyed within a + # transaction immediately after the transaction is committed. #after_rollback callbacks # are called on every record saved or destroyed within a transaction immediately after the # transaction or savepoint is rolled back. # # These callbacks are useful for interacting with other systems since you will be guaranteed # that the callback is only executed when the database is in a permanent state. For example, - # +after_commit+ is a good spot to put in a hook to clearing a cache since clearing it from + # #after_commit is a good spot to put in a hook to clearing a cache since clearing it from # within a transaction could trigger the cache to be regenerated before the database is updated. # # === Caveats @@ -204,9 +206,8 @@ module ActiveRecord # # Note that "TRUNCATE" is also a MySQL DDL statement! module ClassMethods - # See ActiveRecord::Transactions::ClassMethods for detailed documentation. + # See the ConnectionAdapters::DatabaseStatements#transaction API docs. def transaction(options = {}, &block) - # See the ConnectionAdapters::DatabaseStatements#transaction API docs. connection.transaction(options, &block) end @@ -234,7 +235,7 @@ module ActiveRecord # This callback is called after a create, update, or destroy are rolled back. # - # Please check the documentation of +after_commit+ for options. + # Please check the documentation of #after_commit for options. def after_rollback(*args, &block) set_options_for_callbacks!(args) set_callback(:rollback, :after, *args, &block) @@ -319,36 +320,36 @@ module ActiveRecord end def before_committed! # :nodoc: - run_callbacks :before_commit_without_transaction_enrollment - run_callbacks :before_commit + _run_before_commit_without_transaction_enrollment_callbacks + _run_before_commit_callbacks end - # Call the +after_commit+ callbacks. + # Call the #after_commit callbacks. # # Ensure that it is not called if the object was never persisted (failed create), # but call it after the commit of a destroyed object. def committed!(should_run_callbacks: true) #:nodoc: if should_run_callbacks && destroyed? || persisted? - run_callbacks :commit_without_transaction_enrollment - run_callbacks :commit + _run_commit_without_transaction_enrollment_callbacks + _run_commit_callbacks end ensure force_clear_transaction_record_state end - # Call the +after_rollback+ callbacks. The +force_restore_state+ argument indicates if the record + # Call the #after_rollback callbacks. The +force_restore_state+ argument indicates if the record # state should be rolled back to the beginning or just to the last savepoint. def rolledback!(force_restore_state: false, should_run_callbacks: true) #:nodoc: if should_run_callbacks - run_callbacks :rollback - run_callbacks :rollback_without_transaction_enrollment + _run_rollback_callbacks + _run_rollback_without_transaction_enrollment_callbacks end ensure restore_transaction_record_state(force_restore_state) clear_transaction_record_state end - # Add the record to the current transaction so that the +after_rollback+ and +after_commit+ callbacks + # Add the record to the current transaction so that the #after_rollback and #after_commit callbacks # can be called. def add_to_transaction if has_transactional_callbacks? @@ -380,6 +381,10 @@ module ActiveRecord raise ActiveRecord::Rollback unless status end status + ensure + if @transaction_state && @transaction_state.committed? + clear_transaction_record_state + end end protected @@ -453,23 +458,23 @@ module ActiveRecord !_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_before_commit_callbacks.empty? end - # Updates the attributes on this particular ActiveRecord object so that - # if it's associated with a transaction, then the state of the ActiveRecord + # Updates the attributes on this particular Active Record object so that + # if it's associated with a transaction, then the state of the Active Record # object will be updated to reflect the current state of the transaction # - # The @transaction_state variable stores the states of the associated + # 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 ActiveRecord object inside of a transaction carries that transaction's + # Each Active Record 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 + # the TransactionState, and rolls back or commits the Active Record object # as appropriate. # - # Since ActiveRecord objects can be inside multiple transactions, this + # Since Active Record 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. + # checks if the Active Record object reflects the state of the object. def sync_with_transaction_state update_attributes_from_transaction_state(@transaction_state) end |