Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
<?php

namespace MongoDB\Operation;

use Exception;
use MongoDB\Driver\Exception\RuntimeException;
use MongoDB\Driver\Session;
use Throwable;
>
use function call_user_func; use function time; /** * @internal */ class WithTransaction { /** @var callable */ private $callback; /** @var array */ private $transactionOptions; /** * @see Session::startTransaction for supported transaction options * * @param callable $callback A callback that will be invoked within the transaction * @param array $transactionOptions Additional options that are passed to Session::startTransaction */ public function __construct(callable $callback, array $transactionOptions = []) { $this->callback = $callback; $this->transactionOptions = $transactionOptions; } /** * Execute the operation in the given session * * This helper takes care of retrying the commit operation or the entire * transaction if an error occurs. * * If the commit fails because of an UnknownTransactionCommitResult error, the * commit is retried without re-invoking the callback. * If the commit fails because of a TransientTransactionError, the entire * transaction will be retried. In this case, the callback will be invoked * again. It is important that the logic inside the callback is idempotent. * * In case of failures, the commit or transaction are retried until 120 seconds * from the initial call have elapsed. After that, no retries will happen and * the helper will throw the last exception received from the driver. * * @see Client::startSession * * @param Session $session A session object as retrieved by Client::startSession
< * @return void
* @throws RuntimeException for driver errors while committing the transaction * @throws Exception for any other errors, including those thrown in the callback */
< public function execute(Session $session)
> public function execute(Session $session): void
{ $startTime = time(); while (true) { $session->startTransaction($this->transactionOptions); try { call_user_func($this->callback, $session); } catch (Throwable $e) { if ($session->isInTransaction()) { $session->abortTransaction(); }
< if ($e instanceof RuntimeException &&
> if ( > $e instanceof RuntimeException &&
$e->hasErrorLabel('TransientTransactionError') && ! $this->isTransactionTimeLimitExceeded($startTime) ) { continue; } throw $e; } if (! $session->isInTransaction()) { // Assume callback intentionally ended the transaction return; } while (true) { try { $session->commitTransaction(); } catch (RuntimeException $e) {
< if ($e->getCode() !== 50 /* MaxTimeMSExpired */ &&
> if ( > $e->getCode() !== 50 /* MaxTimeMSExpired */ &&
$e->hasErrorLabel('UnknownTransactionCommitResult') && ! $this->isTransactionTimeLimitExceeded($startTime) ) { // Retry committing the transaction continue; }
< if ($e->hasErrorLabel('TransientTransactionError') &&
> if ( > $e->hasErrorLabel('TransientTransactionError') &&
! $this->isTransactionTimeLimitExceeded($startTime) ) { // Restart the transaction, invoking the callback again continue 2; } throw $e; } // Commit was successful break; } // Transaction was successful break; } } /** * Returns whether the time limit for retrying transactions in the convenient transaction API has passed * * @param int $startTime The time the transaction was started
< * @return bool
*/
< private function isTransactionTimeLimitExceeded($startTime)
> private function isTransactionTimeLimitExceeded(int $startTime): bool
{ return time() - $startTime >= 120; } }