From 008415e752b9b8586e61e18b2b7d025ebbdca2d6 Mon Sep 17 00:00:00 2001 From: karthikbhandary2 Date: Sat, 7 Mar 2026 17:58:58 +0530 Subject: [PATCH] added retry logic --- routers/api/v1/org/org.go | 22 +++++++++++++++++++++- tests/integration/api_org_test.go | 11 +++-------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index 25ab2d6586..c7a3330014 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -552,6 +552,10 @@ func DeleteOrgRepos(ctx *context.APIContext) { bgCtx := graceful.GetManager().HammerContext() 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 { repos := make([]*repo_model.Repository, 0, batchSize) @@ -572,16 +576,32 @@ func DeleteOrgRepos(ctx *context.APIContext) { break } + allFailed := true 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 { - 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 { log.Error("Failed to create notice for repo deletion failure: %v", noticeErr) } } 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) } } + + // 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) }() diff --git a/tests/integration/api_org_test.go b/tests/integration/api_org_test.go index 013037193d..5248a8584c 100644 --- a/tests/integration/api_org_test.go +++ b/tests/integration/api_org_test.go @@ -255,8 +255,9 @@ func TestAPIOrgGeneral(t *testing.T) { } func TestAPIDeleteOrgRepos(t *testing.T) { + defer tests.PrepareTestEnv(t)() + t.Run("Delete all repos successfully", func(t *testing.T) { - defer tests.PrepareTestEnv(t)() defer tests.PrintCurrentTest(t)() // Create test org with owner @@ -284,7 +285,6 @@ func TestAPIDeleteOrgRepos(t *testing.T) { }) t.Run("Verify delete status code", func(t *testing.T) { - defer tests.PrepareTestEnv(t)() defer tests.PrintCurrentTest(t)() session := loginUser(t, "user1") @@ -310,7 +310,6 @@ func TestAPIDeleteOrgRepos(t *testing.T) { }) t.Run("Fail without permissions", func(t *testing.T) { - defer tests.PrepareTestEnv(t)() defer tests.PrintCurrentTest(t)() // 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) { - defer tests.PrepareTestEnv(t)() defer tests.PrintCurrentTest(t)() 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) { - defer tests.PrepareTestEnv(t)() defer tests.PrintCurrentTest(t)() 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) { - defer tests.PrepareTestEnv(t)() defer tests.PrintCurrentTest(t)() session := loginUser(t, "user1") @@ -433,7 +429,6 @@ func TestAPIDeleteOrgRepos(t *testing.T) { }) t.Run("Pagination works for large org", func(t *testing.T) { - defer tests.PrepareTestEnv(t)() defer tests.PrintCurrentTest(t)() session := loginUser(t, "user1") @@ -479,6 +474,6 @@ func TestAPIDeleteOrgRepos(t *testing.T) { // Verify all repos were deleted remainingRepos, err := repo_model.GetOrgRepositories(t.Context(), org.ID) assert.NoError(t, err) - assert.Empty(t, remainingRepos, "Org is empty") + assert.Empty(t, remainingRepos, "All repositories should be deleted") }) }