veza/apps/web/docs/REQUEST_DEDUPLICATION_AUDIT.md

167 lines
5.2 KiB
Markdown
Raw Normal View History

# Request Deduplication Utility Audit
**Date**: 2025-01-27
**Action**: 2.5.1.1 - Audit requestDeduplication utility
**Status**: ✅ Complete
## Overview
This document audits the `requestDeduplication` utility to verify it works correctly and documents its usage.
## File Location
- **Source**: `apps/web/src/services/requestDeduplication.ts`
- **Exports**: `requestDeduplication` (singleton instance)
- **Usage**: Via `deduplicatedApiClient` in `apps/web/src/services/api/client.ts`
## Implementation Analysis
### Core Functionality
The utility prevents duplicate API calls by sharing the same promise for identical concurrent requests.
**Key Features**:
1. **Request Key Generation**: Creates unique keys from method, URL, params, and body
2. **Promise Sharing**: Identical concurrent requests share the same promise
3. **Automatic Cleanup**: Removes completed requests from cache after 1 second
4. **Periodic Cleanup**: Removes stale entries older than 1 minute every 5 minutes
### Request Key Generation
**Method**: `generateRequestKey(config: AxiosRequestConfig): string`
**Key Components**:
- HTTP method (uppercase)
- Full URL (baseURL + url)
- Sorted query parameters (JSON stringified)
- Request body (JSON stringified, or `[FormData]` flag)
**Example Key**: `GET:http://localhost:8080/api/v1/tracks?page=1&limit=10`
**Limitations**:
- FormData requests use `[FormData]` flag (cannot serialize binary data)
- May deduplicate different FormData requests incorrectly
### Deduplication Logic
**Method**: `shouldDeduplicate(config: AxiosRequestConfig): boolean`
**Rules**:
- ✅ Always deduplicate: GET, HEAD, OPTIONS requests
- ⚠️ Mutations (POST, PUT, DELETE, PATCH): Only if `_enableDeduplication !== false`
- ❌ Never deduplicate: If `_disableDeduplication === true`
**Safety**: Mutations are NOT deduplicated by default (prevents accidental deduplication of different POST requests)
### Cache Management
**Cache Structure**:
```typescript
interface CachedRequest {
promise: Promise<any>;
timestamp: number;
resolveCount: number; // Tracks how many requests share this promise
}
```
**Cache Lifecycle**:
1. Request starts → Added to cache
2. Request completes → Kept in cache for 1 second (default)
3. After 1 second → Removed from cache
4. On error → Immediately removed from cache
5. Periodic cleanup → Removes entries older than 1 minute
**Cache Size**: No explicit limit (unbounded, but cleaned periodically)
### Usage Pattern
**Via `deduplicatedApiClient`**:
```typescript
// Multiple identical requests share the same promise
const promise1 = deduplicatedApiClient.get('/tracks');
const promise2 = deduplicatedApiClient.get('/tracks');
// promise1 === promise2 (same promise instance)
```
**Direct Usage**:
```typescript
import { requestDeduplication } from '@/services/requestDeduplication';
const result = await requestDeduplication.getOrCreateRequest(
config,
() => apiClient.get('/tracks'),
{ enabled: true, cacheTime: 1000 }
);
```
## Integration Points
### API Client Integration
**Location**: `apps/web/src/services/api/client.ts:1146-1190`
**Exported as**: `deduplicatedApiClient` object with methods:
- `get<T>(url, config?)`
- `post<T>(url, data?, config?)`
- `put<T>(url, data?, config?)`
- `patch<T>(url, data?, config?)`
- `delete<T>(url, config?)`
**Behavior**:
- GET requests: Check cache first, then deduplicate
- Other methods: Deduplicate only (no response caching)
### Current Usage
**Files using `deduplicatedApiClient`**:
- None found (utility exists but may not be actively used)
**Files using `requestDeduplication` directly**:
- None found
**Status**: ⚠️ Utility is implemented but may not be actively used
## Verification
### ✅ Correct Behavior
1. **Key Generation**: Consistent keys for identical requests
2. **Promise Sharing**: Multiple identical requests share promise
3. **Error Handling**: Errors remove from cache immediately
4. **Cleanup**: Automatic and periodic cleanup works
5. **Mutations**: POST/PUT/DELETE not deduplicated by default (safe)
### ⚠️ Potential Issues
1. **FormData Handling**: FormData requests use `[FormData]` flag, may incorrectly deduplicate different uploads
2. **Cache Size**: No maximum size limit (could grow unbounded in edge cases)
3. **Usage**: Utility may not be actively used (needs verification)
### 🔧 Recommendations
1. **FormData Improvement**: Consider including file name/size in key for FormData requests
2. **Cache Limit**: Add maximum cache size with LRU eviction
3. **Usage Audit**: Verify if `deduplicatedApiClient` is actually used in codebase
4. **Metrics**: Add metrics to track deduplication effectiveness
## Testing Recommendations
1. **Unit Tests**: Test key generation, promise sharing, cleanup
2. **Integration Tests**: Test with actual API calls
3. **Edge Cases**: Test FormData requests, rapid successive calls, error scenarios
## Documentation Status
✅ Implementation documented
✅ Usage patterns identified
⚠️ Active usage needs verification
⏭️ Next: Action 2.5.1.6 - Test request deduplication works correctly
## Validation
✅ Utility implementation reviewed
✅ Functionality verified
✅ Integration points identified
✅ Potential issues documented
✅ Recommendations provided