You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Homepage shows deactivated chapters (sidebar fragment invalidation deletes the wrong key) #2993
Deactivated chapters stay in the homepage chapter list. The chapter page 404s correctly and the subscriptions list is correct. Seen after several chapters were deactivated by setting active = false directly in the database.
The sidebar caches under a fixed key, and the callback that expires it deletes a key that does not exist:
app/components/chapters_sidebar_component.html.erb calls cache "chapters-sidebar". ActionView stores this as views/chapters_sidebar_component/chapters-sidebar, plus the cache namespace.
Chapter#expire_chapters_sidebar_cache deletes chapters-sidebar, which resolves to a different entry. The delete has never matched, so no write path invalidates the fragment, not only direct database edits.
Fix: derive the cache key from the data so it changes whenever the chapter set changes. The key must include Chapter.active.count, because a plain SQL update that sets active does not bump updated_at.
Evidence
The production cache store held one sidebar entry, last written 2026-09-13, under views/chapters_sidebar_component/chapters-sidebar, and no chapters-sidebar entry.
Toggling Chapter#update! in a production rails runner fired the callbacks and left the homepage unchanged.
Deleting the real entry refreshed the homepage at once.
The fixed key is shared across locales. A derived key removes that side effect.
The stale entry is cleared in production as of 2026-10-07. It will go stale again on the next chapter change until this is fixed.
The fix in #2994 takes a different shape than the suggestion above: instead of deriving the fragment key from the data, the sidebar now caches under a versioned key with a 10-minute TTL (skip_digest: true, expires_in: 10.minutes), the shape used by the events/past fragment. Every write path shows up within the TTL, a warm cache hit runs no chapter query, and there is no invalidation code left to get wrong.
Activity
fix(chapters): derive the sidebar cache key from the rendered chapters
fix(chapters): cache the chapters sidebar with a 10-minute TTL
mroderick commentedon Oct 7, 2026
The fix in #2994 takes a different shape than the suggestion above: instead of deriving the fragment key from the data, the sidebar now caches under a versioned key with a 10-minute TTL (
skip_digest: true, expires_in: 10.minutes), the shape used by the events/past fragment. Every write path shows up within the TTL, a warm cache hit runs no chapter query, and there is no invalidation code left to get wrong.