# Mark All As Read - Cache Issue Analysis **Date**: 2026-01-01 **Issue**: After marking all as read, list is empty but count still shows 66 --- ## 🔍 Problem Analysis ### Current Flow 1. **User clicks "Mark all as read"** 2. **`markAllAsRead()` is called** 3. **Fetches notifications**: `this.getNotifications(userId, 1, 1000)` - ⚠️ **PROBLEM**: This goes through `NotificationService.getNotifications()` - ⚠️ **PROBLEM**: Which uses **CACHED** data if available - ⚠️ **PROBLEM**: Cached notifications still have `isRead: false` 4. **Filters unread**: Gets 66 unread from cached data 5. **Marks each as read**: Calls Leantime API for each 6. **Invalidates cache**: After marking completes 7. **Count is fetched**: But might use stale cache or be fetched before invalidation ### The Issue **Cache Race Condition**: - `markAllAsRead` uses cached notifications (which are stale) - Marks them as read in Leantime - Invalidates cache - But count might be fetched from cache **before** invalidation completes - Or count cache might not be properly invalidated **Why List is Empty**: - After marking, all notifications are read - List might filter to show only unread - So list is empty (correct behavior) - But count still shows 66 (stale cache) --- ## 🔧 Root Causes ### 1. Using Cached Data in `markAllAsRead` **Current Code**: ```typescript // In markAllAsRead const allNotifications = await this.getNotifications(userId, 1, 1000); ``` **Problem**: `getNotifications()` uses cache, so we're working with stale data. **Solution**: Fetch directly from Leantime API, bypassing cache. --- ### 2. Cache Invalidation Timing **Current Flow**: 1. Mark all as read (uses cached data) 2. Invalidate cache 3. Count is fetched (might use stale cache if fetched too soon) **Problem**: Race condition between invalidation and count fetch. **Solution**: - Invalidate cache **before** marking (or fetch fresh data) - Force immediate count refresh after marking - Add delay before count fetch to ensure cache is cleared --- ### 3. Count Cache Not Properly Invalidated **Current Code**: ```typescript if (success) { await this.invalidateCache(userId); } ``` **Problem**: If `markAllAsRead` fails partially, cache might not be invalidated. **Solution**: Always invalidate cache, even on partial success. --- ## ✅ Recommended Fixes ### Fix 1: Bypass Cache in `markAllAsRead` **Change**: Fetch notifications directly from Leantime API, not through cached service. **Implementation**: - Add a method to fetch notifications directly from adapter (bypassing cache) - Or add a `forceRefresh` parameter to `getNotifications` - Or fetch directly in `markAllAsRead` using Leantime API ### Fix 2: Always Invalidate Cache **Change**: Invalidate cache even if some notifications fail to mark. **Implementation**: - Invalidate cache if **any** notifications were successfully marked - Not just if **all** succeeded ### Fix 3: Force Fresh Count After Marking **Change**: After marking, force an immediate fresh count fetch. **Implementation**: - After `markAllAsRead` completes, immediately call `getNotificationCount()` with cache bypass - Or add a delay before count fetch to ensure cache is cleared --- ## 📊 Expected Behavior After Fixes ### After Mark All As Read **Before**: - List: Empty (all read) ✅ - Count: 66 (stale cache) ❌ **After**: - List: Empty (all read) ✅ - Count: 0 (fresh data) ✅ --- ## 🎯 Next Steps 1. **Fix cache usage in `markAllAsRead`**: Fetch fresh data, not cached 2. **Improve cache invalidation**: Always invalidate, even on partial success 3. **Force count refresh**: Immediately fetch fresh count after marking 4. **Test**: Verify count updates correctly after marking --- **Status**: Analysis complete. Ready to implement fixes.