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

Chain Adapter Laravel Package

cache/chain-adapter

PSR-6 cache pool chain adapter that combines multiple cache pools (e.g., APCu + Redis) into a single CachePoolChain. Part of PHP-Cache, with optional features like tagging and hierarchy via shared docs. Install with composer and use with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require cache/chain-adapter
    
  2. Basic Usage:

    use Cache\ChainAdapter\CachePoolChain;
    use Psr\Cache\CacheItemPoolInterface;
    
    // Define your PSR-6 cache pools (e.g., Redis, APCu, FileCache)
    $redisPool = new RedisCachePool($redisClient);
    $apcuPool = new ApcuCachePool();
    
    // Chain them in order of preference (first pool is highest priority)
    $chainPool = new CachePoolChain([$apcuPool, $redisPool]);
    
  3. First Use Case:

    • Use the chain pool as a drop-in replacement for any CacheItemPoolInterface in Laravel (e.g., in Cache facade or service containers).
    • Example:
      Cache::set('key', 'value', now()->addHour()); // Uses the chain pool
      

Implementation Patterns

Workflows

  1. Fallback Strategy:

    • Configure pools in priority order (e.g., ApcuCachePoolRedisCachePoolFileCachePool).
    • The chain will attempt operations on each pool sequentially until success or exhaustion.
  2. Tag-Based Operations:

    • Leverage the chain’s support for tags (inherited from underlying pools) to group related cache items.
    • Example:
      $item = $chainPool->getItem('user:123');
      $item->setTags(['users', 'premium']);
      $chainPool->save($item);
      
  3. Laravel Integration:

    • Bind the chain pool to Laravel’s Cache facade or service container:
      $app->bind(CacheItemPoolInterface::class, function ($app) {
          return new CachePoolChain([
              new ApcuCachePool(),
              new RedisCachePool($app->make(Redis::class)),
          ]);
      });
      
  4. Conditional Pool Skipping:

    • Use the skip_on_failure option to bypass failed pools:
      $chainPool = new CachePoolChain([$pool1, $pool2], [
          'skip_on_failure' => true, // Skip failed pools instead of halting
      ]);
      

Integration Tips

  • Logging: The chain adapter is LoggerAware. Inject a PSR-3 logger for debugging:
    $chainPool->setLogger($logger);
    
  • Testing: Mock the chain pool to test fallback behavior:
    $mockPool1 = $this->createMock(CacheItemPoolInterface::class);
    $mockPool2 = $this->createMock(CacheItemPoolInterface::class);
    $chainPool = new CachePoolChain([$mockPool1, $mockPool2]);
    
  • Dynamic Chains: Rebuild the chain at runtime (e.g., based on environment):
    $pools = [];
    if (app()->environment('local')) {
        $pools[] = new ApcuCachePool();
    }
    $pools[] = new RedisCachePool($redisClient);
    $chainPool = new CachePoolChain($pools);
    

Gotchas and Tips

Pitfalls

  1. Order Matters:

    • Pools are evaluated in the order they’re added. Place faster/cheaper pools (e.g., APCu) first to optimize performance.
    • Example of bad order (slow first):
      new CachePoolChain([$redisPool, $apcuPool]); // APCu will rarely be used!
      
  2. Tag Inconsistency:

    • Tags are only supported if the underlying pools implement Psr\Cache\CacheItemInterface::setTags(). Verify compatibility before relying on tags.
  3. Exception Handling:

    • By default, the chain throws PoolFailedException if a pool fails. Use skip_on_failure => true to bypass failures:
      $chainPool = new CachePoolChain([$pool1, $pool2], ['skip_on_failure' => true]);
      
  4. Memory Leaks:

    • If using APCu as the first pool, ensure it’s properly configured to avoid memory bloat. Monitor APCu usage in production.

Debugging

  • Enable Logging:

    $chainPool->setLogger(new MonologLogger($logHandler));
    

    Logs will include which pool succeeded/failed for each operation.

  • Check for NoPoolAvailableException:

    • Thrown when all pools fail and skip_on_failure is false. Handle gracefully:
      try {
          $chainPool->getItem('key')->get();
      } catch (NoPoolAvailableException $e) {
          // Fallback to database or generate dynamically
      }
      

Extension Points

  1. Custom Pool Wrappers:

    • Extend the chain to add pre/post-processing logic:
      class LoggingCachePool implements CacheItemPoolInterface {
          private $delegate;
      
          public function __construct(CacheItemPoolInterface $delegate) {
              $this->delegate = $delegate;
          }
      
          public function getItem($key) {
              $logger->info("Accessing key: $key");
              return $this->delegate->getItem($key);
          }
          // Delegate other methods...
      }
      
      Then chain it:
      $chainPool = new CachePoolChain([
          new LoggingCachePool($apcuPool),
          $redisPool,
      ]);
      
  2. Dynamic Pool Addition:

    • Rebuild the chain dynamically (e.g., add a fallback pool during maintenance):
      $chainPool->addPool(new FileCachePool('/tmp/cache'));
      
  3. Conditional Pool Activation:

    • Use Laravel’s Cache events to toggle pools:
      Cache::extend('chain', function ($app) {
          $pools = [new ApcuCachePool()];
          if (config('cache.redis.enabled')) {
              $pools[] = new RedisCachePool($app->make(Redis::class));
          }
          return new CachePoolChain($pools);
      });
      
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.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor