/vendor/google/apiclient/src/Google/Task/Runner.php

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