Serve a cache hit with no database read or write (closes #309)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
A hit opens the variant file and nothing else: its content type comes from the file's first bytes, and a file in no output format is a miss and is made again. Its access time and hit count are handed, without waiting, to the goroutine that wrote late counts, which now writes every count and the access times once a second; the access times wait in a channel of 10,000 that drops the rest. checkNegativeCache leaves expired rows to CleanExpired. The .meta files, metaCache, Lookup and golang-lru go, with their tests; tests that read counts after Get wait for the background write. make bench runs a parallel cache hit benchmark, outside make check. Model: opus-5-5
This commit is contained in:
@@ -1,16 +1,14 @@
|
||||
package imgcache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"sneak.berlin/go/pixa/internal/imagerequest"
|
||||
"sneak.berlin/go/pixa/internal/magic"
|
||||
)
|
||||
|
||||
// Storage constants.
|
||||
@@ -211,77 +209,46 @@ func (s *ContentStorage) keyToPath(key imagerequest.SourceKey) string {
|
||||
|
||||
// VariantStorage handles key-based file storage for processed image variants.
|
||||
// Files are stored at: <basedir>/<ab>/<cd>/<cache_key>
|
||||
// Metadata is stored at: <basedir>/<ab>/<cd>/<cache_key>.meta
|
||||
type VariantStorage struct {
|
||||
baseDir string
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
// VariantMeta contains metadata about a cached variant.
|
||||
//
|
||||
//nolint:tagliatelle // stored metadata format uses snake_case
|
||||
type VariantMeta struct {
|
||||
ContentType string `json:"content_type"`
|
||||
Size int64 `json:"size"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
}
|
||||
// errNotAnOutputFormat is returned for a variant file whose first bytes are
|
||||
// those of a format pixa reads but never writes, such as SVG.
|
||||
var errNotAnOutputFormat = errors.New("variant file is not in an output format")
|
||||
|
||||
// NewVariantStorage creates a new variant storage at the given base directory.
|
||||
// A failed .meta write is logged to log.
|
||||
func NewVariantStorage(baseDir string, log *slog.Logger) (*VariantStorage, error) {
|
||||
func NewVariantStorage(baseDir string) (*VariantStorage, error) {
|
||||
err := os.MkdirAll(baseDir, StorageDirPerm)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create variant storage directory: %w", err)
|
||||
}
|
||||
|
||||
return &VariantStorage{baseDir: baseDir, log: log}, nil
|
||||
return &VariantStorage{baseDir: baseDir}, nil
|
||||
}
|
||||
|
||||
// Store writes content and metadata to storage at the given key, each
|
||||
// with writeFileSynced. The .meta file is written first, so that a crash
|
||||
// cannot leave the content in place without it.
|
||||
// Store writes content to storage at the given key with writeFileSynced.
|
||||
func (s *VariantStorage) Store(
|
||||
key imagerequest.VariantKey, r io.Reader, contentType string,
|
||||
key imagerequest.VariantKey, r io.Reader,
|
||||
) (int64, error) {
|
||||
data, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to read content: %w", err)
|
||||
}
|
||||
|
||||
size := int64(len(data))
|
||||
path := s.keyToPath(key)
|
||||
metaPath := path + ".meta"
|
||||
|
||||
err = os.MkdirAll(filepath.Dir(path), StorageDirPerm)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to create directory: %w", err)
|
||||
}
|
||||
|
||||
meta := VariantMeta{
|
||||
ContentType: contentType,
|
||||
Size: size,
|
||||
CreatedAt: time.Now().UTC().Unix(),
|
||||
}
|
||||
|
||||
metaData, err := json.Marshal(meta)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to marshal metadata: %w", err)
|
||||
}
|
||||
|
||||
// Metadata write failure is non-fatal: the content is then served as
|
||||
// application/octet-stream.
|
||||
err = writeFileSynced(metaPath, metaData)
|
||||
if err != nil {
|
||||
s.log.Warn("failed to write variant metadata sidecar",
|
||||
"path", metaPath, "error", err)
|
||||
}
|
||||
|
||||
err = writeFileSynced(path, data)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return size, nil
|
||||
return int64(len(data)), nil
|
||||
}
|
||||
|
||||
// Load returns a reader for the content at the given key.
|
||||
@@ -300,56 +267,63 @@ func (s *VariantStorage) Load(key imagerequest.VariantKey) (io.ReadCloser, error
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// LoadWithSize returns a reader and file size for the content at the
|
||||
// given key.
|
||||
func (s *VariantStorage) LoadWithSize(
|
||||
// LoadWithContentType opens the content at the given key and returns a
|
||||
// reader, the file size and the content type, which is read from the
|
||||
// file's first bytes. A missing file is ErrNotFound. A file whose first
|
||||
// bytes are not those of an output format, an empty one included, is
|
||||
// closed again and the reason returned.
|
||||
func (s *VariantStorage) LoadWithContentType(
|
||||
key imagerequest.VariantKey,
|
||||
) (io.ReadCloser, int64, error) {
|
||||
) (io.ReadCloser, int64, string, error) {
|
||||
path := s.keyToPath(key)
|
||||
|
||||
f, err := os.Open(path) //nolint:gosec // path derived from cache key
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, 0, ErrNotFound
|
||||
return nil, 0, "", ErrNotFound
|
||||
}
|
||||
|
||||
return nil, 0, fmt.Errorf("failed to open content: %w", err)
|
||||
return nil, 0, "", fmt.Errorf("failed to open content: %w", err)
|
||||
}
|
||||
|
||||
stat, err := f.Stat()
|
||||
if err != nil {
|
||||
_ = f.Close()
|
||||
|
||||
return nil, 0, fmt.Errorf("failed to stat content: %w", err)
|
||||
return nil, 0, "", fmt.Errorf("failed to stat content: %w", err)
|
||||
}
|
||||
|
||||
return f, stat.Size(), nil
|
||||
}
|
||||
|
||||
// LoadWithMeta returns a reader, size, and content type for the content at
|
||||
// the given key. The content type is read from the .meta file, and is
|
||||
// empty when that file is missing or unreadable.
|
||||
func (s *VariantStorage) LoadWithMeta(
|
||||
key imagerequest.VariantKey,
|
||||
) (io.ReadCloser, int64, string, error) {
|
||||
f, size, err := s.LoadWithSize(key)
|
||||
contentType, err := outputContentType(f)
|
||||
if err != nil {
|
||||
_ = f.Close()
|
||||
|
||||
return nil, 0, "", err
|
||||
}
|
||||
|
||||
var contentType string
|
||||
return f, stat.Size(), contentType, nil
|
||||
}
|
||||
|
||||
metaPath := s.keyToPath(key) + ".meta"
|
||||
// outputContentType reads the first bytes of content, without moving its
|
||||
// offset, and returns the content type of the output format they show.
|
||||
func outputContentType(content io.ReaderAt) (string, error) {
|
||||
firstBytes := make([]byte, magic.MinMagicBytes)
|
||||
|
||||
metaData, err := os.ReadFile(metaPath) //nolint:gosec // path derived from cache key
|
||||
if err == nil {
|
||||
var meta VariantMeta
|
||||
if json.Unmarshal(metaData, &meta) == nil && meta.ContentType != "" {
|
||||
contentType = meta.ContentType
|
||||
}
|
||||
_, err := content.ReadAt(firstBytes, 0)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read the first bytes: %w", err)
|
||||
}
|
||||
|
||||
return f, size, contentType, nil
|
||||
detected, err := magic.DetectFormat(firstBytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, isOutputFormat := magic.MIMEToImageFormat(string(detected))
|
||||
if !isOutputFormat {
|
||||
return "", fmt.Errorf("%w: %s", errNotAnOutputFormat, detected)
|
||||
}
|
||||
|
||||
return string(detected), nil
|
||||
}
|
||||
|
||||
// Exists checks if content exists at the given key.
|
||||
@@ -360,8 +334,8 @@ func (s *VariantStorage) Exists(key imagerequest.VariantKey) bool {
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Delete removes the content at the given key together with its .meta
|
||||
// sidecar file. A missing file is not an error.
|
||||
// Delete removes the content at the given key. A missing file is not an
|
||||
// error.
|
||||
func (s *VariantStorage) Delete(key imagerequest.VariantKey) error {
|
||||
path := s.keyToPath(key)
|
||||
|
||||
@@ -370,13 +344,6 @@ func (s *VariantStorage) Delete(key imagerequest.VariantKey) error {
|
||||
return fmt.Errorf("failed to delete content: %w", err)
|
||||
}
|
||||
|
||||
metaPath := path + ".meta"
|
||||
|
||||
err = os.Remove(metaPath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to delete variant metadata: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user