|
35 | 35 | class HttpxRetry:
|
36 | 36 | """HTTPX based retry config"""
|
37 | 37 | # TODO: Decide
|
38 |
| - # urllib3.Retry ignores the status_forcelist and only respects Retry-After header |
39 |
| - # for 413, 429 and 503 errors. |
| 38 | + # urllib3.Retry ignores the status_forcelist when respecting Retry-After header |
| 39 | + # Only 413, 429 and 503 errors are retried with the Retry-After header. |
40 | 40 | # Should we do the same?
|
41 | 41 | # Default status codes to be used for ``status_forcelist``
|
42 | 42 | RETRY_AFTER_STATUS_CODES = frozenset([413, 429, 503])
|
@@ -123,32 +123,39 @@ def _parse_retry_after(self, retry_after_header: str) -> float | None:
|
123 | 123 |
|
124 | 124 | def get_retry_after(self, response: httpx.Response) -> float | None:
|
125 | 125 | """Determine the Retry-After time needed before sending the next request."""
|
126 |
| - retry_after_header = response.headers.get('Retry_After', None) |
| 126 | + retry_after_header = response.headers.get('Retry-After', None) |
127 | 127 | if retry_after_header:
|
128 | 128 | # Convert retry header to a float in seconds
|
129 | 129 | return self._parse_retry_after(retry_after_header)
|
130 | 130 | return None
|
131 | 131 |
|
132 | 132 | def get_backoff_time(self):
|
133 | 133 | """Determine the backoff time needed before sending the next request."""
|
134 |
| - # request_count is the number of previous request attempts |
135 |
| - request_count = len(self.history) |
136 |
| - backoff = self.backoff_factor * (2 ** (request_count-1)) |
| 134 | + # attempt_count is the number of previous request attempts |
| 135 | + attempt_count = len(self.history) |
| 136 | + # Backoff should be set to 0 until after first retry. |
| 137 | + if attempt_count <= 1: |
| 138 | + return 0 |
| 139 | + backoff = self.backoff_factor * (2 ** (attempt_count-1)) |
137 | 140 | if self.backoff_jitter:
|
138 | 141 | backoff += random.random() * self.backoff_jitter
|
139 | 142 | return float(max(0, min(self.backoff_max, backoff)))
|
140 | 143 |
|
141 | 144 | async def sleep_for_backoff(self) -> None:
|
142 | 145 | """Determine and wait the backoff time needed before sending the next request."""
|
143 | 146 | backoff = self.get_backoff_time()
|
144 |
| - logger.debug('Sleeping for %f seconds following failed request', backoff) |
| 147 | + logger.debug('Sleeping for backoff of %f seconds following failed request', backoff) |
145 | 148 | await asyncio.sleep(backoff)
|
146 | 149 |
|
147 | 150 | async def sleep(self, response: httpx.Response) -> None:
|
148 | 151 | """Determine and wait the time needed before sending the next request."""
|
149 | 152 | if self.respect_retry_after_header:
|
150 | 153 | retry_after = self.get_retry_after(response)
|
151 | 154 | if retry_after:
|
| 155 | + logger.debug( |
| 156 | + 'Sleeping for Retry-After header of %f seconds following failed request', |
| 157 | + retry_after |
| 158 | + ) |
152 | 159 | await asyncio.sleep(retry_after)
|
153 | 160 | return
|
154 | 161 | await self.sleep_for_backoff()
|
|
0 commit comments