Skip to content

fix(images): cache blob avatars by durable blob id, not the ephemeral URL - #1155

Merged
bmc08gt merged 1 commit into
code/cashfrom
feat/blob-image-stable-cache-key
Jul 27, 2026
Merged

fix(images): cache blob avatars by durable blob id, not the ephemeral URL#1155
bmc08gt merged 1 commit into
code/cashfrom
feat/blob-image-stable-cache-key

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Problem

Blob avatars that were already loaded get re-downloaded — you see the BlurHash placeholder, then the image, every time — even for images the app has fetched before.

Root cause: a blob's download_url is ephemeral. The proto (blob/v1/model.proto) documents it as "Ephemeral, server-minted URL… Re-issued on every fetch" with an expiry, while the bytes it addresses are immutable. ProfileAvatar keyed Coil's memory and disk caches on that rotating URL (.memoryCacheKey(photoUri) / .data(url)), so every new gRPC response minted a new URL → a new cache key → a guaranteed miss → BlurHash flash + re-download, both within a session and across cold starts.

Fix

Key the caches on the durable blob id instead of the URL, and treat cached blobs as immutable.

  • MediaItem gains cacheKeyForSize() / cacheKeyBelow(), derived from the rendition's blobId (base58) — a stable identity that survives URL rotation and app restarts.
  • ProfileAvatar sets both memoryCacheKey and diskCacheKey to that stable key. .data() still carries the current URL so the actual network fetch works; only the cache identity changes. The placeholderMemoryCacheKey upgrade (smaller rendition → larger) is keyed the same way so it keeps working across surfaces.
  • ImmutableBlobCacheStrategy (new) is wired into the ImageLoader. Coil's default strategy is a stock HTTP cache that would revalidate/re-fetch based on the expiring URL's headers; since blobs are static, this serves a cached response unconditionally and always persists on write.

Result

  • Within a session: re-displaying an avatar (list → detail, re-entering a screen, a rotated URL) hits the memory cache under the blob-id key — no BlurHash flash.
  • Across cold starts: the disk entry is keyed by blob id and served without revalidation, so previously-loaded images appear immediately instead of re-downloading.

Blobs are static, so no eviction logic is needed beyond the existing 50 MB disk LRU cap.

Notes / scope

  • The only wired-up blob image site today is the profile/contact avatar. Chat message media (MessageBubble) is still a TODO and not rendered; when it lands it should reuse MediaItem.cacheKeyForSize() the same way.

Testing

  • :services:flipcash, :apps:flipcash:shared:common-ui, :apps:flipcash:app compile clean.
  • Manual: load a screen with avatars, force-stop the app, relaunch — avatars should render without the BlurHash-then-image flash.

… URL

Blob download URLs are re-minted and expire on every fetch, while the bytes
they address are immutable. The avatar loader keyed Coil's memory/disk caches
on that rotating URL, so every new response produced a fresh cache key and a
guaranteed miss — the BlurHash placeholder flashed and the same bytes were
re-downloaded on every load and cold start.

Key the caches on the durable blob id instead:
- MediaItem gains cacheKeyForSize/cacheKeyBelow, derived from the rendition's
  blobId (base58) rather than the download URL.
- ProfileAvatar sets both memoryCacheKey and diskCacheKey to that stable key
  (data() still carries the current URL for the actual fetch), and keys the
  placeholderMemoryCacheKey upgrade the same way.
- Add ImmutableBlobCacheStrategy so Coil serves cached blobs unconditionally
  and never revalidates/re-downloads against the expiring URL's cache headers.

Result: a loaded avatar is served from cache on re-display and across launches
with no BlurHash flash. Blobs are static, so no eviction logic is needed beyond
the existing 50 MB disk cap.
@github-actions github-actions Bot added type: fix Bug fix area: network gRPC, connectivity, API, exchange rates labels Jul 27, 2026
@bmc08gt
bmc08gt merged commit 830731b into code/cash Jul 27, 2026
3 checks passed
@bmc08gt
bmc08gt deleted the feat/blob-image-stable-cache-key branch July 27, 2026 14:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: network gRPC, connectivity, API, exchange rates type: fix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant