perf(ci): throttle only Docker Hub copies in the daily mirror and raise the limit

The daily mirror waited about 70 s before each of its 195 copies (50 images
per hour), roughly 3.8 h of pauses, and logged no single rate-limit error.
37 of the copies come from ghcr.io, registry.gitlab.com, quay.io,
registry.opencode.de and mcr.microsoft.com, which the Docker Hub pull limit
does not cover, and --only-missing also waited before images it then skipped.
The throttle now waits only before an actual copy from docker.io, and the
schedule allows 150 Docker Hub copies per hour (24 s apart).

Part of #1324

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 5.5 committed 2026-11-01 17:48:09 +01:00
1 parent 1e1d37d679
commit 87bc1be60f
3 files changed
+60 -9

No files matched your search

@@ -7,6 +7,57 @@ import cli.contributing.mirror.sync.__main__ as sync_main
from cli.contributing.mirror.model import ImageRef
class TestThrottle(unittest.TestCase):
def test_only_docker_hub_copies_wait_for_the_throttle(self) -> None:
def image(name: str, registry: str) -> ImageRef:
return ImageRef(
role="r",
service=name,
name=name,
version="1",
source=f"{registry}/{name}:1",
registry=registry,
)
images = [
image("a", "docker.io"),
image("b", "ghcr.io"),
image("c", "quay.io"),
image("d", "docker.io"),
]
with (
patch(
"cli.contributing.mirror.sync.__main__.iter_role_images",
return_value=images,
),
patch(
"cli.contributing.mirror.sync.__main__.iter_runner_images",
return_value=[],
),
patch.object(sync_main.GHCRProvider, "image_base", return_value="g/x"),
patch.object(sync_main.GHCRProvider, "mirror") as mock_mirror,
patch.object(sync_main.time, "monotonic", return_value=0.0),
patch.object(sync_main.time, "sleep") as mock_sleep,
patch(
"sys.argv",
[
"mirror-sync",
"--ghcr-namespace",
"acme",
"--ghcr-repository",
"myrepo",
"--images-per-hour",
"150",
],
),
):
result = sync_main.main()
self.assertEqual(result, 0)
self.assertEqual(mock_mirror.call_count, 4)
self.assertEqual([call.args for call in mock_sleep.call_args_list], [(24.0,)])
class TestMirrorSync(unittest.TestCase):
def test_only_missing_skips_existing_destination(self) -> None:
image = ImageRef(