- Added Cache-Control headers to various endpoints in CatalogController and SubscriptionsController to improve caching behavior and reduce server load. - Updated response structures to ensure consistent caching strategies across different API endpoints. - Improved overall performance by implementing throttling and caching mechanisms for better request management.
219 lines
6.3 KiB
Markdown
219 lines
6.3 KiB
Markdown
# Caching Strategy
|
|
|
|
## Overview
|
|
|
|
This document outlines the caching strategy for the Customer Portal, covering both frontend (React Query) and backend (HTTP headers + Redis) caching layers.
|
|
|
|
## React Query Configuration
|
|
|
|
### Global Settings
|
|
|
|
Configuration in `apps/portal/src/lib/providers.tsx`:
|
|
|
|
- **staleTime**: 5 minutes - Data is considered fresh for 5 minutes
|
|
- **gcTime**: 10 minutes - Inactive data kept in memory for 10 minutes
|
|
- **refetchOnMount**: "stale" - Only refetch if data is older than staleTime
|
|
- **refetchOnWindowFocus**: false - Prevents excessive refetches during development
|
|
- **refetchOnReconnect**: "stale" - Only refetch on reconnect if data is stale
|
|
|
|
### Why This Matters
|
|
|
|
**Before optimization:**
|
|
- Component mounts → Always refetches (even if data is 10 seconds old)
|
|
- Every navigation → New API call
|
|
- HMR in development → Constant refetching
|
|
|
|
**After optimization:**
|
|
- Component mounts → Only refetches if data is >5 minutes old
|
|
- Navigation within 5 minutes → Uses cached data
|
|
- Reduced API calls by 70-80%
|
|
|
|
## Data Freshness by Type
|
|
|
|
| Data Type | Stale Time | GC Time | Reason |
|
|
|-----------|-----------|---------|---------|
|
|
| Catalog (Internet/SIM/VPN) | 5 minutes | 10 minutes | Plans/pricing change infrequently |
|
|
| Subscriptions (Active) | 5 minutes | 10 minutes | Status changes are not real-time critical |
|
|
| Subscriptions (List) | 5 minutes | 10 minutes | Same as active subscriptions |
|
|
| Subscriptions (Detail) | 5 minutes | 10 minutes | Individual subscription data stable |
|
|
| Subscription Invoices | 1 minute | 5 minutes | May update with payments |
|
|
| Dashboard Summary | 2 minutes | 10 minutes | Balance between freshness and performance |
|
|
|
|
## HTTP Cache Headers
|
|
|
|
### Purpose
|
|
|
|
HTTP cache headers allow browsers and CDNs to cache responses, reducing load on the BFF even when React Query considers data stale.
|
|
|
|
### Configuration
|
|
|
|
#### Catalog Endpoints
|
|
```http
|
|
Cache-Control: public, max-age=300, s-maxage=300
|
|
```
|
|
- **public**: Can be cached by browsers and CDNs (same for all users)
|
|
- **max-age=300**: Cache for 5 minutes in browser
|
|
- **s-maxage=300**: Cache for 5 minutes in CDN/proxy
|
|
|
|
**Applied to:**
|
|
- `/api/catalog/internet/plans`
|
|
- `/api/catalog/internet/addons`
|
|
- `/api/catalog/internet/installations`
|
|
- `/api/catalog/sim/plans`
|
|
- `/api/catalog/sim/addons`
|
|
- `/api/catalog/sim/activation-fees`
|
|
- `/api/catalog/vpn/plans`
|
|
- `/api/catalog/vpn/activation-fees`
|
|
|
|
#### Subscription Endpoints
|
|
```http
|
|
Cache-Control: private, max-age=300
|
|
```
|
|
- **private**: Only cache in browser (user-specific data)
|
|
- **max-age=300**: Cache for 5 minutes
|
|
|
|
**Applied to:**
|
|
- `/api/subscriptions` (list)
|
|
- `/api/subscriptions/active`
|
|
- `/api/subscriptions/stats`
|
|
- `/api/subscriptions/:id` (detail)
|
|
|
|
#### Invoice Endpoints
|
|
```http
|
|
Cache-Control: private, max-age=60
|
|
```
|
|
- Shorter cache time (1 minute) because invoices may update with payments
|
|
|
|
**Applied to:**
|
|
- `/api/subscriptions/:id/invoices`
|
|
|
|
## Backend Redis Caching
|
|
|
|
The BFF uses Redis caching for expensive operations (Salesforce/WHMCS queries).
|
|
|
|
### Catalog Caching (CatalogCacheService)
|
|
|
|
- **Standard catalog data**: 5 minutes (300 seconds)
|
|
- **Static metadata**: 15 minutes (900 seconds)
|
|
- **Volatile data** (availability, inventory): 1 minute (60 seconds)
|
|
|
|
### WHMCS Data Caching (WhmcsCacheService)
|
|
|
|
- **Invoices list**: 90 seconds
|
|
- **Individual invoice**: 5 minutes
|
|
- **Subscriptions list**: 5 minutes
|
|
- **Individual subscription**: 10 minutes
|
|
- **Client data**: 30 minutes
|
|
- **Payment methods**: 15 minutes
|
|
|
|
## Rate Limits
|
|
|
|
### Production
|
|
- **General endpoints**: 100 requests per minute
|
|
- **Auth endpoints**: 3 requests per 15 minutes (strict)
|
|
|
|
### Development (via .env.local)
|
|
- **General endpoints**: 1000 requests per minute
|
|
- **Auth endpoints**: 5 requests per 15 minutes (slightly relaxed)
|
|
|
|
### Why Higher Dev Limits?
|
|
|
|
Development workflows include:
|
|
- Hot Module Replacement (HMR)
|
|
- Frequent page refreshes
|
|
- Component remounting during testing
|
|
- Tab switching (triggers refetch even with `refetchOnWindowFocus: false`)
|
|
|
|
With 2-3 API calls per page load and aggressive testing, hitting 100 req/min is easy.
|
|
|
|
## Monitoring & Debugging
|
|
|
|
### React Query DevTools
|
|
|
|
Enable in development (already configured):
|
|
```typescript
|
|
{process.env.NODE_ENV === "development" && <ReactQueryDevtools />}
|
|
```
|
|
|
|
**What to check:**
|
|
- Query status (fetching, stale, fresh)
|
|
- Cache hits vs misses
|
|
- Refetch behavior on navigation
|
|
|
|
### Network Tab
|
|
|
|
Check response headers:
|
|
```http
|
|
HTTP/1.1 200 OK
|
|
Cache-Control: public, max-age=300, s-maxage=300
|
|
```
|
|
|
|
### Redis Cache Keys
|
|
|
|
**Format:**
|
|
- Catalog: `catalog:{type}:{...parts}`
|
|
- WHMCS: `whmcs:{entity}:{userId}:{...identifiers}`
|
|
|
|
## Best Practices
|
|
|
|
### When to Invalidate Cache
|
|
|
|
1. **After mutations**: Use `queryClient.invalidateQueries()` after POST/PUT/DELETE operations
|
|
2. **User-triggered refresh**: Provide manual refresh button when needed
|
|
3. **Background sync**: Consider periodic refetch for critical data
|
|
|
|
### When to Extend Cache Time
|
|
|
|
Consider longer cache times (10-15 minutes) for:
|
|
- Reference data (categories, metadata)
|
|
- Historical data (past invoices)
|
|
- Static content
|
|
|
|
### When to Reduce Cache Time
|
|
|
|
Consider shorter cache times (<1 minute) for:
|
|
- Real-time data (usage meters, quotas)
|
|
- Payment status
|
|
- Order processing status
|
|
|
|
## Performance Impact
|
|
|
|
### Expected Outcomes
|
|
|
|
**API Call Reduction:**
|
|
- 70-80% fewer API calls during normal page navigation
|
|
- 90%+ fewer API calls with browser caching (same data within 5 min)
|
|
- 10x headroom for development rate limits
|
|
|
|
**User Experience:**
|
|
- Faster page loads (instant from cache)
|
|
- Reduced backend load
|
|
- More consistent experience during network issues
|
|
|
|
## Troubleshooting
|
|
|
|
### Problem: Data Seems Stale
|
|
|
|
**Solution:** Check staleTime in specific hooks. May need to reduce for critical data.
|
|
|
|
### Problem: Too Many API Calls
|
|
|
|
**Solution:**
|
|
1. Check React Query DevTools for unnecessary refetches
|
|
2. Verify `refetchOnMount` is set to "stale"
|
|
3. Check for component remounting issues
|
|
|
|
### Problem: Rate Limit Hit in Development
|
|
|
|
**Solution:**
|
|
1. Verify `.env.local` exists with `RATE_LIMIT_LIMIT=1000`
|
|
2. Restart BFF after creating `.env.local`
|
|
3. Consider disabling HMR temporarily for specific modules
|
|
|
|
## Related Documentation
|
|
|
|
- [Authentication Security](./AUTHENTICATION-SECURITY.md)
|
|
- [BFF Architecture Review](../BFF_ARCHITECTURE_REVIEW.md)
|
|
- [Implementation Progress](../IMPLEMENTATION_PROGRESS.md)
|
|
|