Assist_Design/docs/CACHING_STRATEGY.md
barsa 2611e63cfd Enhance caching and response handling in catalog and subscriptions controllers
- 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.
2025-10-29 13:29:28 +09:00

6.3 KiB

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

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

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

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):

{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/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