feat: Add Redis upload cache for multi-replica deployments (#5724)
This commit is contained in:
82
http/upload_cache_redis.go
Normal file
82
http/upload_cache_redis.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package fbhttp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
// redisUploadCache is an upload cache for multi replica deployments
|
||||
type redisUploadCache struct {
|
||||
client *redis.Client
|
||||
}
|
||||
|
||||
func newRedisUploadCache(redisURL string) (*redisUploadCache, error) {
|
||||
if redisURL == "" {
|
||||
return nil, fmt.Errorf("redis URL is required")
|
||||
}
|
||||
|
||||
opts, err := redis.ParseURL(redisURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid redis URL: %w", err)
|
||||
}
|
||||
|
||||
client := redis.NewClient(opts)
|
||||
|
||||
// Test connection
|
||||
if err := client.Ping(context.Background()).Err(); err != nil {
|
||||
return nil, fmt.Errorf("failed to connect to redis: %w", err)
|
||||
}
|
||||
|
||||
return &redisUploadCache{client: client}, nil
|
||||
}
|
||||
|
||||
func (c *redisUploadCache) filePathKey(filePath string) string {
|
||||
return "filebrowser:upload:" + filePath
|
||||
}
|
||||
|
||||
func (c *redisUploadCache) Register(filePath string, fileSize int64) {
|
||||
err := c.client.Set(context.Background(), c.filePathKey(filePath), fileSize, uploadCacheTTL).Err()
|
||||
if err != nil {
|
||||
log.Printf("failed to register upload in redis cache: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *redisUploadCache) Complete(filePath string) {
|
||||
err := c.client.Del(context.Background(), c.filePathKey(filePath)).Err()
|
||||
if err != nil {
|
||||
log.Printf("failed to complete upload in redis cache: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *redisUploadCache) GetLength(filePath string) (int64, error) {
|
||||
result, err := c.client.Get(context.Background(), c.filePathKey(filePath)).Result()
|
||||
if err != nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return 0, fmt.Errorf("no active upload found for the given path")
|
||||
}
|
||||
return 0, fmt.Errorf("redis error: %w", err)
|
||||
}
|
||||
|
||||
size, err := strconv.ParseInt(result, 10, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("invalid upload length in cache: %w", err)
|
||||
}
|
||||
|
||||
return size, nil
|
||||
}
|
||||
|
||||
func (c *redisUploadCache) Touch(filePath string) {
|
||||
err := c.client.Expire(context.Background(), c.filePathKey(filePath), uploadCacheTTL).Err()
|
||||
if err != nil {
|
||||
log.Printf("failed to touch upload in redis cache: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *redisUploadCache) Close() {
|
||||
c.client.Close()
|
||||
}
|
||||
Reference in New Issue
Block a user