141 lines
3.7 KiB
Markdown
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.
|
|
|