Keep variant content types in memory for cache hits (closes #70)

Cache.metaCache was declared and never used, so every hit read and
parsed the variant's .meta file. It is now an LRU of up to 10,000
content types (hashicorp/golang-lru/v2), filled by StoreVariant and by
GetVariant after it reads a .meta file. For a variant it holds, Lookup
skips the disk check and GetVariant skips the .meta read; the variant
file is still opened and its size taken from it. Eviction removes the
entry before deleting the files, and GetVariant removes it when the
file will not open, so a missing variant is never served. The cap is a
constant, not a setting. README.md describes it.

Model: opus-5-5
This commit is contained in:
2026-09-29 09:28:28 +00:00
parent 33c958ad1c
commit ffe64d5fe0
7 changed files with 89 additions and 28 deletions
+47 -16
View File
@@ -14,6 +14,7 @@ import (
"sync"
"time"
lru "github.com/hashicorp/golang-lru/v2"
"sneak.berlin/go/pixa/internal/httpfetcher"
)
@@ -26,6 +27,10 @@ var (
// HTTP status code for successful fetch.
const httpStatusOK = 200
// metaCacheSize is how many variants' content types metaCache holds. A
// variant not among them is served as before, reading its .meta file.
const metaCacheSize = 10000
// CacheConfig holds cache configuration.
type CacheConfig struct {
StateDir string
@@ -49,12 +54,6 @@ type CacheConfig struct {
Logger *slog.Logger
}
// variantMeta stores content type for fast cache hits without reading .meta file.
type variantMeta struct {
ContentType string
Size int64
}
// Cache implements the caching layer for the image proxy.
type Cache struct {
db *sql.DB
@@ -76,9 +75,10 @@ type Cache struct {
evictionStarted bool
evictionStopOnce sync.Once
// In-memory cache of variant metadata (content type, size) to avoid
// reading .meta files
metaCache map[VariantKey]variantMeta
// metaCache holds the content types of the variants most recently
// stored or served, so a hit does not read the variant's .meta file.
// It never stands in for the variant file, which is always opened.
metaCache *lru.Cache[VariantKey, string]
// contentLocks serializes StoreSource and evictSourceBlob per
// content hash, closing the race window between an eviction's row
@@ -101,6 +101,11 @@ func NewCache(db *sql.DB, config CacheConfig) (*Cache, error) {
log = slog.Default()
}
metaCache, err := lru.New[VariantKey, string](metaCacheSize)
if err != nil {
return nil, fmt.Errorf("failed to create variant content type cache: %w", err)
}
c := &Cache{
db: db,
config: config,
@@ -109,7 +114,7 @@ func NewCache(db *sql.DB, config CacheConfig) (*Cache, error) {
evictionPressure: make(chan struct{}, 1),
evictionStop: make(chan struct{}),
evictionDone: make(chan struct{}),
metaCache: make(map[VariantKey]variantMeta),
metaCache: metaCache,
contentLocks: newContentLock(),
}
@@ -154,13 +159,15 @@ type LookupResult struct {
CacheStatus CacheStatus
}
// Lookup checks if a processed variant exists on disk. Hits touch the
// variant's LRU timestamp; a disabled cache always misses.
// Lookup checks if a processed variant exists on disk: a variant held
// in metaCache counts as present without a check of the disk. Hits
// touch the variant's LRU timestamp; a disabled cache always misses.
func (c *Cache) Lookup(ctx context.Context, req *ImageRequest) (*LookupResult, error) {
cacheKey := CacheKey(req)
// Check variant storage directly - no DB needed for cache hits
if !c.disabled && c.variants.Exists(cacheKey) {
// Check memory, then variant storage - no DB needed for cache hits
if !c.disabled &&
(c.metaCache.Contains(cacheKey) || c.variants.Exists(cacheKey)) {
c.touchVariant(ctx, cacheKey)
return &LookupResult{
@@ -177,13 +184,35 @@ func (c *Cache) Lookup(ctx context.Context, req *ImageRequest) (*LookupResult, e
}, nil
}
// GetVariant returns a reader, size, and content type for a cached variant.
// GetVariant returns a reader, size, and content type for a cached
// variant. The content type comes from metaCache, or else from the
// variant's .meta file and is then kept in metaCache.
func (c *Cache) GetVariant(cacheKey VariantKey) (io.ReadCloser, int64, string, error) {
if c.disabled {
return nil, 0, "", ErrNotFound
}
return c.variants.LoadWithMeta(cacheKey)
contentType, known := c.metaCache.Get(cacheKey)
if known {
reader, size, err := c.variants.LoadWithSize(cacheKey)
if err != nil {
// The file is gone, e.g. deleted outside pixa
c.metaCache.Remove(cacheKey)
return nil, 0, "", err
}
return reader, size, contentType, nil
}
reader, size, contentType, err := c.variants.LoadWithMeta(cacheKey)
if err != nil {
return nil, 0, "", err
}
c.metaCache.Add(cacheKey, contentType)
return reader, size, contentType, nil
}
// StoreSource stores fetched source content and metadata. On a
@@ -286,6 +315,8 @@ func (c *Cache) StoreVariant(
return err
}
c.metaCache.Add(cacheKey, contentType)
_, err = c.db.ExecContext(ctx, `
INSERT INTO variant_content (cache_key, size_bytes, content_type)
VALUES (?, ?, ?)