@onlineapps/conn-base-cache
v1.0.8
Published
Redis cache connector with TTL, invalidation, and namespace support for OA Drive microservices
Maintainers
Readme
@onlineapps/conn-base-cache
Redis cache connector with TTL, invalidation, and namespace support for OA Drive microservices.
Features
- 🔑 Automatic key prefixing - All keys use
cache:prefix - ⏱️ TTL support - Automatic expiration for cached data
- 🗂️ Namespace isolation - Separate cache spaces per service
- 📊 Built-in statistics - Hit rate, miss tracking
- 🔄 Atomic operations - Increment, decrement support
- 🎯 Pattern invalidation - Delete by wildcard patterns
- 🔐 Safe namespacing - Never touches
obs:monitoring keys - 🧪 Mock implementation - For testing
Installation
npm install @onlineapps/conn-base-cacheQuick Start
const CacheConnector = require('@onlineapps/conn-base-cache');
const cache = new CacheConnector({
host: 'localhost',
port: 6379,
defaultTTL: 3600, // 1 hour
namespace: 'invoice-service'
});
await cache.connect();
// Set value with TTL
await cache.set('user:123', { name: 'John', role: 'admin' }, 600); // 10 min
// Get value
const user = await cache.get('user:123');
// Delete by pattern
await cache.deleteByPattern('user:*');
// Get statistics
console.log(cache.getStats());
// { hits: 1, misses: 0, sets: 1, deletes: 5, errors: 0, hitRate: '100%' }Configuration
| Option | Environment Variable | Default | Description |
|--------|---------------------|---------|-------------|
| host | REDIS_HOST | localhost | Redis server host |
| port | REDIS_PORT | 6379 | Redis server port |
| password | REDIS_PASSWORD | - | Redis password |
| db | REDIS_DB | 0 | Redis database number |
| defaultTTL | - | 3600 | Default TTL in seconds |
| namespace | - | - | Key namespace |
| maxRetries | - | 3 | Max connection retries |
| retryDelay | - | 100 | Retry delay in ms |
API Reference
Connection Management
connect()
Establish Redis connection.
await cache.connect();disconnect()
Close Redis connection.
await cache.disconnect();Basic Operations
get(key)
Get value from cache.
const value = await cache.get('key');
// Returns parsed object or stringset(key, value, ttl?)
Set value in cache with optional TTL.
await cache.set('key', { data: 'value' }, 3600);
// Returns true on successdelete(key)
Delete single key.
await cache.delete('key');
// Returns true if key existedexists(key)
Check if key exists.
const exists = await cache.exists('key');
// Returns booleanPattern Operations
deleteByPattern(pattern)
Delete all keys matching pattern.
const deleted = await cache.deleteByPattern('session:*');
// Returns number of deleted keysTTL Management
ttl(key)
Get remaining TTL for key.
const seconds = await cache.ttl('key');
// Returns: seconds (-1 if no TTL, -2 if not exists)expire(key, ttl)
Update TTL for existing key.
await cache.expire('key', 7200); // 2 hours
// Returns true on successAtomic Operations
incr(key, increment?)
Increment numeric value.
const newValue = await cache.incr('counter', 5);
// Returns new valuedecr(key, decrement?)
Decrement numeric value.
const newValue = await cache.decr('counter', 1);
// Returns new valueBatch Operations
mget(keys)
Get multiple values at once.
const values = await cache.mget(['key1', 'key2', 'key3']);
// Returns: { key1: value1, key2: value2, key3: value3 }mset(keyValues, ttl?)
Set multiple values at once.
await cache.mset({
'key1': 'value1',
'key2': { data: 'value2' }
}, 3600);
// Returns true on successUtility Methods
withNamespace(namespace)
Create namespaced instance.
const userCache = cache.withNamespace('users');
await userCache.set('123', userData);
// Actual key: cache:invoice-service:users:123wrap(fn, options)
Create cached function (memoization).
const cachedFetch = cache.wrap(async (userId) => {
return await fetchUserFromDB(userId);
}, {
ttl: 600,
keyPrefix: 'user'
});
const user = await cachedFetch(123);
// First call fetches from DB and caches
// Subsequent calls return from cacheflush(confirm)
Clear all cache keys (dangerous!).
await cache.flush(true); // Must confirm with truegetStats()
Get cache statistics.
const stats = cache.getStats();
// { hits: 50, misses: 10, sets: 30, deletes: 5, errors: 0, hitRate: '83.33%' }healthCheck()
Check Redis connection health.
const healthy = await cache.healthCheck();
// Returns booleanUsage Patterns
Service-Level Caching
class InvoiceService {
constructor() {
this.cache = new CacheConnector({
namespace: 'invoice-service'
});
}
async getInvoice(id) {
// Check cache first
const cached = await this.cache.get(`invoice:${id}`);
if (cached) return cached;
// Fetch from database
const invoice = await db.getInvoice(id);
// Cache for 1 hour
await this.cache.set(`invoice:${id}`, invoice, 3600);
return invoice;
}
async invalidateInvoice(id) {
await this.cache.delete(`invoice:${id}`);
}
}API Response Caching
// In service wrapper
async function callApiWithCache(operation, params) {
const cacheKey = `api:${operation}:${JSON.stringify(params)}`;
// Try cache
const cached = await cache.get(cacheKey);
if (cached) {
logger.debug('Cache hit for API call');
return cached;
}
// Make API call
const result = await apiCaller.call(operation, params);
// Cache result
await cache.set(cacheKey, result, 300); // 5 minutes
return result;
}Session Management
const sessionCache = cache.withNamespace('sessions');
// Store session
await sessionCache.set(sessionId, {
userId: 123,
roles: ['admin'],
createdAt: Date.now()
}, 1800); // 30 minutes
// Refresh session TTL
await sessionCache.expire(sessionId, 1800);
// Clear all sessions for user
await sessionCache.deleteByPattern(`*:user:123`);Testing
Use the built-in mock for testing:
const { MockCacheConnector } = require('@onlineapps/conn-base-cache');
describe('MyService', () => {
let cache;
beforeEach(() => {
cache = new MockCacheConnector();
});
it('should cache results', async () => {
await cache.set('test', 'value');
const value = await cache.get('test');
expect(value).toBe('value');
expect(cache.getStats().hits).toBe(1);
});
});Important Notes
- Key Prefixing: All keys automatically get
cache:prefix - Namespace Isolation: Use namespaces to avoid key collisions
- TTL Best Practices: Always set appropriate TTL to prevent memory issues
- Pattern Deletion: Use carefully as it can be expensive on large datasets
- Never Access: This connector never touches
obs:namespace (monitoring)
Integration with Service Wrapper
class ServiceWrapper {
constructor(config) {
this.cache = new CacheConnector({
namespace: config.serviceName
});
}
async processWorkflow(message) {
// Cache workflow state
await this.cache.set(
`workflow:${message.workflowId}`,
message,
600 // 10 minutes
);
}
}Error Handling
All methods throw errors with descriptive messages:
try {
await cache.get('key');
} catch (error) {
if (error.message.includes('Cache get failed')) {
// Handle cache failure
}
}Performance Tips
- Use mget/mset for batch operations
- Enable pipelining for multiple operations
- Set appropriate TTL to balance freshness and performance
- Use namespaces to organize keys
- Monitor hit rate with getStats()
License
MIT
