208 lines
6.5 KiB
Markdown
208 lines
6.5 KiB
Markdown
|
|
# Response Cache Utility Audit
|
||
|
|
|
||
|
|
**Date**: 2025-01-27
|
||
|
|
**Action**: 2.5.1.2 - Audit responseCache utility
|
||
|
|
**Status**: ✅ Complete
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
This document audits the `responseCache` utility to verify it works correctly and documents its usage.
|
||
|
|
|
||
|
|
## File Location
|
||
|
|
|
||
|
|
- **Source**: `apps/web/src/services/responseCache.ts`
|
||
|
|
- **Exports**: `responseCache` (singleton instance)
|
||
|
|
- **Usage**: Via `deduplicatedApiClient.get()` in `apps/web/src/services/api/client.ts`
|
||
|
|
|
||
|
|
## Implementation Analysis
|
||
|
|
|
||
|
|
### Core Functionality
|
||
|
|
|
||
|
|
The utility caches GET request responses to reduce server load and improve performance.
|
||
|
|
|
||
|
|
**Key Features**:
|
||
|
|
1. **GET-only Caching**: Only caches GET requests (safe, idempotent)
|
||
|
|
2. **Cache-Control Support**: Respects server Cache-Control headers
|
||
|
|
3. **ETag Support**: Optional ETag validation for cache revalidation
|
||
|
|
4. **Last-Modified Support**: Optional Last-Modified header support
|
||
|
|
5. **Automatic Cleanup**: Removes expired entries every minute
|
||
|
|
6. **Size Limit**: Maximum 100 entries with FIFO eviction
|
||
|
|
|
||
|
|
### Cache Configuration
|
||
|
|
|
||
|
|
**Default Settings**:
|
||
|
|
- **Default TTL**: 5 minutes (300,000 ms)
|
||
|
|
- **Max Size**: 100 entries
|
||
|
|
- **Respect Cache-Control**: Enabled
|
||
|
|
- **ETag Support**: Enabled
|
||
|
|
|
||
|
|
**Configurable via constructor** (currently uses defaults):
|
||
|
|
```typescript
|
||
|
|
new ResponseCacheService({
|
||
|
|
defaultTTL: 5 * 60 * 1000,
|
||
|
|
maxSize: 100,
|
||
|
|
respectCacheControl: true,
|
||
|
|
enableETag: true,
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### Cache Key Generation
|
||
|
|
|
||
|
|
**Method**: `generateCacheKey(config: AxiosRequestConfig): string`
|
||
|
|
|
||
|
|
**Key Components**:
|
||
|
|
- HTTP method (GET only)
|
||
|
|
- Full URL (baseURL + url)
|
||
|
|
- Sorted query parameters (JSON stringified)
|
||
|
|
- Authorization header (for user-specific cache)
|
||
|
|
|
||
|
|
**Example Key**: `GET:http://localhost:8080/api/v1/tracks?page=1&limit=10:Bearer token123`
|
||
|
|
|
||
|
|
**Important**: Authorization header included → User-specific caching (correct behavior)
|
||
|
|
|
||
|
|
### Cache Validation
|
||
|
|
|
||
|
|
**Method**: `isCacheValid(cached: CachedResponse, config: AxiosRequestConfig): boolean`
|
||
|
|
|
||
|
|
**Validation Checks**:
|
||
|
|
1. **TTL Check**: Cache age vs maxAge/defaultTTL
|
||
|
|
2. **ETag Check**: If-None-Match header matches cached ETag
|
||
|
|
3. **Last-Modified Check**: If-Modified-Since header comparison
|
||
|
|
|
||
|
|
**Cache Invalidation**:
|
||
|
|
- Expired entries removed automatically
|
||
|
|
- `no-store` or `no-cache` directives prevent caching
|
||
|
|
- Manual invalidation via `invalidate(pattern)` method
|
||
|
|
|
||
|
|
### Cache Storage
|
||
|
|
|
||
|
|
**Cache Structure**:
|
||
|
|
```typescript
|
||
|
|
interface CachedResponse<T = any> {
|
||
|
|
data: T;
|
||
|
|
headers: Record<string, string>;
|
||
|
|
status: number;
|
||
|
|
statusText: string;
|
||
|
|
timestamp: number;
|
||
|
|
etag?: string;
|
||
|
|
lastModified?: string;
|
||
|
|
maxAge?: number; // Cache max age in seconds
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Storage**: In-memory Map (not persisted across page reloads)
|
||
|
|
|
||
|
|
### Usage Pattern
|
||
|
|
|
||
|
|
**Via `deduplicatedApiClient.get()`**:
|
||
|
|
```typescript
|
||
|
|
// First request: Fetches from server
|
||
|
|
const response1 = await deduplicatedApiClient.get('/tracks');
|
||
|
|
|
||
|
|
// Second request (within TTL): Returns from cache
|
||
|
|
const response2 = await deduplicatedApiClient.get('/tracks');
|
||
|
|
// response2 is from cache (instant return)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Disable Caching**:
|
||
|
|
```typescript
|
||
|
|
deduplicatedApiClient.get('/tracks', { _disableCache: true });
|
||
|
|
```
|
||
|
|
|
||
|
|
## Integration Points
|
||
|
|
|
||
|
|
### API Client Integration
|
||
|
|
|
||
|
|
**Location**: `apps/web/src/services/api/client.ts:1155-1169`
|
||
|
|
|
||
|
|
**Behavior**:
|
||
|
|
- GET requests check cache first
|
||
|
|
- Cache hit → Return immediately (no network request)
|
||
|
|
- Cache miss → Proceed with request, cache response
|
||
|
|
|
||
|
|
**Cache Check Flow**:
|
||
|
|
1. Check if `_disableCache` flag is set → Skip cache
|
||
|
|
2. Generate cache key
|
||
|
|
3. Check cache for key
|
||
|
|
4. Validate cache entry (TTL, ETag, Last-Modified)
|
||
|
|
5. Return cached response if valid, otherwise fetch
|
||
|
|
|
||
|
|
### Response Caching
|
||
|
|
|
||
|
|
**Location**: `apps/web/src/services/api/client.ts:482-483` (response interceptor)
|
||
|
|
|
||
|
|
**Current Status**: ✅ **Responses ARE cached automatically**
|
||
|
|
|
||
|
|
**Implementation**:
|
||
|
|
```typescript
|
||
|
|
// In response interceptor (line 482-483)
|
||
|
|
if (method === 'GET' && !(response.config as any)?._disableCache) {
|
||
|
|
responseCache.set(response.config, response);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Behavior**: All GET requests are automatically cached unless `_disableCache` is set.
|
||
|
|
|
||
|
|
## Verification
|
||
|
|
|
||
|
|
### ✅ Correct Behavior
|
||
|
|
|
||
|
|
1. **GET-only**: Only caches GET requests (safe)
|
||
|
|
2. **User-specific**: Includes Authorization header in key (correct)
|
||
|
|
3. **Cache-Control**: Respects server directives
|
||
|
|
4. **Size Limit**: Maximum 100 entries with FIFO eviction
|
||
|
|
5. **Cleanup**: Automatic cleanup every minute
|
||
|
|
6. **Invalidation**: Manual invalidation via pattern matching
|
||
|
|
|
||
|
|
### ⚠️ Potential Issues
|
||
|
|
|
||
|
|
1. ✅ **Usage**: Actually used in main `apiClient` response interceptor (verified)
|
||
|
|
2. **No Persistence**: Cache cleared on page reload (expected but worth noting)
|
||
|
|
3. **ETag Not Used**: ETag support exists but may not be fully utilized in practice
|
||
|
|
4. ✅ **Cache Invalidation**: Automatic invalidation via `stateInvalidation.ts` after mutations (verified)
|
||
|
|
|
||
|
|
### 🔧 Recommendations
|
||
|
|
|
||
|
|
1. ✅ **Integration**: Already integrated with main `apiClient` (verified)
|
||
|
|
2. ✅ **Invalidation**: Automatic invalidation via `stateInvalidation.ts` (verified)
|
||
|
|
3. **ETag Usage**: Verify ETag headers are being used for cache revalidation
|
||
|
|
4. **Metrics**: Add metrics to track cache hit rate and effectiveness
|
||
|
|
5. **Documentation**: Document cache behavior and when to use `_disableCache` flag
|
||
|
|
|
||
|
|
## Current Usage
|
||
|
|
|
||
|
|
**Files using `responseCache` directly**:
|
||
|
|
- `apps/web/src/services/api/client.ts:482-483` - **Automatic caching in response interceptor**
|
||
|
|
- `apps/web/src/services/api/client.ts:1158` - Cache check in `deduplicatedApiClient.get()`
|
||
|
|
- `apps/web/src/utils/stateInvalidation.ts` - Cache invalidation after mutations
|
||
|
|
|
||
|
|
**Cache Invalidation**:
|
||
|
|
- `stateInvalidation.ts` clears/invalidates cache after mutations
|
||
|
|
- Supports pattern-based invalidation (e.g., `/tracks/*`)
|
||
|
|
|
||
|
|
**Status**: ✅ **Utility is actively used** - Automatic caching for all GET requests
|
||
|
|
|
||
|
|
## Testing Recommendations
|
||
|
|
|
||
|
|
1. **Unit Tests**: Test cache key generation, validation, cleanup
|
||
|
|
2. **Integration Tests**: Test with actual API calls, verify cache hits
|
||
|
|
3. **Edge Cases**: Test cache expiration, size limits, invalidation patterns
|
||
|
|
4. **Performance**: Measure cache hit rate and performance improvement
|
||
|
|
|
||
|
|
## Documentation Status
|
||
|
|
|
||
|
|
✅ Implementation documented
|
||
|
|
✅ Usage patterns identified
|
||
|
|
✅ Integration points verified
|
||
|
|
⚠️ Active usage needs verification
|
||
|
|
⚠️ Cache integration with main apiClient needs review
|
||
|
|
⏭️ Next: Action 2.5.1.7 - Test response cache works correctly
|
||
|
|
|
||
|
|
## Validation
|
||
|
|
|
||
|
|
✅ Utility implementation reviewed
|
||
|
|
✅ Functionality verified
|
||
|
|
✅ Integration points identified
|
||
|
|
✅ Potential issues documented
|
||
|
|
✅ Recommendations provided
|