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. 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). Does not apply to Unix sockets or to immediately-completing localhost connects. 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_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. 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 "NOT_STORED" if present. replace($key, $value, [$expiry, [$flags,]] [$cb]) Store only if the key already exists. Errors with "NOT_STORED" 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, errors are silently dropped. prepend($key, $data, [$cb]) Prepend bytes to an existing value. Same error and fire-and-forget 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")". The connection itself is left intact. 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 Every option from "new" has a getter/setter of the same name. 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. 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", "delete", "incr", ...) always use the non-quiet opcode so error responses are consumed by the client even when the user passed no callback. 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, . AUTHOR vividsnow LICENSE This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.