PluginProbe
Media Cloud Sync / 1.2.10
Media Cloud Sync v1.2.10
1.4.2 1.4.1 1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 All 36 releases
media-cloud-sync / includes / sdk / google / google / cloud-storage / src / Connection / RetryTrait.php

RetryTrait.php in Media Cloud Sync 1.2.10, at includes/sdk/google/google/cloud-storage/src/Connection/RetryTrait.php

174 lines 7.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Copyright 2022 Google Inc. All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18 namespace Dudlewebs\WPMCS\Google\Cloud\Storage\Connection;
19
20 use Dudlewebs\WPMCS\Google\Cloud\Storage\StorageClient;
21 /**
22 * Trait which provides helper methods for retry logic.
23 *
24 * @internal
25 */
26 trait RetryTrait
27 {
28 /**
29 * The HTTP codes that will be retried by our custom retry function.
30 * @var array
31 */
32 private static $httpRetryCodes = [
33 0,
34 // connetion-refused OR connection-reset gives status code of 0
35 200,
36 // partial download cases
37 408,
38 429,
39 500,
40 502,
41 503,
42 504,
43 ];
44 /**
45 * The operations which can be retried without any conditions
46 * (Idempotent)
47 * @var array
48 */
49 private static $idempotentOps = ['bucket_acl.get', 'bucket_acl.list', 'buckets.delete', 'buckets.get', 'buckets.getIamPolicy', 'buckets.insert', 'buckets.list', 'buckets.lockRetentionPolicy', 'buckets.testIamPermissions', 'default_object_acl.get', 'default_object_acl.list', 'hmacKey.delete', 'hmacKey.get', 'hmacKey.list', 'notifications.delete', 'notifications.get', 'notifications.list', 'object_acl.get', 'object_acl.list', 'objects.get', 'objects.list', 'serviceaccount.get'];
50 /**
51 * The operations which can be retried with specific conditions
52 * (Conditionally idempotent)
53 * @var array
54 */
55 private static $condIdempotentOps = [
56 'buckets.patch' => ['ifMetagenerationMatch', 'etag'],
57 // Currently etag is not supported, so this preCondition never available
58 'buckets.setIamPolicy' => ['etag'],
59 'buckets.update' => ['ifMetagenerationMatch', 'etag'],
60 'hmacKey.update' => ['etag'],
61 'objects.compose' => ['ifGenerationMatch'],
62 'objects.copy' => ['ifGenerationMatch'],
63 'objects.delete' => ['ifGenerationMatch'],
64 'objects.insert' => ['ifGenerationMatch', 'ifGenerationNotMatch'],
65 'objects.patch' => ['ifMetagenerationMatch', 'etag'],
66 'objects.rewrite' => ['ifGenerationMatch'],
67 'objects.update' => ['ifMetagenerationMatch'],
68 ];
69 /**
70 * Retry strategies which enforce certain behaviour like:
71 * - Always retrying a call when an exception occurs(within the limits of 'max retries').
72 * - Never retrying a call when an exception occurs.
73 * - Retrying only when the operation is considered idempotent(default).
74 * These configurations are supplied for per api call basis.
75 *
76 */
77 /**
78 * Header that identifies a specific request hash. The
79 * hash needs to stay the same for multiple retries.
80 */
81 private static $INVOCATION_ID_HEADER = 'gccl-invocation-id';
82 /**
83 * Header that identifies the attempt count for a request. The
84 * value will increment by 1 with every retry.
85 */
86 private static $ATTEMPT_COUNT_HEADER = 'gccl-attempt-count';
87 /**
88 * Return a retry decider function.
89 *
90 * @param string $resource resource name, eg: buckets.
91 * @param string $method method name, eg: get
92 * @param array $args
93 * @return callable
94 */
95 private function getRestRetryFunction($resource, $method, array $args)
96 {
97 if (isset($args['restRetryFunction'])) {
98 return $args['restRetryFunction'];
99 }
100 $methodName = sprintf('%s.%s', $resource, $method);
101 $isOpIdempotent = in_array($methodName, self::$idempotentOps);
102 $preconditionNeeded = array_key_exists($methodName, self::$condIdempotentOps);
103 $preconditionSupplied = $this->isPreConditionSupplied($methodName, $args);
104 $retryStrategy = isset($args['retryStrategy']) ? $args['retryStrategy'] : StorageClient::RETRY_IDEMPOTENT;
105 return function (\Exception $exception) use ($isOpIdempotent, $preconditionNeeded, $preconditionSupplied, $retryStrategy) {
106 return $this->retryDeciderFunction($exception, $isOpIdempotent, $preconditionNeeded, $preconditionSupplied, $retryStrategy);
107 };
108 }
109 /**
110 * This function returns true when the user given
111 * precondtions ($preConditions) has values that are present
112 * in the precondition map ($this->condIdempotentMap) for that method.
113 * eg: condIdempotentMap has entry 'objects.copy' => ['ifGenerationMatch'],
114 * if the user has given 'ifGenerationMatch' in the 'objects.copy' operation,
115 * it will be available in the $preConditions
116 * as an array ['ifGenerationMatch']. This makes the array_intersect
117 * function return a non empty result and this function returns true.
118 *
119 * @param string $methodName method name, eg: buckets.get.
120 * @param array $args arguments which include preconditions provided,
121 * eg: ['ifGenerationMatch' => 0].
122 * @return bool
123 */
124 private function isPreConditionSupplied($methodName, array $args)
125 {
126 if (isset(self::$condIdempotentOps[$methodName])) {
127 // return true if required precondition are given.
128 return !empty(array_intersect(self::$condIdempotentOps[$methodName], array_keys($args)));
129 }
130 return \false;
131 }
132 /**
133 * Decide whether the op needs to be retried or not.
134 *
135 * @param \Exception $exception The exception object received
136 * while sending the request.
137 * @param int $currentAttempt Current retry attempt.
138 * @param bool $isIdempotent
139 * @param bool $preconditionNeeded
140 * @param bool $preconditionSupplied
141 * @param int $maxRetries
142 * @return bool
143 */
144 private function retryDeciderFunction(\Exception $exception, $isIdempotent, $preconditionNeeded, $preconditionSupplied, $retryStrategy)
145 {
146 if ($retryStrategy == StorageClient::RETRY_NEVER) {
147 return \false;
148 }
149 $statusCode = $exception->getCode();
150 // Retry if the exception status code matches
151 // with one of the retriable status code and
152 // the operation is either idempotent or conditionally
153 // idempotent with preconditions supplied.
154 if (in_array($statusCode, self::$httpRetryCodes)) {
155 if ($retryStrategy == StorageClient::RETRY_ALWAYS) {
156 return \true;
157 } elseif ($isIdempotent) {
158 return \true;
159 } elseif ($preconditionNeeded) {
160 return $preconditionSupplied;
161 }
162 }
163 return \false;
164 }
165 /**
166 * Utility func that returns the list of headers that need to be
167 * attached to every request and its retries.
168 */
169 private static function getRetryHeaders($invocationId, $attemptCount)
170 {
171 return [sprintf('%s/%s', self::$INVOCATION_ID_HEADER, $invocationId), sprintf('%s/%d', self::$ATTEMPT_COUNT_HEADER, $attemptCount)];
172 }
173 }
174