The module must support both register for synchronous, static configuration and registerAsync for asynchronous configuration. The registerAsync method should support a useFactory to allow for dependency injection. The module should accept an options object that aligns with cacheable's multi-tier architecture, allowing configuration of Layer 1 (in-memory) and Layer 2 (e.g., Redis) stores.
- The module must gracefully handle scenarios where the Layer 2 cache (e.g., Redis) is unavailable.
- Connectivity protection can be done without
@nestjs/terminusby leveraging the event-driven nature of Node.js Redis clients (e.g.,ioredis). You should listen for the following events:- connect: Emitted when the client successfully connects to the Redis server.
- error: Emitted when any connection error occurs. This is the most important event to listen for.
- reconnecting: Emitted when the client attempts to reconnect after a connection loss.
- end: Emitted when the connection is closed.
- The service should maintain an internal "healthy" state flag. On an error event, set the flag to
falseand log the error. On a connect event, set it back totrue. - When performing a Redis operation, check the flag. If the flag is
false, skip the Redis operation and proceed with a fallback (e.g., fetching data from the database), logging that the cache is offline. This ensures the application remains responsive even if the Redis connection is lost. - It should also implement a health check that can be exposed via the
@nestjs/terminuspackage, allowing Kubernetes or other monitoring systems to check the connection status of the Redis instance.
The module should expose an injectable service that supports core caching methods: get, set, del, and wrap. It must also integrate with NestJS interceptors to enable declarative caching using decorators like @UseInterceptors(CacheableInterceptor). The module should allow for per-method or per-controller Time-to-Live (TTL) overrides.
Comprehensive unit tests for all services and providers are required. Integration tests must verify that the register and registerAsync methods work. End-to-end tests should prove that the caching interceptor correctly caches and retrieves data from both Layer 1 and Layer 2. Failure scenarios, such as the Layer 2 cache being unavailable, must also be tested.
The module should expose key metrics for monitoring cache performance. These metrics should be in a format consumable by tools like Prometheus and Grafana.
| Metric Name | Description |
|---|---|
| cache_hits | Total number of times an item was found in the cache. |
| cache_misses | Total number of times an item was not found in the cache. |
| cache_hit_rate | A gauge or ratio of hits to total requests. |
| cache_evictions | Total number of items removed from the cache. |
| cache_latency_seconds | A histogram of time taken for cache operations. |
| cache_size | The current number of items in the cache. |
| cache_memory_bytes | The memory used by the in-memory cache. |
- Type Safety: A strongly-typed API using TypeScript generics is a must.
- Tiered Caching Configuration: The API should make it easy to configure Layer 1 and Layer 2 stores.
- Logging: A configurable logging strategy is required for debugging in development and for production environments.
- Background Refresh: The module should expose a clear way to enable the cacheable library's background refresh feature to prevent stale data.
- Decorator Support: Custom NestJS decorators like
@Cacheable()should be considered for declarative caching. - Security: Ensure proper sanitation of cache keys and avoid storing sensitive data without encryption.