Phase 3 Plan: Performance Optimization
Objective: To ensure the dictionary feature is fast, scalable, and resilient under load by implementing a multi-layered caching strategy using Redis. This phase is critical for providing a production-ready, responsive user experience.
Key Tasks
- Caching Infrastructure Setup:
- Integrate Redis into the application's infrastructure.
- Configure the NestJS CacheModule to use redis-store as its primary cache store, making it available globally throughout the application.
- Layer 1: Automated Response Caching:
- Apply the NestJS CacheInterceptor using the @UseInterceptors() decorator on all public, high-traffic GET endpoints.
- Target Endpoints:
- GET /languages
- GET /dictionary/
{lang-code}/{word} - GET /dictionary/search/
{lang-code}
- Define appropriate Time-to-Live (TTL) values for each endpoint's cache using @CacheTTL(). For example, a long TTL for /languages and a shorter TTL for search results.
- Layer 2: Granular Service-Level Caching:
- Inject the CacheManager (@Inject(CACHE_MANAGER)) directly into the DictionaryService.
- Identify computationally expensive or frequently accessed data queries within the service that are not fully covered by Layer 1 caching.
- Primary Target: The logic that fetches and assembles the full declension and conjugation tables.
- Implement manual caching: Before executing the database query, check if the result exists in the cache using cacheManager.get(). If not, execute the query and store the result using cacheManager.set() with a custom key (e.g., declension:
{wordId}).
- Cache Invalidation Strategy:
- Implement a robust cache invalidation mechanism to prevent stale data.
- In the service logic for all administrative write endpoints (POST /words, PATCH /words/
{id}, DELETE /words/{id}), manually and precisely delete the relevant cache keys from Redis using cacheManager.del(). - Invalidation Targets: This must include both the Layer 1 response cache key (e.g., for GET /dictionary/de/Hund) and any associated Layer 2 granular cache keys (e.g., declension:
{wordId}).
- Performance and Load Testing:
- Use a load testing tool (e.g., k6, JMeter) to benchmark the performance of the primary lookup and search endpoints before and after implementing caching.
- Simulate high-traffic scenarios to verify performance gains, measure the reduction in API latency, and confirm the decrease in database load.
Dependencies
- Successful completion of Phase 1 and Phase 2.
- A provisioned Redis instance accessible from the application environment.
- Access to a load testing tool and environment.
Acceptance Criteria
- Infrastructure: The NestJS application is successfully connected to and using Redis as its cache store.
- Caching Functionality:
- Making a second identical request to a cached GET endpoint returns a response significantly faster and does not trigger a new database query (verified via logging or monitoring).
- The Redis database contains keys corresponding to both API responses and granular service-level data.
- Invalidation:
- Updating a word entry via the PATCH /words/
{id}endpoint successfully deletes all related cache entries. - A subsequent GET request for that word retrieves the fresh, updated data from the database and re-populates the cache.
- Updating a word entry via the PATCH /words/
- Performance Metrics:
- Load testing demonstrates at least a 70% reduction in average response time for cached endpoints under load.
- Database CPU and I/O metrics are measurably lower during load tests compared to the pre-caching baseline.