@@ -87,6 +87,26 @@ public function clearUrls($urls)
8787 });
8888 }
8989
90+ /**
91+ * Invalidate the given paths.
92+ */
93+ public function invalidatePaths (array $ paths )
94+ {
95+ $ concretePaths = array_filter ($ paths , function (string $ path ) {
96+ return !str_ends_with ($ path , '* ' );
97+ });
98+ $ wildcardPaths = array_filter ($ paths , function (string $ path ) {
99+ return str_ends_with ($ path , '* ' );
100+ });
101+
102+ while (!empty ($ wildcardPaths ) || !empty ($ concretePaths )) {
103+ $ batch = array_splice ($ wildcardPaths , 0 , 15 );
104+ $ batch = array_merge ($ batch , array_splice ($ concretePaths , 0 , 3000 - count ($ batch )));
105+
106+ $ this ->sendInvalidation ($ batch );
107+ }
108+ }
109+
90110 /**
91111 * {@inheritdoc}
92112 */
@@ -104,7 +124,7 @@ public function sendClearRequest(?callable $guard = null)
104124 return ;
105125 }
106126
107- $ this ->createInvalidation ($ paths );
127+ $ this ->invalidatePaths ($ paths );
108128 }
109129
110130 /**
@@ -139,28 +159,6 @@ private function addPath(string $path)
139159 $ this ->invalidationPaths [] = $ path ;
140160 }
141161
142- /**
143- * Create an invalidation request.
144- */
145- private function createInvalidation ($ paths )
146- {
147- if (is_string ($ paths )) {
148- $ paths = [$ paths ];
149- } elseif (!is_array ($ paths )) {
150- throw new \InvalidArgumentException ('"paths" argument must be an array or a string ' );
151- }
152-
153- if (count ($ paths ) > 1 ) {
154- $ paths = $ this ->filterUniquePaths ($ paths );
155- }
156-
157- $ response = $ this ->request ('post ' , "/2020-05-31/distribution/ {$ this ->distributionId }/invalidation " , $ this ->generateInvalidationPayload ($ paths ));
158-
159- if (201 !== $ this ->parseResponseStatusCode ($ response )) {
160- throw new \RuntimeException ($ this ->createExceptionMessage ('Invalidation request failed ' , $ response ));
161- }
162- }
163-
164162 /**
165163 * Filter all paths and only keep unique ones.
166164 */
@@ -180,20 +178,16 @@ private function filterUniquePaths(array $paths): array
180178 });
181179
182180 $ wildcardPaths = $ wildcardPaths ->map (function (string $ path ) use ($ wildcardPaths ) {
183- $ filteredWildcardPaths = preg_grep (sprintf ('/%s/ ' , str_replace ('\* ' , '.* ' , preg_quote ($ path , '/ ' ))), $ wildcardPaths ->all (), PREG_GREP_INVERT );
181+ $ filteredWildcardPaths = preg_grep (sprintf ('/^ %s/ ' , str_replace ('\* ' , '.* ' , preg_quote ($ path , '/ ' ))), $ wildcardPaths ->all (), PREG_GREP_INVERT );
184182 $ filteredWildcardPaths [] = $ path ;
185183
186184 return $ filteredWildcardPaths ;
187185 });
188186
189187 $ wildcardPaths = new Collection (!$ wildcardPaths ->isEmpty () ? array_intersect (...$ wildcardPaths ->all ()) : []);
190188
191- if ($ wildcardPaths ->count () > 15 ) {
192- throw new \RuntimeException ('CloudFront only allows for a maximum of 15 wildcard invalidations ' );
193- }
194-
195189 $ wildcardPaths ->each (function (string $ path ) use (&$ filteredPaths ) {
196- $ filteredPaths = preg_grep (sprintf ('/%s/ ' , str_replace ('\* ' , '.* ' , preg_quote ($ path , '/ ' ))), $ filteredPaths , PREG_GREP_INVERT );
190+ $ filteredPaths = preg_grep (sprintf ('/^ %s/ ' , str_replace ('\* ' , '.* ' , preg_quote ($ path , '/ ' ))), $ filteredPaths , PREG_GREP_INVERT );
197191 });
198192
199193 return array_merge ($ wildcardPaths ->all (), $ filteredPaths );
@@ -244,4 +238,36 @@ private function generateInvalidationPayload(array $paths): string
244238
245239 return $ xml ;
246240 }
241+
242+ /**
243+ * Send an invalidation request.
244+ */
245+ private function sendInvalidation (array $ paths )
246+ {
247+ $ attempts = 0 ;
248+ $ maxAttempts = 3 ;
249+ $ payload = $ this ->generateInvalidationPayload ($ paths );
250+
251+ while ($ attempts < $ maxAttempts ) {
252+ ++$ attempts ;
253+
254+ $ response = $ this ->request ('post ' , "/2020-05-31/distribution/ {$ this ->distributionId }/invalidation " , $ payload );
255+ $ statusCode = $ this ->parseResponseStatusCode ($ response );
256+
257+ if (201 === $ statusCode ) {
258+ return ;
259+ }
260+
261+ $ awsError = $ this ->parseAwsError ($ response ['body ' ] ?? '' );
262+ $ errorCode = $ awsError ['code ' ] ?? '' ;
263+
264+ $ shouldRetry = $ statusCode >= 500 || 429 === $ statusCode || (400 === $ statusCode && in_array ($ errorCode , ['Throttling ' , 'TooManyInvalidationsInProgress ' ], true ));
265+
266+ if (!$ shouldRetry || $ attempts >= $ maxAttempts ) {
267+ throw new \RuntimeException ($ this ->createExceptionMessage ('Invalidation request failed ' , $ response ));
268+ }
269+
270+ sleep ($ attempts * 2 );
271+ }
272+ }
247273}
0 commit comments