NAME
    EV::Memcached - asynchronous memcached client on libev

SYNOPSIS
        use EV;
        use EV::Memcached;

        my $mc = EV::Memcached->new(
            host     => '127.0.0.1',
            port     => 11211,
            on_error => sub { warn "memcached: @_" },
        );

        $mc->set('foo', 'bar', sub {
            my ($ok, $err) = @_;
            warn "set failed: $err" if $err;

            $mc->get('foo', sub {
                my ($value, $err) = @_;
                print "foo = $value\n";   # bar
                $mc->disconnect;
            });
        });

        EV::run;

DESCRIPTION
    A pure-XS memcached client built on the EV event loop. Implements the
    memcached binary protocol directly -- no external C client library is
    needed. All commands are non-blocking; results are delivered through
    callbacks dispatched by the EV loop.

    Highlights:

    *   Binary protocol with pipelining, multi-get via GETKQ + NOOP fence,
        and fire-and-forget quiet variants (SETQ, FLUSHQ).

    *   TCP and Unix socket transports, optional SASL PLAIN authentication
        (automatic re-auth on reconnect).

    *   Flow control via "max_pending", local "waiting_queue" with optional
        replay across reconnects, configurable connect / command / waiting
        timeouts.

    *   Predictable lifecycle: pending callbacks always fire (with the
        disconnect reason on teardown), DESTROY is reentrancy-safe across
        callback contexts.

    AnyEvent applications can use this module unchanged, since AnyEvent runs
    on top of EV when EV is loaded.

ENCODING
    This module treats all keys and values as byte strings. Encode UTF-8
    strings before passing them in:

        use Encode;
        $mc->set(foo => encode_utf8($val), sub { ... });
        $mc->get('foo', sub {
            my $val = decode_utf8($_[0]);
        });

CALLBACK CONVENTIONS
    Every command callback receives "($result, $err)". On success $err is
    "undef"; on protocol error $err holds a string like "NOT_STORED" or
    "NOT_FOUND". On a cache miss for "get"/"gat", both arguments are "undef"
    (a miss is not an error).

    Callback exceptions are caught with "G_EVAL" and reported via "warn" so
    a stray "die" never unwinds the libev event loop. To abort on errors,
    set a flag and break the loop; do not rely on "die" propagating out of a
    callback.

CONSTRUCTOR
  new(%options)
    Construct an instance. All options are optional; with none, the client
    is unconfigured and you must call "connect" / "connect_unix" later.
    Specifying "host" (or "path") at construction time triggers an immediate
    non-blocking connect. "on_connect" is always delivered from the event
    loop, never synchronously from the constructor, so installing handlers
    right after "new" is safe.

        my $mc = EV::Memcached->new(
            host     => '127.0.0.1',
            port     => 11211,
            on_error => sub { warn "@_" },
        );

   Connection
    host => $str
    port => $int (default 11211)
        TCP host and port. Mutually exclusive with "path".

    path => $str
        Unix socket path. Mutually exclusive with "host".

    loop => $ev_loop
        EV loop to attach to. Default: "EV::default_loop".

    priority => $num (-2 to +2)
        EV watcher priority. Higher = serviced before other EV watchers.

    keepalive => $seconds
        TCP keepalive idle time. Set to 0 to disable. Ignored on Unix
        sockets.

   Timeouts and flow control
    connect_timeout => $ms
        Abort an in-progress non-blocking connect after this many
        milliseconds. 0 = no timeout (default). Applies to any connect that
        does not complete immediately (including unix sockets under rare
        kernel backlog conditions); immediately-completing connects finish
        on the next event loop iteration without arming this timer.

    command_timeout => $ms
        Disconnect with "command timeout" error if no response arrives
        within this interval. The timer resets on every response from the
        server. 0 = no timeout (default).

    max_pending => $num
        Cap on concurrent in-flight commands. Excess commands are held in a
        local waiting queue. 0 = unlimited (default).

    waiting_timeout => $ms
        Maximum time a command may sit in the waiting queue before its
        callback fires with "waiting timeout". 0 = unlimited (default).

    resume_waiting_on_reconnect => $bool
        If true, the waiting queue survives a disconnect and is replayed on
        reconnect. Default: false.

   Reconnect
    reconnect => $bool
        Enable automatic reconnection on transport errors.

    reconnect_delay => $ms (default 1000)
        Delay before each reconnect attempt. The delay is always honored via
        a timer; setting it to 0 still defers through the event loop (no
        synchronous retry recursion).

    max_reconnect_attempts => $num
        Give up after this many consecutive failures and emit "max reconnect
        attempts reached". 0 = unlimited (default).

   Authentication
    username => $str
    password => $str
        SASL PLAIN credentials. When both are set, the client authenticates
        after every successful connect (and reconnect). Pre-connect commands
        sit in the waiting queue until SASL completes. Requires a memcached
        build with SASL support and the "-S" flag.

   Event handlers
    on_error => $cb->($errstr)
        Connection-level error callback. Default: write the message to
        "STDERR" via "warn". Callbacks are run under "G_EVAL", so any "die"
        in a custom handler is demoted to a warning -- use an explicit flag
        if you need to terminate.

    on_connect => $cb->()
        Fires once the connection is fully established (after SASL, when
        applicable; on SASL auth failure it never fires). Always delivered
        from the event loop -- never synchronously from "new" / "connect" /
        "connect_unix" -- so a handler installed right after the constructor
        still fires.

    on_disconnect => $cb->()
        Fires after a disconnect, after pending callbacks have been
        cancelled. For server-initiated close, this fires before "on_error".

LIFECYCLE
  connect($host, [$port])
    Connect to a TCP host. Port defaults to 11211. Stops any pending
    auto-reconnect timer and clears any prior "path" setting.

  connect_unix($path)
    Connect via Unix domain socket. Stops any pending auto-reconnect timer
    and clears any prior "host" setting.

  disconnect
    Disconnect cleanly. Cancels any pending reconnect, drains pending
    command callbacks with "(undef, "disconnected")", then fires
    "on_disconnect". For an intentional disconnect, "on_error" does not fire
    -- that distinction lets you tell user-initiated teardown from
    server-side close.

  is_connected
    Returns true while a session is established or in progress (TCP
    handshake / SASL exchange). Commands issued in the connecting phase are
    queued and sent on completion.

  quit([$cb])
    Send a memcached "QUIT" and let the server close the connection. Treated
    as an intentional disconnect: "on_disconnect" fires when the server
    closes, but "on_error" does not, and auto-reconnect is suppressed.

STORAGE COMMANDS
    Each command's callback receives "($result, $err)". $result is 1 on
    success.

  set($key, $value, [$expiry, [$flags,]] [$cb])
    Store unconditionally. Without $cb this becomes fire-and-forget (SETQ):
    no response is received and any server-side failure is silently dropped.

  add($key, $value, [$expiry, [$flags,]] [$cb])
    Store only if the key does not exist. Errors with "EXISTS" if present
    (the server may append a message, e.g. "EXISTS: Data exists for key.").

  replace($key, $value, [$expiry, [$flags,]] [$cb])
    Store only if the key already exists. Errors with "NOT_FOUND" if absent.

  cas($key, $value, $cas, [$expiry, [$flags,]] [$cb])
    Compare-and-swap. The $cas token comes from a prior "gets" / "gats" /
    "mgets". Errors with "EXISTS" on token mismatch or "NOT_FOUND" if the
    key disappeared.

  append($key, $data, [$cb])
    Append bytes to an existing value. Errors with "NOT_STORED" if the key
    does not exist. Without $cb the result is discarded, but the server
    still sends a response (the non-quiet opcode is used); this is not
    fire-and-forget like "set".

  prepend($key, $data, [$cb])
    Prepend bytes to an existing value. Same error and discard semantics as
    "append".

  delete($key, [$cb])
    Delete a key. Errors with "NOT_FOUND" if absent.

RETRIEVAL COMMANDS
  get($key, [$cb->($value, $err)])
    Retrieve a value. On a cache miss, both $value and $err are "undef" -- a
    miss is not an error.

  gets($key, [$cb->($info, $err)])
    Like "get" but returns "{ value => ..., flags => ..., cas => ... }".

  mget(\@keys, [$cb->(\%values, $err)])
    Multi-get, internally pipelined as a sequence of GETKQ packets
    terminated by a NOOP fence. Returns a hash containing only the keys that
    were hits:

        $mc->mget([qw(k1 k2 k3)], sub {
            my ($values, $err) = @_;
            # $values = { k1 => 'v1', k3 => 'v3' }   # k2 was a miss
        });

  mgets(\@keys, [$cb->(\%info, $err)])
    Like "mget" but each value carries metadata:

        $mc->mgets([qw(k1 k2)], sub {
            my ($info, $err) = @_;
            # $info = { k1 => { value => 'v', flags => 0, cas => 123 } }
        });

ATOMIC COUNTERS
  incr($key, [$delta, [$initial, [$expiry,]]] [$cb->($new_value, $err)])
    Atomic increment. $delta defaults to 1. $expiry defaults to 0xFFFFFFFF,
    which means "do not auto-create" (the call then errors with
    "NOT_FOUND"). Pass any other expiry to auto-create with $initial:

        $mc->incr('counter', 1, sub { ... });          # require existing
        $mc->incr('counter', 1, 100, 300, sub { ... }); # auto-create at 100, 5min TTL

    $new_value is the post-increment counter value.

  decr($key, [$delta, [$initial, [$expiry,]]] [$cb->($new_value, $err)])
    Atomic decrement. Memcached clamps the result at 0 (never negative).
    Same auto-create semantics as "incr".

EXPIRATION
  touch($key, $expiry, [$cb])
    Update an existing key's expiration without fetching the value. Errors
    with "NOT_FOUND" if absent.

  gat($key, $expiry, [$cb->($value, $err)])
    Get-and-touch: retrieve and update expiration in one round-trip. Same
    miss semantics as "get".

  gats($key, $expiry, [$cb->($info, $err)])
    Get-and-touch with metadata. Same shape as "gets".

SERVER COMMANDS
  flush([$expiry,] [$cb])
    Invalidate every item. Optional delay in seconds before the flush takes
    effect. Without $cb, sent as fire-and-forget (FLUSHQ).

  noop([$cb])
    No-operation round-trip. Useful as a pipeline fence to wait until all
    previously-sent commands have been processed.

  version([$cb->($version, $err)])
    Server version string.

  stats([$name,] [$cb->(\%stats, $err)])
    Server statistics. Without $name, returns the default stats group.
    Common groups: "settings", "items", "sizes", "slabs", "conns".

AUTHENTICATION
  sasl_auth($username, $password, [$cb])
    Authenticate via SASL PLAIN. Auto-invoked on connect when both
    "username" and "password" were passed to the constructor; call manually
    only when authenticating after a no-auth construction.

  sasl_list_mechs([$cb->($mechs, $err)])
    Query the server's supported mechanisms; returns a space-separated
    string such as "PLAIN".

LOCAL CONTROL
  skip_pending
    Drain the in-flight queue, firing every callback with "(undef,
    "skipped")". Responses for skipped commands are consumed and discarded
    when they later arrive (strict FIFO opaque matching is preserved); the
    connection genuinely stays usable for new commands.

  skip_waiting
    Same, but for the local waiting queue (commands not yet sent).

  pending_count
    Number of commands sent and awaiting a response.

  waiting_count
    Number of commands held in the local waiting queue (because the
    connection is not ready, SASL is in progress, or "max_pending" is
    saturated).

ACCESSORS
    The following options have a getter/setter of the same name (there are
    no accessors for "host", "port", "path", "username", "password",
    "reconnect_delay", "max_reconnect_attempts", or "loop"). Calling without
    arguments reads the current value; with one argument it writes and
    (where meaningful, e.g. "keepalive") takes effect immediately.

    connect_timeout([$ms])
    command_timeout([$ms])
    max_pending([$num])
    waiting_timeout([$ms])
    resume_waiting_on_reconnect([$bool])
    priority([$num])
    keepalive([$seconds])
    "reconnect_enabled"
        Read-only; configure via "reconnect".

    "reconnect($enable, [$delay_ms], [$max_attempts])"
        Reconfigure auto-reconnect at runtime.

    on_error([$cb])
    on_connect([$cb])
    on_disconnect([$cb])
        Get/set the corresponding handler. Pass "undef" to clear.

DESTRUCTION
    If $mc goes out of scope while commands are in flight or queued, every
    pending and waiting callback fires once with "(undef, "disconnected")".
    This holds whether you call "disconnect" first or simply drop the
    reference -- including dropping the last reference from inside one of
    the object's own callbacks (deferred DESTROY fires the remaining
    callbacks before tearing down).

    The one exception is global destruction (interpreter shutdown): no Perl
    callbacks are invoked then; queues are freed silently.

    The clean shutdown idiom is:

        $mc->disconnect;   # drains queues, fires on_disconnect
        undef $mc;

    If a callback closes over $mc (a common mistake -- every reference
    inside a callback closure keeps the object alive), break the cycle
    before dropping the outer reference:

        $mc->on_error(undef);
        $mc->on_connect(undef);
        $mc->on_disconnect(undef);
        undef $mc;

    DESTROY is reentrant-safe: if a callback fired during teardown drops the
    last external reference to a separate "EV::Memcached", that object's
    DESTROY is correctly deferred and run once unwound.

BINARY PROTOCOL NOTES
    The wire format is the memcached binary protocol -- a 24-byte header
    plus body, with each request tagged by an opaque field used for
    in-flight matching and pipelining. Multi-get is sent as a run of GETKQ
    packets ending in a NOOP fence: the server emits a response only on hit,
    and the NOOP reply terminates the batch. Fire-and-forget "set"/"flush"
    use the quiet SETQ / FLUSHQ opcodes so the server sends no response at
    all.

    Commands that can legitimately fail ("add", "replace", "append",
    "prepend", "delete", "incr", ...) always use the non-quiet opcode so
    error responses are consumed by the client even when the user passed no
    callback. Response matching is strict FIFO per connection: responses
    must arrive in request order, so only in-order servers are supported
    (reordering proxies are unsupported). Keys are validated against the
    250-byte protocol limit before any bytes go on the wire.

BENCHMARKS
    Numbers from "bench/benchmark.pl" on Linux, TCP loopback, 100-byte
    values, Perl 5.40, memcached 1.6.41:

                             50K cmds    200K cmds
        Pipeline SET           213K        68K ops/sec
        Pipeline GET           216K        67K ops/sec
        Mixed workload         226K        69K ops/sec
        Fire-and-forget SET    1.13M      1.29M ops/sec  (SETQ)
        Multi-get (GETKQ)      1.30M      1.17M ops/sec  (per key)
        Sequential round-trip   41K        38K ops/sec

    Fire-and-forget is roughly 5x faster than callback mode because there is
    no per-command Perl SV allocation. Multi-get is the fastest read path
    since misses generate no traffic. Callback-mode throughput drops as
    batch size grows because SV allocation for closures dominates; realistic
    workloads (interleaved sends and receives) stay close to the 50K-command
    column.

    "max_pending" overhead (200K commands):

        unlimited        ~131K ops/sec
        max_pending=500  ~126K ops/sec
        max_pending=100  ~120K ops/sec
        max_pending=50   ~117K ops/sec

    Override "BENCH_COMMANDS", "BENCH_VALUE_SIZE", "BENCH_HOST", and
    "BENCH_PORT" to retune.

SEE ALSO
    EV, AnyEvent, Cache::Memcached::Fast, Memcached::Client,
    <https://github.com/memcached/memcached/wiki/BinaryProtocolRevamped>.

AUTHOR
    vividsnow

LICENSE
    This library is free software; you can redistribute it and/or modify it
    under the same terms as Perl itself.

