Allow explicit COMMIT in Transaction objects

Created on 2 November 2023, over 1 year ago

Problem/Motivation

Right now, a Transaction object in Drupal leads to a COMMIT or a RELEASE SAVEPOINT only when it goes out of scope. In practice, it's an autocommit.

We have code like

    try {
      $transaction = $this->connection->startTransaction();
      foreach ($this->insertValues as $insert_values) {
        $stmt->execute($insert_values, $this->queryOptions);
        ...
      }
    }
    catch (\Exception $e) {
      if (isset($transaction)) {
        // One of the INSERTs failed, rollback the whole batch.
        $transaction->rollBack();
      }
      // Rethrow the exception for the calling code.
      throw $e;
    }

or in other cases something like

    try {
      $transaction = $this->connection->startTransaction();
      foreach ($this->insertValues as $insert_values) {
        $stmt->execute($insert_values, $this->queryOptions);
        ...
      }
      unset($transaction);
    }
    catch (\Exception $e) {
      ....
    }

It works, but I personally find unset($transaction) counterintuitive as to be implying a COMMIT on the db.

I propose here to add the possibility to explicitly commit a Transaction object, as an opt-in vs. current behavior. Directionally, removing current behavior and requesting an explicit commit IMHO should be the final goal, but this is too far fetching now.

With the newly introduced TransactionManager, it may be simpler to achieve this now, and we would get into something like

    try {
      $transaction = $this->connection->startTransaction();
      foreach ($this->insertValues as $insert_values) {
        $stmt->execute($insert_values, $this->queryOptions);
        ...
      }
      $transaction->commit();
    }
    catch (\Exception $e) {
      // One of the INSERTs failed, rollback the whole batch and rethrow the exception for the calling code.
      $transaction->rollBack();
      throw $e;
    }

which is also more readable to me.

Proposed resolution

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

โœจ Feature request
Status

Active

Version

11.0 ๐Ÿ”ฅ

Component
Databaseย  โ†’

Last updated 1 day ago

  • Maintained by
  • ๐Ÿ‡ณ๐Ÿ‡ฑNetherlands @daffie
Created by

๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @mondrake
  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น
  • ๐Ÿ‡ณ๐Ÿ‡ฑNetherlands daffie

    +1 for me.

    I really dislike unset($transaction); for the implicit commit.

  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น
  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    OK, let's try this then.

  • Merge request !5244Allow explicit Transaction::commit โ†’ (Open) created by mondrake
  • Status changed to Needs review over 1 year ago
  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น
  • First commit to issue fork.
  • Status changed to Needs work over 1 year ago
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    Rebased to run the pipeline and it was all green

    Ran the test-only feature and they all seemed to still pass shouldn't the base test file fail? https://git.drupalcode.org/issue/drupal-3398767/-/jobs/374406

    Seems like something that could use a change record right?

  • Status changed to Needs review over 1 year ago
  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    It should in this case, but I donโ€™t think #3395977-4: Test-only changes reverts changes to test modules โ†’ was addressed and here the change was to a test base class that got reverted too?

  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น
  • Pipeline finished with Success
    over 1 year ago
    #60938
  • Pipeline finished with Success
    over 1 year ago
    Total: 1520s
    #61018
  • Status changed to Postponed over 1 year ago
  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น
  • Pipeline finished with Failed
    over 1 year ago
    Total: 689s
    #61406
  • Pipeline finished with Failed
    over 1 year ago
    Total: 659s
    #61446
  • Pipeline finished with Failed
    over 1 year ago
    Total: 633s
    #61450
  • Pipeline finished with Failed
    over 1 year ago
    Total: 859s
    #61458
  • Pipeline finished with Success
    over 1 year ago
    Total: 3917s
    #61513
  • Pipeline finished with Success
    over 1 year ago
    Total: 960s
    #61659
  • Pipeline finished with Failed
    over 1 year ago
    Total: 197s
    #62027
  • Status changed to Needs work over 1 year ago
  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น
  • Pipeline finished with Failed
    over 1 year ago
    Total: 555s
    #62040
  • Pipeline finished with Success
    over 1 year ago
    Total: 657s
    #62043
  • Pipeline finished with Success
    over 1 year ago
    #62062
  • Pipeline finished with Failed
    over 1 year ago
    Total: 1477s
    #62079
  • Pipeline finished with Failed
    over 1 year ago
    Total: 665s
    #62144
  • Pipeline finished with Canceled
    over 1 year ago
    Total: 192s
    #62182
  • Status changed to Postponed over 1 year ago
  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    Postponed on โœจ [PP-1] Introduce a Connection::executeDdlStatement method Active . That will give us the possibility to know about an autocommit due to lack of transactional DDL support having happened BEFORE actually attempting a commit on the DB, and appropriate test coverage, too.

    Also we probably need to split the cleanup of DriverSpecificTransactionTestBase - there's a lot of bolierplate that was accumulated by copy/pasting test methods that can be removed.

  • Pipeline finished with Failed
    over 1 year ago
    Total: 527s
    #62185
  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    Filed ๐Ÿ“Œ Cleanup DriverSpecificTransactionTestBase Needs review .

  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น
  • Pipeline finished with Failed
    over 1 year ago
    Total: 514s
    #69995
  • Pipeline finished with Failed
    over 1 year ago
    Total: 519s
    #72266
  • Pipeline finished with Failed
    over 1 year ago
    Total: 699s
    #72788
  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    Note to self: when this is unblocked, do #3386263-10: Ignore, testing issue โ†’ first thing.

  • Pipeline finished with Failed
    about 1 year ago
    Total: 651s
    #121766
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States moshe weitzman Boston, MA

    We now have \Drupal\Core\Database\Database::commitAllOnShutdown. Should we keep this issue open or change it somehow?

  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    #18 this issue is about moving AWAY from ::commitAllOnShutdown() (and in general from committing during object destruction which has drawbacks).

    IMHO, ::commitAllOnShutdown() should only be transitional towards such goal.

  • Pipeline finished with Failed
    7 months ago
    Total: 248s
    #307355
  • Pipeline finished with Failed
    7 months ago
    Total: 1533s
    #307360
  • Pipeline finished with Failed
    7 months ago
    Total: 1712s
    #307411
  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    rebased

  • Assigned to mondrake
  • Status changed to Needs work 4 months ago
  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น
  • Pipeline finished with Failed
    4 months ago
    Total: 407s
    #371547
  • Pipeline finished with Failed
    4 months ago
    Total: 141s
    #371563
  • Pipeline finished with Failed
    4 months ago
    Total: 375s
    #371574
  • Pipeline finished with Failed
    4 months ago
    Total: 526s
    #371580
  • Pipeline finished with Failed
    4 months ago
    Total: 524s
    #371608
  • Pipeline finished with Failed
    4 months ago
    Total: 3999s
    #371635
  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    Updated title and IS

  • Pipeline finished with Failed
    4 months ago
    Total: 1815s
    #372278
  • Pipeline finished with Failed
    4 months ago
    Total: 733s
    #372359
  • Pipeline finished with Success
    4 months ago
    Total: 2029s
    #372509
  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น
  • Pipeline finished with Success
    4 months ago
    Total: 1099s
    #372573
  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    Green on all three core supported databases! Ready for review.

    I am bumping to Major as it's known there are problems with the current handling of autocommit in HEAD.

  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น
  • Pipeline finished with Success
    4 months ago
    Total: 3856s
    #373630
  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น
  • The Needs Review Queue Bot โ†’ tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

    This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

    Consult the Drupal Contributor Guide โ†’ to find step-by-step guides for working with issues.

  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    rebased

  • Pipeline finished with Failed
    4 months ago
    Total: 134s
    #374295
  • Pipeline finished with Success
    4 months ago
    #374310
  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น
  • Pipeline finished with Success
    3 months ago
    Total: 1120s
    #392904
  • Pipeline finished with Success
    3 months ago
    Total: 533s
    #402997
  • Pipeline finished with Failed
    about 2 months ago
    Total: 473s
    #439147
  • Issue was unassigned.
  • ๐Ÿ‡ณ๐Ÿ‡ฑNetherlands daffie
  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    Thanks for review @daffie!

    In general, I just c/p DriverSpecificTransactionTestBase into the new TransactionYieldTest, to try and keep the two test sets (one testing the commit-on-destruct behavior, one testing the commit-on-yield one) in sync, doing as few changes as possible. Anyway, I will look into your input and adjust as much as possible.

  • Pipeline finished with Failed
    about 2 months ago
    Total: 164s
    #444129
  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น
  • Pipeline finished with Success
    about 2 months ago
    Total: 2835s
    #444132
  • Pipeline finished with Success
    about 2 months ago
    Total: 4009s
    #444613
  • ๐Ÿ‡ณ๐Ÿ‡ฑNetherlands daffie

    All the code changes look good to me.
    The CI pipeline is green for all 3 databases.
    For the new method we shall need a CR.

  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น
  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น
  • ๐Ÿ‡ณ๐Ÿ‡ฑNetherlands daffie

    The CR looks good to me.
    All code changes look good to me.
    The CI pipeline is green for all 3 databases.
    For me it is RTBC.

  • Pipeline finished with Failed
    about 1 month ago
    Total: 617s
    #454976
  • Pipeline finished with Success
    about 1 month ago
    Total: 3009s
    #456405
  • Pipeline finished with Success
    about 1 month ago
    Total: 3776s
    #457778
  • Pipeline finished with Success
    26 days ago
    Total: 2974s
    #461779
  • Pipeline finished with Success
    22 days ago
    #465861
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom alexpott ๐Ÿ‡ช๐Ÿ‡บ๐ŸŒ

    Added some comments to the MR.

    I think we should consider having a commit method for situations where you know that you've created the root transaction. I think we should also consider having a startRootTransaction() method to support this. So you can have very explicit code. Yield is a tricky word to use as it already has quite a bit of meaning in PHP and normally means to yield a value as part of an iteration.

  • Pipeline finished with Canceled
    11 days ago
    Total: 73s
    #473968
  • Pipeline finished with Failed
    11 days ago
    Total: 3522s
    #473969
  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    Thanks for review @alexpott.

    Transaction does not have an interface for now.

    We could add an additional Connection::startSavepoint() method, and Transaction::commit() + Transaction::releaseSavepoint() methods. That will somehow force how transactions are managed in code, though, in the sense that devs will have to be aware of whether they are working within the root transaction or within a savepoint. If some code starts a root transaction, you would not be able to encapsulate that code in a higher method if that will try to start a root transaction as well.

    The beauty of the MR here is that you do not really care - you always call Connection::startTransaction and then it's the manager deciding what to do depending on the stack depth.

    I am not going to make changes until we have agreed on a plan forward.

  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    NR for the proposal in #38.

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom alexpott ๐Ÿ‡ช๐Ÿ‡บ๐ŸŒ

    I am not going to make changes until we have agreed on a plan forward.

    Yeah 100%.

    One thing I'm thinking about is rollback... Atm we call rollback on a transaction and that will go back to your previous savepoint or the end the root transaction without a commit - right? So given rollback already does 2 different things... why don't we rename yield to commit() and document that it will commit the root transaction or release the savepoint depending on transaction depth as we already have this behaviour with rollback... what do you think. My concern is the yield is an odd word wrt to databases and transactions.

  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    Apologies if this sounds a bit scholarly... not my intention.

    One thing I'm thinking about is rollback... Atm we call rollback on a transaction and that will go back to your previous savepoint or the end the root transaction without a commit - right? So given rollback already does 2 different things...

    Right. But IMHO we should not assimilate the combination commit/release savepoint to the rollback. Commit permanently saves data to the DBMS, release savepoint doesn't. Rollback, doesn't matter whether to a savepoint or to the begin of the transactions, just returns to a temporary state within the transaction, it does not persist anything. Commit is very a specific, unambiguous action.

    why don't we rename yield to commit() and document that it will commit the root transaction or release the savepoint depending on transaction depth as we already have this behaviour with rollback...

    In an earlier version of the MR, it was like that. Then, I pondered the above and thought that a 'commit' method that does not really persist anything may be seen as a WTF - anyone not aware of all the details/not reading the docs would call 'commit' on a savepoint and then complain data are not saved.

    For me if we add a Transaction::commit() method we should not be allowing ambiguity: it must persist the data as everyone would expect.

    So, how about this next proposal:

    • once instantiating a new Transaction object, store the transaction type in it
    • add a Transaction::commit() and a Transaction::releaseSavepoint() methods, to be used respectively for... what they respectively mean
    • in case we need to let the manager decide whether to commit or release a savepoint, we could still have a method like ::yield() with an appropriate rename, or we could have a simple match construct in calling code like
      match ($transaction->type) {
          StackItemType::Root => $transaction->commit(),
          StackItemType::Savepoint => $transaction->releaseSavepoint(),
      }
  • ๐Ÿ‡ณ๐Ÿ‡ฑNetherlands daffie

    I like the change to only using Transaction::commit() or Transaction::releaseSavepoint() and having no Transaction::yield(). Developer need to learn the difference between to two.
    The proposed changes look good to me.

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom alexpott ๐Ÿ‡ช๐Ÿ‡บ๐ŸŒ

    I like the commitOrRelease name way more than yield. I like specific methods for commit and releaseSavepoint being public.My only concern is that commit not being an alias for commitOrRelease() might be problematic - because ATM DB transaction users don't need to be concerned if something wraps their code in another transaction because the abstraction layer hides this complexity away from them.

  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    So we just go ahead renaming yield() to commitOrRelease() without implementing commit() and releaseSavepoint()?

    Iโ€™d be +1 on that as I really value not having to care about what method to use, and the manager caring about that.

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom alexpott ๐Ÿ‡ช๐Ÿ‡บ๐ŸŒ

    Yeah lets do #44. We can add explicit methods later if needs be.

  • ๐Ÿ‡ณ๐Ÿ‡ฑNetherlands daffie

    Letโ€™s do #44

  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    On ir.

  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    Updated IS with #44

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom catch

    fwiw I was behind on this issue and only read the last few comments just now, but #44 sounds good and means we don't need to worry about implications of #43, so extra +1.

  • Pipeline finished with Canceled
    2 days ago
    Total: 338s
    #480927
  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    Done

  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    Updated CR with the latest approach.

  • Pipeline finished with Success
    2 days ago
    Total: 5615s
    #480934
  • ๐Ÿ‡ณ๐Ÿ‡ฑNetherlands daffie

    All use of the method yield() has been replaced with commitOrRelease().
    The IS and the CR are updated.
    Back to RTBC.

  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    Please consider crediting contributors to ๐Ÿ“Œ Transactions should be allowed to be committed explicitly Needs work , that will become outdated when this is committed.

  • ๐Ÿ‡ฎ๐Ÿ‡นItaly mondrake ๐Ÿ‡ฎ๐Ÿ‡น

    fwiw, the ๐Ÿ“Œ [PP-1] Convert all transactions to explicit COMMIT Postponed follow-up MR is built on top of this MR, and passes all tests.

Production build 0.71.5 2024