From 8c5358294581dc32e9e25230e52e613865a67092 Mon Sep 17 00:00:00 2001 From: Roddy Rappaport Date: Sat, 21 Jun 2025 13:40:34 +0300 Subject: [PATCH] Added retry middleware to aiohttp musl requests I have been notified by @guyush1 that the website is unstable, so we retry the download 5 times. --- Dockerfile | 6 +++- src/docker_utils/download_musl_toolchains.py | 30 ++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2feb456..8af9634 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,12 +25,16 @@ RUN apt update && apt install -y \ patch \ pkg-config \ python3.12 \ - python3-aiohttp \ + python3-pip \ libpython3-dev \ texinfo \ wget \ xz-utils +# We require aiohttp >= 3.12 (For client middleware support), which is newer than the currently +# available python3-aiohttp's version in Ubuntu. +RUN python3.12 -m pip install --break-system-packages aiohttp + COPY src/docker_utils/download_musl_toolchains.py . RUN python3.12 -u download_musl_toolchains.py diff --git a/src/docker_utils/download_musl_toolchains.py b/src/docker_utils/download_musl_toolchains.py index 2478a39..5391131 100755 --- a/src/docker_utils/download_musl_toolchains.py +++ b/src/docker_utils/download_musl_toolchains.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3.12 -from typing import List from pathlib import Path +from typing import List import tarfile import tempfile @@ -10,6 +10,7 @@ import asyncio import aiohttp +import aiohttp.client_exceptions # NOTE: To add new architectures, you can use prebuilt toolchains from https://musl.cc/ or https://more.musl.cc/ . # musl.cc / more.musl.cc are not used here since these websites blacklist Github Actions CI. @@ -26,9 +27,34 @@ MUSL_TOOLCHAINS_DIR = Path("/musl-toolchains") ENTRYPOINT = Path("/entrypoint.sh") +NUM_RETRIES = 3 +RETRY_WAIT = 2 + +async def retry_middleware(req: aiohttp.ClientRequest, handler: aiohttp.ClientHandlerType, max_retries = NUM_RETRIES) -> aiohttp.ClientResponse: + # If every retry ends in a timeoout, resp will not be defined. In such a case we want to raie + # last timeout that has happened as we don't have a valid or invalid response to return. + last_exception = None + resp = None + + for _ in range(max_retries): + try: + resp = await handler(req) + if resp.ok: + return resp + except aiohttp.client_exceptions.ConnectionTimeoutError as e: + last_exception = e + + await asyncio.sleep(RETRY_WAIT) + + if resp is None: + raise last_exception + + return resp + async def download_file(url: str, filename: str): - async with aiohttp.ClientSession() as session: + async with aiohttp.ClientSession(middlewares=(retry_middleware,)) as session: async with session.get(url) as response: + response.raise_for_status() with open(filename, 'wb') as f: async for data in response.content.iter_chunked(CHUNK_SIZE): f.write(data)