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.

