Skip to content

An asynchronous caching abstraction layer for PHP with built-in rate limiting and stale-while-revalidate support, compliant with PSR-16.

License

Notifications You must be signed in to change notification settings

Fyennyi/async-cache-php

Repository files navigation

Async Cache PHP

Latest Stable Version Total Downloads License Tests Test Coverage Static Analysis

An asynchronous caching abstraction layer for PHP with built-in rate limiting and stale-while-revalidate support. This library is designed to wrap promise-based operations (like Guzzle Promises) to provide robust caching strategies suitable for high-load or rate-limited API clients.

Features

  • Asynchronous Caching: Wraps PromiseInterface or any callable returning a value/promise to handle caching transparently without blocking execution.
  • Stale-While-Revalidate: Supports background revalidation and stale-on-error patterns.
  • X-Fetch (Probabilistic Early Recomputation): Implements the X-Fetch algorithm to prevent cache stampedes (dog-pile effect).
  • Atomic Operations: Support for atomic increment and decrement operations using Symfony Lock.
  • Logical vs. Physical TTL: Separates the "freshness" of data from its "existence" in the cache, enabling soft expiration patterns.
  • Rate Limiting Integration: Supports Symfony Rate Limiter for request throttling.
  • PSR-16 & ReactPHP Compatible: Works with any PSR-16 Simple Cache adapter or ReactPHP Cache implementation.

Installation

To install the Async Cache PHP library, run the following command in your terminal:

composer require fyennyi/async-cache-php

Usage

Basic Setup

The easiest way to create a manager is using the AsyncCacheBuilder.

use Fyennyi\AsyncCache\AsyncCacheBuilder;
use Fyennyi\AsyncCache\Storage\ReactCacheAdapter;
use React\Cache\ArrayCache;

// 1. Setup Cache (using ReactPHP ArrayCache as an example)
$cacheAdapter = new ReactCacheAdapter(new ArrayCache());

// 2. Create the Manager using the Builder
$manager = AsyncCacheBuilder::create($cacheAdapter)
    ->build();

Wrapping an Async Operation

Use the wrap method to cache a promise-based operation.

use Fyennyi\AsyncCache\CacheOptions;
use Fyennyi\AsyncCache\Enum\CacheStrategy;
use GuzzleHttp\Client;

$client = new Client();

$options = new CacheOptions(
    ttl: 60,                        // Data is fresh for 60 seconds
    strategy: CacheStrategy::Strict // Default strategy
);

$promise = $manager->wrap(
    'cache_key_user_1',
    fn() => $client->getAsync('https://api.example.com/users/1'),
    $options
);

// Wait for the result (non-blocking if used within ReactPHP event loop)
$response = $promise->wait();

Advanced Configuration Options

The CacheOptions DTO allows you to configure behavior per request:

use Fyennyi\AsyncCache\Enum\CacheStrategy;

new CacheOptions(
    ttl: 300,                        // Time in seconds data is considered fresh
    stale_grace_period: 86400,       // Keep stale data physically in cache for 24h
    strategy: CacheStrategy::Strict, // Strict, Background, or ForceRefresh
    rate_limit_key: 'nominatim',     // Key for rate limiting (if limiter is configured)
    serve_stale_if_limited: true,    // Return stale data if rate limited
    tags: ['geo', 'kyiv'],           // Cache tags (if adapter supports them)
    compression: false,              // Enable data compression
    x_fetch_beta: 1.0                // Beta coefficient for X-Fetch (0 to disable)
);

Atomic Increments

$newValue = $manager->increment('page_views', 1)->wait();

How It Works

  1. Cache Hit: If data is found in the cache and is fresh (within ttl), the promise resolves immediately with the cached value. The factory function is not called.
  2. Cache Miss: If data is not found, the factory function is executed, and the result is stored in the cache.
  3. Stale Data:
    • If data is in the cache but expired (older than ttl), the manager behavior depends on the chosen strategy.
    • Strict: Fetches fresh data while the request waits.
    • Background: Returns stale data immediately and triggers an asynchronous refresh in the background.
  4. X-Fetch: Helps avoid simultaneous cache misses for the same key by probabilistic early recomputation.

Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the project.
  2. Create your feature branch (git checkout -b feature/AmazingFeature).
  3. Commit your changes (git commit -m 'Add some AmazingFeature').
  4. Push to the branch (git push origin feature/AmazingFeature).
  5. Open a Pull Request.

License

This library is licensed under the CSSM Unlimited License v2.0 (CSSM-ULv2). See the LICENSE file for details.

About

An asynchronous caching abstraction layer for PHP with built-in rate limiting and stale-while-revalidate support, compliant with PSR-16.

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

Contributors 2

  •  
  •