remotes: add least-connections mirror strategy (round-robin remains default) #122

Merged
benvin merged 1 commits from benvin/mirrorlist-least-conn into master 2026-08-13 17:23:59 +10:00
Member

Why

The mirrorlist (PR #121) always load-balances round-robin. Round-robin is oblivious to how busy each mirror is, so a slow or saturated mirror keeps getting its fair share of new requests. This adds an opt-in least-connections strategy that favors the mirror currently handling the fewest in-flight requests, steering new work toward idle mirrors. Round-robin stays the default, so existing remotes are unchanged.

How

  • Model: models.Remote gains mirror_strategy (round_robin default/empty, or least_conn). ValidateMirrorStrategy checks the enum and requires a non-empty mirrorlist for least_conn. Empty behaves as round_robin for back-compat.
  • DB: additive mirror_strategy TEXT NOT NULL DEFAULT 'round_robin' column (CREATE TABLE + ADD COLUMN IF NOT EXISTS), wired through remoteCols/scanRemote/CreateRemote/UpdateRemote; empty normalized to round_robin on write.
  • Engine: a per-remote, per-pool-URL atomic in-flight gauge is incremented around each upstream call (head/fetch/checkUpstream). For a least_conn remote the attempt order starts with the least-loaded pool URL (ties broken by the existing round-robin rotation). Round-robin path and failover order are unchanged; single-URL pools are a no-op.
  • Tests: unit tests for least-loaded selection, round-robin default, gauge inc/dec, single-URL no-op, and validation; DB round-trip covers the new column; docker e2e adds a least_conn distribution test plus a real dnf install through a least_conn remote.

Back-compat: unset/empty mirror_strategy is round_robin, so all existing remotes keep their current behavior.

## Why The mirrorlist (PR #121) always load-balances round-robin. Round-robin is oblivious to how busy each mirror is, so a slow or saturated mirror keeps getting its fair share of new requests. This adds an opt-in **least-connections** strategy that favors the mirror currently handling the fewest in-flight requests, steering new work toward idle mirrors. Round-robin stays the default, so existing remotes are unchanged. ## How - **Model**: `models.Remote` gains `mirror_strategy` (`round_robin` default/empty, or `least_conn`). `ValidateMirrorStrategy` checks the enum and requires a non-empty mirrorlist for `least_conn`. Empty behaves as `round_robin` for back-compat. - **DB**: additive `mirror_strategy TEXT NOT NULL DEFAULT 'round_robin'` column (CREATE TABLE + `ADD COLUMN IF NOT EXISTS`), wired through remoteCols/scanRemote/CreateRemote/UpdateRemote; empty normalized to `round_robin` on write. - **Engine**: a per-remote, per-pool-URL atomic in-flight gauge is incremented around each upstream call (head/fetch/checkUpstream). For a `least_conn` remote the attempt order starts with the least-loaded pool URL (ties broken by the existing round-robin rotation). Round-robin path and failover order are unchanged; single-URL pools are a no-op. - **Tests**: unit tests for least-loaded selection, round-robin default, gauge inc/dec, single-URL no-op, and validation; DB round-trip covers the new column; docker e2e adds a `least_conn` distribution test plus a real `dnf` install through a `least_conn` remote. Back-compat: unset/empty `mirror_strategy` is `round_robin`, so all existing remotes keep their current behavior.
unkin-agent added 1 commit 2026-08-13 16:20:42 +10:00
remotes: add least-connections mirror strategy (round-robin remains default)
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
9b18bbf471
Add a per-remote mirror_strategy selector for the mirrorlist, supporting
round_robin (default, unchanged) and least_conn.

- models.Remote gains MirrorStrategy string + MirrorStrategy{RoundRobin,LeastConn}
  constants and ValidateMirrorStrategy (enum check; least_conn requires a
  non-empty mirrorlist). Empty behaves as round_robin for back-compat.
- DB: additive mirror_strategy TEXT NOT NULL DEFAULT 'round_robin' column
  (CREATE TABLE + ADD COLUMN IF NOT EXISTS), wired through remoteCols/scanRemote/
  CreateRemote/UpdateRemote; empty normalized to round_robin on write.
- Engine: least_conn starts each attempt with the pool URL holding the fewest
  in-flight requests via a per-remote/per-URL atomic gauge (incremented around
  each upstream call in head/fetch/checkUpstream), ties broken by the existing
  round-robin rotation. Round-robin path and failover order unchanged;
  single-URL pools are a no-op.
- Tests: unit tests for least-loaded selection, round-robin default, gauge
  inc/dec, single-URL no-op, and strategy validation; DB round-trip covers the
  new column; docker e2e adds a least_conn distribution test and a real dnf
  install through a least_conn remote.
benvin merged commit cd7c2c4383 into master 2026-08-13 17:23:59 +10:00
benvin deleted branch benvin/mirrorlist-least-conn 2026-08-13 17:24:00 +10:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unkin/artifactapi#122