See Release Notes
Long Term Support Release
Differences Between: [Versions 401 and 402] [Versions 401 and 403]
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 class Google_Task_Runner 28 { 29 /** 30 * @var integer $maxDelay The max time (in seconds) to wait before a retry. 31 */ 32 private $maxDelay = 60; 33 /** 34 * @var integer $delay The previous delay from which the next is calculated. 35 */ 36 private $delay = 1; 37 38 /** 39 * @var integer $factor The base number for the exponential back off. 40 */ 41 private $factor = 2; 42 /** 43 * @var float $jitter A random number between -$jitter and $jitter will be 44 * added to $factor on each iteration to allow for a better distribution of 45 * retries. 46 */ 47 private $jitter = 0.5; 48 49 /** 50 * @var integer $attempts The number of attempts that have been tried so far. 51 */ 52 private $attempts = 0; 53 /** 54 * @var integer $maxAttempts The max number of attempts allowed. 55 */ 56 private $maxAttempts = 1; 57 58 /** 59 * @var Google_Client $client The current API client. 60 */ 61 private $client; 62 63 /** 64 * @var string $name The name of the current task (used for logging). 65 */ 66 private $name; 67 /** 68 * @var callable $action The task to run and possibly retry. 69 */ 70 private $action; 71 /** 72 * @var array $arguments The task arguments. 73 */ 74 private $arguments; 75 76 /** 77 * Creates a new task runner with exponential backoff support. 78 * 79 * @param Google_Client $client The current API client 80 * @param string $name The name of the current task (used for logging) 81 * @param callable $action The task to run and possibly retry 82 * @param array $arguments The task arguments 83 * @throws Google_Task_Exception when misconfigured 84 */ 85 public function __construct( 86 Google_Client $client, 87 $name, 88 $action, 89 array $arguments = array() 90 ) { 91 $config = (array) $client->getClassConfig('Google_Task_Runner'); 92 93 if (isset($config['initial_delay'])) { 94 if ($config['initial_delay'] < 0) { 95 throw new Google_Task_Exception( 96 'Task configuration `initial_delay` must not be negative.' 97 ); 98 } 99 100 $this->delay = $config['initial_delay']; 101 } 102 103 if (isset($config['max_delay'])) { 104 if ($config['max_delay'] <= 0) { 105 throw new Google_Task_Exception( 106 'Task configuration `max_delay` must be greater than 0.' 107 ); 108 } 109 110 $this->maxDelay = $config['max_delay']; 111 } 112 113 if (isset($config['factor'])) { 114 if ($config['factor'] <= 0) { 115 throw new Google_Task_Exception( 116 'Task configuration `factor` must be greater than 0.' 117 ); 118 } 119 120 $this->factor = $config['factor']; 121 } 122 123 if (isset($config['jitter'])) { 124 if ($config['jitter'] <= 0) { 125 throw new Google_Task_Exception( 126 'Task configuration `jitter` must be greater than 0.' 127 ); 128 } 129 130 $this->jitter = $config['jitter']; 131 } 132 133 if (isset($config['retries'])) { 134 if ($config['retries'] < 0) { 135 throw new Google_Task_Exception( 136 'Task configuration `retries` must not be negative.' 137 ); 138 } 139 $this->maxAttempts += $config['retries']; 140 } 141 142 if (!is_callable($action)) { 143 throw new Google_Task_Exception( 144 'Task argument `$action` must be a valid callable.' 145 ); 146 } 147 148 $this->name = $name; 149 $this->client = $client; 150 $this->action = $action; 151 $this->arguments = $arguments; 152 } 153 154 /** 155 * Checks if a retry can be attempted. 156 * 157 * @return boolean 158 */ 159 public function canAttmpt() 160 { 161 return $this->attempts < $this->maxAttempts; 162 } 163 164 /** 165 * Runs the task and (if applicable) automatically retries when errors occur. 166 * 167 * @return mixed 168 * @throws Google_Task_Retryable on failure when no retries are available. 169 */ 170 public function run() 171 { 172 while ($this->attempt()) { 173 try { 174 return call_user_func_array($this->action, $this->arguments); 175 } catch (Google_Task_Retryable $exception) { 176 $allowedRetries = $exception->allowedRetries(); 177 178 if (!$this->canAttmpt() || !$allowedRetries) { 179 throw $exception; 180 } 181 182 if ($allowedRetries > 0) { 183 $this->maxAttempts = min( 184 $this->maxAttempts, 185 $this->attempts + $allowedRetries 186 ); 187 } 188 } 189 } 190 } 191 192 /** 193 * Runs a task once, if possible. This is useful for bypassing the `run()` 194 * loop. 195 * 196 * NOTE: If this is not the first attempt, this function will sleep in 197 * accordance to the backoff configurations before running the task. 198 * 199 * @return boolean 200 */ 201 public function attempt() 202 { 203 if (!$this->canAttmpt()) { 204 return false; 205 } 206 207 if ($this->attempts > 0) { 208 $this->backOff(); 209 } 210 211 $this->attempts++; 212 return true; 213 } 214 215 /** 216 * Sleeps in accordance to the backoff configurations. 217 */ 218 private function backOff() 219 { 220 $delay = $this->getDelay(); 221 222 $this->client->getLogger()->debug( 223 'Retrying task with backoff', 224 array( 225 'request' => $this->name, 226 'retry' => $this->attempts, 227 'backoff_seconds' => $delay 228 ) 229 ); 230 231 usleep($delay * 1000000); 232 } 233 234 /** 235 * Gets the delay (in seconds) for the current backoff period. 236 * 237 * @return float 238 */ 239 private function getDelay() 240 { 241 $jitter = $this->getJitter(); 242 $factor = $this->attempts > 1 ? $this->factor + $jitter : 1 + abs($jitter); 243 244 return $this->delay = min($this->maxDelay, $this->delay * $factor); 245 } 246 247 /** 248 * Gets the current jitter (random number between -$this->jitter and 249 * $this->jitter). 250 * 251 * @return float 252 */ 253 private function getJitter() 254 { 255 return $this->jitter * 2 * mt_rand() / mt_getrandmax() - $this->jitter; 256 } 257 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body