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:
+33
-4
@@ -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 -->
|
||||||
|
|||||||
@@ -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` |
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user