Defective Code Logo

Total Downloads Latest Stable Version Latest Stable Version

English | العربية | বাংলা | Bosanski | Deutsch | Español | Français | हिन्दी | Italiano | 日本語 | 한국어 | मराठी | Português | Русский | Kiswahili | தமிழ் | తెలుగు | Türkçe | اردو | Tiếng Việt | 中文

Introduction

Recall is a high-performance Redis client-side caching package for Laravel. It leverages Redis 6's client-side caching feature with automatic invalidation to dramatically reduce Redis round trips and latency. Built specifically for Laravel Octane environments, it uses APCu or Swoole Table as a local cache layer that stays synchronized with Redis through invalidation messages.

When you fetch a value from Redis, Recall stores it locally. When that value changes in Redis (from any client), Redis automatically notifies Recall to invalidate the local copy. This gives you the speed of in-memory caching with the consistency guarantees of Redis.

Key Features

Example

// Configure recall as your cache driver
// config/cache.php
'stores' => [
'recall' => [
'driver' => 'recall',
],
],
 
// Use it like any Laravel cache
use Illuminate\Support\Facades\Cache;
 
// First call: fetches from Redis, stores locally
$user = Cache::store('recall')->get('user:1');
 
// Subsequent calls: served from local APCu/Swoole Table (microseconds)
$user = Cache::store('recall')->get('user:1');
 
// When user:1 is updated anywhere, Redis notifies Recall to invalidate
Cache::store('recall')->put('user:1', $newUserData, 3600);
// Local cache is automatically invalidated on all workers

Installation

Install the package via Composer:

composer require defectivecode/laravel-recall

Requirements

Usage

Basic Setup

  1. Add the Recall cache store to your config/cache.php:
'stores' => [
// ... other stores
 
'recall' => [
'driver' => 'recall',
],
],
  1. Use the cache store in your application:
use Illuminate\Support\Facades\Cache;
 
// Store a value (writes to Redis)
Cache::store('recall')->put('key', 'value', 3600);
 
// Retrieve a value (first call hits Redis, subsequent calls use local cache)
$value = Cache::store('recall')->get('key');
 
// Delete a value
Cache::store('recall')->forget('key');

How It Works

  1. First Read: Value is fetched from Redis and stored in local APCu/Swoole Table cache
  2. Subsequent Reads: Value is served directly from local memory (sub-millisecond)
  3. Write Anywhere: When any client modifies the key in Redis, Redis sends an invalidation message
  4. Automatic Invalidation: Recall receives the message and removes the key from local cache
  5. Next Read: Fresh value is fetched from Redis and cached locally again

This pattern is especially powerful in Laravel Octane environments where workers persist between requests, allowing the local cache to build up and serve many requests from memory.

Octane Integration

Recall automatically integrates with Laravel Octane when available:

No additional configuration is required. The integration is automatic when Octane is installed.

Configuration

Publish the configuration file:

php artisan vendor:publish --tag=recall-config

This creates config/recall.php with the following options:

Enable/Disable

'enabled' => env('RECALL_ENABLED', true),

When disabled, Recall passes through directly to Redis without using the local cache layer. Useful for debugging or gradual rollout.

Redis Store

'redis_store' => env('RECALL_REDIS_STORE', 'redis'),

The Laravel cache store to use for Redis operations. This should reference a Redis store configured in your config/cache.php.

Cache Prefixes

'cache_prefixes' => [],

Only cache keys matching these prefixes locally. Leave empty to cache all keys.

// Only cache user and settings keys locally
'cache_prefixes' => ['users:', 'settings:', 'config:'],

This is useful when you have high-volume keys that change frequently and shouldn't be cached locally.

Local Cache Configuration

'local_cache' => [
// Driver: "apcu" or "swoole"
'driver' => env('RECALL_LOCAL_DRIVER', 'apcu'),
 
// Prefix for local cache keys
'key_prefix' => env('RECALL_LOCAL_PREFIX', 'recall:'),
 
// Default TTL in seconds (safety net if invalidation is missed)
'default_ttl' => (int) env('RECALL_LOCAL_TTL', 3600),
 
// Swoole Table size (power of 2, only for swoole driver)
'table_size' => (int) env('RECALL_SWOOLE_TABLE_SIZE', 65536),
 
// Max bytes per value in Swoole Table (only for swoole driver)
'value_size' => (int) env('RECALL_SWOOLE_VALUE_SIZE', 8192),
],

APCu Driver (Default)

The APCu driver works with all PHP environments and Octane servers (Swoole, RoadRunner, FrankenPHP). It stores cached values in shared memory accessible to all PHP processes.

Requirements:

Swoole Table Driver

The Swoole Table driver uses Swoole's shared memory tables, providing consistent access across coroutines within the same worker. Best for Swoole/OpenSwoole environments.

Configuration tips:

Octane Listeners

'listeners' => [
// Warm connection on worker start (reduces first request latency)
'warm' => env('RECALL_LISTEN_WARM', true),
 
// Process invalidations on tick events (more responsive, slight overhead)
'tick' => env('RECALL_LISTEN_TICK', false),
],

Warm Connections

When enabled, Recall establishes the Redis invalidation subscription when the Octane worker starts. This eliminates connection latency on the first request.

Tick Processing

When enabled, Recall processes invalidation messages on Octane tick events in addition to request events. This provides more responsive cache invalidation at the cost of slight additional overhead.

Advanced Usage

Manual Invalidation Processing

If you need to manually process invalidations (e.g., in a long-running process):

use DefectiveCode\Recall\RecallManager;
 
$manager = app(RecallManager::class);
$manager->processInvalidations();

Flushing Local Cache

To clear only the local cache without affecting Redis:

use DefectiveCode\Recall\RecallManager;
 
$manager = app(RecallManager::class);
$manager->flushLocalCache();

Connection Management

use DefectiveCode\Recall\RecallManager;
 
$manager = app(RecallManager::class);
 
// Check if invalidation subscription is connected
if ($manager->isConnected()) {
// ...
}
 
// Manually disconnect
$manager->disconnect();

Optimization

Worker Request Limits

Laravel Octane cycles workers after a configurable number of requests to prevent memory leaks. When a worker cycles, its local cache is cleared. Increasing this limit allows Recall's local cache to persist longer, improving cache hit rates.

In your config/octane.php:

// Default is 500 requests before cycling
'max_requests' => 10000,

Higher values mean better cache utilization but require confidence that your application doesn't have memory leaks. Monitor your worker memory usage when adjusting this value.

Selective Caching with Prefixes

Use cache_prefixes to control which keys are cached locally. This is valuable when:

// config/recall.php
'cache_prefixes' => [
'users:', // Cache user data locally
'settings:', // Cache application settings
'products:', // Cache product catalog
],

Keys not matching these prefixes will still work but bypass local caching, going directly to Redis.

Memory Considerations

APCu Memory

APCu shares memory across all PHP processes. Configure the memory limit in php.ini:

; Default is 32MB, increase for larger cache needs
apc.shm_size = 128M

Monitor APCu usage with apcu_cache_info():

$info = apcu_cache_info();
$memory = $info['mem_size']; // Current memory usage in bytes

Swoole Table Sizing

Swoole Tables have fixed capacity configured at creation. Plan for your expected cache size:

'local_cache' => [
// Maximum entries (must be power of 2)
'table_size' => 65536, // 64K entries
 
// Maximum serialized value size in bytes
'value_size' => 8192, // 8KB per value
],

Memory usage: table_size × (value_size + overhead). A table with 65536 entries and 8KB values uses approximately 512MB.

Common Patterns

Application Configuration

// Cache feature flags and settings
$features = Cache::store('recall')->remember('config:features', 3600, function () {
return Feature::all()->pluck('enabled', 'name')->toArray();
});
 
// When settings change, all workers automatically receive updates

Frequently Accessed Reference Data

// Cache product categories
$categories = Cache::store('recall')->remember('categories:all', 3600, function () {
return Category::with('children')->whereNull('parent_id')->get();
});
 
// Cache currency exchange rates
$rates = Cache::store('recall')->remember('rates:exchange', 300, function () {
return ExchangeRate::all()->pluck('rate', 'currency')->toArray();
});

Cache Tags Alternative

Recall doesn't support cache tags, but you can achieve similar functionality with prefixes:

// Instead of tags, use consistent prefixes
Cache::store('recall')->put("blog:posts:{$id}", $post, 3600);
Cache::store('recall')->put("blog:comments:{$postId}", $comments, 3600);
 
// Clear all blog-related cache by prefix (requires manual implementation)
// Or rely on automatic invalidation when data changes

Limitations

Redis Cluster Mode

Recall does not support Redis Cluster mode. The CLIENT TRACKING command's REDIRECT option requires both the data connection and invalidation subscriber connection to be on the same Redis node. In a cluster, keys are distributed across multiple nodes based on hash slots, making it impossible to receive invalidations for keys stored on different nodes.

For clustered Redis deployments, consider:

Support Guidelines

Thanks for choosing our open source package! Please take a moment to check out these support guidelines. They'll help you get the most out of our project.

Community Driven Support

Our open-source project is fueled by our awesome community. If you have questions or need assistance, StackOverflow and other online resources are your best bets.

Bugs, and Feature Prioritization

The reality of managing an open-source project means we can't address every reported bug or feature request immediately. We prioritize issues in the following order:

1. Bugs Affecting Our Paid Products

Bugs that impact our paid products will always be our top priority. In some cases, we may only address bugs that affect us directly.

2. Community Pull Requests

If you've identified a bug and have a solution, please submit a pull request. After issues affecting our products, we give the next highest priority to these community-driven fixes. Once reviewed and approved, we'll merge your solution and credit your contribution.

3. Financial Support

For issues outside the mentioned categories, you can opt to fund their resolution. Each open issue is linked to an order form where you can contribute financially. We prioritize these issues based on the funding amount provided.

Community Contributions

Open source thrives when its community is active. Even if you're not fixing bugs, consider contributing through code improvements, documentation updates, tutorials, or by assisting others in community channels. We highly encourage everyone, as a community, to help support open-source work.

To reiterate, DefectiveCode will prioritize bugs based on how they impact our paid products, community pull requests, and the financial support received for issues.

License - MIT License

Copyright © Defective Code, LLC. All rights reserved

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.