simplify: drop SWR cache, always upload fresh to S3

Always overwrites S3 on every preview request — no stale content.
Removed sync.Map cache and background goroutines.
Blob-by-SHA paths still skip upload if already exists (immutable).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-21 01:01:50 +01:00
parent 01e862a238
commit 715f54e39d
+7 -53
View File
@@ -8,7 +8,6 @@ import (
"io"
"net/http"
"os"
"sync"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
@@ -18,19 +17,8 @@ import (
"code.gitea.io/gitea/services/context"
)
// previewCache tracks known blob SHAs on S3 to skip Stat calls.
// Key: S3 object path, Value: blob SHA that was uploaded.
var previewCache sync.Map
// HTMLPreview serves an HTML file via S3 with stale-while-revalidate caching.
//
// Flow:
// 1. Resolve the git blob for the requested file path
// 2. Build the S3 object key: preview/{owner}/{repo}/{path}
// 3. Check in-memory cache for the blob SHA
// 4. HIT+fresh (SHA matches current blob) → redirect to S3 presigned URL
// 5. HIT+stale (SHA mismatch) → redirect to S3 immediately, revalidate async
// 6. MISS → upload synchronously, then redirect
// HTMLPreview serves an HTML file via S3.
// Always uploads the current blob (overwrite), then redirects to presigned S3 URL.
func HTMLPreview(ctx *context.Context) {
if !setting.HTMLPreview.Enabled {
ctx.NotFound(fmt.Errorf("HTML preview is not enabled"))
@@ -43,7 +31,6 @@ func HTMLPreview(ctx *context.Context) {
return
}
// Get the blob
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treePath)
if err != nil {
if git.IsErrNotExist(err) {
@@ -59,34 +46,9 @@ func HTMLPreview(ctx *context.Context) {
}
blob := entry.Blob()
blobSHA := blob.ID.String()
s3Key := buildPreviewS3Key(ctx)
// Check in-memory cache
if cachedSHA, ok := previewCache.Load(s3Key); ok {
if cachedSHA.(string) == blobSHA {
// Cache HIT + fresh — redirect directly
redirectToS3(ctx, s3Key, treePath)
return
}
// Cache HIT + stale — redirect to old version, revalidate in background
go revalidatePreview(s3Key, blob, blobSHA)
redirectToS3(ctx, s3Key, treePath)
return
}
// Cache MISS — check if S3 has it (cold start)
_, err = storage.HTMLPreview.Stat(s3Key)
if err == nil {
// S3 has a version — serve it and revalidate in background
previewCache.Store(s3Key, "") // mark as known but unknown SHA
go revalidatePreview(s3Key, blob, blobSHA)
redirectToS3(ctx, s3Key, treePath)
return
}
// No cached version — upload synchronously
if err := uploadPreview(s3Key, blob, blobSHA); err != nil {
if err := uploadPreview(s3Key, blob); err != nil {
ctx.ServerError("uploadPreview", err)
return
}
@@ -111,16 +73,16 @@ func HTMLPreviewByID(ctx *context.Context) {
}
blobSHA := blob.ID.String()
// For blob-by-SHA, use a content-addressed key (immutable)
s3Key := fmt.Sprintf("blobs/%s", blobSHA)
// Content-addressed: same SHA = same content, skip if exists
_, err = storage.HTMLPreview.Stat(s3Key)
if err == nil {
redirectToS3(ctx, s3Key, blobSHA+".html")
return
}
if err := uploadPreview(s3Key, blob, blobSHA); err != nil {
if err := uploadPreview(s3Key, blob); err != nil {
ctx.ServerError("uploadPreview", err)
return
}
@@ -140,7 +102,6 @@ func redirectToS3(ctx *context.Context, s3Key, name string) {
ContentType: "text/html; charset=utf-8",
})
if err != nil {
// Fallback: serve directly through Gitea
servePreviewDirect(ctx, s3Key)
return
}
@@ -171,7 +132,7 @@ func servePreviewDirect(ctx *context.Context, s3Key string) {
_, _ = io.Copy(ctx.Resp, obj)
}
func uploadPreview(s3Key string, blob *git.Blob, blobSHA string) error {
func uploadPreview(s3Key string, blob *git.Blob) error {
dataRc, err := blob.DataAsync()
if err != nil {
return fmt.Errorf("blob DataAsync: %w", err)
@@ -183,15 +144,8 @@ func uploadPreview(s3Key string, blob *git.Blob, blobSHA string) error {
return fmt.Errorf("S3 save: %w", err)
}
previewCache.Store(s3Key, blobSHA)
log.Debug("HTMLPreview: uploaded %s (blob %s)", s3Key, blobSHA[:8])
log.Debug("HTMLPreview: uploaded %s (%d bytes)", s3Key, blob.Size())
return nil
}
func revalidatePreview(s3Key string, blob *git.Blob, blobSHA string) {
if err := uploadPreview(s3Key, blob, blobSHA); err != nil {
log.Error("HTMLPreview revalidate failed for %s: %v", s3Key, err)
}
}
// isHTMLTreePath is defined in view_file.go