From b88cd0c111b8e9aa77eb6100c189da9b60097cd3 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Thu, 9 Jan 2025 18:10:54 +0000 Subject: [PATCH] [v10.0/forgejo] port(gitea#31954): Add lock for parallel maven upload (#6517) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/6513 Backport #31851 Fix #30171 --- Fixes https://github.com/go-gitea/gitea/issues/30171, this is also a issue in Forgejo. Backport the implementation that uses the existing sync module which does not work for multiple instances which is perfectly fine for Forgejo for now. (cherry picked from commit 9c990ac043a0167dc59f1c822988ed2316f7c1df) Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6517 Reviewed-by: Gusted Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- routers/api/packages/maven/maven.go | 6 ++++ tests/integration/api_packages_maven_test.go | 33 ++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/routers/api/packages/maven/maven.go b/routers/api/packages/maven/maven.go index 521ef2209a..92f20255e1 100644 --- a/routers/api/packages/maven/maven.go +++ b/routers/api/packages/maven/maven.go @@ -24,6 +24,7 @@ import ( "code.gitea.io/gitea/modules/log" packages_module "code.gitea.io/gitea/modules/packages" maven_module "code.gitea.io/gitea/modules/packages/maven" + "code.gitea.io/gitea/modules/sync" "code.gitea.io/gitea/routers/api/packages/helper" "code.gitea.io/gitea/services/context" packages_service "code.gitea.io/gitea/services/packages" @@ -228,6 +229,8 @@ func servePackageFile(ctx *context.Context, params parameters, serveContent bool helper.ServePackageFile(ctx, s, u, pf, opts) } +var mavenUploadLock = sync.NewExclusivePool() + // UploadPackageFile adds a file to the package. If the package does not exist, it gets created. func UploadPackageFile(ctx *context.Context) { params, err := extractPathParameters(ctx) @@ -246,6 +249,9 @@ func UploadPackageFile(ctx *context.Context) { packageName := params.GroupID + "-" + params.ArtifactID + mavenUploadLock.CheckIn(packageName) + defer mavenUploadLock.CheckOut(packageName) + buf, err := packages_module.CreateHashedBufferFromReader(ctx.Req.Body) if err != nil { apiError(ctx, http.StatusInternalServerError, err) diff --git a/tests/integration/api_packages_maven_test.go b/tests/integration/api_packages_maven_test.go index 7ada3b28ac..b453f10b69 100644 --- a/tests/integration/api_packages_maven_test.go +++ b/tests/integration/api_packages_maven_test.go @@ -8,6 +8,7 @@ import ( "net/http" "strconv" "strings" + "sync" "testing" "code.gitea.io/gitea/models/db" @@ -254,3 +255,35 @@ func TestPackageMaven(t *testing.T) { assert.NotContains(t, resp.Body.String(), "Internal server error") }) } + +func TestPackageMavenConcurrent(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + + groupID := "com.gitea" + artifactID := "test-project" + packageVersion := "1.0.1" + + root := fmt.Sprintf("/api/packages/%s/maven/%s/%s", user.Name, strings.ReplaceAll(groupID, ".", "/"), artifactID) + + putFile := func(t *testing.T, path, content string, expectedStatus int) { + req := NewRequestWithBody(t, "PUT", root+path, strings.NewReader(content)). + AddBasicAuth(user.Name) + MakeRequest(t, req, expectedStatus) + } + + t.Run("Concurrent Upload", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func(i int) { + putFile(t, fmt.Sprintf("/%s/%s.jar", packageVersion, strconv.Itoa(i)), "test", http.StatusCreated) + wg.Done() + }(i) + } + wg.Wait() + }) +}