From 68feaba2ed622754aa59d2f30735d87f0a4bd4fd Mon Sep 17 00:00:00 2001 From: Ban Yongping <3092544409@qq.com> Date: Thu, 13 Aug 2026 22:46:45 +0800 Subject: [PATCH] fix(migrations): use all configured GitHub tokens (#38841) GitHub migrations accept multiple comma-separated OAuth tokens, but clients with unknown rate data are never selected. After the first client is used, every later token stays unknown and can never participate in quota-aware selection. Select each client with unknown rate data once before falling back to the existing highest-remaining-rate choice. The regression test covers initial probing of all clients and then selection by remaining quota. Fixes https://github.com/go-gitea/gitea/issues/34342 Assisted-by: Codex:GPT-5 --------- Co-authored-by: silverwind --- services/migrations/github.go | 8 ++++++-- services/migrations/github_test.go | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/services/migrations/github.go b/services/migrations/github.go index 35ed7d7abd2..7c05e424b09 100644 --- a/services/migrations/github.go +++ b/services/migrations/github.go @@ -152,12 +152,16 @@ func (g *GithubDownloaderV3) waitAndPickClient(ctx context.Context) { var recentIdx int var maxRemaining int for i := 0; i < len(g.clients); i++ { - if g.rates[i] != nil && g.rates[i].Remaining > maxRemaining { + if g.rates[i] == nil { // probe unknown clients once, else their rate never gets learned + g.curClientIdx = i + return + } + if g.rates[i].Remaining > maxRemaining { maxRemaining = g.rates[i].Remaining recentIdx = i } } - g.curClientIdx = recentIdx // if no max remain, it will always pick the first client. + g.curClientIdx = recentIdx for g.rates[g.curClientIdx] != nil && g.rates[g.curClientIdx].Remaining <= GithubLimitRateRemaining { timer := time.NewTimer(time.Until(g.rates[g.curClientIdx].Reset.Time)) diff --git a/services/migrations/github_test.go b/services/migrations/github_test.go index 53ac4b8f99b..c3a4ef74450 100644 --- a/services/migrations/github_test.go +++ b/services/migrations/github_test.go @@ -14,6 +14,7 @@ import ( "gitea.dev/models/unittest" base "gitea.dev/modules/migration" + "github.com/google/go-github/v89/github" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -473,3 +474,25 @@ func TestGithubMultiToken(t *testing.T) { }) } } + +func TestGithubMultiTokenClientSelection(t *testing.T) { + downloader := &GithubDownloaderV3{ + clients: make([]*github.Client, 3), + rates: make([]*github.Rate, 3), + } + + downloader.waitAndPickClient(t.Context()) + assert.Equal(t, 0, downloader.curClientIdx) + + downloader.rates[0] = &github.Rate{Remaining: 100} + downloader.waitAndPickClient(t.Context()) + assert.Equal(t, 1, downloader.curClientIdx) + + downloader.rates[1] = &github.Rate{Remaining: 200} + downloader.waitAndPickClient(t.Context()) + assert.Equal(t, 2, downloader.curClientIdx) + + downloader.rates[2] = &github.Rate{Remaining: 50} + downloader.waitAndPickClient(t.Context()) + assert.Equal(t, 1, downloader.curClientIdx) +}