NAME EV::Redis - Asynchronous redis client using hiredis and EV SYNOPSIS use EV::Redis; my $redis = EV::Redis->new; $redis->connect('127.0.0.1'); # or my $redis = EV::Redis->new( host => '127.0.0.1' ); # command $redis->set('foo' => 'bar', sub { my ($res, $err) = @_; print $res; # OK $redis->get('foo', sub { my ($res, $err) = @_; print $res; # bar $redis->disconnect; }); }); # start main loop EV::run; DESCRIPTION EV::Redis is a fork of EV::Hiredis by Daisuke Murase (typester), extended with reconnection, flow control, TLS, and RESP3 support. It is a drop-in replacement for EV::Hiredis, except that commands which would pair replies with the wrong callbacks croak (see Reply order note). This is an asynchronous client for Redis using hiredis and EV as backend. It connects to EV with C-level interface so that it runs faster. ANYEVENT INTEGRATION AnyEvent has a support for EV as its one of backends, so EV::Redis can be used in your AnyEvent applications seamlessly. NO UTF-8 SUPPORT Unlike other redis modules, this module doesn't support utf-8 string. This module handles all values as bytes: a string with characters above 0xFF croaks with a "Wide character" error. Encode utf-8 strings before passing them: use Encode; # set $val $redis->set(foo => encode_utf8 $val, sub { ... }); # get $val $redis->get('foo', sub { my $val = decode_utf8 $_[0]; }); SIGPIPE Writing to a connection the server has closed raises "SIGPIPE", which kills the process by default. Set "$SIG{PIPE} = 'IGNORE'" to get the error in "on_error" instead. FORK A child process must not use an inherited object: its connection fails in the child with "connection inherited from the parent process" (with "reconnect", the child then connects on its own); the parent is not affected. A loop other than the default needs "$loop->loop_fork" in the child. Do not fork inside a callback. Objects cannot be copied: "Storable" and "Sereal" with "freeze_callbacks" croak on them. Other serialized or "Clone" copies are inert: their methods croak and destroying them does nothing. RESP3 REPLIES After "HELLO 3", maps and sets arrive as array references (a map as a flat key, value list), doubles as numbers, booleans as 1 or 0, and big numbers and verbatim strings as plain strings. Attribute replies are not supported: one ahead of a reply is dropped, one inside an array or map fails the connection. METHODS new(%options); Create new EV::Redis instance. Available %options are: * host => 'Str' * port => 'Int' Hostname and port number of redis-server to connect. Mutually exclusive with "path". * path => 'Str' UNIX socket path to connect. Mutually exclusive with "host". * on_error => $cb->($errstr) Called on connection-level errors. The default handler dies, but exceptions thrown in handlers and command callbacks are caught and reported as warnings, so connection errors become warnings unless you install your own. "undef" here keeps the default; on_error(undef) removes it. They never end "EV::run", including one from a %SIG handler such as "alarm": use "command_timeout" or an "EV::timer" for timeouts. * on_connect => $cb->() Called when the connection is established (with "tls", once TCP is up; a failed handshake then arrives as "on_error"). * on_disconnect => $cb->() Called when the connection closes, normally or on error. * on_push => $cb->($reply) Called with RESP3 push messages (Redis 6.0+), as an array reference. The handlers can be set later with the methods of the same name. * connect_timeout => $num_of_milliseconds Connection timeout. * command_timeout => $num_of_milliseconds Command timeout. * max_pending => $num * waiting_timeout => $num_of_milliseconds See the methods of the same name. * resume_waiting_on_reconnect => $bool If true and "reconnect" is on, commands waiting locally are kept across a lost connection and sent after reconnecting. Otherwise (the default) a lost connection or failed connect attempt cancels them. disconnect(), or giving up on reconnecting, cancels them either way. A transaction does not survive: commands issued inside "WATCH"/"MULTI".."EXEC" are replayed only when none of the transaction reached the server, and fail with the lost connection otherwise. * reconnect => $bool Enable automatic reconnection on connection failure or unexpected disconnection. Default is disabled (0). * reconnect_delay => $num_of_milliseconds Delay between reconnection attempts. Default is 1000 (1 second). Used only with "reconnect", as is "max_reconnect_attempts". * max_reconnect_attempts => $num Maximum number of reconnect attempts in a row; an established connection resets the count. 0 (default) means unlimited. * priority => $num Priority for the underlying libev IO watchers. Higher priority watchers are invoked before lower priority ones. Valid range is -2 (lowest) to +2 (highest), with 0 being the default. See EV documentation for details on priorities. * keepalive => $seconds Enable TCP keepalive probes on idle connections, with this interval in seconds (at most 32767; the interval itself is set with glibc and on macOS only). 0 means disabled (default). Ignored for unix sockets. * prefer_ipv4 => $bool * prefer_ipv6 => $bool Resolve host names to that address family, falling back to the other only when the name has no address of it. IPv4 is the default. * source_addr => 'Str' Local address to bind the outbound connection to. Useful on multi-homed servers to select a specific network interface. Ignored for unix sockets. * tcp_user_timeout => $num_of_milliseconds Set TCP_USER_TIMEOUT (Linux): how long sent data may stay unacknowledged before the connection is dropped. Ignored for unix sockets; where the system lacks the option, TCP connects fail. * cloexec => $bool Set close-on-exec on the Redis connection socket. Prevents the file descriptor from leaking to child processes after fork/exec. Default is enabled. * reuseaddr => $bool Set SO_REUSEADDR on the Redis connection socket. Allows rebinding to an address that is still in TIME_WAIT state. Default is disabled. Only takes effect with "source_addr". * tls => $bool Enable TLS/SSL encryption for the connection. Requires that the module was built with TLS support (auto-detected at build time, or forced with "EV_REDIS_SSL=1"). Only valid with "host" connections, not "path". * tls_ca => 'Str' Path to CA certificate file for server verification. If not specified, uses the system default CA store. * tls_capath => 'Str' Path to a directory containing CA certificate files in OpenSSL-compatible format (hashed filenames). Alternative to "tls_ca" for multiple CA certs. * tls_cert => 'Str' Path to client certificate file for mutual TLS authentication. Must be specified together with "tls_key". * tls_key => 'Str' Path to client private key file. Must be specified together with "tls_cert". * tls_server_name => 'Str' Server name for SNI, sent on every connection; without it no SNI is sent. It is not checked against the certificate. * tls_verify => $bool Verify the server certificate (default true). Only the chain is checked, not the host name, so use "tls_ca" with a CA dedicated to your Redis servers rather than the system store. * loop => 'EV::Loop', EV loop for running this instance. Default is "EV::default_loop". All parameters are optional. Unknown ones warn, unless "new" is called on a subclass. If parameters about connection (host&port or path) is not passed, you should call "connect" or "connect_unix" method by hand to connect to redis-server. connect($hostname [, $port]) connect_unix($path) Connect to a redis-server for "$hostname:$port" (default 6379) or $path. Croaks if a connection is already active or the port or path is invalid. A failure found at once (a missing socket, a name that does not resolve) reaches "on_error" before it returns. Host names are resolved synchronously, by each reconnect too, outside "connect_timeout": pass an IP address where DNS can be slow. command($commands..., [$cb->($result, $error)]) Do a redis command and return its result by callback. Returns "REDIS_OK" (0), or "REDIS_ERR" (-1) if it could not be enqueued (the callback gets the error too). $redis->command('get', 'foo', sub { my ($result, $error) = @_; print $result; # value for key 'foo' print $error; # redis error string, undef if no error }); On error, $error holds the message and $result is undef; otherwise $error is undef. An error inside an array reply (such as "EXEC"'s result for a failed queued command) arrives as its error text. The callback is optional: only a code reference in the last position is taken as one, so an "undef" there is sent as an argument. Without one the command is fire-and-forget: its reply and errors are discarded (connection errors still reach "on_error"): $redis->set('counter', 42); # fire-and-forget, no callback With a million commands outstanding, a closure per command makes their completion take minutes; share one code reference instead. All commands can also be called via the AUTOLOAD interface: $redis->command('get', 'foo', sub { ... }); is equivalent to: $redis->get('foo', sub { ... }); The Redis "COMMAND" command itself is reached as "$redis->command('command', ...)", since "command" is this method. Note: command() croaks with "connection required before calling command" while not connected, unless a reconnect is pending: commands then wait locally (see "resume_waiting_on_reconnect"). In "on_error" and "on_disconnect" it still croaks, as the reconnect is scheduled after they return. A retry issued from a failed command's callback waits for the reconnect or fails in turn (after a failed connect with no reconnect scheduled, it croaks); it never recurses. Pub/Sub note: "subscribe" and "psubscribe" take at least one name and a persistent callback, which also receives the unsubscribe confirmations (a callback passed to "unsubscribe" is ignored unless the command is refused). Subscribing again to a name moves it to the new callback. An error reply on a subscribed connection closes it, so keep pub/sub on a connection of its own; set "keepalive" to notice a server that vanished, as a subscribed connection has no timeout. When the connection closes, a subscribe callback gets one error for each channel or pattern it still holds. Sharded pub/sub ("ssubscribe", "sunsubscribe") is not supported and croaks; "spublish" works. MONITOR note: "monitor" requires an idle connection, so it cannot be issued from within a reply callback; once it is active command() croaks on that connection. "pmonitor" is not supported. Use a dedicated one. Reply order note: hiredis hands each reply to the oldest waiting callback, so commands that change how the server answers croak: "CLIENT REPLY OFF" and "SKIP", "REPLCONF ACK" and "GETACK", "SYNC", "PSYNC", and "RESET" while subscribed. Pub/sub commands, "monitor" and "HELLO" inside "MULTI" fail through their callback; pub/sub and "HELLO" first wait locally for outstanding transaction replies, a round trip each. An "EXEC" cancelled before it was sent leaves the transaction open. Nested event loop note: while a callback for an event on this connection runs, its I/O is paused, so a nested "EV::run" inside it cannot receive replies for the same connection. Use a separate connection to wait for Redis inside a callback. disconnect Disconnect from redis-server; safe when already disconnected. Stops any pending reconnect and cancels waiting commands with "disconnected" before it returns. Commands already sent are not cancelled (except on a subscribed connection): the connection closes once they are answered, then "on_disconnect" runs. It runs only for a connection that was established. So it does not drop a server that stopped answering: use "command_timeout", or destroy the object. Sending "QUIT" instead is reported as a lost connection, and "reconnect" connects again. is_connected Returns true (1) if a connection context is active (including while the connection is being established), false (0) otherwise. has_ssl Class method. Returns true (1) if the module was built with TLS support, false (0) otherwise. if (EV::Redis->has_ssl) { # TLS connections are available } connect_timeout([$ms]) Get or set the connection timeout in milliseconds (0 disables; undef if never set). It covers the connect attempt after name resolution; with "tls", the TCP connect only ("command_timeout" ends a stalled handshake once a command is outstanding). Without it, a unix socket whose server has a full listen queue is retried in a busy loop. command_timeout([$ms]) Get or set the command timeout in milliseconds (0 disables; undef if never set). It fires when replies are outstanding and nothing has arrived for that long; new commands do not extend it, a large command going out does. Subscriptions are not covered; an idle MONITOR connection times out. A command that timed out, or lost its connection, may still have run, or run later: retry only commands safe to run twice. Changes apply at once. on_error([$cb->($errstr)]) Set the error callback. Like all handler methods ("on_error", "on_connect", "on_disconnect", "on_push"): a CODE reference replaces the handler and is returned; "undef" or no argument clears it (so the current handler cannot be read); any other value clears it with a warning. on_connect([$cb->()]) Set the connect callback. Commands issued from it go first, past "max_pending", so it suits per-connection setup such as "AUTH" or "SELECT". Commands waiting locally go after that setup; one sent while the connection was being established goes before it, unless both "reconnect" and "resume_waiting_on_reconnect" are on. on_disconnect([$cb->()]) Set the disconnect callback, called on both normal and error disconnections. on_push([$cb->($reply)]) Set the RESP3 push callback (Redis 6.0+); it receives the push message as an array reference. $redis->on_push(sub { my ($msg) = @_; # $msg is an array ref, e.g. ['invalidate', ['key1', 'key2']] }); reconnect($enable, $delay_ms, $max_attempts) Configure automatic reconnection. $redis->reconnect(1); # enable with defaults (1s delay, unlimited) $redis->reconnect(1, 0); # enable with immediate reconnect $redis->reconnect(1, 2000); # enable with 2 second delay $redis->reconnect(1, 1000, 5); # enable with 1s delay, max 5 attempts $redis->reconnect(0); # disable $delay_ms defaults to 1000; 0 retries at once, in a busy loop against a server that refuses connections. $max_attempts defaults to 0 (unlimited). Explicit undef keeps the current value of that argument. It reconnects after a failed connect or an unexpected disconnection, not after disconnect(). A new connection starts fresh: subscriptions, "AUTH", "SELECT" and "HELLO" are not restored, so issue them from "on_connect". reconnect_enabled Returns true (1) if automatic reconnection is enabled, false (0) otherwise. pending_count Returns the number of commands sent to Redis awaiting replies, not counting (p)subscribe, (p)unsubscribe and monitor. Inside a reply callback the count still includes that command. waiting_count Returns the number of commands queued locally, not yet sent: over "max_pending", during a reconnect, or held for transaction replies, and those queued behind them. max_pending($limit) Get or set the maximum number of commands sent to Redis at once (0, the default, means unlimited); further commands wait locally and go out as replies arrive. (P)subscribe, (p)unsubscribe and monitor hold no slot. waiting_timeout($ms) Get or set the maximum time in milliseconds a command can wait locally before it fails with "waiting timeout" (0, the default, means unlimited). Time held only for transaction replies does not count. resume_waiting_on_reconnect($bool) Get or set the option of the same name (see "new"). priority($priority) Get or set the priority for the underlying libev IO watchers. Higher priority watchers are invoked before lower priority ones when multiple watchers are pending. Valid range is -2 (lowest) to +2 (highest), with 0 being the default. Values outside this range are clamped automatically. Can be changed at any time, including while connected. $redis->priority(1); # higher priority $redis->priority(-1); # lower priority $redis->priority(99); # clamped to 2 my $prio = $redis->priority; # get current priority keepalive($seconds) Get or set the TCP keepalive interval (see "new"). A positive value set while connected over TCP applies at once, and croaks if the system refuses it; 0 applies from the next connection. prefer_ipv4($bool) prefer_ipv6($bool) Get or set the address family preference (see "new"); setting one to a true value clears the other. Takes effect on the next connection. source_addr($addr) Get or set the local address to bind TCP connections to ("undef" clears). Takes effect on the next connection. tcp_user_timeout($ms) cloexec($bool) reuseaddr($bool) Get or set the option of the same name (see "new"). Takes effect on the next connection. skip_waiting Cancel only waiting (not yet sent) command callbacks. Each callback is invoked with "(undef, "skipped")". In-flight commands continue normally. Commands issued by those callbacks are not cancelled. skip_pending Cancel all pending and waiting command callbacks: each is invoked at once with "(undef, "skipped")", and replies that arrive later are discarded. Commands issued by those callbacks are not cancelled. On a MONITOR connection the monitor stream is left running. can($method) Returns a code reference for real methods, and for Redis commands once they have been called; undef otherwise. DESTRUCTION BEHAVIOR When an EV::Redis object is destroyed with commands still pending or waiting, their callbacks get "disconnected" (pending ones get the connection error, if one is in flight). An object still alive at global destruction (a package variable, or one kept by a reference cycle) runs no callbacks. Circular references: callbacks that close over $redis form a cycle that keeps the object alive. Break it by clearing the handlers: $redis->on_error(undef); $redis->on_connect(undef); $redis->on_disconnect(undef); $redis->on_push(undef); BENCHMARKS Measured on Linux with Unix socket connection, 100,000 commands with 100-byte values, Perl 5.40, Redis 8.x ("bench/benchmark.pl" in the source repository, "BENCH_COMMANDS=100000"): Pipeline SET ~107K ops/sec Pipeline GET ~112K ops/sec Mixed workload ~112K ops/sec Fire-and-forget SET ~655K ops/sec Sequential round-trip ~39K ops/sec (SET+GET pairs) Fire-and-forget mode (no callback) is roughly 6x faster than callback mode due to zero Perl-side overhead per command. Pipeline throughput is bounded by the event loop round-trip, not by hiredis or the network. Flow control ("max_pending") has minimal impact at reasonable limits: unlimited ~180K ops/sec max_pending=500 ~186K ops/sec max_pending=100 ~146K ops/sec Run "perl bench/benchmark.pl" in a checkout of the repository for full results. Set "BENCH_COMMANDS" and "BENCH_VALUE_SIZE" environment variables to customize; the default of 10,000 commands gives different rates. AUTHOR Daisuke Murase (typester) (original EV::Hiredis) vividsnow COPYRIGHT AND LICENSE Copyright (c) 2013 Daisuke Murase, 2026 vividsnow. All rights reserved. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.