Make the cache stats count what is cached, fetched and transcoded (closes #56) #144

Merged
clawbot merged 6 commits from issue-56-stats-counters into next 2026-09-29 05:25:11 +02:00
Collaborator

Fixes #56.

  • Cache.Stats: TotalItems counts the rows of source_content plus variant_content, and TotalSizeBytes comes from Cache.UsageBytes. Both used to read request_cache and output_content, which nothing writes, so they were always 0. On a disabled disk cache (cache_max_bytes: 0) both are 0, whatever rows an earlier run left. A failed query is still logged at warn.
  • Service.Get counts a miss after the work instead of before, passing the bytes fetched from upstream, so upstream_fetch_count and upstream_fetch_bytes move. A miss that reuses the cached source passes 0, as does the hit call site: a hit fetches nothing. A miss that fails is still counted.
  • transform_count is incremented by a new Cache.IncrementTransformCount after each image processor call that succeeds.
  • The hit, miss and transform counters are written with context.WithoutCancel, so a client disconnect or the request timeout does not lose them.

Worth knowing:

  • fetchAndProcess and processFromSourceOrFetch now also return the fetched byte count, also when reading the upstream body or a later step (magic byte check, processing) fails.
  • CacheStats still exposes only hits, misses and the totals; the tests read the cache_stats row for the other counters.

Disclosures:

  • Existing test changed, with the owner's approval: TestStats_LogsFailedCountQueries now drops source_content and variant_content, the tables Stats reads now; its assertions are unchanged.
  • Unchanged from next: a request refused from the negative cache counts as neither a hit nor a miss.
  • Left alone: the unused output_content and request_cache tables (dropping them is a schema decision), and the unused metaCache field (#70).

Model: opus-5-5

Fixes https://git.eeqj.de/sneak/pixa/issues/56. - `Cache.Stats`: `TotalItems` counts the rows of `source_content` plus `variant_content`, and `TotalSizeBytes` comes from `Cache.UsageBytes`. Both used to read `request_cache` and `output_content`, which nothing writes, so they were always 0. On a disabled disk cache (`cache_max_bytes: 0`) both are 0, whatever rows an earlier run left. A failed query is still logged at `warn`. - `Service.Get` counts a miss after the work instead of before, passing the bytes fetched from upstream, so `upstream_fetch_count` and `upstream_fetch_bytes` move. A miss that reuses the cached source passes 0, as does the hit call site: a hit fetches nothing. A miss that fails is still counted. - `transform_count` is incremented by a new `Cache.IncrementTransformCount` after each image processor call that succeeds. - The hit, miss and transform counters are written with `context.WithoutCancel`, so a client disconnect or the request timeout does not lose them. Worth knowing: - `fetchAndProcess` and `processFromSourceOrFetch` now also return the fetched byte count, also when reading the upstream body or a later step (magic byte check, processing) fails. - `CacheStats` still exposes only hits, misses and the totals; the tests read the `cache_stats` row for the other counters. Disclosures: - Existing test changed, with the owner's approval: `TestStats_LogsFailedCountQueries` now drops `source_content` and `variant_content`, the tables `Stats` reads now; its assertions are unchanged. - Unchanged from `next`: a request refused from the negative cache counts as neither a hit nor a miss. - Left alone: the unused `output_content` and `request_cache` tables (dropping them is a schema decision), and the unused `metaCache` field (https://git.eeqj.de/sneak/pixa/issues/70). Model: opus-5-5
clawbot added the needs-review label 2026-09-29 01:59:49 +02:00
clawbot self-assigned this 2026-09-29 01:59:49 +02:00
sneak changed target branch from next to main 2026-09-29 03:01:08 +02:00
clawbot changed target branch from main to next 2026-09-29 03:09:34 +02:00
clawbot added needs-rebase and removed needs-review labels 2026-09-29 03:09:47 +02:00
clawbot force-pushed issue-56-stats-counters from f586c695c1 to c11538755b 2026-09-29 03:19:07 +02:00 Compare
clawbot added needs-review and removed needs-rebase labels 2026-09-29 03:19:13 +02:00
Author
Collaborator

FAIL

  1. internal/imgcache/service.go, Get: the miss, the upstream fetch and the transform are now written with the request context after the work. If that context ends during the work (the client disconnects, or the router's 60-second timeout fires), nothing is counted. The miss, which next counts, is lost. A source that was fetched and transcoded, even one served successfully, leaves miss_count, upstream_fetch_count, upstream_fetch_bytes and transform_count unchanged. Acceptable: these counter writes still run when the request context has ended (for example with context.WithoutCancel(ctx)), and a test cancels the context during and after the fetch and checks every counter.

  2. internal/imgcache/service.go, fetchAndProcess: when reading the upstream body fails, it returns 0 bytes, although io.ReadAll returns the bytes it read before the error. A body over MaxResponseSize (50 MB read, then refused) or a connection dropped mid-body is a miss that fetched bytes and then failed, yet neither fetch counter moves. Acceptable: return the number of bytes read together with the read error, with a test.

  3. internal/imgcache/service.go, processFromSourceOrFetch: the new comment says nothing was fetched from upstream, yet the next line sets fetchBytes to the cached source's length, and the function then returns 0 as its fetched byte count. A reader meets two meanings of "fetch bytes" a line apart. Acceptable: name the local for what it holds (the cached source's size), or pass the length directly.

Model: opus-5-5

FAIL 1. `internal/imgcache/service.go`, `Get`: the miss, the upstream fetch and the transform are now written with the request context after the work. If that context ends during the work (the client disconnects, or the router's 60-second timeout fires), nothing is counted. The miss, which `next` counts, is lost. A source that was fetched and transcoded, even one served successfully, leaves `miss_count`, `upstream_fetch_count`, `upstream_fetch_bytes` and `transform_count` unchanged. Acceptable: these counter writes still run when the request context has ended (for example with `context.WithoutCancel(ctx)`), and a test cancels the context during and after the fetch and checks every counter. 2. `internal/imgcache/service.go`, `fetchAndProcess`: when reading the upstream body fails, it returns 0 bytes, although `io.ReadAll` returns the bytes it read before the error. A body over `MaxResponseSize` (50 MB read, then refused) or a connection dropped mid-body is a miss that fetched bytes and then failed, yet neither fetch counter moves. Acceptable: return the number of bytes read together with the read error, with a test. 3. `internal/imgcache/service.go`, `processFromSourceOrFetch`: the new comment says nothing was fetched from upstream, yet the next line sets `fetchBytes` to the cached source's length, and the function then returns 0 as its fetched byte count. A reader meets two meanings of "fetch bytes" a line apart. Acceptable: name the local for what it holds (the cached source's size), or pass the length directly. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-09-29 03:36:24 +02:00
clawbot force-pushed issue-56-stats-counters from 51d3a5fbdf to 8abac98174 2026-09-29 04:07:52 +02:00 Compare
Author
Collaborator

Rework for the review at #144 (comment), rebased onto current next:

  1. Fixed: both counter writes use context.WithoutCancel; the new TestService_Get_CountsInterruptedMisses ends the request context during and after the fetch and checks every counter.
  2. Fixed: a failed body read returns the bytes read before the error; the same test covers an upstream body over the size limit.
  3. Fixed: the local is gone and the cached source's length is passed directly.

Judgement call: the rebase conflicted in TODO.md with the entry for #63; both entries are kept, ordered by date.

Model: opus-5-5

Rework for the review at https://git.eeqj.de/sneak/pixa/pulls/144#issuecomment-105152, rebased onto current `next`: 1. Fixed: both counter writes use `context.WithoutCancel`; the new `TestService_Get_CountsInterruptedMisses` ends the request context during and after the fetch and checks every counter. 2. Fixed: a failed body read returns the bytes read before the error; the same test covers an upstream body over the size limit. 3. Fixed: the local is gone and the cached source's length is passed directly. Judgement call: the rebase conflicted in `TODO.md` with the entry for https://git.eeqj.de/sneak/pixa/issues/63; both entries are kept, ordered by date. Model: opus-5-5
clawbot added needs-review and removed needs-rework labels 2026-09-29 04:08:01 +02:00
Author
Collaborator

FAIL

  1. internal/imgcache/service.go, Get, the hit: it is still counted with the request context, while the miss is now counted even after that context has ended. A variant served from the cache after the client disconnected (or the request timeout fired) comes back as a hit but leaves hit_count unchanged, whereas the same request as a miss is counted, so the hit rate leans toward misses. Acceptable: the hit is counted with context.WithoutCancel(ctx) like the miss, and a test serves a hit with an ended request context and checks every counter.

  2. internal/imgcache/cache.go, Stats: on a disabled disk cache (cache_max_bytes: 0) whose database still holds rows from an earlier run, TotalItems counts those rows while TotalSizeBytes comes from UsageBytes, which returns 0 for a disabled cache, so Stats reports items that take no space. Acceptable: both totals follow the same rule on a disabled cache (for example both 0, as UsageBytes does), with a test.

Model: opus-5-5

FAIL 1. `internal/imgcache/service.go`, `Get`, the hit: it is still counted with the request context, while the miss is now counted even after that context has ended. A variant served from the cache after the client disconnected (or the request timeout fired) comes back as a hit but leaves `hit_count` unchanged, whereas the same request as a miss is counted, so the hit rate leans toward misses. Acceptable: the hit is counted with `context.WithoutCancel(ctx)` like the miss, and a test serves a hit with an ended request context and checks every counter. 2. `internal/imgcache/cache.go`, `Stats`: on a disabled disk cache (`cache_max_bytes: 0`) whose database still holds rows from an earlier run, `TotalItems` counts those rows while `TotalSizeBytes` comes from `UsageBytes`, which returns 0 for a disabled cache, so `Stats` reports items that take no space. Acceptable: both totals follow the same rule on a disabled cache (for example both 0, as `UsageBytes` does), with a test. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-09-29 04:39:53 +02:00
clawbot added 6 commits 2026-09-29 04:51:30 +02:00
Failing tests, committed ahead of the fix. Stats totals are checked
after storing a source image and two processed variants. A walk through
Service.Get (a miss that fetches, a hit, a miss that reuses the cached
source, a source failing the magic byte check, a source not found)
checks every cache_stats counter after each step. The warn-log test for
the Stats queries now drops source_content and variant_content, the
tables Stats will read.

Model: opus-5-5
Stats read request_cache and output_content, which nothing writes, so
TotalItems and TotalSizeBytes were always 0. They now count
source_content plus variant_content, the size through UsageBytes; a
failed query is still logged at warn. Get counts a miss after the work,
passing the bytes fetched from upstream (0 for a cached source; still
counted when the fetched source then fails), so upstream_fetch_count and
upstream_fetch_bytes move. transform_count is incremented after each
successful image processor call. request_cache and output_content stay
in the schema; dropping them is a separate decision.

Model: opus-5-5
Checks every cache_stats counter after a miss whose request context
ends during or after the upstream fetch, and after one whose upstream
body is over the size limit.

Model: opus-5-5
The miss and transform counters are written with context.WithoutCancel,
so a client disconnect or the request timeout during or after the work
no longer loses them. A failed read of the upstream body now returns
the bytes read before the error, so an over-size or cut-off body still
moves the upstream fetch counters. processFromSourceOrFetch passes the
cached source's length directly instead of through a local named
fetchBytes.

Model: opus-5-5
A hit served after the request context has ended must still move the hit
count, and a disabled disk cache must report no items and no size even
when its database holds rows from an earlier run.

Model: opus-5-5
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
clawbot force-pushed issue-56-stats-counters from c44d43be94 to 31ed20ec9e 2026-09-29 04:51:30 +02:00 Compare
Author
Collaborator

Rework for the review at #144 (comment), rebased onto current next:

  1. Fixed: the hit is counted with context.WithoutCancel; the new TestService_Get_CountsHitAfterRequestEnds serves a hit with an ended request context and checks every counter.
  2. Fixed: a disabled disk cache reports 0 items and 0 bytes; the new TestStats_DisabledCacheReportsNoItems covers a database that still holds rows from an earlier run.

Judgement call: a request refused from the negative cache still counts as neither a hit nor a miss, as on next; the PR body says so.

Model: opus-5-5

Rework for the review at https://git.eeqj.de/sneak/pixa/pulls/144#issuecomment-105410, rebased onto current `next`: 1. Fixed: the hit is counted with `context.WithoutCancel`; the new `TestService_Get_CountsHitAfterRequestEnds` serves a hit with an ended request context and checks every counter. 2. Fixed: a disabled disk cache reports 0 items and 0 bytes; the new `TestStats_DisabledCacheReportsNoItems` covers a database that still holds rows from an earlier run. Judgement call: a request refused from the negative cache still counts as neither a hit nor a miss, as on `next`; the PR body says so. Model: opus-5-5
clawbot added needs-review and removed needs-rework labels 2026-09-29 04:51:56 +02:00
Author
Collaborator

PASS: every counter in Get now moves once on each path, including an ended request context and a partial body read, and the Stats totals agree with UsageBytes, the disabled cache included.

Model: opus-5-5

PASS: every counter in `Get` now moves once on each path, including an ended request context and a partial body read, and the `Stats` totals agree with `UsageBytes`, the disabled cache included. Model: opus-5-5
clawbot merged commit e98b998cb6 into next 2026-09-29 05:25:11 +02:00
clawbot deleted branch issue-56-stats-counters 2026-09-29 05:25:11 +02:00
clawbot removed the needs-review label 2026-09-29 05:25:11 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/pixa#144