TransactionNameNonUniqueException: A transaction named drupal_transaction is already in use

Created on 14 May 2024, 7 months ago
Updated 10 August 2024, 4 months ago

Problem/Motivation

I have a custom module that needs to send article information (title, url, etc) to an external REST API after every update. It uses post/root transaction callback to do it (after DB transaction) and worked fine with 10.1.x. After updating to latest 10.2.x tests are failing with following error:

Drupal\Core\Database\TransactionNameNonUniqueException: A transaction named drupal_transaction is already in use. Active stack: 66434febcfa244.10114297\drupal_transaction

With assertions enabled the error message is:

PHPUnit\Framework\Exception: PHP Fatal error:  Uncaught AssertionError: Transaction $stack was not empty. array (
  '66434266067936.22564777' =>
  \Drupal\Core\Database\Transaction\StackItem::__set_state(array(
     'name' => 'drupal_transaction',
     'type' =>
    \Drupal\Core\Database\Transaction\StackItemType::Root,
  )),
) in drupal/core/lib/Drupal/Core/Database/Transaction/TransactionManagerBase.php:100
Stack trace:
#0 drupal/core/lib/Drupal/Core/Database/Transaction/TransactionManagerBase.php(100): assert()
#1 [internal function]: Drupal\Core\Database\Transaction\TransactionManagerBase->__destruct()
#2 {main}
  thrown in drupal/core/lib/Drupal/Core/Database/Transaction/TransactionManagerBase.php on line 100
Fatal error: Uncaught AssertionError: Transaction $stack was not empty. array (
  '66434266067936.22564777' =>
  \Drupal\Core\Database\Transaction\StackItem::__set_state(array(
     'name' => 'drupal_transaction',
     'type' =>
    \Drupal\Core\Database\Transaction\StackItemType::Root,
  )),
) in drupal/core/lib/Drupal/Core/Database/Transaction/TransactionManagerBase.php:100
Stack trace:
#0 drupal/core/lib/Drupal/Core/Database/Transaction/TransactionManagerBase.php(100): assert()
#1 [internal function]: Drupal\Core\Database\Transaction\TransactionManagerBase->__destruct()
#2 {main}
  thrown in drupal/core/lib/Drupal/Core/Database/Transaction/TransactionManagerBase.php on line 100

drupal/vendor/phpunit/phpunit/src/Framework/TestSuite.php:684
drupal/vendor/phpunit/phpunit/src/TextUI/TestRunner.php:651
drupal/vendor/phpunit/phpunit/src/TextUI/Command.php:144
drupal/vendor/phpunit/phpunit/src/TextUI/Command.php:97

According to git bisect 27bb1ff6 is the first bad commit.

If I add \Drupal::urlGenerator()->generate('<front>'); to test ::setUp() the error disappears.

Steps to reproduce

  1. Modify entity_crud_hook_test.module:
    diff --git a/core/modules/system/tests/modules/entity_crud_hook_test/entity_crud_hook_test.module b/core/modules/system/tests/modules/entity_crud_hook_test/entity_crud_hook_test.module
    index 3d6bc4c9d6..3346981cef 100644
    --- a/core/modules/system/tests/modules/entity_crud_hook_test/entity_crud_hook_test.module
    +++ b/core/modules/system/tests/modules/entity_crud_hook_test/entity_crud_hook_test.module
    @@ -94,8 +94,16 @@ function entity_crud_hook_test_file_presave() {
     /**
      * Implements hook_ENTITY_TYPE_presave() for node entities.
      */
    -function entity_crud_hook_test_node_presave() {
    -  $GLOBALS['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
    +function entity_crud_hook_test_node_presave($node) {
    +  $GLOBALS['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
    +  $db = \Drupal::database();
    +  if ($db->inTransaction()) {
    +    $db->addRootTransactionEndCallback(function ($success) use ($node) {
    +      if ($success) {
    +        $node->toUrl()->toString();
    +      }
    +    });
    +  }
     }
    
     /**
    
  2. Run test:
    SIMPLETEST_DB=sqlite://tmp/core.sqlite vendor/bin/phpunit -c core core/tests/Drupal/KernelTests/Core/Entity/EntityCrudHookTest.php --filter=testNodeHooks
    
  3. See error:
    1) Drupal\KernelTests\Core\Entity\EntityCrudHookTest::testNodeHooks
    Drupal\Core\Database\TransactionNameNonUniqueException: A transaction named drupal_transaction is already in use. Active stack: 664358001e5317.21795200\drupal_transaction
    
    drupal/core/lib/Drupal/Core/Database/Transaction/TransactionManagerBase.php:234
    drupal/core/modules/sqlite/src/Driver/Database/sqlite/Connection.php:603
    drupal/core/lib/Drupal/Core/Routing/MatcherDumper.php:119
    drupal/core/lib/Drupal/Core/ProxyClass/Routing/MatcherDumper.php:84
    drupal/core/lib/Drupal/Core/Routing/RouteBuilder.php:194
    drupal/core/lib/Drupal/Core/ProxyClass/Routing/RouteBuilder.php:83
    drupal/core/tests/Drupal/KernelTests/RouteProvider.php:31
    drupal/core/tests/Drupal/KernelTests/RouteProvider.php:48
    drupal/core/lib/Drupal/Core/Routing/UrlGenerator.php:443
    drupal/core/lib/Drupal/Core/Routing/UrlGenerator.php:276
    drupal/core/lib/Drupal/Core/Render/MetadataBubblingUrlGenerator.php:108
    drupal/core/lib/Drupal/Core/Url.php:765
    drupal/core/modules/system/tests/modules/entity_crud_hook_test/entity_crud_hook_test.module:103
    drupal/core/lib/Drupal/Core/Database/Transaction/TransactionManagerBase.php:447
    drupal/core/lib/Drupal/Core/Database/Transaction/TransactionManagerBase.php:433
    drupal/core/lib/Drupal/Core/Database/Transaction/TransactionManagerBase.php:291
    drupal/core/lib/Drupal/Core/Database/Transaction.php:93
    drupal/core/lib/Drupal/Core/Entity/EntityBase.php:354
    drupal/core/tests/Drupal/KernelTests/Core/Entity/EntityCrudHookTest.php:326
    drupal/vendor/phpunit/phpunit/src/Framework/TestResult.php:728
    

Proposed resolution

TBD

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

🐛 Bug report
Status

Active

Version

11.0 🔥

Component
Database 

Last updated about 4 hours ago

  • Maintained by
  • 🇳🇱Netherlands @daffie
Created by

🇫🇮Finland olli

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

Comments & Activities

  • Issue created by @olli
  • 🇫🇮Finland olli

    In general this happens if you add a post transaction callback with anything that starts a transaction. In this particular case it might be the route provider used in kernel tests.

    To reproduce this outside tests, add a post transaction callback that saves a node. For example:

    function example_node_presave(\Drupal\node\NodeInterface $node) {
      $db = \Drupal::database();
      if ($db->inTransaction()) {
        $db->transactionManager()->addPostTransactionCallback(function ($success) use ($node) {
          if ($success) {
            if ($node->label() === '#3447097') {
              $node->set('title', 'issue 3447097');
              $node->save();
            }
          }
        });
      }
    }
    

    Add or edit node, set title to #3447097 and save.

Production build 0.71.5 2024