@bcoders.gr/redis-client v1.2.3
@bcoders.gr/redis-client
A high-performance, self-contained Redis client with advanced compression, multi-level caching, bulk operations, and comprehensive performance monitoring. Engineered for production workloads with intelligent cache management and optimized data handling.
🚀 Performance Features
- Multi-level Caching: Intelligent caching for compression, decompression, and key formatting
- Parallel Processing: Concurrent compression/decompression for bulk operations
- Batch Operations: Optimized pipeline processing with configurable batch sizes
- Performance Analytics: Real-time statistics tracking with cache hit rates and timing metrics
- Memory Optimization: Smart cache management with automatic cleanup and size limits
- High-Resolution Timing: Precise performance measurements using
performance.now()
🛠️ Core Features
- Built-in Compression: Automatic zlib compression/decompression with caching for all stored values
- Performance Monitoring: Comprehensive logging of slow operations with configurable thresholds
- Keyspace Event Subscriptions: Subscribe to Redis keyspace events for real-time notifications
- Connection Management: Robust connection handling with auto-reconnection and connection pooling
- Pipeline Support: Advanced batch operations for improved performance
- Namespace Operations: Easy management of key namespaces with optimized bulk operations
- Self-contained: No external utility dependencies - all logic built-in with performance optimizations
Installation
npm install @bcoders.gr/redis-clientBasic Usage
import RedisClient from '@bcoders.gr/redis-client';
// Create a client instance with different connection options:
// 1. Default local socket connection with default credentials
const redis = new RedisClient('my-app');
// 2. With custom username and password
const redis = new RedisClient('my-app', {}, 'admin', 'secret');
// 3. Legacy boolean option (backward compatibility)
const redis = new RedisClient('my-app', false, 'user', 'pass'); // local socket
const redis = new RedisClient('my-app', true, 'user', 'pass'); // remote socket
// 4. Custom socket path with credentials
const redis = new RedisClient('my-app', '/custom/path/redis.sock', 'admin', 'secret');
// 5. IP address connection with credentials
const redis = new RedisClient('my-app', '192.168.1.100', 'user', 'password');
const redis = new RedisClient('my-app', '192.168.1.100:6380', 'admin', 'secret');
// 6. Full configuration object (overrides individual username/password)
const redis = new RedisClient('my-app', {
host: '192.168.1.100',
port: 6379,
username: 'admin',
password: 'secret'
});
// 7. Socket path with configuration object
const redis = new RedisClient('my-app', {
path: '/custom/redis.sock',
username: 'admin',
password: 'secret'
});
// Set a value (automatically compressed)
await redis.setKey('users', 'john', {
name: 'John Doe',
email: 'john@example.com'
});
// Get a value (automatically decompressed)
const user = await redis.getKey('users', 'john');
console.log(user); // { name: 'John Doe', email: 'john@example.com' }
// Get an entire namespace (new in v1.2.2)
const allUsers = await redis.getNamespaceSnapshot('users');
console.log(`Retrieved ${Object.keys(allUsers).length} users`);
// Check if key exists
const exists = await redis.checkKey('users', 'john');
// Delete a key
await redis.deleteKey('users', 'john');
// Clean up
redis.destroy();Constructor API
new RedisClient(alias, connectionOptions, username, password)Parameters:
alias(string, optional): A unique identifier for this client instance. Default:'default'connectionOptions(various types, optional): Connection configuration. Default:{}- Boolean:
truefor remote socket (/media/redis/remote.sock),falsefor local socket (/media/redis/local.sock) - String: Socket path (if contains
/or.sock) or IP address with optional port (192.168.1.100:6379) - Object: Full configuration object with properties:
host: Redis server hostname (default: 'localhost')port: Redis server port (default: 6379)path: Unix socket pathusername: Redis usernamepassword: Redis passwordsocket: Socket configuration object
- Boolean:
username(string, optional): Redis username. Default:'root'password(string, optional): Redis password. Default:'root'
Note: If connectionOptions is an object with username and password properties, they will override the individual username and password parameters.
Advanced Features
High-Performance Bulk Operations
// Optimized bulk operations with parallel compression
const bulkData = {
'user1': { name: 'John', age: 30 },
'user2': { name: 'Jane', age: 25 },
'user3': { name: 'Bob', age: 35 }
};
// Set multiple keys at once (parallel compression)
await redis.setBulk('users', bulkData, 3600); // with TTL
// Get multiple keys at once (parallel decompression)
const users = await redis.getBulk('users', ['user1', 'user2', 'user3']);
// Batched operations for large datasets
const largeDataset = {}; // ... 1000+ keys
await redis.setBulkBatched('dataset', largeDataset, 100); // Process in batches of 100
const results = await redis.getBulkBatched('dataset', Object.keys(largeDataset), 100);Performance Monitoring & Analytics
// Get comprehensive performance statistics
const stats = redis.getPerformanceStats();
console.log('Cache Hit Rate:', stats.cacheHitRate); // e.g., "85.2%"
console.log('Total Operations:', stats.operationCount);
console.log('Average Compression Time:', stats.avgCompressionTime);
// Configure performance thresholds
redis.performanceThresholds = {
slowGet: 5, // Log GET operations slower than 5ms
slowSet: 15, // Log SET operations slower than 15ms
cacheSize: 3000 // Maximum cache entries
};
// Reset statistics for new measurement period
redis.resetPerformanceStats();
// Optimize caches manually
redis.optimizeCaches();Advanced Operations
// Atomic counter operations
await redis.incrementCounter('stats', 'page_views', 1);
await redis.decrementCounter('stats', 'inventory', 5);
// Execute Lua scripts for complex atomic operations
const script = `
local key = KEYS[1]
local increment = ARGV[1]
local current = redis.call('GET', key) or 0
local new_value = current + increment
redis.call('SET', key, new_value)
return new_value
`;
const result = await redis.executeScript(script, ['counter'], [10]);Namespace Operations
// Get all keys in a namespace
const userKeys = await redis.getKeys('users');
// Get namespace size
const size = await redis.getNamespaceSize('users');
// Get all keys matching pattern
const allUserKeys = await redis.getNamespace('users');Pipeline Operations
// Create a pipeline for batch operations
const pipeline = redis.createPipeline();
if (pipeline) {
// Add operations to pipeline
pipeline.set('key1', 'value1');
pipeline.set('key2', 'value2');
pipeline.del('oldkey');
// Execute all operations at once
const results = await redis.executePipeline(pipeline);
}Keyspace Event Subscriptions
// Subscribe to keyspace events
redis.on('keyspace', (event) => {
console.log('Key event:', event);
// { namespace: 'users', key: 'john', event: 'set' }
});
await redis.subscribeToKeyspaceEvents('users');
// Now any changes to 'users:*' keys will trigger events
await redis.setKey('users', 'jane', { name: 'Jane' }); // Triggers event
// Unsubscribe when done
await redis.unsubscribeFromNamespace('users');Expiration
// Set a key with TTL (time to live)
await redis.setKey('session', 'token123', { userId: 456 }, 3600); // 1 hour
// Set expiration on existing key
await redis.expireKey('session', 'token123', 1800); // 30 minutesConfiguration
Constructor Options
const redis = new RedisClient(alias, remote);alias(string): Identifier for logging and debugging (default: 'default')remote(boolean): Whether to use remote socket path (default: false)
Performance Thresholds
The client automatically logs slow operations. Default thresholds:
- GET operations: 10ms
- SET operations: 30ms
These are configurable via the performanceThresholds property:
redis.performanceThresholds = {
slowGet: 5, // Log GET operations slower than 5ms
slowSet: 20 // Log SET operations slower than 20ms
};Connection Paths
- Local:
/media/redis/local.sock - Remote:
/media/redis/remote.sock
Both connections use:
- Username:
root - Password:
root
API Reference
Performance Methods
getPerformanceStats()- Get comprehensive performance statistics and cache metricsresetPerformanceStats()- Reset all performance countersclearCaches()- Clear all internal caches manuallyoptimizeCaches()- Optimize cache sizes and remove old entries
Key Operations
setKey(namespace, key, value, expirationInSeconds?)- Store a key-value pairgetKey(namespace, key)- Retrieve a value by keycheckKey(namespace, key)- Check if a key existsdeleteKey(namespace, key, pipeline?)- Delete a keyexpireKey(namespace, key, seconds)- Set expiration on a key
High-Performance Bulk Operations
setBulk(namespace, keyValuePairs, expirationInSeconds?)- Set multiple keys with parallel compressiongetBulk(namespace, keys)- Get multiple keys with parallel decompressiondeleteBulk(namespace, keys)- Delete multiple keys in a single pipelinesetBulkBatched(namespace, keyValuePairs, batchSize?, expirationInSeconds?)- Process large datasets in batchesgetBulkBatched(namespace, keys, batchSize?)- Retrieve large datasets in batches
Advanced Operations
incrementCounter(namespace, key, increment?)- Atomic counter incrementdecrementCounter(namespace, key, decrement?)- Atomic counter decrementexecuteScript(script, keys?, args?)- Execute Lua scripts for complex atomic operations
Namespace Operations
getNamespaceSize(namespace)- Get number of keys in namespacegetKeys(namespace)- Get all keys in namespace (using SCAN)getNamespace(namespace)- Get all keys matching namespace patterngetNamespaceSnapshot(namespace, batchSize?)- Get complete snapshot of all keys and values from a namespace
Connection Management
reconnectClient()- Manually reconnect the clientdestroy()- Close all connections and clean upclose()- Close main connection only
Pipeline Operations
createPipeline()- Create a new pipelineexecutePipeline(pipeline)- Execute a pipeline
Subscription Management
subscribeToKeyspaceEvents(namespace)- Subscribe to keyspace eventsunsubscribeFromNamespace(namespace)- Unsubscribe from namespace
Data Handling
Compression
All values are automatically compressed using zlib before storage and decompressed when retrieved. This significantly reduces memory usage for large objects.
Serialization
- Strings are stored as-is (after compression)
- Objects are JSON.stringify'd before compression
- Retrieved values are automatically parsed back to their original type
Error Handling
The client includes comprehensive error handling:
- Connection errors are logged and trigger reconnection attempts
- Operation errors are logged with context
- Failed operations return
nullorfalserather than throwing (configurable)
Namespace Snapshot
The getNamespaceSnapshot() method provides a complete snapshot of all keys and values from a Redis namespace in a single operation.
// Get all data from 'pair' namespace
const pairData = await client.getNamespaceSnapshot('pair');
console.log(`Retrieved ${Object.keys(pairData).length} pairs`);
// Sample of first few items
const sampleKeys = Object.keys(pairData).slice(0, 3);
sampleKeys.forEach(key => {
console.log(`${key}: ${JSON.stringify(pairData[key])}`);
});
// For large datasets, use a custom batch size
const blockData = await client.getNamespaceSnapshot('block', 200);Benefits of getNamespaceSnapshot
- Simplified Data Retrieval: Get all namespace data in a single call
- Clean Result Structure: Keys have namespace prefix removed for easier processing
- High Performance: Uses optimized bulk batched operations under the hood
- Memory Efficient: Processes data in batches to manage memory usage
- Automatic Type Handling: Preserves original value types (objects, arrays, etc.)
Performance
- Multi-level Caching: Compression, decompression, and key formatting results are cached for optimal performance
- Parallel Processing: Bulk operations use parallel compression/decompression for maximum throughput
- Intelligent Cache Management: Automatic cache size management with LRU-like cleanup strategies
- High-Resolution Timing: Precise performance measurements using
performance.now()for accurate benchmarking - Optimized Data Structures: Uses efficient Set-based JSON detection and optimized hash functions
- Batch Processing: Large datasets are processed in configurable batches to prevent memory overload
- Connection Pooling: Extended connection caching reduces connection overhead
- Performance Analytics: Real-time statistics tracking helps identify bottlenecks and optimization opportunities
Testing
# Run basic functionality tests
npm test
# Run comprehensive performance benchmarks
npm run test:performance
# Run race condition fix verification
npm run test:fixes
# Run performance benchmarks (alias)
npm run benchmarkRequirements
- Node.js 16+
- Redis server with Unix socket support
- Redis server configured for keyspace events (for subscriptions)
License
MIT
Contributing
Issues and pull requests welcome!