Skip to content

Commit a098d3c

Browse files
authored
Merge pull request #841 from iangelak/810-git-tag-providers-retry-errors
fix: Add retry logic to GitHub and GitLab tag providers
2 parents 74b0af4 + 040bcd3 commit a098d3c

4 files changed

Lines changed: 21 additions & 24 deletions

File tree

docs/http-retry.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ The retry mechanism specifically handles these error conditions:
4343

4444
### Server Errors
4545

46+
- `408 Request Timeout` - Request took too long to complete
4647
- `504 Gateway Timeout` - Server overwhelmed or upstream timeout
4748
- `502 Bad Gateway` - Proxy/gateway errors
4849
- `503 Service Unavailable` - Temporary server overload

src/fromager/http_retry.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
This module provides a robust retry mechanism for HTTP requests with exponential
44
backoff, jitter, and specific handling for common network failures including:
5+
- Request timeouts (408)
56
- Server timeouts (502, 503, 504)
67
- Rate limiting (429, GitHub API rate limits)
78
- Connection errors and incomplete reads
@@ -35,7 +36,7 @@
3536
DEFAULT_RETRY_CONFIG = {
3637
"total": 5,
3738
"backoff_factor": 1.0,
38-
"status_forcelist": [429, 500, 502, 503, 504],
39+
"status_forcelist": [408, 429, 500, 502, 503, 504],
3940
"allowed_methods": ["GET", "PUT", "POST", "HEAD", "OPTIONS"],
4041
"raise_on_status": False,
4142
}

src/fromager/request_session.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
FROMAGER_RETRY_CONFIG = {
77
"total": int(os.environ.get("FROMAGER_HTTP_RETRIES", "8")),
88
"backoff_factor": float(os.environ.get("FROMAGER_HTTP_BACKOFF_FACTOR", "1.5")),
9-
"status_forcelist": [429, 500, 502, 503, 504],
9+
"status_forcelist": [408, 429, 500, 502, 503, 504],
1010
"allowed_methods": ["GET", "PUT", "POST", "HEAD", "OPTIONS"],
1111
"raise_on_status": False,
1212
}

src/fromager/resolver.py

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from .candidate import Candidate
3434
from .constraints import Constraints
3535
from .extras_provider import ExtrasProvider
36+
from .http_retry import RETRYABLE_EXCEPTIONS, retry_on_exception
3637
from .request_session import session
3738
from .requirements_file import RequirementType
3839

@@ -760,6 +761,12 @@ def __init__(
760761
def cache_key(self) -> str:
761762
return f"{self.organization}/{self.repo}"
762763

764+
@retry_on_exception(
765+
exceptions=RETRYABLE_EXCEPTIONS,
766+
max_attempts=5,
767+
backoff_factor=1.5,
768+
max_backoff=120.0,
769+
)
763770
def _find_tags(
764771
self,
765772
identifier: str,
@@ -773,17 +780,8 @@ def _find_tags(
773780

774781
nexturl = self.api_url.format(self=self)
775782
while nexturl:
776-
try:
777-
resp = session.get(nexturl, headers=headers)
778-
resp.raise_for_status()
779-
except Exception as e:
780-
logger.error(
781-
"%s: Failed to fetch GitHub tags from %s: %s",
782-
identifier,
783-
nexturl,
784-
e,
785-
)
786-
raise
783+
resp = session.get(nexturl, headers=headers)
784+
resp.raise_for_status()
787785

788786
for entry in resp.json():
789787
name = entry["name"]
@@ -834,23 +832,20 @@ def __init__(
834832
def cache_key(self) -> str:
835833
return f"{self.server_url}/{self.project_path}"
836834

835+
@retry_on_exception(
836+
exceptions=RETRYABLE_EXCEPTIONS,
837+
max_attempts=5,
838+
backoff_factor=1.5,
839+
max_backoff=120.0,
840+
)
837841
def _find_tags(
838842
self,
839843
identifier: str,
840844
) -> Iterable[tuple[str, Version]]:
841845
nexturl: str = self.api_url
842846
while nexturl:
843-
try:
844-
resp: Response = session.get(nexturl)
845-
resp.raise_for_status()
846-
except Exception as e:
847-
logger.error(
848-
"%s: Failed to fetch GitLab tags from %s: %s",
849-
identifier,
850-
nexturl,
851-
e,
852-
)
853-
raise
847+
resp: Response = session.get(nexturl)
848+
resp.raise_for_status()
854849
for entry in resp.json():
855850
name = entry["name"]
856851
version = self._match_function(identifier, name)

0 commit comments

Comments
 (0)