spiral/roadrunner-kv
RoadRunner KV provides a fast, simple key-value storage layer for PHP apps running on RoadRunner. Store and retrieve data via an in-memory or configured KV backend with minimal overhead—useful for caching, flags, and lightweight shared state across workers.
composer require spiral/roadrunner-kv
.rr.yaml or rr.yaml — ensure the kv plugin is enabled and configured (e.g., memory, redis, etc.). Example:
kv:
default: mem
pools:
mem:
driver: memory
config:
ttl: 60
use Spiral\RoadRunner\KeyValue\KeyValueInterface;
$kv = new KeyValueInterface('mem'); // Use configured pool name
$kv->set('user:123', ['name' => 'Alice']);
$data = $kv->get('user:123');
First use case: Cache expensive computation (e.g., API responses) within the worker lifecycle for < 1s latency access — ideal for micro-optimizations in RPC/HTTP services.
In-worker caching
Use get()/set() inside command handlers to avoid redundant work:
$cacheKey = 'expensive:' . md5($request->getQuery());
if ($cached = $kv->get($cacheKey)) {
return $cached;
}
$result = $this->doHeavyComputation($request);
$kv->set($cacheKey, $result, 30); // TTL in seconds (if supported)
Cross-worker state sharing
Use Redis-backed KV pool to share small, non-critical state (e.g., feature flags, rate limit counters):
// Increment without races (if driver supports atomic ops)
$count = $kv->increment('rate:limit:user:123');
Explicit lifecycle management
Since PHP workers are long-lived, avoid memory leaks:
$kv->delete('session:' . $sessionId); // Clean up after logout
Integration with Laravel/Lumen
Register as a singleton in service provider:
$this->app->singleton(KeyValueInterface::class, function () {
return new KeyValueInterface(config('roadrunner.kv.default'));
});
Typed error handling
Prefer catching specific exceptions:
try {
$value = $kv->get('missing-key');
} catch (KeyNotFoundException $e) {
// Gracefully handle
} catch (\Spiral\RoadRunner\KeyValue\Exception\DriverException $e) {
logger()->error('KV driver failure', ['error' => $e->getMessage()]);
}
⚠️ No built-in serialization
You must serialize/deserialize complex data yourself (e.g., json_encode()/json_decode()). The client only handles string|int|float|bool|null.
⚠️ TTL support is driver-dependent
set($key, $value, 60) works with Redis or file drivers — but not with the default memory driver unless explicitly configured in RoadRunner config.
🔧 Pool naming matters
Ensure the pool name passed to new KeyValueInterface('pool') matches exactly what’s defined in .rr.yaml. Mismatched names cause runtime failures.
🧪 Testing tip
Use Memory driver in tests for fast isolation. Override pool config via environment or custom RR config:
// .rr.test.yaml
kv:
default: test
pools:
test:
driver: memory
🔄 Driver fallbacks:
If Redis is unavailable and you use redis driver, errors may bubble silently — add heartbeat health checks (e.g., ping()) during boot.
🛠 Extension point: Extend client with your own helpers:
class CachedService {
public function __construct(private KeyValueInterface $kv) {}
public function getUser(int $id): array {
return $this->kv->remember("user:$id", fn() => $this->fetchFromDb($id), 60);
}
}
(Note: remember() is not built-in — add it as a trait or wrapper.)
📊 Profiling: Enable RoadRunner KV debug mode (kv.log: true) to inspect request/response latency — great for optimizing hot paths.
How can I help you explore Laravel packages today?