Fix hot cache to include ContentType and SizeBytes
Hot cache entries now store all data needed to serve a cache hit without any database access: - OutputHash (for file lookup) - ContentType (for Content-Type header) - SizeBytes (for Content-Length header) Previously hot cache only stored OutputHash, causing empty Content-Type headers on cached WebP responses.
This commit is contained in:
6
Makefile
6
Makefile
@@ -28,9 +28,11 @@ test:
|
|||||||
go test -v ./...
|
go test -v ./...
|
||||||
|
|
||||||
# Build the binary
|
# Build the binary
|
||||||
build:
|
build: ./bin/pixad
|
||||||
|
|
||||||
|
./bin/pixad: ./internal/*/*.go ./cmd/pixad/*.go
|
||||||
@echo "Building pixad..."
|
@echo "Building pixad..."
|
||||||
go build -ldflags "$(LDFLAGS)" -o bin/pixad ./cmd/pixad
|
go build -ldflags "$(LDFLAGS)" -o $@ ./cmd/pixad
|
||||||
|
|
||||||
# Clean build artifacts
|
# Clean build artifacts
|
||||||
clean:
|
clean:
|
||||||
|
|||||||
@@ -30,6 +30,13 @@ type CacheConfig struct {
|
|||||||
HotCacheEnabled bool
|
HotCacheEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hotCacheEntry stores all data needed to serve a cache hit without DB access.
|
||||||
|
type hotCacheEntry struct {
|
||||||
|
OutputHash string
|
||||||
|
ContentType string
|
||||||
|
SizeBytes int64
|
||||||
|
}
|
||||||
|
|
||||||
// Cache implements the caching layer for the image proxy.
|
// Cache implements the caching layer for the image proxy.
|
||||||
type Cache struct {
|
type Cache struct {
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
@@ -37,7 +44,7 @@ type Cache struct {
|
|||||||
dstContent *ContentStorage
|
dstContent *ContentStorage
|
||||||
srcMetadata *MetadataStorage
|
srcMetadata *MetadataStorage
|
||||||
config CacheConfig
|
config CacheConfig
|
||||||
hotCache map[string]string // cache_key -> output_hash
|
hotCache map[string]hotCacheEntry // cache_key -> entry
|
||||||
hotCacheMu sync.RWMutex
|
hotCacheMu sync.RWMutex
|
||||||
hotCacheEnabled bool
|
hotCacheEnabled bool
|
||||||
}
|
}
|
||||||
@@ -69,7 +76,7 @@ func NewCache(db *sql.DB, config CacheConfig) (*Cache, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if config.HotCacheEnabled && config.HotCacheSize > 0 {
|
if config.HotCacheEnabled && config.HotCacheSize > 0 {
|
||||||
c.hotCache = make(map[string]string, config.HotCacheSize)
|
c.hotCache = make(map[string]hotCacheEntry, config.HotCacheSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
@@ -80,6 +87,7 @@ type LookupResult struct {
|
|||||||
Hit bool
|
Hit bool
|
||||||
OutputHash string
|
OutputHash string
|
||||||
ContentType string
|
ContentType string
|
||||||
|
SizeBytes int64
|
||||||
CacheStatus CacheStatus
|
CacheStatus CacheStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,13 +98,15 @@ func (c *Cache) Lookup(ctx context.Context, req *ImageRequest) (*LookupResult, e
|
|||||||
// Check hot cache first
|
// Check hot cache first
|
||||||
if c.hotCacheEnabled {
|
if c.hotCacheEnabled {
|
||||||
c.hotCacheMu.RLock()
|
c.hotCacheMu.RLock()
|
||||||
outputHash, ok := c.hotCache[cacheKey]
|
entry, ok := c.hotCache[cacheKey]
|
||||||
c.hotCacheMu.RUnlock()
|
c.hotCacheMu.RUnlock()
|
||||||
|
|
||||||
if ok && c.dstContent.Exists(outputHash) {
|
if ok && c.dstContent.Exists(entry.OutputHash) {
|
||||||
return &LookupResult{
|
return &LookupResult{
|
||||||
Hit: true,
|
Hit: true,
|
||||||
OutputHash: outputHash,
|
OutputHash: entry.OutputHash,
|
||||||
|
ContentType: entry.ContentType,
|
||||||
|
SizeBytes: entry.SizeBytes,
|
||||||
CacheStatus: CacheHit,
|
CacheStatus: CacheHit,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -114,14 +124,15 @@ func (c *Cache) Lookup(ctx context.Context, req *ImageRequest) (*LookupResult, e
|
|||||||
|
|
||||||
// Check database
|
// Check database
|
||||||
var outputHash, contentType string
|
var outputHash, contentType string
|
||||||
|
var sizeBytes int64
|
||||||
var fetchedAt time.Time
|
var fetchedAt time.Time
|
||||||
|
|
||||||
err = c.db.QueryRowContext(ctx, `
|
err = c.db.QueryRowContext(ctx, `
|
||||||
SELECT rc.output_hash, oc.content_type, rc.fetched_at
|
SELECT rc.output_hash, oc.content_type, oc.size_bytes, rc.fetched_at
|
||||||
FROM request_cache rc
|
FROM request_cache rc
|
||||||
JOIN output_content oc ON rc.output_hash = oc.content_hash
|
JOIN output_content oc ON rc.output_hash = oc.content_hash
|
||||||
WHERE rc.cache_key = ?
|
WHERE rc.cache_key = ?
|
||||||
`, cacheKey).Scan(&outputHash, &contentType, &fetchedAt)
|
`, cacheKey).Scan(&outputHash, &contentType, &sizeBytes, &fetchedAt)
|
||||||
|
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return &LookupResult{Hit: false, CacheStatus: CacheMiss}, nil
|
return &LookupResult{Hit: false, CacheStatus: CacheMiss}, nil
|
||||||
@@ -144,7 +155,11 @@ func (c *Cache) Lookup(ctx context.Context, req *ImageRequest) (*LookupResult, e
|
|||||||
// Update hot cache
|
// Update hot cache
|
||||||
if c.hotCacheEnabled {
|
if c.hotCacheEnabled {
|
||||||
c.hotCacheMu.Lock()
|
c.hotCacheMu.Lock()
|
||||||
c.hotCache[cacheKey] = outputHash
|
c.hotCache[cacheKey] = hotCacheEntry{
|
||||||
|
OutputHash: outputHash,
|
||||||
|
ContentType: contentType,
|
||||||
|
SizeBytes: sizeBytes,
|
||||||
|
}
|
||||||
c.hotCacheMu.Unlock()
|
c.hotCacheMu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,6 +174,7 @@ func (c *Cache) Lookup(ctx context.Context, req *ImageRequest) (*LookupResult, e
|
|||||||
Hit: true,
|
Hit: true,
|
||||||
OutputHash: outputHash,
|
OutputHash: outputHash,
|
||||||
ContentType: contentType,
|
ContentType: contentType,
|
||||||
|
SizeBytes: sizeBytes,
|
||||||
CacheStatus: CacheHit,
|
CacheStatus: CacheHit,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -277,7 +293,11 @@ func (c *Cache) StoreOutput(
|
|||||||
// Update hot cache
|
// Update hot cache
|
||||||
if c.hotCacheEnabled {
|
if c.hotCacheEnabled {
|
||||||
c.hotCacheMu.Lock()
|
c.hotCacheMu.Lock()
|
||||||
c.hotCache[cacheKey] = outputHash
|
c.hotCache[cacheKey] = hotCacheEntry{
|
||||||
|
OutputHash: outputHash,
|
||||||
|
ContentType: contentType,
|
||||||
|
SizeBytes: size,
|
||||||
|
}
|
||||||
c.hotCacheMu.Unlock()
|
c.hotCacheMu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user