PluginProbe ʕ •ᴥ•ʔ
ShareThis Dashboard for Google Analytics / 3.2.2
ShareThis Dashboard for Google Analytics v3.2.2
3.3.2 trunk 1.0.7 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.1 2.1.2 2.1.3 2.1.4 2.1.5 2.2.5 2.3.5 2.3.6 2.3.7 2.3.8 2.4.0 2.4.1 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 3.0.0 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.3.0 3.3.1
googleanalytics / lib / analytics-admin / vendor / google / apiclient / src / Task / Runner.php
googleanalytics / lib / analytics-admin / vendor / google / apiclient / src / Task Last commit date
Composer.php 3 years ago Exception.php 3 years ago Retryable.php 3 years ago Runner.php 3 years ago
Runner.php
294 lines
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 namespace Google\Task;
19
20 use Google\Service\Exception as GoogleServiceException;
21 use Google\Task\Exception as GoogleTaskException;
22
23 /**
24 * A task runner with exponential backoff support.
25 *
26 * @see https://developers.google.com/drive/web/handle-errors#implementing_exponential_backoff
27 */
28 class Runner
29 {
30 const TASK_RETRY_NEVER = 0;
31 const TASK_RETRY_ONCE = 1;
32 const TASK_RETRY_ALWAYS = -1;
33
34 /**
35 * @var integer $maxDelay The max time (in seconds) to wait before a retry.
36 */
37 private $maxDelay = 60;
38
39 /**
40 * @var integer $delay The previous delay from which the next is calculated.
41 */
42 private $delay = 1;
43
44 /**
45 * @var integer $factor The base number for the exponential back off.
46 */
47 private $factor = 2;
48
49 /**
50 * @var float $jitter A random number between -$jitter and $jitter will be
51 * added to $factor on each iteration to allow for a better distribution of
52 * retries.
53 */
54 private $jitter = 0.5;
55
56 /**
57 * @var integer $attempts The number of attempts that have been tried so far.
58 */
59 private $attempts = 0;
60
61 /**
62 * @var integer $maxAttempts The max number of attempts allowed.
63 */
64 private $maxAttempts = 1;
65
66 /**
67 * @var callable $action The task to run and possibly retry.
68 */
69 private $action;
70
71 /**
72 * @var array $arguments The task arguments.
73 */
74 private $arguments;
75
76 /**
77 * @var array $retryMap Map of errors with retry counts.
78 */
79 protected $retryMap = [
80 '500' => self::TASK_RETRY_ALWAYS,
81 '503' => self::TASK_RETRY_ALWAYS,
82 'rateLimitExceeded' => self::TASK_RETRY_ALWAYS,
83 'userRateLimitExceeded' => self::TASK_RETRY_ALWAYS,
84 6 => self::TASK_RETRY_ALWAYS, // CURLE_COULDNT_RESOLVE_HOST
85 7 => self::TASK_RETRY_ALWAYS, // CURLE_COULDNT_CONNECT
86 28 => self::TASK_RETRY_ALWAYS, // CURLE_OPERATION_TIMEOUTED
87 35 => self::TASK_RETRY_ALWAYS, // CURLE_SSL_CONNECT_ERROR
88 52 => self::TASK_RETRY_ALWAYS, // CURLE_GOT_NOTHING
89 'lighthouseError' => self::TASK_RETRY_NEVER
90 ];
91
92 /**
93 * Creates a new task runner with exponential backoff support.
94 *
95 * @param array $config The task runner config
96 * @param string $name The name of the current task (used for logging)
97 * @param callable $action The task to run and possibly retry
98 * @param array $arguments The task arguments
99 * @throws \Google\Task\Exception when misconfigured
100 */
101 // @phpstan-ignore-next-line
102 public function __construct(
103 $config,
104 $name,
105 $action,
106 array $arguments = []
107 ) {
108 if (isset($config['initial_delay'])) {
109 if ($config['initial_delay'] < 0) {
110 throw new GoogleTaskException(
111 'Task configuration `initial_delay` must not be negative.'
112 );
113 }
114
115 $this->delay = $config['initial_delay'];
116 }
117
118 if (isset($config['max_delay'])) {
119 if ($config['max_delay'] <= 0) {
120 throw new GoogleTaskException(
121 'Task configuration `max_delay` must be greater than 0.'
122 );
123 }
124
125 $this->maxDelay = $config['max_delay'];
126 }
127
128 if (isset($config['factor'])) {
129 if ($config['factor'] <= 0) {
130 throw new GoogleTaskException(
131 'Task configuration `factor` must be greater than 0.'
132 );
133 }
134
135 $this->factor = $config['factor'];
136 }
137
138 if (isset($config['jitter'])) {
139 if ($config['jitter'] <= 0) {
140 throw new GoogleTaskException(
141 'Task configuration `jitter` must be greater than 0.'
142 );
143 }
144
145 $this->jitter = $config['jitter'];
146 }
147
148 if (isset($config['retries'])) {
149 if ($config['retries'] < 0) {
150 throw new GoogleTaskException(
151 'Task configuration `retries` must not be negative.'
152 );
153 }
154 $this->maxAttempts += $config['retries'];
155 }
156
157 if (!is_callable($action)) {
158 throw new GoogleTaskException(
159 'Task argument `$action` must be a valid callable.'
160 );
161 }
162
163 $this->action = $action;
164 $this->arguments = $arguments;
165 }
166
167 /**
168 * Checks if a retry can be attempted.
169 *
170 * @return boolean
171 */
172 public function canAttempt()
173 {
174 return $this->attempts < $this->maxAttempts;
175 }
176
177 /**
178 * Runs the task and (if applicable) automatically retries when errors occur.
179 *
180 * @return mixed
181 * @throws \Google\Service\Exception on failure when no retries are available.
182 */
183 public function run()
184 {
185 while ($this->attempt()) {
186 try {
187 return call_user_func_array($this->action, $this->arguments);
188 } catch (GoogleServiceException $exception) {
189 $allowedRetries = $this->allowedRetries(
190 $exception->getCode(),
191 $exception->getErrors()
192 );
193
194 if (!$this->canAttempt() || !$allowedRetries) {
195 throw $exception;
196 }
197
198 if ($allowedRetries > 0) {
199 $this->maxAttempts = min(
200 $this->maxAttempts,
201 $this->attempts + $allowedRetries
202 );
203 }
204 }
205 }
206 }
207
208 /**
209 * Runs a task once, if possible. This is useful for bypassing the `run()`
210 * loop.
211 *
212 * NOTE: If this is not the first attempt, this function will sleep in
213 * accordance to the backoff configurations before running the task.
214 *
215 * @return boolean
216 */
217 public function attempt()
218 {
219 if (!$this->canAttempt()) {
220 return false;
221 }
222
223 if ($this->attempts > 0) {
224 $this->backOff();
225 }
226
227 $this->attempts++;
228
229 return true;
230 }
231
232 /**
233 * Sleeps in accordance to the backoff configurations.
234 */
235 private function backOff()
236 {
237 $delay = $this->getDelay();
238
239 usleep((int) ($delay * 1000000));
240 }
241
242 /**
243 * Gets the delay (in seconds) for the current backoff period.
244 *
245 * @return int
246 */
247 private function getDelay()
248 {
249 $jitter = $this->getJitter();
250 $factor = $this->attempts > 1 ? $this->factor + $jitter : 1 + abs($jitter);
251
252 return $this->delay = min($this->maxDelay, $this->delay * $factor);
253 }
254
255 /**
256 * Gets the current jitter (random number between -$this->jitter and
257 * $this->jitter).
258 *
259 * @return float
260 */
261 private function getJitter()
262 {
263 return $this->jitter * 2 * mt_rand() / mt_getrandmax() - $this->jitter;
264 }
265
266 /**
267 * Gets the number of times the associated task can be retried.
268 *
269 * NOTE: -1 is returned if the task can be retried indefinitely
270 *
271 * @return integer
272 */
273 public function allowedRetries($code, $errors = [])
274 {
275 if (isset($this->retryMap[$code])) {
276 return $this->retryMap[$code];
277 }
278
279 if (
280 !empty($errors) &&
281 isset($errors[0]['reason'], $this->retryMap[$errors[0]['reason']])
282 ) {
283 return $this->retryMap[$errors[0]['reason']];
284 }
285
286 return 0;
287 }
288
289 public function setRetryMap($retryMap)
290 {
291 $this->retryMap = $retryMap;
292 }
293 }
294