PluginProbe
UpdraftPlus: WP Backup & Migration Plugin / 1.16.5
UpdraftPlus: WP Backup & Migration Plugin v1.16.5
1.26.7 1.26.6 1.26.5 1.26.4 1.26.3 1.9.19 1.9.25 1.9.26 1.9.30 1.9.31 1.9.32 1.9.4 1.9.40 1.9.41 1.9.42 1.9.43 1.9.44 1.9.45 1.9.46 1.9.5 1.9.50 1.9.51 1.9.60 1.9.62 1.9.63 All 371 releases
updraftplus / includes / Google / Task / Runner.php

Runner.php in UpdraftPlus: WP Backup & Migration Plugin 1.16.5, at includes/Google/Task/Runner.php

258 lines 6.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 }
258