Skip to content

A robust, enterprise-grade Node.js module for persistent caching with advanced features including file locking, corruption detection, health monitoring, and graceful degradation.

Notifications You must be signed in to change notification settings

titansys/persistent-cache

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

32 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

persistent-cache

A robust, enterprise-grade Node.js module for persistent caching with advanced features including file locking, corruption detection, health monitoring, and graceful degradation.

🚀 Features

  • 🔒 Concurrent-Safe: File locking prevents race conditions and data corruption
  • 🛡️ Corruption Detection: JSON validation with automatic error recovery
  • 📊 Health Monitoring: Real-time cache health tracking and reporting
  • 🔄 Graceful Degradation: Continues operation even with partial corruption
  • ⚡ High Performance: Memory + disk hybrid caching for optimal speed
  • 🔧 Zero Breaking Changes: Fully backward compatible with v1.x
  • 🔐 Security Hardened: All known vulnerabilities patched

📦 Installation

npm install @titansys/persistent-cache

🎯 Quick Start

const cache = require('@titansys/persistent-cache');

// Create a cache instance
const myCache = cache();

// Store data
myCache.put('user:123', { name: 'John', email: 'john@example.com' }, (err) => {
    if (err) throw err;
    console.log('User saved to cache');
});

// Retrieve data
myCache.get('user:123', (err, user) => {
    if (err) throw err;
    console.log('Retrieved user:', user);
});

📚 Core API

Basic Operations

cache.put(key, data, callback)

Asynchronously stores data in the cache.

cache.put('myKey', { foo: 'bar' }, (err) => {
    if (err) console.error('Cache error:', err);
    else console.log('Data cached successfully');
});

cache.putSync(key, data)

Synchronously stores data in the cache.

try {
    cache.putSync('myKey', { foo: 'bar' });
    console.log('Data cached successfully');
} catch (err) {
    console.error('Cache error:', err);
}

cache.get(key, callback)

Asynchronously retrieves data from the cache.

cache.get('myKey', (err, data) => {
    if (err) console.error('Cache error:', err);
    else if (data === undefined) console.log('Key not found');
    else console.log('Retrieved:', data);
});

cache.getSync(key)

Synchronously retrieves data from the cache.

try {
    const data = cache.getSync('myKey');
    if (data === undefined) {
        console.log('Key not found');
    } else {
        console.log('Retrieved:', data);
    }
} catch (err) {
    console.error('Cache error:', err);
}

cache.delete(key, callback) / cache.deleteSync(key)

Removes an entry from the cache.

// Async
cache.delete('myKey', (err) => {
    if (err) console.error('Delete error:', err);
    else console.log('Key deleted');
});

// Sync
try {
    cache.deleteSync('myKey');
    console.log('Key deleted');
} catch (err) {
    console.error('Delete error:', err);
}

cache.keys(callback) / cache.keysSync()

Lists all keys in the cache.

// Async
cache.keys((err, keys) => {
    if (err) console.error('Keys error:', err);
    else console.log('Cache keys:', keys);
});

// Sync
try {
    const keys = cache.keysSync();
    console.log('Cache keys:', keys);
} catch (err) {
    console.error('Keys error:', err);
}

cache.unlink(callback)

Completely removes the cache directory and all its contents.

cache.unlink((err) => {
    if (err) console.error('Unlink error:', err);
    else console.log('Cache completely removed');
});

🏥 Health Monitoring API

cache.healthCheck(callback)

Performs a comprehensive health check of all cache files.

cache.healthCheck((err, report) => {
    if (err) {
        console.error('Health check failed:', err);
        return;
    }

    console.log('Health Report:');
    console.log(`✅ Healthy files: ${report.healthy}`);
    console.log(`❌ Corrupted files: ${report.corrupted}`);
    console.log(`🔧 Repaired files: ${report.repaired}`);

    // Detailed file status
    report.files.forEach(file => {
        console.log(`${file.name}: ${file.status}`);
        if (file.error) console.log(`  Error: ${file.error}`);
    });
});

cache.healthCheckSync()

Synchronous version of health check.

try {
    const report = cache.healthCheckSync();
    console.log('Health report:', report);
} catch (err) {
    console.error('Health check failed:', err);
}

cache.startHealthMonitoring(callback)

Starts periodic health monitoring.

cache.startHealthMonitoring((err, report) => {
    if (err) {
        console.error('Health monitoring error:', err);
        return;
    }

    if (report.corrupted > 0) {
        console.warn(`⚠️  Found ${report.corrupted} corrupted files`);
        // Take action: alert, repair, etc.
    }
});

console.log('Health monitoring started');

cache.stopHealthMonitoring()

Stops periodic health monitoring.

cache.stopHealthMonitoring();
console.log('Health monitoring stopped');

cache.isHealthMonitoring()

Check if health monitoring is currently active.

if (cache.isHealthMonitoring()) {
    console.log('Health monitoring is active');
} else {
    console.log('Health monitoring is inactive');
}

⚙️ Configuration Options

const cache = require('@titansys/persistent-cache');

const myCache = cache({
    base: './my-cache',           // Base directory
    name: 'user-cache',           // Cache name
    duration: 24 * 60 * 60 * 1000, // 24 hours in ms
    memory: true,                 // Enable memory caching
    persist: true,                // Enable disk persistence

    // 🆕 Robustness Options
    lockTimeout: 10000,           // Lock timeout in ms (default: 5000)
    healthCheckInterval: 300000,  // Health check interval in ms (default: 5 min)
    autoRepair: true              // Auto-remove corrupted files (default: false)
});

Core Options

options.base

  • Type: string
  • Default: Main module directory
  • Description: Base directory where cache files are stored

options.name

  • Type: string
  • Default: 'cache'
  • Description: Cache name (creates subdirectory base/name)

options.duration

  • Type: number
  • Default: undefined (infinite)
  • Description: Cache entry TTL in milliseconds

options.memory

  • Type: boolean
  • Default: true
  • Description: Enable in-memory caching for performance

options.persist

  • Type: boolean
  • Default: true
  • Description: Enable disk persistence

🆕 Robustness Options

options.lockTimeout

  • Type: number
  • Default: 5000
  • Description: File lock timeout in milliseconds. Prevents indefinite blocking on concurrent access.

options.healthCheckInterval

  • Type: number
  • Default: 300000 (5 minutes)
  • Description: Interval for periodic health checks when monitoring is enabled.

options.autoRepair

  • Type: boolean
  • Default: false
  • Description: Automatically remove corrupted cache files during health checks.

🔒 Concurrency & Locking

The cache uses file-based locking to prevent race conditions:

// Multiple processes can safely write to the same cache
const cache1 = require('persistent-cache')({ name: 'shared' });
const cache2 = require('persistent-cache')({ name: 'shared' });

// These operations are safe to run concurrently
cache1.put('key1', 'value1', callback1);
cache2.put('key2', 'value2', callback2);

Lock Features:

  • ⏱️ Configurable timeout (default: 5 seconds)
  • 🧹 Automatic cleanup on process exit
  • 🔄 Stale lock detection and recovery
  • 🚫 Deadlock prevention

🛡️ Error Handling & Graceful Degradation

The cache handles errors gracefully without breaking your application:

const cache = require('persistent-cache')();

// Even if cache files are corrupted, operations continue
cache.get('potentially-corrupted-key', (err, data) => {
    // err will be null, data will be undefined for corrupted entries
    // Warning logged to console for debugging
    if (data === undefined) {
        console.log('Key not found or corrupted - using fallback');
        // Your fallback logic here
    }
});

Graceful Degradation Features:

  • 📝 Corrupted files return undefined instead of throwing errors
  • ⚠️ Warning logs for debugging (can be disabled)
  • 🔄 Memory fallback when disk operations fail
  • 🎯 Isolated corruption (bad entries don't affect good ones)

🔐 Security

  • Zero Vulnerabilities: All dependencies updated to secure versions
  • 🔒 File Locking: Prevents concurrent write corruption
  • 🛡️ Input Validation: JSON structure validation before parsing
  • 🧹 Safe Cleanup: Proper lock cleanup on process termination

📊 Performance

  • Memory + Disk Hybrid: Best of both worlds
  • Efficient Locking: Minimal performance overhead
  • Lazy Validation: Only validates on read operations
  • Background Monitoring: Health checks don't block operations

Benchmarks

Memory Cache: ~0.1ms per operation
Disk Cache: ~1-5ms per operation (depending on file size)
Lock Acquisition: ~0.5ms average
Health Check: ~10ms per 100 files

🔄 Migration from v1.x

No code changes required! All v1.x APIs work exactly the same:

// v1.x code works unchanged
const cache = require('persistent-cache')();
cache.put('key', 'value', callback);
cache.get('key', callback);

New features are opt-in:

  • Health monitoring is disabled by default
  • Auto-repair is disabled by default
  • All new APIs are additive

🐛 Troubleshooting

Health Check Issues

cache.healthCheck((err, report) => {
    if (report.corrupted > 0) {
        console.log('Corrupted files found:', report.files.filter(f => f.status === 'corrupted'));

        // Option 1: Enable auto-repair
        const repairedCache = require('persistent-cache')({
            name: 'same-cache',
            autoRepair: true
        });

        // Option 2: Manual cleanup
        report.files
            .filter(f => f.status === 'corrupted')
            .forEach(f => cache.delete(f.name, () => {}));
    }
});

Lock Timeout Issues

// Increase timeout for slow systems
const cache = require('persistent-cache')({
    lockTimeout: 15000  // 15 seconds
});

Performance Tuning

// Memory-only for maximum speed
const fastCache = require('persistent-cache')({
    persist: false,
    memory: true
});

// Disk-only for memory constrained environments
const diskCache = require('persistent-cache')({
    persist: true,
    memory: false
});

📜 License

MIT

👥 Author

Titan Systems - https://github.com/titansys

🔗 Links

About

A robust, enterprise-grade Node.js module for persistent caching with advanced features including file locking, corruption detection, health monitoring, and graceful degradation.

Resources

Stars

Watchers

Forks

Contributors 2

  •  
  •