Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/vole.git

Write over SSH with a registered key:

git clone ssh://git@rodent.software:2222/vole.git

A free and open-source forge that lives in your Git repo.

Phase 4: one repository may be bounded, and a freed slot wakes the parked

8d33608319bb83725256d02196925309fe521cda · Keenan Nemetz · 17d ago · parent 379dad1e

Phase 4: one repository may be bounded, and a freed slot wakes the parked

max_jobs_per_repo caps how many of one repository's jobs run at once; unset
there is still no cap. Ordering and bounding answer different questions: the
lease already serves the least busy repository first, which stops a burst
taking the whole fleet, but ordering is not a bound — and what an operator
wants when one repository's build saturates the instance is a number.

A repository at its cap is skipped rather than blocking, so the other
repositories an account may run are still served. That makes nil from the
lease mean two things: "nothing to do", and "work exists, but not for you
yet". The second meaning is why Finish now wakes the parked lease — releasing
a slot is work somebody can pick up, and without it a runner parked on a cap
would sit out its whole wait against a queue it could already have taken.
Both are one line of the same query: `? = 0 OR COALESCE(busy.n, 0) < ?`, so
"no cap" does not become a second query string that can drift.

The runners page renders `4 of 4 (at the cap)` beside the depth, because a
queue that is full and not draining is exactly the question that page exists
to answer: "7 waiting, 4 running" is a mystery where the cap is not shown.

Measured: TestPerRepoCapBoundsOneRepositoryAndResumesWhenASlotFrees puts two
jobs in each of two repositories with a cap of 1, so "nothing to lease"
cannot be confused with "no work" — both repositories served once, a third
runner refused, a finish waking the parked lease and the freed slot's own
repository becoming leasable. TestRunnersPageSaysWhenARepositoryIsAtItsCap
and TestMaxJobsPerRepoLoads cover the page and the key.

internal/config/config.go +14

@@ -30,6 +30,13 @@ type Config struct {
30 30 // LeaseSeconds is how long a runner may hold a job without a heartbeat before
31 31 // the job is handed to somebody else.
32 32 LeaseSeconds int `toml:"lease_seconds"`
33 // MaxJobsPerRepo bounds how many jobs one repository may have running at once.
34 // Zero means no bound: fairness is then ordering alone (the lease serves the
35 // least busy repository first), which stops a burst taking the whole fleet but
36 // does not limit one repository's footprint. A cap is what an operator wants
37 // when they need the bound itself — one repository's build saturating every
38 // runner on the instance — and it applies per repository, not per account.
39 MaxJobsPerRepo int `toml:"max_jobs_per_repo"`
33 40 // PagesDomain is the suffix a repository's site is served under:
34 41 // <repo>.<pages_domain>. Empty disables site hosting entirely.
35 42 PagesDomain string `toml:"pages_domain"`
@@ -155,6 +162,13 @@ lfs_object_max_bytes = 2147483648
155 162 # pointer, gets a warning naming the git lfs track command. Zero disables it.
156 163 lfs_warn_bytes = 5242880
157 164
165 # How many jobs one repository may have running at once. Zero disables the bound,
166 # which leaves fairness as ordering alone: the lease serves the least busy
167 # repository first, so a burst cannot take the whole fleet, but nothing limits how
168 # much of it one repository uses. Set it when one repository's build saturates the
169 # instance and the ordering is not enough.
170 # max_jobs_per_repo = 4
171
158 172 # Static sites: a repository's pages branch is served at
159 173 # <repo>.<pages_domain>. Leave empty to serve no sites at all.
160 174 # pages_domain = "pages.example.com"