Cache.Stats reads never-populated output_content/request_cache tables, so TotalItems and TotalSizeBytes are always 0 #56

Closed
opened 2026-08-07 23:13:03 +02:00 by clawbot · 5 comments
Collaborator

Discovered while implementing #51 (out of scope there, filing per repo policy).

Cache.Stats in internal/imgcache/cache.go computes TotalItems from request_cache and TotalSizeBytes from output_content:

_ = c.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM request_cache`).Scan(&stats.TotalItems)
_ = c.db.QueryRowContext(ctx, `SELECT COALESCE(SUM(size_bytes), 0) FROM output_content`).Scan(&stats.TotalSizeBytes)

No code path ever inserts into output_content or request_cache (they were created by migration 001 for a design that stored variants content-addressed with a request mapping, but the implementation went with key-addressed VariantStorage instead). Both stats are therefore always 0.

Since #51 (PR #55), real accounting exists: variant_content tracks processed variants and source_content tracks source blobs, and Cache.UsageBytes returns the true total. Suggested fix:

  1. Point TotalItems/TotalSizeBytes at variant_content + source_content (or reuse UsageBytes).
  2. Decide whether to drop the dead output_content/request_cache tables in a future migration or leave them for the originally intended design.

Also worth a look in the same pass: the metaCache field on Cache (in-memory variant meta cache) is initialized but never read or written.

Discovered while implementing #51 (out of scope there, filing per repo policy). `Cache.Stats` in `internal/imgcache/cache.go` computes `TotalItems` from `request_cache` and `TotalSizeBytes` from `output_content`: ``` _ = c.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM request_cache`).Scan(&stats.TotalItems) _ = c.db.QueryRowContext(ctx, `SELECT COALESCE(SUM(size_bytes), 0) FROM output_content`).Scan(&stats.TotalSizeBytes) ``` No code path ever inserts into `output_content` or `request_cache` (they were created by migration 001 for a design that stored variants content-addressed with a request mapping, but the implementation went with key-addressed `VariantStorage` instead). Both stats are therefore always 0. Since #51 (PR #55), real accounting exists: `variant_content` tracks processed variants and `source_content` tracks source blobs, and `Cache.UsageBytes` returns the true total. Suggested fix: 1. Point `TotalItems`/`TotalSizeBytes` at `variant_content` + `source_content` (or reuse `UsageBytes`). 2. Decide whether to drop the dead `output_content`/`request_cache` tables in a future migration or leave them for the originally intended design. Also worth a look in the same pass: the `metaCache` field on `Cache` (in-memory variant meta cache) is initialized but never read or written.
clawbot added this to the 1.0.0 milestone 2026-08-09 03:37:49 +02:00
Author
Collaborator

Adding a related finding to this issue's scope rather than filing a near-duplicate, found during the 1.0.0 survey and verified against main at 61f42e6.

The stats problem is wider than Cache.Stats reading the wrong tables — the counter columns are never written either:

  • IncrementStats(ctx, hit, fetchBytes) (internal/imgcache/cache.go:324-344) has a fetchBytes > 0 branch that updates upstream_fetch_bytes, but both call sites pass 0 (internal/imgcache/service.go:134 and :148). So upstream_fetch_count and upstream_fetch_bytes never move.
  • transform_count has no writer anywhere in the codebase.

So even after this issue repoints TotalItems/TotalSizeBytes at the real accounting tables (variant_content/source_content, which PR #55 introduces and which do have correct writers), the remaining stats columns will still be permanently zero.

Suggest folding into this issue's definition of done, since it is the same "stats surface reports zeros" defect:

  1. IncrementStats receives the real fetched byte count from both call sites in service.go rather than 0.
  2. transform_count is incremented when a transcode actually happens, or the column is dropped if it is not worth maintaining.
  3. A test asserting each counter actually moves — a stats field with no test is how all of these silently became zero in the first place.

Also still open from this issue's original body: the dead metaCache field. That has grown enough scope (it is the mechanism behind README.md:49's 1-5k r/s claim, and it needs bounding plus eviction-invalidation) that I filed it separately rather than leaving it as a footnote here.

Milestoned 1.0.0. Do this after PR #55 merges — it introduces the accounting tables this should read from.

Adding a related finding to this issue's scope rather than filing a near-duplicate, found during the 1.0.0 survey and verified against `main` at `61f42e6`. The stats problem is wider than `Cache.Stats` reading the wrong tables — **the counter columns are never written either**: - `IncrementStats(ctx, hit, fetchBytes)` (`internal/imgcache/cache.go:324-344`) has a `fetchBytes > 0` branch that updates `upstream_fetch_bytes`, but **both** call sites pass `0` (`internal/imgcache/service.go:134` and `:148`). So `upstream_fetch_count` and `upstream_fetch_bytes` never move. - `transform_count` has no writer anywhere in the codebase. So even after this issue repoints `TotalItems`/`TotalSizeBytes` at the real accounting tables (`variant_content`/`source_content`, which PR #55 introduces and which do have correct writers), the remaining stats columns will still be permanently zero. Suggest folding into this issue's definition of done, since it is the same "stats surface reports zeros" defect: 1. `IncrementStats` receives the real fetched byte count from both call sites in `service.go` rather than `0`. 2. `transform_count` is incremented when a transcode actually happens, or the column is dropped if it is not worth maintaining. 3. A test asserting each counter actually moves — a stats field with no test is how all of these silently became zero in the first place. Also still open from this issue's original body: the dead `metaCache` field. That has grown enough scope (it is the mechanism behind `README.md:49`'s 1-5k r/s claim, and it needs bounding plus eviction-invalidation) that I filed it separately rather than leaving it as a footnote here. Milestoned `1.0.0`. Do this after PR #55 merges — it introduces the accounting tables this should read from.
clawbot self-assigned this 2026-09-29 01:38:44 +02:00
Author
Collaborator

Plan, checked against current next:

  • Cache.Stats: TotalItems becomes the number of rows in source_content plus variant_content, and TotalSizeBytes comes from Cache.UsageBytes. A failed query is still logged at warn and Stats still succeeds. The existing test for those warnings drops request_cache and output_content to force the failure; it will drop source_content and variant_content instead, the tables Stats reads after this change.
  • Upstream fetch counters: Service.Get counts a miss after the image is produced (or fails), passing the bytes that request fetched from upstream: 0 when the source came from the disk cache, otherwise the length fetched, also when a later step (magic byte check, processing) fails. The hit call site keeps passing 0, because a hit fetches nothing.
  • transform_count: a new Cache.IncrementTransformCount, called in processAndStore once the image processor returns without error (every such call decodes and re-encodes the image).
  • Tests, committed first: one for each counter through Service.Get, and one for the Stats totals after storing a source and a variant.
  • 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

Plan, checked against current `next`: - `Cache.Stats`: `TotalItems` becomes the number of rows in `source_content` plus `variant_content`, and `TotalSizeBytes` comes from `Cache.UsageBytes`. A failed query is still logged at `warn` and `Stats` still succeeds. The existing test for those warnings drops `request_cache` and `output_content` to force the failure; it will drop `source_content` and `variant_content` instead, the tables `Stats` reads after this change. - Upstream fetch counters: `Service.Get` counts a miss after the image is produced (or fails), passing the bytes that request fetched from upstream: 0 when the source came from the disk cache, otherwise the length fetched, also when a later step (magic byte check, processing) fails. The hit call site keeps passing 0, because a hit fetches nothing. - `transform_count`: a new `Cache.IncrementTransformCount`, called in `processAndStore` once the image processor returns without error (every such call decodes and re-encodes the image). - Tests, committed first: one for each counter through `Service.Get`, and one for the `Stats` totals after storing a source and a variant. - 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
Author
Collaborator

Built as planned in #144. One difference from the plan: the counters are covered by one test that walks Service.Get through a miss that fetches, a hit, a miss that reuses the cached source and two misses that fail, checking every counter after each step, rather than a separate test per counter.

Model: opus-5-5

Built as planned in https://git.eeqj.de/sneak/pixa/pulls/144. One difference from the plan: the counters are covered by one test that walks `Service.Get` through a miss that fetches, a hit, a miss that reuses the cached source and two misses that fail, checking every counter after each step, rather than a separate test per counter. Model: opus-5-5
Author
Collaborator

Question for sneak: approve one edit to an existing test in #144? This repo's rules require your approval before an existing test is changed.

TestStats_LogsFailedCountQueries (added today by #72) makes the two Stats count queries fail by dropping the tables they read, then checks a warning is logged. This issue points Stats at the tables that actually hold the cache (source_content, variant_content) instead of two tables nothing writes, so the test now drops those two instead. Its assertions are unchanged.

Options: (1) approve (recommended; the test still checks the same thing, against the tables Stats now reads); (2) refuse, and the test keeps dropping tables Stats no longer reads, so it fails and the fix cannot land.

Until you answer, the PR goes through review but is not merged; other work carries on.

Model: opus-5-5

Question for sneak: approve one edit to an existing test in https://git.eeqj.de/sneak/pixa/pulls/144? This repo's rules require your approval before an existing test is changed. `TestStats_LogsFailedCountQueries` (added today by https://git.eeqj.de/sneak/pixa/issues/72) makes the two `Stats` count queries fail by dropping the tables they read, then checks a warning is logged. This issue points `Stats` at the tables that actually hold the cache (`source_content`, `variant_content`) instead of two tables nothing writes, so the test now drops those two instead. Its assertions are unchanged. Options: (1) approve (recommended; the test still checks the same thing, against the tables `Stats` now reads); (2) refuse, and the test keeps dropping tables `Stats` no longer reads, so it fails and the fix cannot land. Until you answer, the PR goes through review but is not merged; other work carries on. Model: opus-5-5
sneak was assigned by clawbot 2026-09-29 02:00:42 +02:00
Owner

approved

approved
sneak was unassigned by clawbot 2026-09-29 02:59:19 +02:00
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/pixa#56