fix(api): add index tiebreaker to commit status ordering (#39508) (#39525)

Backport #39508 by @perfectra1n

Commit status list orders only by `created_unix`/`updated_unix`, which
have 1-second resolution while CI often posts many statuses per second.
With LIMIT/OFFSET paging, databases (e.g. PostgreSQL using a Sort plan)
may order tied rows differently per page, so `GET
/repos/{owner}/{repo}/commits/{ref}/statuses` returns some statuses
twice and never returns others.

This became visible after https://github.com/go-gitea/gitea/pull/36521
made requests without `page` paginated. Clients like Renovate that page
until `X-Total-Count` can miss a context's newest status and see a stale
`pending`, blocking automerge.

Fix: add `index` (unique per commit) as a tiebreaker to the
timestamp-based orders.

Co-authored-by: Jon Fuller <jonfuller2012@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
Giteabot
2026-10-01 18:03:00 +00:00
committed by GitHub
co-authored by Jon Fuller silverwind
parent f2a08e0261
commit e48591ba64
2 changed files with 11 additions and 24 deletions
+4 -4
View File
@@ -311,17 +311,17 @@ func (opts *CommitStatusOptions) ToConds() builder.Cond {
func (opts *CommitStatusOptions) ToOrders() string {
switch opts.SortType {
case "oldest":
return "created_unix ASC"
return "created_unix ASC, `index` ASC"
case "recentupdate":
return "updated_unix DESC"
return "updated_unix DESC, `index` DESC"
case "leastupdate":
return "updated_unix ASC"
return "updated_unix ASC, `index` ASC"
case "leastindex":
return "`index` DESC"
case "highestindex":
return "`index` ASC"
default:
return "created_unix DESC"
return "created_unix DESC, `index` DESC" // timestamps have 1s resolution, `index` keeps paging stable
}
}