Share one fetch and transcode among concurrent misses (closes #65) #160

Open
clawbot wants to merge 2 commits from issue-65-share-concurrent-misses into next
Collaborator

Requests that missed the same variant at once each fetched and transcoded it (#65). A miss now goes through a singleflight.Group from golang.org/x/sync, keyed on CacheKey, which includes quality and fit.

  • The first request runs the existing path (cached source or fetch, transcode, store) unchanged, taking the upstream connection and processing slot as before. The others wait for its image or error, holding no slot and no source bytes, each with its own reader over the shared bytes.
  • Context: the shared work uses context.WithoutCancel of the first request's context, so the others are still served when that client leaves; upstream_fetch_timeout and the two 10-second waits still bound it. A waiting request returns as soon as its own context ends (DoChan with a select).
  • Counting: every request counts one miss, waiters included; the shared work counts its upstream fetch and transcode once each.
  • Every waiter gets the shared error; the next request starts afresh or is answered from the negative cache.
  • A panic in the shared work becomes an error; singleflight would re-raise it outside the router's recoverer and stop pixad.

Judgement call: the request running the work waits for it even when its own context ends, as on next; letting it leave like the others would change TestService_Get_CountsInterruptedMisses, which needs the owner's approval.
Judgement call: a request whose context has already ended starts no work.
Disclosure: IncrementStats keeps its fetchBytes parameter, now always 0 from Get, as existing tests pass it bytes.
Disclosure: a request that misses just as another's work finishes may transcode again; its source is cached by then.

Model: opus-5-5

Requests that missed the same variant at once each fetched and transcoded it (https://git.eeqj.de/sneak/pixa/issues/65). A miss now goes through a `singleflight.Group` from `golang.org/x/sync`, keyed on `CacheKey`, which includes quality and fit. - The first request runs the existing path (cached source or fetch, transcode, store) unchanged, taking the upstream connection and processing slot as before. The others wait for its image or error, holding no slot and no source bytes, each with its own reader over the shared bytes. - Context: the shared work uses `context.WithoutCancel` of the first request's context, so the others are still served when that client leaves; `upstream_fetch_timeout` and the two 10-second waits still bound it. A waiting request returns as soon as its own context ends (`DoChan` with a `select`). - Counting: every request counts one miss, waiters included; the shared work counts its upstream fetch and transcode once each. - Every waiter gets the shared error; the next request starts afresh or is answered from the negative cache. - A panic in the shared work becomes an error; singleflight would re-raise it outside the router's recoverer and stop pixad. Judgement call: the request running the work waits for it even when its own context ends, as on `next`; letting it leave like the others would change `TestService_Get_CountsInterruptedMisses`, which needs the owner's approval. Judgement call: a request whose context has already ended starts no work. Disclosure: `IncrementStats` keeps its `fetchBytes` parameter, now always 0 from `Get`, as existing tests pass it bytes. Disclosure: a request that misses just as another's work finishes may transcode again; its source is cached by then. Model: opus-5-5
clawbot added the needs-review label 2026-09-29 11:34:52 +02:00
clawbot self-assigned this 2026-09-29 11:34:52 +02:00
clawbot added needs-rebase and removed needs-review labels 2026-09-29 12:45:02 +02:00
clawbot added 2 commits 2026-09-29 13:07:59 +02:00
Requests that miss the same variant while its fetch is held must make
one fetch and one transcode between them, all get the same image and
each count one miss. Variants differing only in quality or fit stay
apart. A waiting request whose context ends returns at once; the first
request's client leaving does not stop the work. A shared failure
reaches every request, the negative cache answers the next one, and an
uncached failure is fetched again. A request that has already ended
fetches nothing, and a panic comes back as an error. All but the
variants test fail on next.

Model: opus-5-5
Requests that missed the same variant at once each fetched and
transcoded it. They now share one call through
golang.org/x/sync/singleflight, keyed on the variant cache key. The
first request processes the variant with a context that does not end
with its own; the others wait for its result, holding no connection or
processing slot, and return as soon as their own context ends. The
processing request waits even then, as before. Each request counts one
miss; the processing counts its fetch and transcode once. A panic
while processing becomes an error instead of stopping pixad.

Model: opus-5-5
clawbot force-pushed issue-65-share-concurrent-misses from aead642746 to 4009490242 2026-09-29 13:07:59 +02:00 Compare
clawbot added needs-review and removed needs-rebase labels 2026-09-29 13:08:10 +02:00
Author
Collaborator

Rebased onto the current next. Only TODO.md conflicted; I kept both Completed Steps entries, with this one on top.

internal/imgcache/cache.go and internal/imgcache/service.go merged without a conflict and needed no change. The shared processing calls StoreVariant once, which fills the in-memory content types from #70. Any later request for that variant is a cache hit and gets its type from memory through GetVariant.

Judgement call: requests that wait on the shared processing still get its image directly, as before, rather than reading it back from the cache.

Model: opus-5-5

Rebased onto the current `next`. Only `TODO.md` conflicted; I kept both Completed Steps entries, with this one on top. `internal/imgcache/cache.go` and `internal/imgcache/service.go` merged without a conflict and needed no change. The shared processing calls `StoreVariant` once, which fills the in-memory content types from https://git.eeqj.de/sneak/pixa/issues/70. Any later request for that variant is a cache hit and gets its type from memory through `GetVariant`. Judgement call: requests that wait on the shared processing still get its image directly, as before, rather than reading it back from the cache. Model: opus-5-5
Some checks are pending
check / check (push) Waiting to run
You are not authorized to merge this pull request.
This pull request can be merged automatically.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin issue-65-share-concurrent-misses:issue-65-share-concurrent-misses
git checkout issue-65-share-concurrent-misses
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/pixa#160