From 59415e424e312d8d19a38dc55eee63e55ac4921c Mon Sep 17 00:00:00 2001 From: Janos Dobronszki Date: Fri, 26 Dec 2025 14:20:11 +0100 Subject: [PATCH 1/4] perf tuning --- .../services/file/http_upload_file.go | 50 ++++++++++++------- .../file/storage_provider_cloud_cache.go | 2 +- .../image/http_serve_uploaded_image.go | 13 ++++- 3 files changed, 44 insertions(+), 21 deletions(-) diff --git a/server/internal/services/file/http_upload_file.go b/server/internal/services/file/http_upload_file.go index 5bf3ee2749..fa65b2fe3d 100644 --- a/server/internal/services/file/http_upload_file.go +++ b/server/internal/services/file/http_upload_file.go @@ -6,7 +6,6 @@ import ( "io" "log/slog" "net/http" - "os" "path/filepath" "time" @@ -80,22 +79,6 @@ func (fs *FileService) UploadFile( // prevent enumeration, as there is no concept of file ownership. fileId := sdk.OpaqueId("file") - destinationFilePath := filepath.Join(fs.uploadFolder, fileId) - dstFile, err := os.Create(destinationFilePath) - if err != nil { - logger.Error("Failed to create destination file", slog.Any("error", err)) - endpoint.InternalServerError(w) - return - } - defer dstFile.Close() - - written, err := io.Copy(dstFile, part) - if err != nil { - logger.Error("Failed to copy file data", slog.Any("error", err)) - endpoint.InternalServerError(w) - return - } - if fs.nodeId == "" { err := fs.getNodeId(r.Context()) if err != nil { @@ -105,15 +88,18 @@ func (fs *FileService) UploadFile( } } + // Calculate the nested path: e.g., "81/d2/file_81d2..." + // This prevents directory saturation on your SSD. + intricatePath := calculateIntricatePath(fileId) + // @todo this is fairly weird that we process multiple files but only a single one is returned uploadRecord = file.Upload{ Id: sdk.Id("upl"), FileId: fileId, NodeId: fs.nodeId, FileName: part.FileName(), - FilePath: fileId, + FilePath: intricatePath, UserId: isAuthRsp.User.Id, - FileSize: written, CreatedAt: time.Now(), } err = fs.uploadStore.Upsert(uploadRecord) @@ -122,6 +108,15 @@ func (fs *FileService) UploadFile( endpoint.InternalServerError(w) return } + + written, err := fs.storage.Save(r.Context(), &uploadRecord, part) + if err != nil { + logger.Error("Failed to save file to storage", slog.Any("error", err)) + endpoint.InternalServerError(w) + return + } + + uploadRecord.FileSize = written } jsonData, _ := json.Marshal(file.UploadFileResponse{ @@ -151,3 +146,20 @@ func (fs *FileService) getNodeId(ctx context.Context) error { fs.nodeId = nodeRsp.Node.Id return nil } + +func calculateIntricatePath(fileId string) string { + // Input: "file_81d259fc..." + // Output: "81/d2/file_81d259fc..." + + prefix := "file_" + idPart := fileId + if len(fileId) > len(prefix) && fileId[:len(prefix)] == prefix { + idPart = fileId[len(prefix):] + } + + if len(idPart) < 4 { + return fileId + } + + return filepath.Join(idPart[0:2], idPart[2:4], fileId) +} diff --git a/server/internal/services/file/storage_provider_cloud_cache.go b/server/internal/services/file/storage_provider_cloud_cache.go index 6ad02c77e6..e22bb11b04 100644 --- a/server/internal/services/file/storage_provider_cloud_cache.go +++ b/server/internal/services/file/storage_provider_cloud_cache.go @@ -59,7 +59,7 @@ func (p *CloudCacheProvider) Save(ctx context.Context, u *file.Upload, content i // Use local file as source for Cloud upload f, _, err := p.local.Open(ctx, u.FilePath) if err != nil { - return written, nil + return written, err } defer f.Close() diff --git a/server/internal/services/image/http_serve_uploaded_image.go b/server/internal/services/image/http_serve_uploaded_image.go index 1fd071b615..d815c1a821 100644 --- a/server/internal/services/image/http_serve_uploaded_image.go +++ b/server/internal/services/image/http_serve_uploaded_image.go @@ -155,7 +155,7 @@ func (cs *ImageService) ServeUploadedImage(w http.ResponseWriter, r *http.Reques } // Check disk - cachePath := filepath.Join(cs.imageCacheFolder, hash) + cachePath := cs.getCachePath(hash) if data, err := os.ReadFile(cachePath); err == nil { if len(data) < memCacheLimit { cs.imageDataCache.Add(hash, data) @@ -290,3 +290,14 @@ func (cs *ImageService) ServeUploadedImage(w http.ResponseWriter, r *http.Reques res := val.(*imgResult) w.Write(res.Data) } + +func (cs *ImageService) getCachePath(hash string) string { + // Shard by the first 4 characters of the SHA1 hash + subfolder := filepath.Join(hash[0:2], hash[2:4]) + fullDir := filepath.Join(cs.imageCacheFolder, subfolder) + + // Ensure the directories exist + _ = os.MkdirAll(fullDir, 0755) + + return filepath.Join(fullDir, hash) +} From 6ea500facae18d62f7b6342ab36c3772b30efd4a Mon Sep 17 00:00:00 2001 From: Janos Dobronszki Date: Fri, 26 Dec 2025 14:36:16 +0100 Subject: [PATCH 2/4] file-svc: gcs fixes --- .../image/http_serve_uploaded_image.go | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/server/internal/services/image/http_serve_uploaded_image.go b/server/internal/services/image/http_serve_uploaded_image.go index d815c1a821..ab16603937 100644 --- a/server/internal/services/image/http_serve_uploaded_image.go +++ b/server/internal/services/image/http_serve_uploaded_image.go @@ -106,6 +106,10 @@ func (cs *ImageService) ServeUploadedImage(w http.ResponseWriter, r *http.Reques } contentType, _ := cs.metaCache.Get(fileId) + cacheKeyData := fmt.Sprintf("%s-%d-%d-%d", fileId, width, height, quality) + h := sha1.New() + h.Write([]byte(cacheKeyData)) + hash := hex.EncodeToString(h.Sum(nil)) var rsp *os.File @@ -115,24 +119,28 @@ func (cs *ImageService) ServeUploadedImage(w http.ResponseWriter, r *http.Reques err error ) - rsp, hrsp, err = cs.options.ClientFactory.Client(client.WithToken(cs.token)). - FileSvcAPI.ServeUpload(context.Background(), fileId).Execute() + _, err, _ = cs.sf.Do(hash, func() (interface{}, error) { + rsp, hrsp, err = cs.options.ClientFactory.Client(client.WithToken(cs.token)). + FileSvcAPI.ServeUpload(context.Background(), fileId).Execute() + if err != nil { + endpoint.WriteErr(w, http.StatusInternalServerError, err) + return nil, errors.Wrap(err, "error calling serve upload to get content type") + } + + contentType = hrsp.Header["Content-Type"][0] + cs.metaCache.Add(fileId, contentType) + + return nil, nil + }) + if err != nil { endpoint.WriteErr(w, http.StatusInternalServerError, err) return } defer rsp.Close() - - contentType = hrsp.Header["Content-Type"][0] - cs.metaCache.Add(fileId, contentType) } - cacheKeyData := fmt.Sprintf("%s-%d-%d-%d", fileId, width, height, quality) - h := sha1.New() - h.Write([]byte(cacheKeyData)) - hash := hex.EncodeToString(h.Sum(nil)) - switch contentType { case "image/jpeg", "image/jpg": w.Header().Set("Content-Type", "image/jpeg") @@ -166,9 +174,7 @@ func (cs *ImageService) ServeUploadedImage(w http.ResponseWriter, r *http.Reques } val, err, _ := cs.sf.Do(hash, func() (interface{}, error) { - if data, err := os.ReadFile(cachePath); err == nil { - return &imgResult{Data: data, ContentType: contentType}, nil - } + logger.Info("getting from file service", hash) if rsp == nil { var ( From 636ab95ce1439295e7380d0dcb7d5afbbb6f1649 Mon Sep 17 00:00:00 2001 From: Janos Dobronszki Date: Fri, 26 Dec 2025 14:45:04 +0100 Subject: [PATCH 3/4] fix serve upload --- server/internal/services/file/http_serve_upload.go | 4 +++- server/internal/services/file/http_upload_file.go | 10 +++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/server/internal/services/file/http_serve_upload.go b/server/internal/services/file/http_serve_upload.go index 458e1f3977..a406fdc2a5 100644 --- a/server/internal/services/file/http_serve_upload.go +++ b/server/internal/services/file/http_serve_upload.go @@ -49,7 +49,9 @@ func (fs *FileService) ServeUpload( fs.cache.Add(fileId, upload) } - src, size, err := fs.storage.Open(r.Context(), fileId) + intricatePath := calculateIntricatePath(fileId) + + src, size, err := fs.storage.Open(r.Context(), intricatePath) if err != nil { logger.Error("Failed to open file stream", slog.Any("error", err)) endpoint.WriteString(w, http.StatusNotFound, "File not found") diff --git a/server/internal/services/file/http_upload_file.go b/server/internal/services/file/http_upload_file.go index fa65b2fe3d..bf2d169756 100644 --- a/server/internal/services/file/http_upload_file.go +++ b/server/internal/services/file/http_upload_file.go @@ -88,8 +88,6 @@ func (fs *FileService) UploadFile( } } - // Calculate the nested path: e.g., "81/d2/file_81d2..." - // This prevents directory saturation on your SSD. intricatePath := calculateIntricatePath(fileId) // @todo this is fairly weird that we process multiple files but only a single one is returned @@ -147,10 +145,12 @@ func (fs *FileService) getNodeId(ctx context.Context) error { return nil } +// Calculate the nested path: e.g., "81/d2/file_81d2..." +// This prevents directory saturation on your SSD. +// +// Input: "file_81d259fc..." +// Output: "81/d2/file_81d259fc..." func calculateIntricatePath(fileId string) string { - // Input: "file_81d259fc..." - // Output: "81/d2/file_81d259fc..." - prefix := "file_" idPart := fileId if len(fileId) > len(prefix) && fileId[:len(prefix)] == prefix { From 6e5a4af91a3183bb98476022893e1fa2a9a666ef Mon Sep 17 00:00:00 2001 From: Janos Dobronszki Date: Fri, 26 Dec 2025 14:46:04 +0100 Subject: [PATCH 4/4] fix wrong fix --- server/internal/services/file/http_serve_upload.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/server/internal/services/file/http_serve_upload.go b/server/internal/services/file/http_serve_upload.go index a406fdc2a5..54264013d0 100644 --- a/server/internal/services/file/http_serve_upload.go +++ b/server/internal/services/file/http_serve_upload.go @@ -49,9 +49,7 @@ func (fs *FileService) ServeUpload( fs.cache.Add(fileId, upload) } - intricatePath := calculateIntricatePath(fileId) - - src, size, err := fs.storage.Open(r.Context(), intricatePath) + src, size, err := fs.storage.Open(r.Context(), upload.FilePath) if err != nil { logger.Error("Failed to open file stream", slog.Any("error", err)) endpoint.WriteString(w, http.StatusNotFound, "File not found")