NeahNew/MARK_ALL_READ_CACHE_ISSUE.md
2026-01-06 18:53:55 +01:00

141 lines
3.7 KiB
Markdown

# 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.