diff --git a/coderd/files.go b/coderd/files.go index f82d1aa926c22..0a7ba2be0cb33 100644 --- a/coderd/files.go +++ b/coderd/files.go @@ -118,11 +118,22 @@ func (api *API) postFile(rw http.ResponseWriter, r *http.Request) { Data: data, }) if err != nil { - httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Internal error saving file.", - Detail: err.Error(), - }) - return + if database.IsUniqueViolation(err, database.UniqueFilesHashCreatedByKey) { + // The file was uploaded by some concurrent process since the last time we checked for it, fetch it again. + file, err = api.Database.GetFileByHashAndCreator(ctx, database.GetFileByHashAndCreatorParams{ + Hash: hash, + CreatedBy: apiKey.UserID, + }) + } + // At this point the first error was either not the UniqueViolation OR there's still an error even after we + // attempt to fetch the file again, so we should return here. + if err != nil { + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "Internal error saving file.", + Detail: err.Error(), + }) + return + } } httpapi.Write(ctx, rw, http.StatusCreated, codersdk.UploadResponse{ diff --git a/coderd/files_test.go b/coderd/files_test.go index 974db6b18fc69..fb13cb30e48f1 100644 --- a/coderd/files_test.go +++ b/coderd/files_test.go @@ -5,6 +5,7 @@ import ( "bytes" "context" "net/http" + "sync" "testing" "github.com/google/uuid" @@ -69,6 +70,30 @@ func TestPostFiles(t *testing.T) { _, err = client.Upload(ctx, codersdk.ContentTypeTar, bytes.NewReader(data)) require.NoError(t, err) }) + t.Run("InsertConcurrent", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, nil) + _ = coderdtest.CreateFirstUser(t, client) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + var wg sync.WaitGroup + var end sync.WaitGroup + wg.Add(1) + end.Add(3) + for range 3 { + go func() { + wg.Wait() + data := make([]byte, 1024) + _, err := client.Upload(ctx, codersdk.ContentTypeTar, bytes.NewReader(data)) + end.Done() + require.NoError(t, err) + }() + } + wg.Done() + end.Wait() + }) } func TestDownload(t *testing.T) { pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy