src/Core/Framework/DataAbstractionLayer/Doctrine/RetryableTransaction.php line 29

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Framework\DataAbstractionLayer\Doctrine;
  3. use Doctrine\DBAL\Connection;
  4. use Doctrine\DBAL\Exception\RetryableException;
  5. use Shopware\Core\Framework\Log\Package;
  6. #[Package('core')]
  7. class RetryableTransaction
  8. {
  9.     /**
  10.      * Executes the given closure inside a DBAL transaction. In case of a deadlock (RetryableException) the transaction
  11.      * is rolled back and the closure will be retried. Because it may run multiple times the closure should not cause
  12.      * any side effects outside of its own scope.
  13.      *
  14.      * @return mixed
  15.      */
  16.     public static function retryable(Connection $connection\Closure $closure)
  17.     {
  18.         return self::retry($connection$closure0);
  19.     }
  20.     /**
  21.      * @param \Closure(Connection):mixed $closure The function to execute transactionally.
  22.      *
  23.      * @return mixed
  24.      */
  25.     private static function retry(Connection $connection\Closure $closureint $counter)
  26.     {
  27.         ++$counter;
  28.         try {
  29.             return $connection->transactional($closure);
  30.         } catch (RetryableException $retryableException) {
  31.             if ($connection->getTransactionNestingLevel() > 0) {
  32.                 // If this RetryableTransaction was executed inside another transaction, do not retry this nested
  33.                 // transaction. Remember that the whole (outermost) transaction was already rolled back by the database
  34.                 // when any RetryableException is thrown.
  35.                 // Rethrow the exception here so only the outermost transaction is retried which in turn includes this
  36.                 // nested transaction.
  37.                 throw $retryableException;
  38.             }
  39.             if ($counter 10) {
  40.                 throw $retryableException;
  41.             }
  42.             // Randomize sleep to prevent same execution delay for multiple statements
  43.             usleep(random_int(1020));
  44.             return self::retry($connection$closure$counter);
  45.         }
  46.     }
  47. }