From 31ed20ec9e67593a446a7f289a42c7d22edae9c4 Mon Sep 17 00:00:00 2001 From: clawbot <35+clawbot@noreply.example.org> Date: Tue, 29 Sep 2026 02:46:30 +0000 Subject: [PATCH] Count a late hit, and report no totals on a disabled cache (closes #56) A hit is now counted with the request context detached from its cancellation, as the miss already is, so a hit served after the client left or the request timed out still moves the hit count. On a disabled disk cache, Stats reports no items and no size, whatever rows an earlier run left in the database; the size already followed that rule. Model: opus-5-5 --- TODO.md | 14 ++++++++------ internal/imgcache/cache.go | 25 ++++++++++++++----------- internal/imgcache/service.go | 3 ++- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/TODO.md b/TODO.md index 5722d2e..622d81f 100644 --- a/TODO.md +++ b/TODO.md @@ -46,12 +46,14 @@ exhaustion counts the cached source images and processed variants (`source_content` plus `variant_content`) and takes their size from `Cache.UsageBytes`, instead of reading `request_cache` and `output_content`, which nothing - writes; those two tables are left in the schema. A miss is counted after - it is served or fails, even when the request context has ended by then, - with the bytes it read from upstream, so `upstream_fetch_count` and - `upstream_fetch_bytes` move, including for an upstream body that fails - partway or a fetched source that then fails the magic byte check; - `transform_count` counts each image the image processor transcodes. + writes; those two tables are left in the schema; a disabled disk cache + reports no items and no size. A hit is counted even when the request + context has ended. A miss is counted after it is served or fails, also + when the request context has ended by then, with the bytes it read from + upstream, so `upstream_fetch_count` and `upstream_fetch_bytes` move, + including for an upstream body that fails partway or a fetched source + that then fails the magic byte check; `transform_count` counts each image + the image processor transcodes. - 2026-09-28 strip metadata from processed images (closes #82): every output is exported with govips' `StripMetadata`, so it carries no EXIF, XMP, IPTC or ICC profile; the image is first turned upright with `AutoRotate` (before sizes are diff --git a/internal/imgcache/cache.go b/internal/imgcache/cache.go index ed3905c..e15b90a 100644 --- a/internal/imgcache/cache.go +++ b/internal/imgcache/cache.go @@ -419,18 +419,21 @@ func (c *Cache) Stats(ctx context.Context) (*CacheStats, error) { return nil, fmt.Errorf("failed to get cache stats: %w", err) } - // Count and size the cached source images and processed variants - err = c.db.QueryRowContext(ctx, ` - SELECT (SELECT COUNT(*) FROM source_content) - + (SELECT COUNT(*) FROM variant_content) - `).Scan(&stats.TotalItems) - if err != nil { - c.log.Warn("failed to count cache items for stats", "error", err) - } + // Count and size the cached source images and processed variants. A + // disabled cache holds none, whatever rows an earlier run left. + if !c.disabled { + err = c.db.QueryRowContext(ctx, ` + SELECT (SELECT COUNT(*) FROM source_content) + + (SELECT COUNT(*) FROM variant_content) + `).Scan(&stats.TotalItems) + if err != nil { + c.log.Warn("failed to count cache items for stats", "error", err) + } - stats.TotalSizeBytes, err = c.UsageBytes(ctx) - if err != nil { - c.log.Warn("failed to sum cache size for stats", "error", err) + stats.TotalSizeBytes, err = c.UsageBytes(ctx) + if err != nil { + c.log.Warn("failed to sum cache size for stats", "error", err) + } } // Compute hit rate as a ratio diff --git a/internal/imgcache/service.go b/internal/imgcache/service.go index 767bbf0..d85dee8 100644 --- a/internal/imgcache/service.go +++ b/internal/imgcache/service.go @@ -143,7 +143,8 @@ func (s *Service) Get(ctx context.Context, req *ImageRequest) (*ImageResponse, e s.log.Error("failed to get cached variant", "key", result.CacheKey, "error", err) // Fall through to re-process } else { - s.cache.IncrementStats(ctx, true, 0) + // Counted also when the request context has ended meanwhile + s.cache.IncrementStats(context.WithoutCancel(ctx), true, 0) return &ImageResponse{ Content: reader,