Retry failed GitHub release scans with backoff (#133)
ci/woodpecker/tag/docker Pipeline was successful

Releasing a GitHub sync lease always advanced `last_synced_at`, even after a failed scan (e.g. a rate-limit 403). A remote that failed once waited a full `mutable_ttl` before retrying, and a cold remote kept returning 503 until then.

- record scan outcomes in one shared lease helper for github_rpm/deb/alpine
- keep `last_synced_at` and the ETag on failure; retry from 60s with exponential backoff, capped at min(10m, ttl/4)
- honour `Retry-After` / `X-RateLimit-Reset`, clamped to `mutable_ttl`
- add `sync_failures` / `next_retry_at` columns (migration 0002)

Reviewed-on: #133
Co-authored-by: unkin-agent <unkin-agent@unkin.net>
Co-committed-by: unkin-agent <unkin-agent@unkin.net>
This commit was merged in pull request #133.
This commit is contained in:
2026-10-09 23:15:55 +11:00
committed by BenVincent
parent 1780b3d77c
commit 492607a164
18 changed files with 477 additions and 134 deletions
+9
View File
@@ -78,6 +78,7 @@ type githubFixture struct {
notModHit int // releases-list requests answered 304
releaseAuth string // Authorization header seen on the last releases request
assetAuth string // Authorization header seen on the last asset request
failStatus int // when set, the releases list answers this status (rate-limit style)
mu sync.Mutex
}
@@ -100,6 +101,14 @@ func newGitHubFixture(t *testing.T, withDigest bool) *githubFixture {
f.mu.Lock()
f.releasesHit++
f.releaseAuth = r.Header.Get("Authorization")
if f.failStatus != 0 {
status := f.failStatus
f.mu.Unlock()
w.Header().Set("X-RateLimit-Remaining", "0")
w.Header().Set("X-RateLimit-Reset", strconv.FormatInt(time.Now().Add(30*time.Second).Unix(), 10))
http.Error(w, "API rate limit exceeded", status)
return
}
etag := f.etag
if etag != "" && r.Header.Get("If-None-Match") == etag {
f.notModHit++