From 2827620cfe25a8278e4f6e0d269bbfc80cb1e93a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Roth?= Date: Wed, 20 May 2026 13:40:37 +0000 Subject: [PATCH] fix(publish): pre-register published repo key before task submission MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit apiPublishRepoOrSnapshot appended published.Key() to resources inside the task closure, after maybeRunTaskInBackground had already been called. The task's locked-resource set is fixed at submission time, so that append had no effect — the published repo key was never registered as a resource. Two concurrent POST /api/publish/{prefix} requests for the same prefix/distribution therefore did not conflict in the task queue: both ran in parallel, each loaded an empty PublishedRepoCollection from the DB, both passed CheckDuplicate, and the second Add silently overwrote the first. Fix: compute the published repo key ("U{storagePrefix}>>{distribution}") from the already-known storage/prefix/distribution values and append it to resources before calling maybeRunTaskInBackground, so concurrent creates for the same destination are serialised by the task queue. The now-dead append inside the closure is removed. --- api/publish.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/api/publish.go b/api/publish.go index 67b260d4..b0cedfc0 100644 --- a/api/publish.go +++ b/api/publish.go @@ -300,6 +300,17 @@ func apiPublishRepoOrSnapshot(c *gin.Context) { collection := collectionFactory.PublishedRepoCollection() + // Pre-register the published repo key in resources so that concurrent + // POST requests for the same prefix/distribution are serialized by the + // task queue rather than racing on CheckDuplicate + Add. + if b.Distribution != "" { + storagePrefix := prefix + if storage != "" { + storagePrefix = storage + ":" + prefix + } + resources = append(resources, "U"+storagePrefix+">>"+b.Distribution) + } + taskName := fmt.Sprintf("Publish %s repository %s/%s with components \"%s\" and sources \"%s\"", b.SourceKind, param, b.Distribution, strings.Join(components, `", "`), strings.Join(names, `", "`)) maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (*task.ProcessReturnValue, error) { @@ -332,8 +343,6 @@ func apiPublishRepoOrSnapshot(c *gin.Context) { return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, fmt.Errorf("unable to publish: %s", err) } - resources = append(resources, string(published.Key())) - if b.Origin != "" { published.Origin = b.Origin }