Skip to content

b7r-dev/frache

Repository files navigation

Frache

Advanced and intelligent caching for Node.js using ioredis and queueing.

frache_logo_sm

Frache is a powerful, feature-rich caching library that provides intelligent caching strategies, warmup tasks, and advanced Redis operations for Node.js applications. Built with TypeScript and designed for production use.

npm version License: MIT TypeScript

✨ Features

🎯 Core Caching

  • Simple API: set(), get(), del(), clear() with intuitive options
  • Singleton Pattern: Thread-safe singleton with custom Redis client support
  • TypeScript First: Full TypeScript support with comprehensive type definitions
  • Flexible TTL: Per-key TTL with intelligent defaults
  • Namespace Support: Organize cache keys with namespaces

🧠 Intelligent Caching

  • Cache-aside Pattern: getOrSet() for seamless cache-aside implementation
  • Compression: Automatic compression for large values
  • Serialization: Smart JSON serialization with fallback handling
  • Tag-based Invalidation: Group and invalidate related cache entries
  • Batch Operations: setMany(), getMany(), delMany() for efficiency

πŸ”₯ Advanced Features

  • Warmup Tasks: Proactive cache warming with priority queues
  • Data Structures: Lists, sets, counters with Redis-native operations
  • Memory Management: Intelligent memory usage with configurable limits
  • Event System: Comprehensive event emission for monitoring
  • Statistics: Built-in performance metrics and hit/miss tracking

πŸ“Š Monitoring & Observability

  • Performance Metrics: Hit rates, response times, memory usage
  • Event Listeners: Cache hits, misses, errors, warmup events
  • Health Checks: Built-in health monitoring
  • Debug Support: Comprehensive logging and error handling

πŸš€ Quick Start

Installation

npm install frache ioredis

Basic Usage

import { Cache } from 'frache';

// Initialize cache (singleton)
const cache = Cache.getInstance({
  defaultTtl: 3600, // 1 hour
  defaultNamespace: 'myapp',
});

// Basic operations
await cache.set('user:123', { name: 'John', age: 30 });
const user = await cache.get('user:123');
await cache.del('user:123');

// Cache-aside pattern
const user = await cache.getOrSet('user:456', async () => {
  return await database.getUser(456);
}, { ttl: 1800 });

Advanced Usage

import { AdvancedCache } from 'frache';

const cache = AdvancedCache.getInstance();

// Batch operations
await cache.setMany([
  { key: 'user:1', value: { name: 'Alice' } },
  { key: 'user:2', value: { name: 'Bob' } },
]);

const users = await cache.getMany(['user:1', 'user:2']);

// Data structures
await cache.listPush('recent-users', 'user:123');
await cache.setAdd('active-users', 'user:456');
await cache.increment('page-views', 1);

// Tag-based invalidation
await cache.set('product:1', product, { tags: ['products', 'category:electronics'] });
await cache.clear({ tags: ['products'] }); // Clear all product cache

🎯 Cache-aside Pattern

Perfect for database caching scenarios:

class UserService {
  async getUser(id: number) {
    return cache.getOrSet(`user:${id}`, async () => {
      // This only runs on cache miss
      return await database.users.findById(id);
    }, {
      ttl: 3600,
      tags: ['users', `user:${id}`]
    });
  }

  async updateUser(id: number, data: any) {
    const user = await database.users.update(id, data);

    // Invalidate related cache
    await cache.clear({ tags: [`user:${id}`] });

    return user;
  }
}

πŸ”₯ Warmup Tasks

Proactively warm your cache for better performance:

// Register warmup tasks
cache.registerWarmupTask({
  id: 'popular-products',
  name: 'Cache Popular Products',
  priority: 1,
  execute: async () => {
    const products = await database.getPopularProducts();
    for (const product of products) {
      await cache.set(`product:${product.id}`, product, { ttl: 7200 });
    }
  }
});

// Queue for execution
cache.queueWarmupTask('popular-products');

// Or run immediately
await cache.runWarmupTask('popular-products');

πŸ“Š Monitoring & Events

// Listen to cache events
cache.on('hit', (event) => {
  console.log(`Cache hit: ${event.key}`);
});

cache.on('miss', (event) => {
  console.log(`Cache miss: ${event.key}`);
});

cache.on('warmup', (event) => {
  console.log(`Warmup task ${event.data.task.name}: ${event.data.status}`);
});

// Get performance statistics
const stats = cache.getStats();
console.log(`Hit rate: ${(stats.hits / (stats.hits + stats.misses) * 100).toFixed(2)}%`);

βš™οΈ Configuration

const cache = Cache.getInstance({
  // Redis configuration
  redis: new Redis('redis://localhost:6379'),

  // Cache settings
  defaultTtl: 3600,
  defaultNamespace: 'myapp',
  keyPrefix: 'cache',

  // Performance settings
  enableCompression: true,
  maxMemory: 100 * 1024 * 1024, // 100MB

  // Warmup settings
  enableWarmup: true,
  warmupInterval: 60000, // 1 minute
});

πŸ—οΈ Architecture

Frache is built with a clean, extensible architecture:

  • Cache: Core caching functionality with singleton pattern
  • AdvancedCache: Extended functionality with data structures
  • Utils: Serialization, compression, key generation utilities
  • Types: Comprehensive TypeScript definitions
  • Events: Event-driven architecture for monitoring

πŸ“ˆ Performance

Frache is designed for high performance:

  • Memory Efficient: Smart compression and TTL management
  • Network Optimized: Batch operations reduce Redis round trips
  • CPU Friendly: Efficient serialization and key generation
  • Scalable: Singleton pattern prevents connection proliferation

Benchmarks

Cache Operations (1000 iterations):
β”œβ”€β”€ set(): ~0.5ms avg
β”œβ”€β”€ get(): ~0.3ms avg (hit)
β”œβ”€β”€ getOrSet(): ~0.4ms avg (hit), ~15ms avg (miss)
└── batch operations: ~60% faster than individual calls

πŸ”§ API Reference

Core Methods

Method Description Example
set(key, value, options?) Store a value cache.set('key', 'value', { ttl: 300 })
get(key, options?) Retrieve a value cache.get('key')
del(key, options?) Delete a value cache.del('key')
clear(options?) Clear multiple values cache.clear({ namespace: 'users' })
getOrSet(key, factory, options?) Cache-aside pattern cache.getOrSet('key', () => fetchData())

Advanced Methods

Method Description Example
setMany(entries) Set multiple values cache.setMany([{key: 'k1', value: 'v1'}])
getMany(keys) Get multiple values cache.getMany(['k1', 'k2'])
increment(key, amount?) Increment counter cache.increment('views', 1)
listPush(key, value) Add to list cache.listPush('queue', item)
setAdd(key, value) Add to set cache.setAdd('tags', 'important')

Warmup Methods

Method Description Example
registerWarmupTask(task) Register warmup task cache.registerWarmupTask({id: 'task1', ...})
queueWarmupTask(id, options?) Queue task for execution cache.queueWarmupTask('task1')
runWarmupTask(id) Run task immediately cache.runWarmupTask('task1')

πŸ“š Examples

πŸ§ͺ Testing

Frache includes comprehensive tests:

npm test              # Run all tests
npm run test:watch    # Watch mode
npm run test:coverage # Coverage report

Test Coverage: 96+ tests covering all functionality

🀝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

πŸ“„ License

MIT Β© Benjamin Weller

πŸ”— Links


Made with ❀️ for the Node.js community

About

Advanced and intelligent caching for Node.js using ioredis and queueing.

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors