mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-14 02:11:37 +02:00
added retry logic
This commit is contained in:
parent
988ec17d9d
commit
008415e752
@ -552,6 +552,10 @@ func DeleteOrgRepos(ctx *context.APIContext) {
|
|||||||
bgCtx := graceful.GetManager().HammerContext()
|
bgCtx := graceful.GetManager().HammerContext()
|
||||||
|
|
||||||
const batchSize = 50
|
const batchSize = 50
|
||||||
|
const maxRetries = 3
|
||||||
|
// Track failed deletions with retry limit to prevent infinite loops when repos cannot be deleted.
|
||||||
|
// If a repo fails 3 times, skip it; if all remaining repos have hit max retries, exit the loop.
|
||||||
|
failedRepos := make(map[int64]int) // repo ID -> retry count
|
||||||
|
|
||||||
for {
|
for {
|
||||||
repos := make([]*repo_model.Repository, 0, batchSize)
|
repos := make([]*repo_model.Repository, 0, batchSize)
|
||||||
@ -572,16 +576,32 @@ func DeleteOrgRepos(ctx *context.APIContext) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allFailed := true
|
||||||
for _, repo := range repos {
|
for _, repo := range repos {
|
||||||
|
// Skip repos that have failed too many times
|
||||||
|
if failedRepos[repo.ID] >= maxRetries {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if err := repo_service.DeleteRepository(bgCtx, doer, repo, true); err != nil {
|
if err := repo_service.DeleteRepository(bgCtx, doer, repo, true); err != nil {
|
||||||
desc := fmt.Sprintf("Failed to delete repository %s (ID: %d) in org ID %d: %v", repo.Name, repo.ID, orgID, err)
|
failedRepos[repo.ID]++
|
||||||
|
desc := fmt.Sprintf("Failed to delete repository %s (ID: %d) in org ID %d (attempt %d/%d): %v",
|
||||||
|
repo.Name, repo.ID, orgID, failedRepos[repo.ID], maxRetries, err)
|
||||||
if noticeErr := system_model.CreateNotice(bgCtx, system_model.NoticeRepository, desc); noticeErr != nil {
|
if noticeErr := system_model.CreateNotice(bgCtx, system_model.NoticeRepository, desc); noticeErr != nil {
|
||||||
log.Error("Failed to create notice for repo deletion failure: %v", noticeErr)
|
log.Error("Failed to create notice for repo deletion failure: %v", noticeErr)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
allFailed = false
|
||||||
|
delete(failedRepos, repo.ID) // Remove from failed map if it succeeds
|
||||||
log.Info("Successfully deleted repository %s (ID: %d) in org ID %d", repo.Name, repo.ID, orgID)
|
log.Info("Successfully deleted repository %s (ID: %d) in org ID %d", repo.Name, repo.ID, orgID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If all repos in this batch have failed max retries, break to avoid infinite loop
|
||||||
|
if allFailed && len(failedRepos) > 0 {
|
||||||
|
log.Error("All remaining repositories failed to delete after %d retries for org ID %d", maxRetries, orgID)
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
log.Info("Completed deletion of repositories in org ID %d", orgID)
|
log.Info("Completed deletion of repositories in org ID %d", orgID)
|
||||||
}()
|
}()
|
||||||
|
|||||||
@ -255,8 +255,9 @@ func TestAPIOrgGeneral(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAPIDeleteOrgRepos(t *testing.T) {
|
func TestAPIDeleteOrgRepos(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
t.Run("Delete all repos successfully", func(t *testing.T) {
|
t.Run("Delete all repos successfully", func(t *testing.T) {
|
||||||
defer tests.PrepareTestEnv(t)()
|
|
||||||
defer tests.PrintCurrentTest(t)()
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
// Create test org with owner
|
// Create test org with owner
|
||||||
@ -284,7 +285,6 @@ func TestAPIDeleteOrgRepos(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Verify delete status code", func(t *testing.T) {
|
t.Run("Verify delete status code", func(t *testing.T) {
|
||||||
defer tests.PrepareTestEnv(t)()
|
|
||||||
defer tests.PrintCurrentTest(t)()
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
session := loginUser(t, "user1")
|
session := loginUser(t, "user1")
|
||||||
@ -310,7 +310,6 @@ func TestAPIDeleteOrgRepos(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Fail without permissions", func(t *testing.T) {
|
t.Run("Fail without permissions", func(t *testing.T) {
|
||||||
defer tests.PrepareTestEnv(t)()
|
|
||||||
defer tests.PrintCurrentTest(t)()
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
// user2 is owner of org3
|
// user2 is owner of org3
|
||||||
@ -333,7 +332,6 @@ func TestAPIDeleteOrgRepos(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("No system notice created on successful deletion", func(t *testing.T) {
|
t.Run("No system notice created on successful deletion", func(t *testing.T) {
|
||||||
defer tests.PrepareTestEnv(t)()
|
|
||||||
defer tests.PrintCurrentTest(t)()
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
session := loginUser(t, "user1")
|
session := loginUser(t, "user1")
|
||||||
@ -382,7 +380,6 @@ func TestAPIDeleteOrgRepos(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Returns 204 when repos already deleted", func(t *testing.T) {
|
t.Run("Returns 204 when repos already deleted", func(t *testing.T) {
|
||||||
defer tests.PrepareTestEnv(t)()
|
|
||||||
defer tests.PrintCurrentTest(t)()
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
session := loginUser(t, "user1")
|
session := loginUser(t, "user1")
|
||||||
@ -415,7 +412,6 @@ func TestAPIDeleteOrgRepos(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Returns 204 when no repos exist", func(t *testing.T) {
|
t.Run("Returns 204 when no repos exist", func(t *testing.T) {
|
||||||
defer tests.PrepareTestEnv(t)()
|
|
||||||
defer tests.PrintCurrentTest(t)()
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
session := loginUser(t, "user1")
|
session := loginUser(t, "user1")
|
||||||
@ -433,7 +429,6 @@ func TestAPIDeleteOrgRepos(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Pagination works for large org", func(t *testing.T) {
|
t.Run("Pagination works for large org", func(t *testing.T) {
|
||||||
defer tests.PrepareTestEnv(t)()
|
|
||||||
defer tests.PrintCurrentTest(t)()
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
session := loginUser(t, "user1")
|
session := loginUser(t, "user1")
|
||||||
@ -479,6 +474,6 @@ func TestAPIDeleteOrgRepos(t *testing.T) {
|
|||||||
// Verify all repos were deleted
|
// Verify all repos were deleted
|
||||||
remainingRepos, err := repo_model.GetOrgRepositories(t.Context(), org.ID)
|
remainingRepos, err := repo_model.GetOrgRepositories(t.Context(), org.ID)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Empty(t, remainingRepos, "Org is empty")
|
assert.Empty(t, remainingRepos, "All repositories should be deleted")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user