npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@titansys/persistent-cache

v2.0.2

Published

A robust, enterprise-grade persistent cache with file locking, corruption detection, health monitoring, and graceful degradation.

Readme

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: '[email protected]' }, (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