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