Weave 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

Roadrunner Kv Laravel Package

spiral/roadrunner-kv

PSR-16 cache bridge for the RoadRunner KV plugin. Install via Composer, configure one or more kv drivers in RoadRunner, then use Spiral\RoadRunner\KeyValue\Factory over RPC to select a named store and set, get, or delete cached values. Ideal for RoadRunner-powered PHP apps.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install the package via Composer (note the renamed package — see Gotchas):

    composer require roadrunner/kv
    

    The old name spiral/roadrunner-kv is superseded by roadrunner/kv as of v4.5.0. Do not require both.

  2. Enable and configure the RoadRunner KV plugin in .rr.yaml. This package now ships RoadRunner v3 support, and the plugin's config shape differs between RR v2 and v3. For RR v3:

    version: '3'
    
    rpc:
      listen: tcp://127.0.0.1:6001
    
    kv:
      memory:                 # name becomes the pool selector
        driver: memory
        config: {}
      redis:
        driver: redis
        config:
          addrs:
            - localhost:6379
    

    If you are still on RR v2, keep the older kv.pools.<name> / kv.default layout. After installing, confirm the exact shape against your rr binary version and the vendor/roadrunner/kv/ source — v4.5.0 is the release where the two RR generations overlap.

  3. Build the client through the factory, not by instantiating the interface (it is an interface, not a constructor):

    use Spiral\Goridge\RPC\RPC;
    use Spiral\RoadRunner\KeyValue\Factory;
    
    $factory = new Factory(RPC::create('tcp://127.0.0.1:6001'));
    $kv = $factory->select('memory');   // pool name from .rr.yaml
    
    $kv->set('user:123', json_encode(['name' => 'Alice']));
    $data = json_decode($kv->get('user:123'), true);
    

First use case: Cache expensive computation (an upstream API response, a report query) inside the worker lifecycle for sub-millisecond reads with a TTL — the highest-value, lowest-risk entry point.


Implementation Patterns

  • In-worker caching with TTL

    $cacheKey = 'expensive:' . md5($request->getQueryString());
    $cached = $kv->get($cacheKey);
    if ($cached !== null) {
        return json_decode($cached, true);
    }
    $result = $this->doHeavyComputation($request);
    $kv->set($cacheKey, json_encode($result), 30); // TTL seconds, driver-dependent
    return $result;
    
  • Cross-worker state sharing Use a Redis-backed pool when more than one worker must agree on a value (feature flags, cheap rate-limit counters). Be aware the client exposes simple get/set/delete semantics rather than atomic increments — for counters where races matter, use Redis directly through a dedicated client, or serialize access.

  • PSR-16 adapter If you want Laravel-familiar cache semantics, use the package's PSR-16 implementation instead of hand-rolling:

    $storage = $factory->select('redis')->asSimpleCache(); // PSR-16 SimpleCache
    

    This gives you get/set/has/delete with the same pooling behaviour and is a good seam for swapping in ArrayAdapter in tests.

  • Laravel service container binding

    // app/Providers/RoadRunnerServiceProvider.php
    public function register(): void
    {
        $this->app->singleton(Factory::class, fn () => new Factory(
            RPC::create(config('roadrunner.rpc'))
        ));
    
        $this->app->bind('kv', fn ($app) => $app->make(Factory::class)
            ->select(config('roadrunner.kv.pool', 'memory')));
    }
    

    Bind the factory as the singleton and the pool as a scoped/aliased binding — this lets you switch pools per call site without extra connections.

  • Guard for non-RoadRunner contexts The client talks to the rpc plugin over a socket. Under php artisan serve, Octane-less queue workers, or a plain PHPUnit run there is no RoadRunner process, so select() will fail at first I/O. Wrap the binding:

    if (! config('roadrunner.enabled')) {
        $this->app->bind('kv', fn () => new \Illuminate\Cache\Repository(
            new \Illuminate\Cache\ArrayStore()
        ));
    }
    
  • Typed error handling Catch the package's exceptions rather than \Exception, and log driver failures distinctly from cache misses — a miss is normal, a driver failure is an incident.


Gotchas and Tips

  • ⚠️ Breaking: package rename (v4.5.0) spiral/roadrunner-kv → roadrunner/kv. Update composer.json, any composer require lines in CI, Dockerfiles, and docs. Since the old name may still resolve to an older tag, a stale require silently pins you to pre-v4.5 behaviour. Run composer why roadrunner/kv after upgrading to confirm you have one package, not two.

  • ⚠️ Breaking: RoadRunner v3 support means the KV config shape changed The kv plugin config is not forward-compatible between RR v2 and v3 (pool map vs. pools/default keys). Upgrading the PHP package without upgrading rr — or vice versa — is the most likely failure mode. Keep .rr.yaml and the rr binary version pinned together, and note that v4.5.0 is a transitional release carrying both generations.

  • ⚠️ Verify the namespace on upgrade The rename is at the Composer level; the PHP namespace in the 4.x line may still be Spiral\RoadRunner\KeyValue\*. Check the actual files after composer update before mass-renaming use statements, and pin the major so a future namespace move doesn't surprise you.

  • ⚠️ No built-in serialization The client handles scalars and strings only. json_encode/json_decode yourself — and remember it, because storing an array will not throw, it will store something surprising.

  • ⚠️ TTL is driver-dependent The memory driver ignores or truncates TTLs depending on config; redis honours them. Never rely on expiry for correctness — only for cost control. Expire defensively with a versioned key or a timestamp inside the payload if staleness matters.

  • 🔧 Pool name must match exactly $factory->select('memory') against a config key redis is a runtime RPC error, not a boot error. Resolve the pool name from config, never hardcode it.

  • 🔧 RPC endpoint must match too RPC::create('tcp://127.0.0.1:6001') has to mirror rpc.listen. In containerised deployments the socket is commonly a Unix socket (unix:///var/run/rr.sock) — mismatch here produces confusing "connection refused" errors that look like cache failures.

  • 🧪 Testing In tests, bind the interface to the PSR-16 ArrayAdapter-style in-memory implementation (or the package's simple-cache adapter over the memory pool) so no RoadRunner process is needed. Reserve a real rr serve smoke test for one integration test that exercises TTL and pool selection.

  • 🛠 Extension point remember() does not exist. Add it as a thin wrapper and put your serialization policy in one place:

    class CachedService
    {
        public function __construct(private KeyValueInterface $kv) {}
    
        public function getUser(int $id): array
        {
            $key = "user:$id";
            $raw = $this->kv->get($key);
            if ($raw !== null) {
                return json_decode($raw, true);
            }
            $user = $this->fetchFromDb($id);
            $this->kv->set($key, json_encode($user), 60);
            return $user;
        }
    }
    
  • 📊 Profiling and debugging RoadRunner ships its own logging/metrics for plugins; enable the rpc and kv log channels at the RR level (v3 uses logs: with per-plugin level). If reads are slow, measure RPC round-trip time at the socket boundary first — a busy loop of get() calls inside one request costs more than one batched fetch, so design keys to allow reading a single blob per request.

  • 🔄 Health checks Ping the RPC endpoint during boot (a trivial kv call such as a has() on a sentinel key) and fail fast with a clear message, rather than letting the first user request discover a dead socket.

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.
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo
stevenay/myanfont
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin