Skip to content
  • Edouard CHIN's avatar
    474a4174
    Fix `create_or_find_by` not rolling back a transaction: · 474a4174
    Edouard CHIN authored
    - Fix #54830 (This indirectly fix the issue)
    - ### Problem
    
      The `create_or_find_by` method doesn't allow rolling back
      a transaction. This is a different behaviour than its counterpart
      method `find_or_create_by`.
      The reason is because we are in the double transaction edge case and
      the outer transaction doesn't see the rollback.
    
      ### Details
    
      ```ruby
      class User < ApplicationRecord
        after_save do
          raise ActiveRecord::Rollback
        end
      end
    
      User.find_or_create_by(name: "John") => Correctly rolled back
      User.create_or_find_by(name: "John") => Does not roll back
      ```
    
      ### Solution
    
      We need to be able to know whether the inner transaction succeeded,
      which is not possible using `.create`, as this method always returns
      the record (rather than the status of the transaction).
      https://github.com/rails/rails/blob/1eac7f27748ffa43c5ce91c036c928fc22d4c597/activerecord/lib/active_record/persistence.rb#L39
    
      Using `new` followed by a save is equivalent to `create`, but
      with `save` we get the transaction return status and we can
      properly rollback the outer transaction.
    474a4174
    Fix `create_or_find_by` not rolling back a transaction:
    Edouard CHIN authored
    - Fix #54830 (This indirectly fix the issue)
    - ### Problem
    
      The `create_or_find_by` method doesn't allow rolling back
      a transaction. This is a different behaviour than its counterpart
      method `find_or_create_by`.
      The reason is because we are in the double transaction edge case and
      the outer transaction doesn't see the rollback.
    
      ### Details
    
      ```ruby
      class User < ApplicationRecord
        after_save do
          raise ActiveRecord::Rollback
        end
      end
    
      User.find_or_create_by(name: "John") => Correctly rolled back
      User.create_or_find_by(name: "John") => Does not roll back
      ```
    
      ### Solution
    
      We need to be able to know whether the inner transaction succeeded,
      which is not possible using `.create`, as this method always returns
      the record (rather than the status of the transaction).
      https://github.com/rails/rails/blob/1eac7f27748ffa43c5ce91c036c928fc22d4c597/activerecord/lib/active_record/persistence.rb#L39
    
      Using `new` followed by a save is equivalent to `create`, but
      with `save` we get the transaction return status and we can
      properly rollback the outer transaction.
Loading