@pesalink/cache-package

1.0.9 • Public • Published

CacheService

A comprehensive Redis caching service with support for multiple data structures, circuit breaker pattern, and connection pooling. This service provides a singleton pattern implementation with built-in error handling and monitoring capabilities.

Features

  • Singleton Pattern: Ensures single Redis connection instance
  • Circuit Breaker: Protects against Redis failures with automatic recovery
  • Multiple Data Structures: Supports strings, hashes, JSON, lists, and sets
  • Connection Pooling: Configurable concurrency limits and retry strategies
  • Environment Isolation: Automatic key prefixing based on environment
  • Comprehensive Error Handling: Timeout protection and graceful degradation
  • Health Monitoring: Built-in health checks and connection monitoring

Installation

To use the CacheService in your microservice, follow these steps:

npm install @pesalink/cache-package

## Environment Variables

```env
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_USER=username              # Optional
REDIS_PASSWORD=password          # Optional
REDIS_TLS=true                   # Optional, enables TLS
REDIS_CLUSTER=true               # Optional, enables cluster mode
REDIS_MAX_CONCURRENT_OPS=100     # Optional, default: 100
REDIS_CACHE_TIMEOUT=10000        # Optional, default: 10000ms
NODE_ENV=production              # Optional, used for key prefixing
ENVIRONMENT=production           # Alternative to NODE_ENV

Usage

Basic Setup

const CacheService = require('./CacheService');

// Get singleton instance
const cache = CacheService.getInstance();

// Health check
const isHealthy = await cache.healthCheck();
console.log('Redis is healthy:', isHealthy);

Key Building

The service automatically builds environment-prefixed keys from objects:

const keyObj = { userId: 123, type: 'profile' };
// Generates key: "production|type:profile|userId:123"

API Reference

String Methods

getCache(keyObj)

Retrieves cached data stored as JSON string.

const keyObj = { userId: 123, type: 'profile' };
const userData = await cache.getCache(keyObj);
console.log(userData); // null or parsed JSON object

setCache(keyObj, value, expiryInSeconds = 3600)

Stores data as JSON string with optional expiry.

const keyObj = { userId: 123, type: 'profile' };
const userData = { name: 'John', email: 'john@example.com' };
await cache.setCache(keyObj, userData, 7200); // 2 hours expiry

Hash Methods

setHashCache(keyObj, hashData, expiryInSeconds?)

Stores data as Redis hash fields.

const keyObj = { userId: 123, type: 'session' };
const sessionData = {
  token: 'abc123',
  lastActive: '2023-01-01T00:00:00Z',
  ipAddress: '192.168.1.1'
};
await cache.setHashCache(keyObj, sessionData, 3600);

getHashCache(keyObj)

Retrieves all fields from a Redis hash.

const keyObj = { userId: 123, type: 'session' };
const sessionData = await cache.getHashCache(keyObj);
console.log(sessionData); // { token: 'abc123', lastActive: '...', ipAddress: '...' }

getHashFields(keyObj, fields)

Retrieves specific fields from a Redis hash.

const keyObj = { userId: 123, type: 'session' };
const fields = ['token', 'lastActive'];
const partialData = await cache.getHashFields(keyObj, fields);
console.log(partialData); // { token: 'abc123', lastActive: '...' }

JSON Methods (Requires RedisJSON Module)

setJsonCache(keyObj, jsonData, expiryInSeconds?)

Stores data using Redis JSON module.

const keyObj = { userId: 123, type: 'preferences' };
const preferences = {
  theme: 'dark',
  notifications: { email: true, sms: false },
  settings: { language: 'en', timezone: 'UTC' }
};
await cache.setJsonCache(keyObj, preferences, 86400); // 24 hours

getJsonCache(keyObj)

Retrieves JSON data from Redis.

const keyObj = { userId: 123, type: 'preferences' };
const preferences = await cache.getJsonCache(keyObj);
console.log(preferences); // Full JSON object

getJsonPaths(keyObj, paths)

Retrieves specific JSON paths.

const keyObj = { userId: 123, type: 'preferences' };
const paths = ['$.theme', '$.notifications.email'];
const partialPrefs = await cache.getJsonPaths(keyObj, paths);

List Methods

pushToList(keyObj, items, expiryInSeconds?, left = false)

Adds items to a Redis list.

const keyObj = { userId: 123, type: 'activity_log' };
const activities = [
  { action: 'login', timestamp: new Date() },
  { action: 'view_profile', timestamp: new Date() }
];

// Push to right (RPUSH)
await cache.pushToList(keyObj, activities, 604800); // 7 days

// Push to left (LPUSH)
await cache.pushToList(keyObj, activities, 604800, true);

getListRange(keyObj, start = 0, stop = -1)

Retrieves items from a Redis list.

const keyObj = { userId: 123, type: 'activity_log' };

// Get all items
const allActivities = await cache.getListRange(keyObj);

// Get first 10 items
const recentActivities = await cache.getListRange(keyObj, 0, 9);

// Get last 5 items
const lastActivities = await cache.getListRange(keyObj, -5, -1);

Set Methods

addToSet(keyObj, items, expiryInSeconds?)

Adds items to a Redis set.

const keyObj = { userId: 123, type: 'viewed_products' };
const productIds = [101, 102, 103, 104];
await cache.addToSet(keyObj, productIds, 2592000); // 30 days

getSetMembers(keyObj)

Retrieves all members from a Redis set.

const keyObj = { userId: 123, type: 'viewed_products' };
const viewedProducts = await cache.getSetMembers(keyObj);
console.log(viewedProducts); // [101, 102, 103, 104]

Utility Methods

getCachedData(cacheKey, dbFetchMethod, expiryInSeconds = 3600)

Implements cache-aside pattern with automatic fallback.

const keyObj = { userId: 123, type: 'profile' };

const userData = await cache.getCachedData(
  keyObj,
  async () => {
    // This function runs only if cache miss
    return await database.getUserProfile(123);
  },
  7200 // 2 hours cache
);

deleteCache(keyObj)

Deletes cached data.

const keyObj = { userId: 123, type: 'profile' };
await cache.deleteCache(keyObj);

invalidateCache(keyObj)

Alias for deleteCache().

const keyObj = { userId: 123, type: 'profile' };
await cache.invalidateCache(keyObj);

healthCheck()

Performs Redis health check.

const isHealthy = await cache.healthCheck();
if (!isHealthy) {
  console.error('Redis is not responding');
}

disconnect()

Gracefully closes Redis connection.

await cache.disconnect();

Error Handling

The service includes comprehensive error handling:

  • Circuit Breaker: Automatically opens when Redis is unreachable
  • Operation Timeouts: All operations have configurable timeouts
  • Retry Logic: Built-in retry strategy for connection failures
  • Graceful Degradation: Operations fail fast when circuit is open
try {
  const data = await cache.getCache(keyObj);
} catch (error) {
  if (error.message === 'Redis circuit breaker is open') {
    // Handle circuit breaker state
    console.log('Redis is temporarily unavailable');
  } else if (error.message === 'Redis operation limit reached') {
    // Handle concurrency limit
    console.log('Too many concurrent operations');
  } else {
    // Handle other Redis errors
    console.error('Redis operation failed:', error);
  }
}

Best Practices

1. Key Design

// Good: Hierarchical and descriptive
const keyObj = { 
  service: 'user-service',
  userId: 123, 
  type: 'profile',
  version: 'v1'
};

// Avoid: Generic or unclear keys
const keyObj = { id: 123, data: 'stuff' };

2. Expiration Strategy

// Different TTLs for different data types
await cache.setCache(userProfile, userData, 3600);      // 1 hour
await cache.setCache(sessionData, session, 1800);       // 30 minutes  
await cache.setCache(staticConfig, config, 86400);      // 24 hours

3. Error Handling

// Always handle cache failures gracefully
async function getUserData(userId) {
  try {
    const cached = await cache.getCache({ userId, type: 'profile' });
    if (cached) return cached;
  } catch (error) {
    logger.warn('Cache read failed, falling back to database', error);
  }
  
  // Always have a fallback
  return await database.getUser(userId);
}

4. Monitoring

// Regular health checks
setInterval(async () => {
  const healthy = await cache.healthCheck();
  if (!healthy) {
    // Alert or take corrective action
    logger.error('Redis health check failed');
  }
}, 30000); // Every 30 seconds

Data Structure Selection Guide

Use Case Data Structure Method
Simple key-value String getCache(), setCache()
Object with multiple fields Hash getHashCache(), setHashCache()
Complex nested objects JSON getJsonCache(), setJsonCache()
Ordered collections List pushToList(), getListRange()
Unique collections Set addToSet(), getSetMembers()

Performance Considerations

  • Connection Pooling: Singleton pattern ensures efficient connection usage
  • Concurrent Operations: Configurable limits prevent Redis overload
  • Timeouts: All operations have timeout protection
  • JSON vs Hash vs String: Choose based on access patterns:
    • Use Hash for partial field access
    • Use JSON for complex queries and updates
    • Use String for simple objects

License

This service is part of the PesaLink package ecosystem.

Readme

Keywords

Package Sidebar

Install

npm i @pesalink/cache-package

Weekly Downloads

50

Version

1.0.9

License

MIT

Unpacked Size

47.4 kB

Total Files

5

Last publish

Collaborators

  • pesalink