EV-Memcached
view release on metacpan or search on metacpan
Revision history for EV::Memcached
0.01 2026-04-06
- Initial release
- Pure XS binary protocol, no external C library
- Full command set, multi-get (mget/mgets), fire-and-forget (SETQ/FLUSHQ)
- SASL PLAIN auth (manual + auto-auth on connect/reconnect)
- Pipelining, flow control, reconnection, connect/command timeouts
- TCP, Unix socket, keepalive, EV event loop integration
Keep waiting queue on disconnect for replay after reconnect.
reconnect => $bool
Enable automatic reconnection.
reconnect_delay => $ms (default 1000)
max_reconnect_attempts => $num (0 = unlimited)
priority => $num (-2 to +2)
EV watcher priority.
keepalive => $seconds
TCP keepalive interval.
username => 'Str'
password => 'Str'
SASL PLAIN authentication credentials. When both are set, the client
automatically authenticates after connecting (and after each
reconnect). Requires memcached started with "-S" flag and SASL
support compiled in.
loop => EV::Loop
EV loop to use. Default: "EV::default_loop".
waiting_timeout([$ms])
Get/set local queue timeout.
resume_waiting_on_reconnect([$bool])
Get/set waiting queue behavior on disconnect.
priority([$num])
Get/set EV watcher priority (-2 to +2).
keepalive([$seconds])
Get/set TCP keepalive.
skip_pending
Cancel all pending command callbacks with "(undef, "skipped")".
skip_waiting
Cancel all waiting command callbacks with "(undef, "skipped")".
on_error([$cb])
on_connect([$cb])
on_disconnect([$cb])
lib/EV/Memcached.pm view on Meta::CPAN
Enable automatic reconnection.
=item reconnect_delay => $ms (default 1000)
=item max_reconnect_attempts => $num (0 = unlimited)
=item priority => $num (-2 to +2)
EV watcher priority.
=item keepalive => $seconds
TCP keepalive interval.
=item username => 'Str'
=item password => 'Str'
SASL PLAIN authentication credentials. When both are set, the client
automatically authenticates after connecting (and after each reconnect).
Requires memcached started with C<-S> flag and SASL support compiled in.
=item loop => EV::Loop
lib/EV/Memcached.pm view on Meta::CPAN
Get/set local queue timeout.
=head2 resume_waiting_on_reconnect([$bool])
Get/set waiting queue behavior on disconnect.
=head2 priority([$num])
Get/set EV watcher priority (-2 to +2).
=head2 keepalive([$seconds])
Get/set TCP keepalive.
=head2 skip_pending
Cancel all pending command callbacks with C<(undef, "skipped")>.
=head2 skip_waiting
Cancel all waiting command callbacks with C<(undef, "skipped")>.
=head2 on_error([$cb])
src/EV__Memcached.xs view on Meta::CPAN
ev_timer waiting_timer;
int waiting_timer_active;
/* Safety */
int callback_depth;
int in_cb_cleanup;
int in_wait_cleanup;
/* Options */
int priority;
int keepalive;
/* SASL auth */
char *username;
char *password;
};
/* ================================================================
* Shared error strings (initialized in BOOT)
* ================================================================ */
src/EV__Memcached.xs view on Meta::CPAN
stop_writing(self);
if (self->connect_timer_active) {
ev_timer_stop(self->loop, &self->connect_timer);
self->connect_timer_active = 0;
}
/* Setup read watcher */
start_reading(self);
/* TCP keepalive */
if (self->keepalive > 0) {
int one = 1;
setsockopt(self->fd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one));
#ifdef TCP_KEEPIDLE
setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPIDLE,
&self->keepalive, sizeof(self->keepalive));
#endif
}
mc_send_sasl_auth(aTHX_ self, NULL);
emit_connect(aTHX_ self);
if (check_destroyed(self)) return;
/* Defer waiting queue drain until SASL auth completes (if active) */
if (!self->username)
src/EV__Memcached.xs view on Meta::CPAN
self->connected = 1;
ev_io_init(&self->rio, io_cb, self->fd, EV_READ);
self->rio.data = (void *)self;
ev_io_init(&self->wio, io_cb, self->fd, EV_WRITE);
self->wio.data = (void *)self;
ev_set_priority(&self->rio, self->priority);
ev_set_priority(&self->wio, self->priority);
start_reading(self);
/* TCP keepalive */
if (self->keepalive > 0 && !self->path) {
int one = 1;
setsockopt(self->fd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one));
#ifdef TCP_KEEPIDLE
setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPIDLE,
&self->keepalive, sizeof(self->keepalive));
#endif
}
self->reconnect_attempts = 0;
mc_send_sasl_auth(aTHX_ self, NULL);
emit_connect(aTHX_ self);
if (check_destroyed(self)) return;
if (!self->username)
src/EV__Memcached.xs view on Meta::CPAN
}
else if (strEQ(k, "on_disconnect")) {
if (SvOK(v) && SvROK(v)) RETVAL->on_disconnect = newSVsv(v);
}
else if (strEQ(k, "max_pending")) RETVAL->max_pending = SvIV(v);
else if (strEQ(k, "waiting_timeout")) RETVAL->waiting_timeout_ms = SvIV(v);
else if (strEQ(k, "connect_timeout")) RETVAL->connect_timeout_ms = SvIV(v);
else if (strEQ(k, "command_timeout")) RETVAL->command_timeout_ms = SvIV(v);
else if (strEQ(k, "resume_waiting_on_reconnect")) RETVAL->resume_waiting_on_reconnect = SvTRUE(v) ? 1 : 0;
else if (strEQ(k, "priority")) RETVAL->priority = SvIV(v);
else if (strEQ(k, "keepalive")) RETVAL->keepalive = SvIV(v);
else if (strEQ(k, "reconnect")) do_reconnect = SvTRUE(v) ? 1 : 0;
else if (strEQ(k, "reconnect_delay")) reconnect_delay = SvIV(v);
else if (strEQ(k, "max_reconnect_attempts")) max_reconnect_attempts = SvIV(v);
else if (strEQ(k, "username")) {
if (SvOK(v)) RETVAL->username = savepv(SvPV_nolen(v));
}
else if (strEQ(k, "password")) {
if (SvOK(v)) RETVAL->password = savepv(SvPV_nolen(v));
}
else if (strEQ(k, "loop")) {
src/EV__Memcached.xs view on Meta::CPAN
} else {
ev_set_priority(&self->wio, self->priority);
}
}
RETVAL = self->priority;
}
OUTPUT:
RETVAL
int
keepalive(EV::Memcached self, ...)
CODE:
{
if (items > 1) {
self->keepalive = SvIV(ST(1));
if (self->keepalive < 0) self->keepalive = 0;
/* Apply to current connection if active */
if (self->connected && self->fd >= 0 && self->keepalive > 0) {
int one = 1;
setsockopt(self->fd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one));
#ifdef TCP_KEEPIDLE
setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPIDLE,
&self->keepalive, sizeof(self->keepalive));
#endif
}
}
RETVAL = self->keepalive;
}
OUTPUT:
RETVAL
void
skip_pending(EV::Memcached self)
CODE:
{
cancel_pending_impl(aTHX_ self, err_skipped, 1);
}
t/04_features.t view on Meta::CPAN
run_ev();
$mc->disconnect;
}
# --- new() constructor in XS ---
{
my $mc = EV::Memcached->new(
host => $host,
port => $port,
max_pending => 10,
keepalive => 5,
priority => 1,
connect_timeout => 3000,
on_error => sub { diag "error: @_" },
);
$mc->on_connect(sub { EV::break });
my $t = EV::timer 5, 0, sub { fail("timeout"); EV::break };
EV::run;
ok($mc->is_connected, "XS new() connected");
is($mc->max_pending, 10, "XS new() max_pending");
( run in 1.203 second using v1.01-cache-2.11-cpan-39bf76dae61 )