Skip to content

iazaran/smart-cache

Laravel SmartCache - Optimize Caching for Large Data

Latest Version License PHP Version Tests

SmartCache optimizes Laravel caching for large datasets through intelligent compression (up to 70% size reduction), smart chunking, and automatic optimization - while maintaining Laravel's familiar Cache API.

🎯 The Problem It Solves

Caching large datasets (10K+ records, API responses, reports) in Laravel can cause:

  • Memory issues - Large arrays consume too much RAM
  • Storage waste - Uncompressed data fills Redis/Memcached quickly
  • Slow performance - Serializing/deserializing huge objects takes time
  • Cache stampede - Multiple processes regenerating expensive data simultaneously

SmartCache fixes all of this automatically.

📦 Installation

composer require iazaran/smart-cache

That's it! Works out-of-the-box. No configuration required.

Requirements:

  • PHP 8.1+
  • Laravel 8.0 - 12.x
  • Any cache driver (Redis, File, Database, Memcached, Array)

🚀 Quick Start

Drop-in Replacement

Use exactly like Laravel's Cache facade:

use SmartCache\Facades\SmartCache;

// Works exactly like Laravel Cache
SmartCache::put('users', $users, 3600);
$users = SmartCache::get('users');

// Remember pattern
$users = SmartCache::remember('users', 3600, function() {
    return User::all();
});

✨ The Magic: Large data is automatically compressed and chunked - reducing cache size by up to 70%!

Helper Function

// Store
smart_cache(['products' => $products], 3600);

// Retrieve
$products = smart_cache('products');

💡 Core Features (Automatic Optimization)

1. Intelligent Compression

Large data is automatically compressed:

// Large API response - automatically compressed
$apiData = Http::get('api.example.com/large-dataset')->json();
SmartCache::put('api_data', $apiData, 3600);
// Automatically compressed with gzip, saving up to 70% space

When it applies: Data > 50KB (configurable) Benefit: 60-80% size reduction

2. Smart Chunking

Large arrays are automatically split into manageable chunks:

// 10,000+ records - automatically chunked
$users = User::with('profile', 'posts')->get();
SmartCache::put('all_users', $users, 3600);
// Automatically split into 1000-item chunks

When it applies: Arrays with 5000+ items (configurable) Benefit: Better memory usage, faster access

3. Automatic Strategy Selection

SmartCache chooses the best optimization automatically:

Data Type Size Strategy Benefit
Large Arrays (5000+ items) Any Chunking Better memory, faster access
Text/Strings >50KB Compression 60-80% size reduction
Mixed Objects >50KB Compression Optimal serialization
API Responses >100KB Both Best performance
Small Data <50KB None Fastest (no overhead)

📈 Real Performance Impact

Production Results (E-commerce Platform):

  • 72% cache size reduction (15MB → 4.2MB)
  • 800MB daily Redis memory savings
  • 40% faster retrieval vs standard Laravel Cache
  • 94.3% cache hit ratio
  • 23ms average retrieval time

🔧 Advanced Features (Opt-in)

All advanced features are opt-in and disabled by default for maximum compatibility.

🔒 Atomic Locks - Prevent Cache Stampede

Prevent multiple processes from regenerating expensive cache simultaneously:

$lock = SmartCache::lock('expensive_operation', 10);

if ($lock->get()) {
    // Only one process executes this
    $data = expensiveApiCall();
    SmartCache::put('api_data', $data, 3600);
    $lock->release();
}

// Or use callback pattern
SmartCache::lock('regenerate_cache', 30)->get(function() {
    return regenerateExpensiveData();
});

Benefit: Prevents cache stampede, reduces server load

⚡ Cache Memoization - 10-100x Faster

Cache data in memory for the current request:

$memo = SmartCache::memo();

// First call hits cache, subsequent calls are instant
$users = $memo->remember('users', 3600, fn() => User::all());
$users = $memo->get('users'); // Instant! (from memory)
$users = $memo->get('users'); // Still instant!

// Perfect for loops
foreach ($products as $product) {
    $category = $memo->get("category_{$product->category_id}");
}

Benefit: 10-100x faster repeated access within same request/job

🔢 Batch Operations

Optimize multiple cache operations:

// Retrieve multiple keys
$values = SmartCache::many(['key1', 'key2', 'key3']);

// Store multiple keys
SmartCache::putMany([
    'key1' => 'value1',
    'key2' => 'value2',
], 3600);

// Delete multiple keys
SmartCache::deleteMultiple(['key1', 'key2', 'key3']);

🎯 Adaptive Compression

Auto-optimize compression levels based on data characteristics:

// Enable in config
config(['smart-cache.strategies.compression.mode' => 'adaptive']);

// Automatically selects optimal level:
SmartCache::put('hot_data', $frequentlyAccessed, 3600);  // Level 3-4 (faster)
SmartCache::put('cold_data', $rarelyAccessed, 3600);     // Level 7-9 (smaller)

How it works:

  • Analyzes data compressibility
  • Tracks access frequency
  • Hot data = faster compression
  • Cold data = higher compression

💾 Lazy Loading

Load large datasets on-demand to save memory:

// Enable in config
config(['smart-cache.strategies.chunking.lazy_loading' => true]);

// Returns LazyChunkedCollection
$largeDataset = SmartCache::get('100k_records');

// Chunks loaded on-demand (max 3 in memory)
foreach ($largeDataset as $record) {
    processRecord($record);
}

// Access specific items
$item = $largeDataset[50000]; // Only loads needed chunk

Benefit: 30-50% memory savings for large datasets

🧠 Smart Serialization

Auto-select best serialization method:

// Automatically chooses:
SmartCache::put('simple', ['key' => 'value'], 3600);  // JSON (fastest)
SmartCache::put('complex', $objectGraph, 3600);       // igbinary/PHP

Methods: JSON → igbinary (if available) → PHP serialize

📡 Cache Events

Monitor cache operations in real-time:

// Enable in config
config(['smart-cache.events.enabled' => true]);

// Listen to events
Event::listen(CacheHit::class, fn($e) => Log::info("Hit: {$e->key}"));
Event::listen(CacheMissed::class, fn($e) => Log::warning("Miss: {$e->key}"));
Event::listen(OptimizationApplied::class, fn($e) =>
    Log::info("Optimized {$e->key}: {$e->ratio}% reduction")
);

Events: CacheHit, CacheMissed, KeyWritten, KeyForgotten, OptimizationApplied

🔐 Encryption Strategy

Encrypt sensitive cached data automatically:

// Enable in config
config(['smart-cache.encryption.enabled' => true]);
config(['smart-cache.encryption.keys' => ['user_*', 'payment_*']]);

// Sensitive data is automatically encrypted
SmartCache::put('user_123_ssn', $sensitiveData, 3600);
// Data encrypted at rest, decrypted on retrieval

Benefit: Secure sensitive data in cache without code changes

🏷️ Cache Namespacing

Group and manage cache keys by namespace:

// Set namespace for operations
SmartCache::namespace('users')->put('profile', $data, 3600);
SmartCache::namespace('users')->put('settings', $settings, 3600);

// Flush entire namespace
SmartCache::flushNamespace('users'); // Clears all user:* keys

// Get all keys in namespace
$keys = SmartCache::getNamespaceKeys('users');

Benefit: Organize cache keys, easy bulk invalidation

⏱️ TTL Jitter

Prevent thundering herd with randomized TTL:

// Add 10% jitter to TTL
SmartCache::withJitter(0.1)->put('popular_data', $data, 3600);
// Actual TTL: 3240-3960 seconds (±10%)

// Or use dedicated methods
SmartCache::putWithJitter('key', $value, 3600, 0.15); // 15% jitter
SmartCache::rememberWithJitter('key', 3600, 0.1, fn() => expensiveCall());

Benefit: Prevents cache stampede when many keys expire simultaneously

🔌 Circuit Breaker

Auto-fallback when cache backend fails:

// Check if cache is available
if (SmartCache::isAvailable()) {
    $data = SmartCache::get('key');
}

// Execute with automatic fallback
$data = SmartCache::withFallback(
    fn() => SmartCache::get('key'),           // Primary
    fn() => Database::query('SELECT ...')     // Fallback
);

// Get circuit breaker stats
$stats = SmartCache::getCircuitBreakerStats();
// Returns: state, failure_count, success_count, last_failure_at

States: Closed (normal) → Open (failing) → Half-Open (testing)

🚦 Rate Limiting & Stampede Protection

Prevent cache stampede with rate limiting:

// Throttle cache operations
$result = SmartCache::throttle('api_call', 10, 60, function() {
    return expensiveApiCall();
}); // Max 10 calls per 60 seconds

// Remember with stampede protection (XFetch algorithm)
$data = SmartCache::rememberWithStampedeProtection('key', 3600, function() {
    return expensiveComputation();
});

Benefit: Prevents multiple processes from regenerating cache simultaneously

🔥 Cache Warming

Pre-warm cache with artisan command:

# Warm cache using registered warmers
php artisan smart-cache:warm

# Warm specific warmer
php artisan smart-cache:warm --warmer=ProductCacheWarmer

Register warmers in your service provider:

use SmartCache\Contracts\CacheWarmer;

class ProductCacheWarmer implements CacheWarmer
{
    public function warm(): void
    {
        $products = Product::all();
        SmartCache::put('all_products', $products, 3600);
    }

    public function getKey(): string
    {
        return 'products';
    }
}

// Register in AppServiceProvider
$this->app->tag([ProductCacheWarmer::class], 'smart-cache.warmers');

🧹 Orphan Chunk Cleanup

Automatically clean up orphan chunks:

# Clean up orphan chunks
php artisan smart-cache:cleanup-chunks

# Dry run (show what would be cleaned)
php artisan smart-cache:cleanup-chunks --dry-run

📊 Cache Statistics Dashboard

View cache statistics via web interface:

// Enable in config
config(['smart-cache.dashboard.enabled' => true]);
config(['smart-cache.dashboard.prefix' => 'smart-cache']);
config(['smart-cache.dashboard.middleware' => ['web', 'auth']]);

Routes:

  • GET /smart-cache/dashboard - HTML dashboard
  • GET /smart-cache/statistics - JSON statistics
  • GET /smart-cache/health - Health check
  • GET /smart-cache/keys - Managed keys list

🌊 Modern Patterns (Laravel 12+)

SWR (Stale-While-Revalidate)

Serve stale data while refreshing in background:

$apiData = SmartCache::swr('github_repos', function() {
    return Http::get('https://api.github.com/user/repos')->json();
}, 300, 900); // 5min fresh, 15min stale

Extended Stale Serving

For slowly changing data:

$siteConfig = SmartCache::stale('site_config', function() {
    return Config::fromDatabase();
}, 3600, 86400); // 1hour fresh, 24hour stale

Refresh-Ahead

Proactively refresh before expiration:

$analytics = SmartCache::refreshAhead('daily_analytics', function() {
    return Analytics::generateReport();
}, 1800, 300); // 30min TTL, 5min refresh window

Smart Invalidation

Pattern-based cache clearing:

// Clear by pattern
SmartCache::flushPatterns([
    'user_*',           // All user keys
    'api_v2_*',         // All API v2 cache
    '/product_\d+/'     // Regex: product_123, product_456
]);

// Dependency tracking
SmartCache::dependsOn('user_posts', 'user_profile');
SmartCache::invalidate('user_profile'); // Also clears user_posts

📊 Monitoring & Management

Performance Metrics

$metrics = SmartCache::getPerformanceMetrics();
// Returns: hit_ratio, compression_savings, operation_timing, etc.

$analysis = SmartCache::analyzePerformance();
// Returns: health score, recommendations, issues

CLI Commands

# Status overview
php artisan smart-cache:status

# Detailed analysis
php artisan smart-cache:status --force

# Clear cache
php artisan smart-cache:clear
php artisan smart-cache:clear expensive_api_call

HTTP Management

Execute commands via web interface (no SSH needed):

$status = SmartCache::executeCommand('status');
$clearResult = SmartCache::executeCommand('clear', ['key' => 'api_data']);

⚙️ Configuration

Publish Config (Optional)

php artisan vendor:publish --tag=smart-cache-config

Key Configuration Options

// config/smart-cache.php
return [
    // Size thresholds for optimization
    'thresholds' => [
        'compression' => 1024 * 50,  // 50KB - compress data larger than this
        'chunking' => 1024 * 100,    // 100KB - chunk arrays larger than this
    ],

    // Optimization strategies
    'strategies' => [
        'compression' => [
            'enabled' => true,
            'mode' => 'fixed',       // 'fixed' or 'adaptive'
            'level' => 6,            // 1-9 (higher = better compression)
        ],
        'chunking' => [
            'enabled' => true,
            'chunk_size' => 1000,    // Items per chunk
            'lazy_loading' => false, // Enable for memory savings
            'smart_sizing' => false, // Auto-calculate chunk size
        ],
    ],

    // Events (disabled by default for performance)
    'events' => [
        'enabled' => false,
    ],

    // Performance monitoring
    'monitoring' => [
        'enabled' => true,
        'metrics_ttl' => 3600,
    ],

    // Encryption for sensitive data
    'encryption' => [
        'enabled' => false,
        'keys' => [],              // Keys to encrypt: ['user_*', 'payment_*']
        'patterns' => [],          // Regex patterns: ['/secret_.*/']
    ],

    // Circuit breaker for cache backend failures
    'circuit_breaker' => [
        'enabled' => true,
        'failure_threshold' => 5,  // Failures before opening
        'success_threshold' => 2,  // Successes to close
        'timeout' => 30,           // Seconds before half-open
    ],

    // Rate limiting for cache operations
    'rate_limiter' => [
        'enabled' => true,
        'default_limit' => 100,    // Max operations per window
        'window' => 60,            // Window in seconds
    ],

    // TTL jitter to prevent thundering herd
    'jitter' => [
        'enabled' => false,
        'percentage' => 0.1,       // 10% jitter by default
    ],

    // Statistics dashboard
    'dashboard' => [
        'enabled' => false,
        'prefix' => 'smart-cache',
        'middleware' => ['web'],
    ],
];

🔧 Supported Cache Drivers

Driver Compression Chunking Locks All Features
Redis ✅ Full Support
File ✅ Full Support
Database ✅ Full Support
Array ✅ Full Support
Memcached ✅ Full Support

All Laravel cache drivers are fully supported!

🚀 Migration from Laravel Cache

SmartCache is a drop-in replacement - your existing code works unchanged:

// Before (Laravel Cache)
use Illuminate\Support\Facades\Cache;

Cache::put('users', $users, 3600);
$users = Cache::get('users');

// After (SmartCache) - just change the import
use SmartCache\Facades\SmartCache;

SmartCache::put('users', $users, 3600);  // Now automatically optimized!
$users = SmartCache::get('users');       // Automatically restored!

That's it! No code changes needed. You immediately get:

  • ✅ Automatic compression for large data
  • ✅ Smart chunking for large arrays
  • ✅ All new features available

📚 Documentation

📖 Full Documentation - Complete guide with examples

Quick Links

🧪 Testing

SmartCache includes 300+ comprehensive tests covering all functionality:

composer test

# With coverage
composer test-coverage

🤝 Contributing

Contributions are welcome! Please see CONTRIBUTING.md for details.

📄 License

MIT License. See LICENSE for details.

🔗 Links


Built with ❤️ for the Laravel community

Optimize caching for large data - from simple apps to enterprise systems