fiber-interop / predis
Cooperative Predis for fiber-interop/api: a StreamConnection that waits on the socket with async\readable()/writable(), so Redis commands suspend only the calling task.
Requires
- php: ^8.1
- fiber-interop/api: ^0.2
- predis/predis: ^2
Requires (Dev)
- fiber-interop/amp: *
- fiber-interop/phasync: *
- fiber-interop/react: *
- fiber-interop/revolt: *
- fiber-interop/swoole: *
- phpunit/phpunit: ^10.5 || ^11
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-14 18:26:59 UTC
README
Cooperative Predis for
fiber-interop/api: inside async\run() a Redis command
suspends only the calling task, on whichever adapter is installed. Outside async\run() it is plain
blocking Predis.
composer require fiber-interop/predis
use function async\{run, go, await_all}; $values = run(fn() => await_all(array_map( fn($key) => go(fn() => async\predis\client('tcp://127.0.0.1:6379')->get($key)), ['a', 'b', 'c'], )));
API
async\predis\client(array|string $parameters = [], array $options = []): Predis\Client—new Predis\Client($parameters, $options)with thetcp,tlsandunixschemes mapped toasync\predis\Connection. Aconnectionsentry in$optionsoverrides ours per scheme.async\predis\Connection extends Predis\Connection\StreamConnection— use it directly in your ownconnectionsoption if you build the client yourself. Everything above the socket (commands, pipelines, transactions, pub/sub loops, replication/cluster wrappers, exceptions) is Predis's own.
How it waits: on the socket
The connection's socket is non-blocking. Connecting uses STREAM_CLIENT_ASYNC_CONNECT and waits on
async\writable() (bounded by Predis's timeout parameter); the TLS handshake loops
stream_socket_enable_crypto() on async\readable(); writes loop fwrite() on async\writable()
(partial writes); replies are parsed from a buffer filled by fread() on async\readable(). Each
wait is bounded by read_write_timeout exactly as in Predis (unset: default_socket_timeout;
<= 0: forever). Refused, timed-out and lost connections raise Predis's ConnectionException.
Outside async\run() the same loops wait with stream_select().
Limits
- One connection is one socket: don't use the same
Predis\Clientfrom two tasks at once; give each task its own client (connections are cheap) or serialise access. DEBUG SLEEP, Lua scripts and other server-side work still block the single-threaded server for every client; only client-side waits (e.g.BLPOP) overlap.- A connection made outside
async\run()stays usable inside, and the reverse. - Persistent connections (
persistent) work but are shared per process, so the rule above applies.
Tests
docker run -d --name fi-redis -p 127.0.0.1:16379:6379 redis:7
composer update
FI_RUNTIME=revolt vendor/bin/phpunit # amp, react, phasync, swoole
REDIS_URI overrides the server (default tcp://127.0.0.1:16379). The suite covers overlap of ten
clients, other tasks running during BLPOP, pipelines, transactions, pub/sub, AUTH/SELECT,
connection refused inside and outside run(), a 1 MB round trip and plain blocking use.
License
MIT