website icon indicating copy to clipboard operation
website copied to clipboard

Rollback a managed transaction without throwing an exception

Open wolever opened this issue 8 years ago • 4 comments

Currently it's impossible to rollback a managed transaction without raising an exception. This makes it fairly convoluted to implement logic where rollbacks are the "expected" case.

For example, I want to wrap my test cases inside a transaction that will unconditionally rollback when each one completes.

The obvious implementation is:

sequelize.transaction(txn => {
  try {
    return await testcase()
  } finally {
    txn.rollback()
  }
})

But this doesn't work, because sequelize.transaction(…) unconditionally tries to commit the transaction, even if it has already been committed or rolled back.

The other obvious implementation is:

sequelize.transaction().then(txn => {
   return testcase.finally(_ => txn.rollback())
})

But this doesn't work because un-managed transactions created by sequelize.transaction() don't use CLS.

I believe the fix could be as simple as changing the implementation of sequelize.transaction(…) from this:

      return transaction.prepareEnvironment()
        .then(() => autoCallback(transaction))
        .tap(() => transaction.commit())
        …

To this:

      return transaction.prepareEnvironment()
        .then(() => autoCallback(transaction))
        .tap(() => if (!transaction.finished) transaction.commit())
        …

wolever avatar Feb 21 '18 07:02 wolever

Why dont you use unmanaged transactions, which expose t.commit / t.rollback to achieve finer control.

Managed transaction are designed in this way; do something, if you wish to rollback throw an error. For micro management, use "unmanaged" transaction

sushantdhiman avatar Feb 25 '18 09:02 sushantdhiman

Because there's no way to use CLS with unmanaged transactions (it seems to be explicitly impossible; see also: https://github.com/sequelize/sequelize/issues/9082 )

wolever avatar Feb 26 '18 07:02 wolever

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If this is still an issue, just leave a comment 🙂

stale[bot] avatar May 27 '18 07:05 stale[bot]

I see that with the latest version of Sequlize, the docs do reflect now that CLS does not work with unmanaged transactions https://sequelize.org/docs/v7/querying/transactions/#unmanaged-transactions

With this being the case though, how can we make use of managed transactions to share transaction without throwing an error to result in a rollback? The use case for this is in the unit tests that you write. I don't want to pass the transaction along to every query that my unit test does, but I don't want the data to be persisted after a fail or successful test.

Can someone please advice?

cocode32 avatar Oct 24 '24 14:10 cocode32