This is a fork from this issue. Based on the snippet from Database abstraction layer shown below, a commit will be done automatically when $txn goes out of scope.

function my_transaction_function() {
  // The transaction opens here.
  $txn = db_transaction();

  .
  .
  .

  // $txn goes out of scope here.  Unless the transaction was rolled back, it
  // gets automatically commited here.
}

It doesn't feel quite right to have commit as the default action because:

  • Quote from PHP:
    When the script ends or when a connection is about to be closed, if you have an outstanding transaction, PDO will automatically roll it back. This is a safety measure to help avoid inconsistency in the cases where the script terminates unexpectedly--if you didn't explicitly commit the transaction, then it is assumed that something went awry, so the rollback is performed for the safety of your data.
    The common practice for a database transaction, whether is MySQL or pgSQL, is to do a default rollback when there is no explicit commit action given. This ensures that for whatever bad happens, inconsistent data are not written. In other words, the coder is sure the data at that point in time is consistent and therefore codes an explicit commit() action there. If something goes wrong, the commit() code will not be executed and rollback should happen.
  • Quote from PHP:
    The destructor will be called even if script execution is stopped using exit().
    Therefore, when exit(), drupal_exit() or drupal_goto() is executed, it is as good as doing a commit (tested and true). This might not be the real intention of the coder.
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

bvanmeurs’s picture

Totally agree.

It was a poor decision to not require an explicit commit in the first place.

sun’s picture

Version: 7.0 » 8.x-dev
Category: bug » task

There's no actual bug report here, so this is something to discuss for D8.

catch’s picture

Priority: Major » Normal

I don't think this is major and there are other issues open for transaction handling that need to be resolved first.

c960657’s picture

I tend to agree that we should require transactions to be committed explicitly. People use transactions because they care about consistency. It seems counter-productive if transactions may be committed “by mistake” e.g. due to an uncaught exception (like in #1022416: Wrong use of db_transaction() in block module), or if you forget to call $transaction->rollback() in a function using the “return early” pattern. When you return early, it is often due to some error detection that may be hard to reproduce during testing.

If commits should be made explicitly, and your forget to call $transaction->commit(), then you will notice immediately and fix the problem.

Also, I think an explicit commit() will make code easier to read. It's somewhat similar to the recommendation of always using curly brackets in if statements, even when they are not required.

pounard’s picture

Agree and it cause some pain when modules use it in a wrong way, such as I described in the #1608374: Database autocommit disabling at shutdown issue.

c960657’s picture

FileSize
21.73 KB

How about this?

c960657’s picture

Status: Active » Needs review

Status: Needs review » Needs work

The last submitted patch, auto-commit-2.patch, failed testing.

pounard’s picture

It sounds like a very good step toward more robust code. I like it.

c960657’s picture

Status: Needs work » Needs review
FileSize
22.81 KB
c960657’s picture

FileSize
22.86 KB

Reroll (due to conflict with the fix for #1376778: Consistent 'duplicate key' detection in core).

Status: Needs review » Needs work

The last submitted patch, auto-commit-4.patch, failed testing.

c960657’s picture

Status: Needs work » Needs review

#11: auto-commit-4.patch queued for re-testing.

Status: Needs review » Needs work

The last submitted patch, auto-commit-4.patch, failed testing.

c960657’s picture

Status: Needs work » Needs review

#11: auto-commit-4.patch queued for re-testing.

c960657’s picture

Now the test bot is happy.

c960657’s picture

Title: Should db_transaction() do a default commit when variable goes out of scope? » db_transaction() should commit (by default) when variable goes out of scope
FileSize
18.41 KB

Reroll.

Status: Needs review » Needs work

The last submitted patch, auto-commit-5.patch, failed testing.

jbrown’s picture

Title: db_transaction() should commit (by default) when variable goes out of scope » Transactions should be committed explicitly

Seems the title was wrong.

c960657’s picture

Status: Needs work » Needs review
FileSize
18.67 KB

Status: Needs review » Needs work

The last submitted patch, auto-commit-6.patch, failed testing.

c960657’s picture

Status: Needs work » Needs review
FileSize
20.91 KB
jbrown’s picture

#22: auto-commit-7.patch queued for re-testing.

Damien Tournoud’s picture

Version: 8.x-dev » 9.x-dev

We are well into API freeze.

Stefan Freudenberg’s picture

Given that the current code contravenes a feature of PDO to protect data integrity I would say the current implementation is a logical bug. I also do not see how the API freeze justifies postponing this issue. The only change I see is the change to a protected property of a class that is unlikely to be subclassed. Did I overlook a signature change to a public method?

sun’s picture

Version: 9.x-dev » 8.x-dev

I actually wonder whether explicitly committing transactions wouldn't (1) increase reliability and (2) be more consistent with the rest of D8's architecture?

Perhaps, as a compromise, could this change be implemented in a backwards-compatible way? I.e., allow code to explicitly commit + update transactions throughout core, but keep the auto-commit on destruction behavior (unless committed already)?

I don't want to step on @Damien's toes (as he's one of the dedicated database system maintainers), so only tentatively moving back to D8 for this potential compromise. (@Damien, please move it back to D9, if that's a no-go.)

Stefan Freudenberg’s picture

Hm. As stated in the issue description and comments, an automatic commit in the PHP shutdown sequence is surprising. It can lead to transactions being committed although not all queries and code have been executed that should occur together or not at all. It is bad for developer experience to design a behaviour that contravenes documentation on php.net.

jhedstrom’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll

Not sure if this can still go in or not, but will need a reroll if so.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Manuel Garcia’s picture

drupal8 (reroll) $ git rebase 8.3.x
First, rewinding head to replay your work on top of it...
Applying: Applied patch
Using index info to reconstruct a base tree...
M	core/includes/database.inc
M	core/includes/menu.inc
M	core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php
M	core/lib/Drupal/Core/Database/Query/Insert.php
M	core/lib/Drupal/Core/Database/Transaction.php
A	core/lib/Drupal/Core/Entity/DatabaseStorageController.php
A	core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
M	core/lib/Drupal/Core/Routing/MatcherDumper.php
A	core/modules/field_sql_storage/field_sql_storage.module
A	core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
A	core/modules/system/lib/Drupal/system/Tests/Database/TransactionTest.php
Falling back to patching base and 3-way merge...
Auto-merging core/tests/Drupal/KernelTests/Core/Database/TransactionTest.php
CONFLICT (content): Merge conflict in core/tests/Drupal/KernelTests/Core/Database/TransactionTest.php
CONFLICT (modify/delete): core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php deleted in HEAD and modified in Applied patch. Version Applied patch of core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php left in tree.
CONFLICT (modify/delete): core/modules/field_sql_storage/field_sql_storage.module deleted in HEAD and modified in Applied patch. Version Applied patch of core/modules/field_sql_storage/field_sql_storage.module left in tree.
Auto-merging core/lib/Drupal/Core/Routing/MatcherDumper.php
CONFLICT (content): Merge conflict in core/lib/Drupal/Core/Routing/MatcherDumper.php
CONFLICT (modify/delete): core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php deleted in HEAD and modified in Applied patch. Version Applied patch of core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php left in tree.
CONFLICT (modify/delete): core/lib/Drupal/Core/Entity/DatabaseStorageController.php deleted in HEAD and modified in Applied patch. Version Applied patch of core/lib/Drupal/Core/Entity/DatabaseStorageController.php left in tree.
Auto-merging core/lib/Drupal/Core/Database/Transaction.php
CONFLICT (content): Merge conflict in core/lib/Drupal/Core/Database/Transaction.php
Auto-merging core/lib/Drupal/Core/Database/Query/Insert.php
CONFLICT (content): Merge conflict in core/lib/Drupal/Core/Database/Query/Insert.php
Auto-merging core/lib/Drupal/Core/Database/Driver/sqlite/Connection.php
Auto-merging core/includes/menu.inc
CONFLICT (content): Merge conflict in core/includes/menu.inc
Auto-merging core/includes/database.inc
CONFLICT (content): Merge conflict in core/includes/database.inc
Failed to merge in the changes.

This looks to me like the patch has to be rethought a bit rather than rerolled...

tameeshb’s picture

Issue tags: -Needs reroll

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

anoopjohn’s picture

Was there a decision on this?

Version: 8.6.x-dev » 8.8.x-dev

Drupal 8.6.x will not receive any further development aside from security fixes. Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Inaetaru’s picture

When there is a variable typo error in code between DB operations during transaction, catch block with rollback isn't called and transaction is committed, at least in dev environment. Just out of curiosity: is this behaviour considered a bug (transaction is committed although there was an error) or a feature (to make sure developers make 100% coverage tests)?

Version: 8.9.x-dev » 9.2.x-dev

Drupal 8 is end-of-life as of November 17, 2021. There will not be further changes made to Drupal 8. Bugfixes are now made to the 9.3.x and higher branches only. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.15 was released on June 1st, 2022 and is the final full bugfix release for the Drupal 9.3.x series. Drupal 9.3.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.4.x-dev branch from now on, and new development or disruptive changes should be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Anybody’s picture

Title: Transactions should be committed explicitly » Transactions should be allowed to be committed explicitly
Version: 9.4.x-dev » 10.0.x-dev

Switching this to Drupal 10 and hooking in :)

Just came across this and was wondering about why explicit commits are forbidden: https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Database%...

Guess I'm missing the details here yet, but I think there are valid examples where you want to decide explicitly at which point to commit or roll back (only the part from starting the transaction to the commit / rollback, not everything).

For example, only a certain part of an execution should be rolled back or committed. The current implementation seems to roll back anything after the transation was started, even behind the try / catch block.

Where can we find further information about these decisions any why they were made? I'd clearly vote to allow explicit commits where it makes sense!

Thanks :)

Reading the docs at https://www.drupal.org/docs/drupal-apis/database-api/database-transactions I'm totally sure that

// Commit the transaction by unsetting the $transaction variable.
unset($transaction);

is totally unintuitive and against what all other frameworks, especially PDO, Doctrine, etc. do:

What's expected here is ->commit(); and not unset(). That should be hidden behind the API or whatever. Current unset() implementation might be technically perfect, but is bad DX.

I'd bet that nobody will intuitively understand

unset($transaction);

without a comment! That may lead to crazy results...

mondrake’s picture

Did not see this before, there’s now a MR for this built on top of the new TransactionManager in D10.2

Anybody’s picture

Version: 10.0.x-dev » 11.x-dev