ML-151: document Assets.Cache TTL and invalidation strategy

Expand @moduledoc with cache key structure, 7-day TTL via
@one_week_seconds, periodic pruning by PruneAssetCache (every 12h),
and rationale for TTL-only invalidation (content-addressable immutable
assets). Update architecture.md entry accordingly.
This commit is contained in:
Claudio Ortolina
2026-04-30 13:05:59 +01:00
parent c489fde1cc
commit b8b3b24ffa
3 changed files with 65 additions and 5 deletions
@@ -1,9 +1,10 @@
--- ---
id: ML-151 id: ML-151
title: Document Assets.Cache TTL and cache invalidation strategy title: Document Assets.Cache TTL and cache invalidation strategy
status: To Do status: Done
assignee: [] assignee: []
created_date: '2026-04-30 10:48' created_date: '2026-04-30 10:48'
updated_date: '2026-04-30 12:04'
labels: labels:
- documentation - documentation
- assets - assets
@@ -28,7 +29,35 @@ Add a `@moduledoc` to `Assets.Cache` that explains:
## Acceptance Criteria ## Acceptance Criteria
<!-- AC:BEGIN --> <!-- AC:BEGIN -->
- [ ] #1 `Assets.Cache` `@moduledoc` documents the TTL value and where it is configured - [x] #1 `Assets.Cache` `@moduledoc` documents the TTL value and where it is configured
- [ ] #2 The invalidation strategy (TTL-based expiry) is explained with rationale - [x] #2 The invalidation strategy (TTL-based expiry) is explained with rationale
- [ ] #3 Any architectural implications are captured in `docs/architecture.md` - [x] #3 Any architectural implications are captured in `docs/architecture.md`
<!-- AC:END --> <!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
## Plan
### What we're documenting
`Assets.Cache` is an ETS-based cache for transformed binary asset data. Assets are stored by SHA256 hash and are immutable (write-once). The cache key is `{payload, format}`.
### Key facts to document
- **TTL**: `@one_week_seconds` = 7 days, defined as a module attribute
- **Pruning**: `prune/0` defaults to 7-day TTL, called every 12h by `PruneAssetCache` Oban cron worker
- **Invalidation strategy**: TTL-based expiry only — no explicit invalidation
### Rationale
Explicit invalidation isn't needed because:
1. Assets are content-addressable (SHA256) and immutable — updates create new hashes, so old entries are never requested again
2. Periodic pruning (every 12h) cleans up stale entries
3. ETS table is in-memory, cleared on restart
### Changes
1. Expand `@moduledoc` in `lib/music_library/assets/cache.ex`
2. Expand the `Assets.Cache` entry in `docs/architecture.md` Business Logic Modules table
<!-- SECTION:PLAN:END -->
+1 -1
View File
@@ -123,7 +123,7 @@ Last.fm schemas (separate, not Ecto-persisted to main DB):
| `Req.RateLimiter` | ETS-backed Req request step enforcing per-API minimum intervals between requests | | `Req.RateLimiter` | ETS-backed Req request step enforcing per-API minimum intervals between requests |
| `Req.RateLimiter.Clock` | Behaviour for time operations (allows test clock injection) | | `Req.RateLimiter.Clock` | Behaviour for time operations (allows test clock injection) |
| `Req.RateLimiter.SystemClock` | Real clock implementation using System.monotonic_time | | `Req.RateLimiter.SystemClock` | Real clock implementation using System.monotonic_time |
| `Assets.Cache` | ETS-based asset cache with TTL | | `Assets.Cache` | ETS-based asset cache with TTL (7-day TTL, TTL-only invalidation since assets are content-addressable and immutable) |
| `Assets.Image` / `Assets.Transform` | Image processing via Vix (libvips) | | `Assets.Image` / `Assets.Transform` | Image processing via Vix (libvips) |
| `Colors.Extractor` | Behaviour for dominant color extraction (configurable, allows test stubbing) | | `Colors.Extractor` | Behaviour for dominant color extraction (configurable, allows test stubbing) |
| `Colors.KMeansExtractor` | Color extraction via K-Means clustering (dominant_colors library), implements `Colors.Extractor` | | `Colors.KMeansExtractor` | Color extraction via K-Means clustering (dominant_colors library), implements `Colors.Extractor` |
+31
View File
@@ -1,6 +1,37 @@
defmodule MusicLibrary.Assets.Cache do defmodule MusicLibrary.Assets.Cache do
@moduledoc """ @moduledoc """
ETS-based asset cache with TTL for serving frequently accessed images. ETS-based asset cache with TTL for serving frequently accessed images.
## Cache key
Each entry is keyed by `{payload, format}` where `payload` is a transform
parameter string (encoding width and asset hash) and `format` is the output
MIME type (e.g. `"image/webp"`).
## TTL and pruning
The TTL is configured via the `@one_week_seconds` module attribute
(`60 * 60 * 24 * 7` = 7 days). The `prune/0` function deletes all entries
whose `inserted_at` timestamp is older than this threshold.
Pruning runs automatically every 12 hours via the `PruneAssetCache` Oban
cron worker.
## Invalidation strategy
This cache uses **TTL-based expiry only** — there is no explicit
invalidation. This is sufficient because:
1. Assets are content-addressable (SHA256 hashes) and immutable once
written. When an image is replaced (e.g. a new cover is uploaded),
the new asset gets a new hash, producing a different cache key. The
old entry is never requested again and naturally expires.
2. The ETS table is in-memory and cleared on application restart.
Explicit invalidation would add complexity (tracking which cache entries
derive from which original asset hash) with no practical benefit given
the above properties.
""" """
@spec new() :: :ets.table() @spec new() :: :ets.table()