|
3 | 3 | module Sequent |
4 | 4 | module Core |
5 | 5 | module Transactions |
6 | | - ## |
7 | | - # Always require a new transaction. |
8 | | - # |
9 | | - # Read: |
10 | | - # http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html |
11 | | - # |
12 | | - # Without this change, there is a potential bug: |
13 | | - # |
14 | | - # ```ruby |
15 | | - # ActiveRecord::Base.transaction do |
16 | | - # Sequent.configuration.command_service.execute_commands command |
17 | | - # end |
18 | | - # |
19 | | - # on Command do |
20 | | - # do.some.things |
21 | | - # fail ActiveRecord::Rollback |
22 | | - # end |
23 | | - # ``` |
24 | | - # |
25 | | - # In this example, you might be surprised to find that `do.some.things` |
26 | | - # does not get rolled back! This is because AR doesn't automatically make |
27 | | - # a "savepoint" for us when we call `.transaction` in a nested manner. In |
28 | | - # order to enable this behaviour, we have to call `.transaction` like |
29 | | - # this: `.transaction(requires_new: true)`. |
30 | | - # |
31 | 6 | class ActiveRecordTransactionProvider |
32 | | - def transactional(&block) |
33 | | - ActiveRecord::Base.transaction(requires_new: true, &block) |
| 7 | + attr_reader :requires_new |
| 8 | + |
| 9 | + ## |
| 10 | + # Configure if save points should be used to simulate nested transactions. This is only |
| 11 | + # useful in combination with the `ActiveRecord::Rollback` exception. |
| 12 | + # |
| 13 | + # Read: https://api.rubyonrails.org/classes/ActiveRecord/Rollback.html and |
| 14 | + # http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html |
| 15 | + # |
| 16 | + # ```ruby |
| 17 | + # ActiveRecord::Base.transaction do |
| 18 | + # Sequent.configuration.command_service.execute_commands command |
| 19 | + # end |
| 20 | + # |
| 21 | + # on Command do |
| 22 | + # do.some.things |
| 23 | + # fail ActiveRecord::Rollback |
| 24 | + # end |
| 25 | + # ``` |
| 26 | + # |
| 27 | + # In this example, you might be surprised to find that `do.some.things` does not get rolled |
| 28 | + # back! This only happens with `ActiveRecord::Rollback`, since it is handled by the inner |
| 29 | + # transaction. All other exceptions are automatically propagated and will cause the parent |
| 30 | + # transaction to rollback. |
| 31 | + # |
| 32 | + # Note that using save points with PostgreSQL adds additional overhead and is rarely useful, |
| 33 | + # so our advice is to only use `ActiveRecord::Rollback` directly inside of an |
| 34 | + # `ActiveRecord::Base.transaction(requires_new: true) do ... end` block so it is clear what |
| 35 | + # the expected behavior is. |
| 36 | + def initialize(requires_new: false) |
| 37 | + @requires_new = requires_new |
| 38 | + warn <<~EOS if @requires_new |
| 39 | + [DEPRECATED] avoid using `requires_new: true` globally, use explicit `ActiveRecord::Base.transaction(requires_new: true)` |
| 40 | + blocks with `ActiveRecord::Rollback` instead if nested transactions are needed. |
| 41 | + EOS |
| 42 | + end |
| 43 | + |
| 44 | + def transaction(requires_new: @requires_new, &block) |
| 45 | + ActiveRecord::Base.transaction(requires_new:, &block) |
34 | 46 | end |
35 | 47 |
|
| 48 | + # Deprecated alias |
| 49 | + alias transactional transaction |
| 50 | + |
36 | 51 | def after_commit(&block) |
37 | 52 | ActiveRecord::Base.current_transaction.after_commit(&block) |
38 | 53 | end |
|
0 commit comments