Differences Between: [Versions 310 and 311] [Versions 311 and 401] [Versions 39 and 311]
1 <?php 2 3 namespace MongoDB\Operation; 4 5 use Exception; 6 use MongoDB\Driver\Exception\RuntimeException; 7 use MongoDB\Driver\Session; 8 use Throwable; 9 use function call_user_func; 10 use function time; 11 12 /** 13 * @internal 14 */ 15 class WithTransaction 16 { 17 /** @var callable */ 18 private $callback; 19 20 /** @var array */ 21 private $transactionOptions; 22 23 /** 24 * @see Session::startTransaction for supported transaction options 25 * 26 * @param callable $callback A callback that will be invoked within the transaction 27 * @param array $transactionOptions Additional options that are passed to Session::startTransaction 28 */ 29 public function __construct(callable $callback, array $transactionOptions = []) 30 { 31 $this->callback = $callback; 32 $this->transactionOptions = $transactionOptions; 33 } 34 35 /** 36 * Execute the operation in the given session 37 * 38 * This helper takes care of retrying the commit operation or the entire 39 * transaction if an error occurs. 40 * 41 * If the commit fails because of an UnknownTransactionCommitResult error, the 42 * commit is retried without re-invoking the callback. 43 * If the commit fails because of a TransientTransactionError, the entire 44 * transaction will be retried. In this case, the callback will be invoked 45 * again. It is important that the logic inside the callback is idempotent. 46 * 47 * In case of failures, the commit or transaction are retried until 120 seconds 48 * from the initial call have elapsed. After that, no retries will happen and 49 * the helper will throw the last exception received from the driver. 50 * 51 * @see Client::startSession 52 * 53 * @param Session $session A session object as retrieved by Client::startSession 54 * @return void 55 * @throws RuntimeException for driver errors while committing the transaction 56 * @throws Exception for any other errors, including those thrown in the callback 57 */ 58 public function execute(Session $session) 59 { 60 $startTime = time(); 61 62 while (true) { 63 $session->startTransaction($this->transactionOptions); 64 65 try { 66 call_user_func($this->callback, $session); 67 } catch (Throwable $e) { 68 if ($session->isInTransaction()) { 69 $session->abortTransaction(); 70 } 71 72 if ($e instanceof RuntimeException && 73 $e->hasErrorLabel('TransientTransactionError') && 74 ! $this->isTransactionTimeLimitExceeded($startTime) 75 ) { 76 continue; 77 } 78 79 throw $e; 80 } 81 82 if (! $session->isInTransaction()) { 83 // Assume callback intentionally ended the transaction 84 return; 85 } 86 87 while (true) { 88 try { 89 $session->commitTransaction(); 90 } catch (RuntimeException $e) { 91 if ($e->getCode() !== 50 /* MaxTimeMSExpired */ && 92 $e->hasErrorLabel('UnknownTransactionCommitResult') && 93 ! $this->isTransactionTimeLimitExceeded($startTime) 94 ) { 95 // Retry committing the transaction 96 continue; 97 } 98 99 if ($e->hasErrorLabel('TransientTransactionError') && 100 ! $this->isTransactionTimeLimitExceeded($startTime) 101 ) { 102 // Restart the transaction, invoking the callback again 103 continue 2; 104 } 105 106 throw $e; 107 } 108 109 // Commit was successful 110 break; 111 } 112 113 // Transaction was successful 114 break; 115 } 116 } 117 118 /** 119 * Returns whether the time limit for retrying transactions in the convenient transaction API has passed 120 * 121 * @param int $startTime The time the transaction was started 122 * @return bool 123 */ 124 private function isTransactionTimeLimitExceeded($startTime) 125 { 126 return time() - $startTime >= 120; 127 } 128 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body