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" | |