From 19149a67d32a6f03344d7dcd074de7b812c345b5 Mon Sep 17 00:00:00 2001 From: Matt Van Horn Date: Sat, 9 May 2026 01:19:05 -0700 Subject: [PATCH] fix(config): cache read no mutation (#1256) ## What? Closes #1251 `GetCachedEmailBody` is a read operation but it was stamping `LastAccessedAt` on the matched entry and rewriting the body cache file on every hit, violating the read/write boundary the issue calls out. Drops the two flagged lines (`config/cache.go:505-506`) and adds a one-line note above the function that `SaveEmailBody` is the access-time owner. ## Why? The issue's analysis is correct: every email view path in `main.go` already chases `GetCachedEmailBody` with `SaveEmailBody`, which sets `LastAccessedAt = time.Now()` on the entry it persists. That keeps the LRU-style eviction in `evict` (sorted by `LastAccessedAt`) working unchanged, with one fewer disk write per email view. I traced both call sites to verify eviction recency is preserved on cache hits: - `main.go:707-744` (UpdatePreviewMsg): cache hit returns `PreviewBodyFetchedMsg{Err: nil}`. The handler at `main.go:746-779` runs `config.SaveEmailBody` whenever `msg.Err == nil`, which updates `LastAccessedAt` (`config/cache.go:552`). - `main.go:1247-1278` (ViewEmailMsg): cache hit returns `EmailBodyFetchedMsg{Err: nil}`. The handler at `main.go:1282-1328` runs `config.SaveEmailBody` whenever `msg.Err == nil`, same path. So an email the user repeatedly opens still bumps to the front of the eviction queue on every open; the work just happens in `SaveEmailBody` (which already exists) instead of duplicating it inside `GetCachedEmailBody`. --- config/cache.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/cache.go b/config/cache.go index 0774516fe36c83fa445da06985a16b89da452ae2..04979e28b7601a2727d1e09c72899794159be9ab 100644 --- a/config/cache.go +++ b/config/cache.go @@ -495,6 +495,8 @@ func saveEmailBodyCache(cache *EmailBodyCache) error { } // GetCachedEmailBody returns the cached body for a specific email, or nil if not cached. +// LastAccessedAt is updated by SaveEmailBody, not here -- a read should not +// mutate cache state. func GetCachedEmailBody(folderName string, uid uint32, accountID string) *CachedEmailBody { cache, err := LoadEmailBodyCache(folderName) if err != nil { @@ -502,8 +504,6 @@ func GetCachedEmailBody(folderName string, uid uint32, accountID string) *Cached } for i, b := range cache.Bodies { if b.UID == uid && b.AccountID == accountID { - cache.Bodies[i].LastAccessedAt = time.Now() - _ = saveEmailBodyCache(cache) return &cache.Bodies[i] } }