PluginProbe
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! / 3.8.0
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! v3.8.0
3.8.0 3.7.5 3.7.4 3.7.3 3.7.2 1-final 3.7.1 3.7.0 3.6.8 3.6.7 3.6.6 3.6.5 3.6.4 3.6.3 3.6.2 3.6.1 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 All 112 releases
templately / includes / Utils / Response / RetryPolicy.php

RetryPolicy.php in Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! 3.8.0, at includes/Utils/Response/RetryPolicy.php

105 lines 3.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Templately\Utils\Response;
4
5 /**
6 * When an outbound cloud request is worth repeating (spec 043 / PRD PHP-1).
7 *
8 * `Http::post()` retried on `WP_Error` only, three times, **with no delay at
9 * all** — a tight loop. That is the worst possible shape: it misses every
10 * retryable HTTP status (a 502 from a restarting upstream was final), and for the
11 * transport failures it did catch it fired three requests within milliseconds at
12 * a server that had just failed to answer one.
13 *
14 * Decisions live here rather than in the client so the outbound retry and the
15 * `retryable` flag the UI shows a Retry button for cannot disagree.
16 */
17 class RetryPolicy {
18
19 /**
20 * Attempts AFTER the first. Beyond this the user is better served by an error
21 * they can act on than by a request that keeps not finishing.
22 */
23 const MAX_ATTEMPTS = 3;
24
25 const BASE_DELAY_MS = 300;
26 const MAX_DELAY_MS = 5000;
27
28 /**
29 * HTTP statuses worth repeating.
30 *
31 * All transient by nature: a gateway restarting, a worker timing out, an edge
32 * dropping the connection. Deliberately NO other 4xx — a 401 or a 422 fails
33 * identically however many times it is sent.
34 */
35 private static $retryable_statuses = [ 408, 502, 503, 504, 522, 524 ];
36
37 /**
38 * @param mixed $response A `wp_remote_*` result.
39 * @param int $attempt 0 for the first failure.
40 * @return bool
41 */
42 public static function should_retry( $response, $attempt ) {
43 if ( $attempt >= self::MAX_ATTEMPTS - 1 ) {
44 return false;
45 }
46
47 // Transport failure — DNS, refused, reset. Worth another attempt.
48 if ( is_wp_error( $response ) ) {
49 return true;
50 }
51
52 $status = (int) wp_remote_retrieve_response_code( $response );
53
54 // A 429 is NOT auto-retried. `Retry-After` says when to come back, and
55 // looping is precisely what the server is asking us to stop doing; the
56 // normalizer surfaces it with the delay so the caller can honour it once.
57 return in_array( $status, self::$retryable_statuses, true );
58 }
59
60 /**
61 * Backoff in MICROseconds, ready for `usleep()`.
62 *
63 * Exponential with full jitter. The jitter matters more than the curve: every
64 * site that failed on the same upstream blip would otherwise retry in the same
65 * instant and recreate the load that caused it.
66 *
67 * Filterable so tests do not sleep.
68 *
69 * @param int $attempt
70 * @return int
71 */
72 public static function delay( $attempt ) {
73 $exponential = self::BASE_DELAY_MS * pow( 2, max( 0, $attempt ) );
74 $capped = (int) min( $exponential, self::MAX_DELAY_MS );
75 $jittered = wp_rand( 0, $capped );
76
77 /**
78 * Backoff before the next outbound retry, in milliseconds.
79 *
80 * Return 0 to disable sleeping (what the test suite does).
81 *
82 * @since 3.7.0
83 * @param int $jittered Milliseconds.
84 * @param int $attempt 0 for the first retry.
85 */
86 $delay_ms = (int) apply_filters( 'templately_http_retry_delay', $jittered, $attempt );
87
88 return max( 0, $delay_ms ) * 1000;
89 }
90
91 /**
92 * Sleep for the computed backoff. No-op when the filter returns 0.
93 *
94 * @param int $attempt
95 * @return void
96 */
97 public static function wait( $attempt ) {
98 $microseconds = self::delay( $attempt );
99
100 if ( $microseconds > 0 ) {
101 usleep( $microseconds );
102 }
103 }
104 }
105