fix(actions): make runner list pagination order deterministic (#38313)

The runner-listing API endpoints (`admin`, `org`, `user`, `repo`, and
`shared`) return runners in a non-deterministic order across pages. When
paging through runners, some runners from page 1 could reappear on page
2 (and others get skipped entirely).

The cause is in `FindRunnerOptions.ToOrders()`. Most sort modes order by
a **non-unique** column only:

- default / `online` / `offline` → `last_online`
- `alphabetically` / `reversealphabetically` → `name`

When multiple runners tie on the sort key (e.g. every offline runner
shares `last_online = 0`, or two runners have the same name), the
database is free to return the tied rows in any order between separate
queries. Combined with `LIMIT`/`OFFSET` pagination, this means the same
runner can land on more than one page.

## Fix

Append the unique primary key `id` as a stable tiebreaker to each
non-unique sort order:

The `newest`/`oldest` modes already sort by the unique `id`, so they are
left unchanged.
This commit is contained in:
bircni
2026-07-03 20:35:47 +00:00
committed by GitHub
parent f7fd510224
commit d4c4142123
2 changed files with 85 additions and 5 deletions
+8 -5
View File
@@ -251,21 +251,24 @@ func (opts FindRunnerOptions) ToConds() builder.Cond {
}
func (opts FindRunnerOptions) ToOrders() string {
// A unique tiebreaker (id) is appended so that runners sharing the same
// last_online or name keep a deterministic order across paginated queries,
// otherwise the same runner may appear on more than one page.
switch opts.Sort {
case "online":
return "last_online DESC"
return "last_online DESC, id ASC"
case "offline":
return "last_online ASC"
return "last_online ASC, id ASC"
case "alphabetically":
return "name ASC"
return "name ASC, id ASC"
case "reversealphabetically":
return "name DESC"
return "name DESC, id ASC"
case "newest":
return "id DESC"
case "oldest":
return "id ASC"
}
return "last_online DESC"
return "last_online DESC, id ASC"
}
// GetRunnerByUUID returns a runner via uuid