Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]
1 <?php 2 /* 3 * Copyright 2014 Google Inc. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 if (!class_exists('Google_Client')) { 19 require_once dirname(__FILE__) . '/../autoload.php'; 20 } 21 22 /** 23 * A task runner with exponential backoff support. 24 * 25 * @see https://developers.google.com/drive/web/handle-errors#implementing_exponential_backoff 26 */ 27 #[AllowDynamicProperties] 28 class Google_Task_Runner 29 { 30 /** 31 * @var integer $maxDelay The max time (in seconds) to wait before a retry. 32 */ 33 private $maxDelay = 60; 34 /** 35 * @var integer $delay The previous delay from which the next is calculated. 36 */ 37 private $delay = 1; 38 39 /** 40 * @var integer $factor The base number for the exponential back off. 41 */ 42 private $factor = 2; 43 /** 44 * @var float $jitter A random number between -$jitter and $jitter will be 45 * added to $factor on each iteration to allow for a better distribution of 46 * retries. 47 */ 48 private $jitter = 0.5; 49 50 /** 51 * @var integer $attempts The number of attempts that have been tried so far. 52 */ 53 private $attempts = 0; 54 /** 55 * @var integer $maxAttempts The max number of attempts allowed. 56 */ 57 private $maxAttempts = 1; 58 59 /** 60 * @var Google_Client $client The current API client. 61 */ 62 private $client; 63 64 /** 65 * @var string $name The name of the current task (used for logging). 66 */ 67 private $name; 68 /** 69 * @var callable $action The task to run and possibly retry. 70 */ 71 private $action; 72 /** 73 * @var array $arguments The task arguments. 74 */ 75 private $arguments; 76 77 /** 78 * Creates a new task runner with exponential backoff support. 79 * 80 * @param Google_Client $client The current API client 81 * @param string $name The name of the current task (used for logging) 82 * @param callable $action The task to run and possibly retry 83 * @param array $arguments The task arguments 84 * @throws Google_Task_Exception when misconfigured 85 */ 86 public function __construct( 87 Google_Client $client, 88 $name, 89 $action, 90 array $arguments = array() 91 ) { 92 $config = (array) $client->getClassConfig('Google_Task_Runner'); 93 94 if (isset($config['initial_delay'])) { 95 if ($config['initial_delay'] < 0) { 96 throw new Google_Task_Exception( 97 'Task configuration `initial_delay` must not be negative.' 98 ); 99 } 100 101 $this->delay = $config['initial_delay']; 102 } 103 104 if (isset($config['max_delay'])) { 105 if ($config['max_delay'] <= 0) { 106 throw new Google_Task_Exception( 107 'Task configuration `max_delay` must be greater than 0.' 108 ); 109 } 110 111 $this->maxDelay = $config['max_delay']; 112 } 113 114 if (isset($config['factor'])) { 115 if ($config['factor'] <= 0) { 116 throw new Google_Task_Exception( 117 'Task configuration `factor` must be greater than 0.' 118 ); 119 } 120 121 $this->factor = $config['factor']; 122 } 123 124 if (isset($config['jitter'])) { 125 if ($config['jitter'] <= 0) { 126 throw new Google_Task_Exception( 127 'Task configuration `jitter` must be greater than 0.' 128 ); 129 } 130 131 $this->jitter = $config['jitter']; 132 } 133 134 if (isset($config['retries'])) { 135 if ($config['retries'] < 0) { 136 throw new Google_Task_Exception( 137 'Task configuration `retries` must not be negative.' 138 ); 139 } 140 $this->maxAttempts += $config['retries']; 141 } 142 143 if (!is_callable($action)) { 144 throw new Google_Task_Exception( 145 'Task argument `$action` must be a valid callable.' 146 ); 147 } 148 149 $this->name = $name; 150 $this->client = $client; 151 $this->action = $action; 152 $this->arguments = $arguments; 153 } 154 155 /** 156 * Checks if a retry can be attempted. 157 * 158 * @return boolean 159 */ 160 public function canAttmpt() 161 { 162 return $this->attempts < $this->maxAttempts; 163 } 164 165 /** 166 * Runs the task and (if applicable) automatically retries when errors occur. 167 * 168 * @return mixed 169 * @throws Google_Task_Retryable on failure when no retries are available. 170 */ 171 public function run() 172 { 173 while ($this->attempt()) { 174 try { 175 return call_user_func_array($this->action, $this->arguments); 176 } catch (Google_Task_Retryable $exception) { 177 $allowedRetries = $exception->allowedRetries(); 178 179 if (!$this->canAttmpt() || !$allowedRetries) { 180 throw $exception; 181 } 182 183 if ($allowedRetries > 0) { 184 $this->maxAttempts = min( 185 $this->maxAttempts, 186 $this->attempts + $allowedRetries 187 ); 188 } 189 } 190 } 191 } 192 193 /** 194 * Runs a task once, if possible. This is useful for bypassing the `run()` 195 * loop. 196 * 197 * NOTE: If this is not the first attempt, this function will sleep in 198 * accordance to the backoff configurations before running the task. 199 * 200 * @return boolean 201 */ 202 public function attempt() 203 { 204 if (!$this->canAttmpt()) { 205 return false; 206 } 207 208 if ($this->attempts > 0) { 209 $this->backOff(); 210 } 211 212 $this->attempts++; 213 return true; 214 } 215 216 /** 217 * Sleeps in accordance to the backoff configurations. 218 */ 219 private function backOff() 220 { 221 $delay = $this->getDelay(); 222 223 $this->client->getLogger()->debug( 224 'Retrying task with backoff', 225 array( 226 'request' => $this->name, 227 'retry' => $this->attempts, 228 'backoff_seconds' => $delay 229 ) 230 ); 231 232 usleep($delay * 1000000); 233 } 234 235 /** 236 * Gets the delay (in seconds) for the current backoff period. 237 * 238 * @return float 239 */ 240 private function getDelay() 241 { 242 $jitter = $this->getJitter(); 243 $factor = $this->attempts > 1 ? $this->factor + $jitter : 1 + abs($jitter); 244 245 return $this->delay = min($this->maxDelay, $this->delay * $factor); 246 } 247 248 /** 249 * Gets the current jitter (random number between -$this->jitter and 250 * $this->jitter). 251 * 252 * @return float 253 */ 254 private function getJitter() 255 { 256 return $this->jitter * 2 * mt_rand() / mt_getrandmax() - $this->jitter; 257 } 258 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body