- Completed Action 2.5.1.1: Audited requestDeduplication utility - Created REQUEST_DEDUPLICATION_AUDIT.md with complete analysis - Verified core functionality: promise sharing, key generation, cleanup - Documented usage patterns via deduplicatedApiClient - Identified potential issues: FormData handling, unbounded cache size - Noted utility may not be actively used (needs verification) - Provided recommendations for improvements and testing
5.2 KiB
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
deduplicatedApiClientinapps/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:
- Request Key Generation: Creates unique keys from method, URL, params, and body
- Promise Sharing: Identical concurrent requests share the same promise
- Automatic Cleanup: Removes completed requests from cache after 1 second
- 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:
interface CachedRequest {
promise: Promise<any>;
timestamp: number;
resolveCount: number; // Tracks how many requests share this promise
}
Cache Lifecycle:
- Request starts → Added to cache
- Request completes → Kept in cache for 1 second (default)
- After 1 second → Removed from cache
- On error → Immediately removed from cache
- Periodic cleanup → Removes entries older than 1 minute
Cache Size: No explicit limit (unbounded, but cleaned periodically)
Usage Pattern
Via deduplicatedApiClient:
// Multiple identical requests share the same promise
const promise1 = deduplicatedApiClient.get('/tracks');
const promise2 = deduplicatedApiClient.get('/tracks');
// promise1 === promise2 (same promise instance)
Direct Usage:
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
- Key Generation: Consistent keys for identical requests
- Promise Sharing: Multiple identical requests share promise
- Error Handling: Errors remove from cache immediately
- Cleanup: Automatic and periodic cleanup works
- Mutations: POST/PUT/DELETE not deduplicated by default (safe)
⚠️ Potential Issues
- FormData Handling: FormData requests use
[FormData]flag, may incorrectly deduplicate different uploads - Cache Size: No maximum size limit (could grow unbounded in edge cases)
- Usage: Utility may not be actively used (needs verification)
🔧 Recommendations
- FormData Improvement: Consider including file name/size in key for FormData requests
- Cache Limit: Add maximum cache size with LRU eviction
- Usage Audit: Verify if
deduplicatedApiClientis actually used in codebase - Metrics: Add metrics to track deduplication effectiveness
Testing Recommendations
- Unit Tests: Test key generation, promise sharing, cleanup
- Integration Tests: Test with actual API calls
- 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