197 lines
6.8 KiB
Markdown
197 lines
6.8 KiB
Markdown
|
|
# Offline Queue Utility Audit
|
||
|
|
|
||
|
|
**Date**: 2025-01-27
|
||
|
|
**Action**: 2.5.1.3 - Audit offlineQueue utility
|
||
|
|
**Status**: ✅ Complete
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
This document audits the `offlineQueue` utility to verify it works correctly and documents its usage.
|
||
|
|
|
||
|
|
## File Location
|
||
|
|
|
||
|
|
- **Source**: `apps/web/src/services/offlineQueue.ts`
|
||
|
|
- **Exports**: `offlineQueue` (singleton instance), `QueuedRequest`, `QueueOptions`
|
||
|
|
- **Purpose**: Queue failed requests when offline and replay when connection is restored
|
||
|
|
|
||
|
|
## Implementation Analysis
|
||
|
|
|
||
|
|
### Core Functionality
|
||
|
|
|
||
|
|
The utility stores failed requests due to network issues and replays them when connection is restored.
|
||
|
|
|
||
|
|
**Key Features**:
|
||
|
|
1. **Offline Detection**: Uses `navigator.onLine` API
|
||
|
|
2. **Request Queuing**: Stores failed mutation requests (POST, PUT, DELETE, PATCH)
|
||
|
|
3. **Priority Support**: High, normal, low priority levels
|
||
|
|
4. **Persistent Storage**: Saves queue to localStorage
|
||
|
|
5. **Automatic Replay**: Processes queue when connection restored
|
||
|
|
6. **Retry Logic**: Maximum 3 retries with exponential backoff
|
||
|
|
|
||
|
|
### Queue Configuration
|
||
|
|
|
||
|
|
**Default Settings**:
|
||
|
|
- **Max Queue Size**: 100 requests
|
||
|
|
- **Default Max Retries**: 3 attempts
|
||
|
|
- **Default Retry Delay**: 1 second (increases with retry count)
|
||
|
|
- **Request Expiry**: 24 hours (old requests filtered on load)
|
||
|
|
|
||
|
|
### Request Filtering
|
||
|
|
|
||
|
|
**Method**: `shouldQueueRequest(config: AxiosRequestConfig): boolean`
|
||
|
|
|
||
|
|
**Rules**:
|
||
|
|
- ❌ **GET requests**: Not queued (can be retried fresh when online)
|
||
|
|
- ✅ **Mutations**: POST, PUT, DELETE, PATCH are queued
|
||
|
|
- **Rationale**: GET requests are idempotent and can be retried, mutations need to be preserved
|
||
|
|
|
||
|
|
### Queue Management
|
||
|
|
|
||
|
|
**Priority System**:
|
||
|
|
- **High priority**: Processed first
|
||
|
|
- **Normal priority**: Processed after high
|
||
|
|
- **Low priority**: Processed last, first to be evicted if queue is full
|
||
|
|
|
||
|
|
**Queue Eviction**:
|
||
|
|
- If queue is full (100 requests):
|
||
|
|
1. Try to remove oldest low-priority request
|
||
|
|
2. If no low-priority, remove oldest request (FIFO)
|
||
|
|
|
||
|
|
**Queue Persistence**:
|
||
|
|
- Saved to `localStorage` key: `veza_offline_queue`
|
||
|
|
- Loaded on service initialization
|
||
|
|
- Old requests (>24 hours) filtered out on load
|
||
|
|
|
||
|
|
### Queue Processing
|
||
|
|
|
||
|
|
**Method**: `processQueue(): Promise<void>`
|
||
|
|
|
||
|
|
**Processing Flow**:
|
||
|
|
1. Check if already processing or offline → Skip
|
||
|
|
2. Process requests in priority order (high → normal → low)
|
||
|
|
3. Retry each request via `apiClient.request()`
|
||
|
|
4. On success → Remove from queue
|
||
|
|
5. On failure → Increment retry count
|
||
|
|
6. Max retries reached → Remove from queue (failed)
|
||
|
|
7. Otherwise → Move to end of queue for retry
|
||
|
|
8. Delay between requests (100ms) to avoid overwhelming server
|
||
|
|
9. Exponential backoff on retries
|
||
|
|
|
||
|
|
**Online Event Listener**:
|
||
|
|
- Listens for `window.addEventListener('online')`
|
||
|
|
- Automatically processes queue when connection restored
|
||
|
|
|
||
|
|
### Storage Format
|
||
|
|
|
||
|
|
**localStorage Key**: `veza_offline_queue`
|
||
|
|
|
||
|
|
**Storage Format**: JSON array of `QueuedRequest` objects
|
||
|
|
```typescript
|
||
|
|
[
|
||
|
|
{
|
||
|
|
id: "req_1234567890_abc123",
|
||
|
|
config: { method: "POST", url: "/tracks", data: {...} },
|
||
|
|
timestamp: 1234567890000,
|
||
|
|
retryCount: 0,
|
||
|
|
priority: "normal"
|
||
|
|
}
|
||
|
|
]
|
||
|
|
```
|
||
|
|
|
||
|
|
**Limitations**:
|
||
|
|
- AxiosRequestConfig may contain non-serializable data (functions, File objects)
|
||
|
|
- File uploads may not serialize correctly to JSON
|
||
|
|
|
||
|
|
## Integration Points
|
||
|
|
|
||
|
|
### API Client Integration
|
||
|
|
|
||
|
|
**Location**: `apps/web/src/services/api/client.ts:999-1007` (error interceptor)
|
||
|
|
|
||
|
|
**Actual Implementation**:
|
||
|
|
```typescript
|
||
|
|
// FE-API-015: Queue request for offline replay if it's a network error
|
||
|
|
if (!error.response && originalRequest && offlineQueue.shouldQueueRequest(originalRequest)) {
|
||
|
|
const priority = originalRequest.method === 'POST' ? 'high' : 'normal';
|
||
|
|
try {
|
||
|
|
await offlineQueue.queueRequest(originalRequest, { priority });
|
||
|
|
} catch (queueError) {
|
||
|
|
logger.error('[API] Failed to queue request for offline replay', { error: queueError });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Behavior**:
|
||
|
|
- ✅ Queues network errors (no response received)
|
||
|
|
- ✅ Only queues mutations (POST, PUT, DELETE, PATCH)
|
||
|
|
- ✅ Priority: POST = high, others = normal
|
||
|
|
- ✅ Error handling: Catches queue errors gracefully
|
||
|
|
|
||
|
|
**Status**: ✅ **Verified - Fully integrated**
|
||
|
|
|
||
|
|
### Current Usage
|
||
|
|
|
||
|
|
**Files using `offlineQueue`**:
|
||
|
|
- ✅ `apps/web/src/services/api/client.ts:999-1007` - **Error interceptor queues network errors**
|
||
|
|
- ✅ `apps/web/src/components/OfflineIndicator.tsx` - Shows queue size in UI
|
||
|
|
- ✅ `apps/web/src/services/api/client.ts:204` - `processQueue()` function (may be related)
|
||
|
|
|
||
|
|
**UI Integration**:
|
||
|
|
- `OfflineIndicator.tsx` displays queue size
|
||
|
|
- Updates when queue size changes
|
||
|
|
|
||
|
|
**Status**: ✅ **Utility is actively used and integrated**
|
||
|
|
|
||
|
|
## Verification
|
||
|
|
|
||
|
|
### ✅ Correct Behavior
|
||
|
|
|
||
|
|
1. **Offline Detection**: Uses `navigator.onLine` API
|
||
|
|
2. **Request Filtering**: Only queues mutations (correct)
|
||
|
|
3. **Priority System**: High/normal/low priority support
|
||
|
|
4. **Persistence**: Saves to localStorage, survives page reloads
|
||
|
|
5. **Automatic Replay**: Processes queue on online event
|
||
|
|
6. **Retry Logic**: Exponential backoff, max 3 retries
|
||
|
|
7. **Queue Limits**: Maximum 100 requests, FIFO eviction
|
||
|
|
8. **Old Request Cleanup**: Filters requests older than 24 hours
|
||
|
|
|
||
|
|
### ⚠️ Potential Issues
|
||
|
|
|
||
|
|
1. **Serialization**: AxiosRequestConfig may contain non-serializable data (File objects, functions)
|
||
|
|
2. **File Uploads**: FormData/file uploads may not serialize correctly to JSON
|
||
|
|
3. ✅ **Integration**: Verified - Error interceptor queues network errors (line 999-1007)
|
||
|
|
4. ✅ **Error Filtering**: Only queues network errors (`!error.response`) - Correct behavior
|
||
|
|
5. **Token Refresh**: Queued requests may have expired tokens when replayed (needs verification)
|
||
|
|
|
||
|
|
### 🔧 Recommendations
|
||
|
|
|
||
|
|
1. ✅ **Integration**: Verified - Error interceptor queues requests correctly
|
||
|
|
2. **File Upload Handling**: Special handling for FormData/file uploads (may need to skip queueing or handle differently)
|
||
|
|
3. **Token Refresh**: Verify tokens are refreshed before replaying queued requests
|
||
|
|
4. ✅ **Error Filtering**: Already correct - Only queues network errors
|
||
|
|
5. ✅ **UI Feedback**: Already implemented - OfflineIndicator shows queue size
|
||
|
|
6. **Testing**: Test with actual offline scenarios, file uploads, token expiration
|
||
|
|
|
||
|
|
## Testing Recommendations
|
||
|
|
|
||
|
|
1. **Unit Tests**: Test queueing, priority, persistence, retry logic
|
||
|
|
2. **Integration Tests**: Test with actual API calls, offline simulation
|
||
|
|
3. **Edge Cases**: Test queue full, old requests, file uploads, token expiration
|
||
|
|
4. **Browser Testing**: Test `navigator.onLine` behavior across browsers
|
||
|
|
|
||
|
|
## Documentation Status
|
||
|
|
|
||
|
|
✅ Implementation documented
|
||
|
|
✅ Usage patterns identified
|
||
|
|
✅ Integration verified (error interceptor)
|
||
|
|
✅ UI integration verified (OfflineIndicator)
|
||
|
|
⏭️ Next: Action 2.5.1.4 - Add UI for offline queue management (may already be partially implemented)
|
||
|
|
|
||
|
|
## Validation
|
||
|
|
|
||
|
|
✅ Utility implementation reviewed
|
||
|
|
✅ Functionality verified
|
||
|
|
✅ Integration points identified
|
||
|
|
✅ Potential issues documented
|
||
|
|
✅ Recommendations provided
|