Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Bounded Cache Laravel Package

graham-campbell/bounded-cache

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package:
    composer require graham-campbell/bounded-cache:^3.1
    
  2. Register the cache store in Laravel’s config/cache.php:
    'stores' => [
        'bounded' => [
            'driver' => 'bounded',
            'connection' => 'file', // or any PSR-16-compliant store
            'min_ttl' => 60,        // Optional: Default min TTL in seconds
            'max_ttl' => 300,       // Optional: Default max TTL in seconds
        ],
    ],
    
  3. Extend Laravel’s cache manager in AppServiceProvider:
    use GrahamCampbell\BoundedCache\BoundedCache;
    use GrahamCampbell\BoundedCache\BoundedCacheInterface;
    
    public function register()
    {
        Cache::extend('bounded', function ($app) {
            $connection = Cache::connection('file'); // Underlying store
            return new BoundedCache($connection, $app['config']['cache.stores.bounded.min_ttl'], $app['config']['cache.stores.bounded.max_ttl']);
        });
    }
    
  4. First use case: Cache a product listing with bounded TTL:
    $products = Cache::store('bounded')->remember('products', now()->addMinutes(15), function () {
        return Product::all();
    });
    

Key Starting Points

  • PSR-16 Compliance: The package implements Psr\SimpleCache\CacheInterface, so it works with any PSR-16-compliant backend.
  • Laravel Integration: Use Cache::store('bounded') or extend the default cache via Cache::extend().
  • Configuration: Set min_ttl and max_ttl in config/cache.php for global defaults.

Implementation Patterns

Core Workflows

1. Basic Caching with Bounded TTL

// Set with explicit TTL (must respect min/max bounds)
Cache::store('bounded')->set('key', 'value', now()->addMinutes(30));

// Get with fallback
$value = Cache::store('bounded')->get('key', 'default');

// Remember (auto-respects bounds)
$value = Cache::store('bounded')->remember('key', now()->addMinutes(10), function () {
    return expensiveOperation();
});

2. Per-Key TTL Overrides

// Override min/max bounds for a specific key
$cache = new BoundedCache(Cache::store('file'), minTTL: 30, maxTTL: 600);
$cache->set('key', 'value', now()->addMinutes(120), minTTL: 60, maxTTL: 1200);

3. Laravel-Specific Patterns

  • Cache Tags: Works with Laravel’s cache tags if the underlying store supports them (e.g., Redis).
    Cache::store('bounded')->tags(['products'])->put('featured', $products, now()->addMinutes(15));
    
  • Queue-Based Invalidation: Use Laravel’s Cache::forget() or Cache::tags() to manually evict keys.
  • Cache Events: Listen for cache.hit or cache.miss events (if the underlying store emits them).

4. Hybrid Caching Strategy

Combine with other Laravel cache drivers:

// Use bounded cache for short-lived data, Redis for long-lived
if (config('app.env') === 'local') {
    $cache = Cache::store('bounded');
} else {
    $cache = Cache::store('redis');
}

Integration Tips

Custom Cache Driver

Extend the package to create a reusable driver:

// app/Cache/BoundedRedisDriver.php
namespace App\Cache;

use GrahamCampbell\BoundedCache\BoundedCache;
use Illuminate\Cache\RedisStore;

class BoundedRedisDriver extends BoundedCache
{
    public function __construct()
    {
        parent::__construct(new RedisStore(), minTTL: 60, maxTTL: 3600);
    }
}

Register in AppServiceProvider:

Cache::extend('bounded_redis', function () {
    return new BoundedRedisDriver();
});

Middleware for Bounded Caching

Automatically apply bounded caching to API responses:

// app/Http/Middleware/BoundedCacheMiddleware.php
public function handle($request, Closure $next)
{
    $response = $next($request);
    if ($request->wantsJson() && !$request->is('health*')) {
        $response->setCache([
            'max_age' => 300, // 5 minutes
            'shared_max_age' => 300,
            'stale_while_revalidate' => 600,
        ]);
    }
    return $response;
}

Testing Bounded Cache

Use Laravel’s Cache::shouldReceive() or mock the BoundedCache interface:

use GrahamCampbell\BoundedCache\BoundedCacheInterface;

$mock = Mockery::mock(BoundedCacheInterface::class);
$mock->shouldReceive('get')
     ->with('key')
     ->andReturn('cached_value');

$this->app->instance(BoundedCacheInterface::class, $mock);

Gotchas and Tips

Pitfalls

  1. TTL Bound Violations

    • If you set a TTL outside the min_ttl/max_ttl bounds, the package clamps the value to the nearest bound. For example:
      $cache = new BoundedCache(Cache::store('file'), minTTL: 60, maxTTL: 300);
      $cache->set('key', 'value', now()->addMinutes(5)); // TTL clamped to 60
      $cache->set('key', 'value', now()->addHours(1));  // TTL clamped to 300
      
    • Tip: Use BoundedCacheInterface::setWithBounds() to explicitly set bounds per key.
  2. Underlying Store Limitations

    • If the underlying cache store (e.g., file driver) has its own eviction policies, bounded TTLs may not work as expected. For example, a file cache might still hit disk limits.
    • Tip: Monitor cache size and eviction rates, especially in high-traffic environments.
  3. Race Conditions

    • In multi-process environments (e.g., Laravel queues), concurrent set()/get() operations might lead to inconsistent TTL enforcement.
    • Tip: Use Laravel’s Cache::lock() for critical sections or implement a distributed lock (e.g., Redis).
  4. Laravel Cache Events

    • Laravel’s cache:flushed or cache:hit events may not propagate through BoundedCache. If you rely on these, wrap the package in a decorator to emit custom events.
    • Tip: Extend BoundedCache to dispatch events:
      use Illuminate\Support\Facades\Cache as LaravelCache;
      
      class EventfulBoundedCache extends BoundedCache
      {
          public function get($key, $default = null)
          {
              $value = parent::get($key, $default);
              if ($value !== $default) {
                  LaravelCache::dispatch('cache.hit', $key);
              }
              return $value;
          }
      }
      
  5. PHP 8.1+ Features

    • The package uses named arguments (e.g., new BoundedCache(..., minTTL: 60)). Ensure your Laravel app and dependencies support PHP 8.1+.
    • Tip: Use php artisan package:discover to check for compatibility issues.
  6. Memory Bloat

    • Without proper bounds, the cache can grow uncontrollably. Always set maxTTL and monitor memory usage.
    • Tip: Use Laravel’s cache:clear command or implement a cron job to periodically clear stale keys:
      // app/Console/Commands/ClearBoundedCache.php
      public function handle()
      {
          $cache = Cache::store('bounded');
          $keys = $cache->getKeys(); // If supported by underlying store
          foreach ($keys as $key) {
              $cache->delete($key);
          }
      }
      

Debugging Tips

  1. Log Eviction Events Add logging to track when keys are evicted due to TTL bounds:
    $cache = new BoundedCache(Cache::store('file'), minTTL: 60, maxTTL: 300);
    $cache->setLogger(function ($message) {
        \Log::debug('
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
datacore/hub-sdk
alengo/sulu-http-cache-bundle
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
imbo/imbo-coding-standard
visualbuilder/filament-lottie
servicioslineaonce/starter-kit
atomcoder/laravel-reorderable
irajul/filament-shadcn-theme
agtp/agtp-php
agtp/mod-php
centraldesktop/protobuf-php
trappistes/laravel-custom-fields
splash/sonata-admin
splash/metadata