Subject: | Problems when using more than one instance of C::M |
Hi,
beginning with version 1.29 it is impossible to use more than one
instance of Cache::Memcached. Calling "disconnect_all" on one
instance can leave another instance in a broken state, making it
die on subsequent calls.
I've attached a small script that can reproduce this problem here
using Perl 5.12.1 and Cache::MemCached 1.29. The same script works
properly on version 1.28.
I think there is some confusion around the instance variables "buckets",
"buck2sock" and the lexical variables "sock_map" and "cache_sock".
I have attached a patch that makes "sock_map" and "cache_sock" local
to each instance. The patch seems to fix this issue for us but it has
not gotten much testing and I'm not sure whether it has any side
effects.
Cheers,
Dennis
Subject: | instances.diff |
--- /tmp/Memcached.pm 2010-11-09 15:54:43.447990000 +0100
+++ /opt/perl/5.12.1/local/share/perl/5.12.1/Cache/Memcached.pm 2010-11-09 16:39:07.000000000 +0100
@@ -27,6 +27,8 @@
connect_timeout cb_connect_fail
parser_class
buck2sock
+ cache_sock
+ sock_map
};
# flag definitions
@@ -56,7 +58,6 @@
eval { $FLAG_NOSIGNAL = MSG_NOSIGNAL; };
my %host_dead; # host -> unixtime marked dead until
-my %cache_sock; # host -> socket
my $PROTO_TCP;
@@ -80,6 +81,9 @@
$self->{'readonly'} = $args->{'readonly'};
$self->{'parser_class'} = $args->{'parser_class'} || $parser_class;
+ $self->{'cache_sock'} = {};
+ $self->{'sock_map'} = {};
+
# TODO: undocumented
$self->{'connect_timeout'} = $args->{'connect_timeout'} || 0.25;
$self->{'select_timeout'} = $args->{'select_timeout'} || 1.0;
@@ -104,6 +108,9 @@
$self->init_buckets;
$self->{'buck2sock'}= [];
+ $self->{'cache_sock'} = {};
+ $self->{'sock_map'} = {};
+
$self->{'_single_sock'} = undef;
if (@{$self->{'servers'}} == 1) {
$self->{'_single_sock'} = $self->{'servers'}[0];
@@ -164,16 +171,14 @@
$self->{'stat_callback'} = $stat_callback;
}
-my %sock_map; # stringified-$sock -> "$ip:$port"
-
sub _dead_sock {
my ($self, $sock, $ret, $dead_for) = @_;
- if (my $ipport = $sock_map{$sock}) {
+ if (my $ipport = $self->{sock_map}{$sock}) {
my $now = time();
$host_dead{$ipport} = $now + $dead_for
if $dead_for;
- delete $cache_sock{$ipport};
- delete $sock_map{$sock};
+ delete $self->{cache_sock}{$ipport};
+ delete $self->{sock_map}{$sock};
}
$self->{'buck2sock'} = [] if $self;
return $ret; # 0 or undef, probably, depending on what caller wants
@@ -181,10 +186,10 @@
sub _close_sock {
my ($self, $sock) = @_;
- if (my $ipport = $sock_map{$sock}) {
+ if (my $ipport = $self->{sock_map}{$sock}) {
close $sock;
- delete $cache_sock{$ipport};
- delete $sock_map{$sock};
+ delete $self->{cache_sock}{$ipport};
+ delete $self->{sock_map}{$sock};
}
$self->{'buck2sock'} = [];
}
@@ -231,7 +236,7 @@
sub sock_to_host { # (host) #why is this public? I wouldn't have to worry about undef $self if it weren't.
my Cache::Memcached $self = ref $_[0] ? shift : undef;
my $host = $_[0];
- return $cache_sock{$host} if $cache_sock{$host};
+ return $self->{cache_sock}{$host} if $self->{cache_sock}{$host};
my $now = time();
my ($ip, $port) = $host =~ /(.*):(\d+)$/;
@@ -255,12 +260,12 @@
if ($HAVE_SOCKET6 && index($prefip, ':') != -1) {
no strict 'subs'; # for PF_INET6 and AF_INET6, weirdly imported
socket($sock, PF_INET6, SOCK_STREAM, $proto);
- $sock_map{$sock} = $host;
+ $self->{sock_map}{$sock} = $host;
$sin = Socket6::pack_sockaddr_in6($port,
Socket6::inet_pton(AF_INET6, $prefip));
} else {
socket($sock, PF_INET, SOCK_STREAM, $proto);
- $sock_map{$sock} = $host;
+ $self->{sock_map}{$sock} = $host;
$sin = Socket::sockaddr_in($port, Socket::inet_aton($prefip));
}
@@ -279,12 +284,12 @@
if ($HAVE_SOCKET6 && index($ip, ':') != -1) {
no strict 'subs'; # for PF_INET6 and AF_INET6, weirdly imported
socket($sock, PF_INET6, SOCK_STREAM, $proto);
- $sock_map{$sock} = $host;
+ $self->{sock_map}{$sock} = $host;
$sin = Socket6::pack_sockaddr_in6($port,
Socket6::inet_pton(AF_INET6, $ip));
} else {
socket($sock, PF_INET, SOCK_STREAM, $proto);
- $sock_map{$sock} = $host;
+ $self->{sock_map}{$sock} = $host;
$sin = Socket::sockaddr_in($port, Socket::inet_aton($ip));
}
@@ -297,7 +302,7 @@
}
} else { # it's a unix domain/local socket
socket($sock, PF_UNIX, SOCK_STREAM, 0);
- $sock_map{$sock} = $host;
+ $self->{sock_map}{$sock} = $host;
$sin = Socket::sockaddr_un($host);
my $timeout = $self ? $self->{connect_timeout} : 0.25;
unless (_connect_sock($sock,$sin,$timeout)) {
@@ -312,7 +317,7 @@
$| = 1;
select($old);
- $cache_sock{$host} = $sock;
+ $self->{cache_sock}{$host} = $sock;
return $sock;
}
@@ -353,10 +358,10 @@
sub disconnect_all {
my Cache::Memcached $self = shift;
my $sock;
- foreach $sock (values %cache_sock) {
+ foreach $sock (values %{ $self->{cache_sock} } ) {
close $sock;
}
- %cache_sock = ();
+ $self->{cache_sock} = {};
$self->{'buck2sock'} = [];
}
@@ -714,8 +719,8 @@
};
foreach (keys %$sock_keys) {
- my $ipport = $sock_map{$_} or die "No map found matching for $_";
- my $sock = $cache_sock{$ipport} or die "No sock found for $ipport";
+ my $ipport = $self->{sock_map}{$_} or die "No map found matching for $_";
+ my $sock = $self->{cache_sock}{$ipport} or die "No sock found for $ipport";
print STDERR "processing socket $_\n" if $self->{'debug'} >= 2;
$writing{$_} = $sock;
if ($self->{namespace}) {
Subject: | cache-memcached.pl |
use strict;
use warnings;
use Cache::Memcached;
my @servers = qw(host01:11211 host02:11211 host03:11211);
my $memd1 = Cache::Memcached->new( { servers => \@servers } );
my $memd2 = Cache::Memcached->new( { servers => \@servers } );
$memd1->get_multi("foo");
$memd2->get_multi("bar");
$memd2->disconnect_all;
$memd1->get_multi("foo");
# dies with: "No sock found for host03:11211 at /opt/perl/5.12.1/local/share/perl/5.12.1/Cache/Memcached.pm line 718"