Skip to content

Commit

Permalink
Fix buck2sock corruption by having a sockets generation counter.
Browse files Browse the repository at this point in the history
This works by keeping track of when we need to regenerate the buck2sock map based on the generation of the sockets cache.
  • Loading branch information
Jonathan Steinert committed May 19, 2012
1 parent c70ca99 commit 3039298
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 6 deletions.
4 changes: 4 additions & 0 deletions ChangeLog
@@ -1,3 +1,7 @@
* Fix buck2sock corruption that causes errors like
"No map found matching for GLOB" or "No sock found for" (hachi@cpan.org)
https://rt.cpan.org/Ticket/Display.html?id=62872

* Fix t/05_reconnect_timeout.t to not fail on networks that fast-reject
TEST-NET-1 https://rt.cpan.org/Ticket/Display.html?id=74500

Expand Down
34 changes: 28 additions & 6 deletions lib/Cache/Memcached.pm
Expand Up @@ -26,7 +26,7 @@ use fields qw{
bucketcount _single_sock _stime
connect_timeout cb_connect_fail
parser_class
buck2sock
buck2sock buck2sock_generation
};

# flag definitions
Expand Down Expand Up @@ -57,6 +57,7 @@ eval { $FLAG_NOSIGNAL = MSG_NOSIGNAL; };

my %host_dead; # host -> unixtime marked dead until
my %cache_sock; # host -> socket
my $socket_cache_generation = 1; # Set to 1 here because below the buck2sock_generation is set to 0, keep them in order.

my $PROTO_TCP;

Expand All @@ -69,6 +70,7 @@ sub new {
my $args = (@_ == 1) ? shift : { @_ }; # hashref-ify args

$self->{'buck2sock'}= [];
$self->{'buck2sock_generation'} = 0;
$self->set_servers($args->{'servers'});
$self->{'debug'} = $args->{'debug'} || 0;
$self->{'no_rehash'} = $args->{'no_rehash'};
Expand Down Expand Up @@ -102,7 +104,9 @@ sub set_servers {
$self->{'buckets'} = undef;
$self->{'bucketcount'} = 0;
$self->init_buckets;
$self->{'buck2sock'}= [];

# We didn't close any sockets, so we reset the buck2sock generation, not increment the global socket cache generation.
$self->{'buck2sock_generation'} = 0;

$self->{'_single_sock'} = undef;
if (@{$self->{'servers'}} == 1) {
Expand Down Expand Up @@ -155,7 +159,11 @@ sub enable_compress {
sub forget_dead_hosts {
my Cache::Memcached $self = shift;
%host_dead = ();
$self->{'buck2sock'} = [];

# We need to globally recalculate our buck2sock in all objects, so we increment the global generation.
$socket_cache_generation++;

return 1;
}

sub set_stat_callback {
Expand All @@ -175,7 +183,9 @@ sub _dead_sock {
delete $cache_sock{$ipport};
delete $sock_map{$sock};
}
$self->{'buck2sock'} = [] if $self;
# We need to globally recalculate our buck2sock in all objects, so we increment the global generation.
$socket_cache_generation++;

return $ret; # 0 or undef, probably, depending on what caller wants
}

Expand All @@ -186,7 +196,11 @@ sub _close_sock {
delete $cache_sock{$ipport};
delete $sock_map{$sock};
}
$self->{'buck2sock'} = [];

# We need to globally recalculate our buck2sock in all objects, so we increment the global generation.
$socket_cache_generation++;

return 1;
}

sub _connect_sock { # sock, sin, timeout
Expand Down Expand Up @@ -357,7 +371,9 @@ sub disconnect_all {
close $sock;
}
%cache_sock = ();
$self->{'buck2sock'} = [];

# We need to globally recalculate our buck2sock in all objects, so we increment the global generation.
$socket_cache_generation++;
}

# writes a line, then reads result. by default stops reading after a
Expand Down Expand Up @@ -604,6 +620,12 @@ sub get_multi {
} else {
my $bcount = $self->{'bucketcount'};
my $sock;

if ($self->{'buck2sock_generation'} != $socket_cache_generation) {
$self->{'buck2sock_generation'} = $socket_cache_generation;
$self->{'buck2sock'} = [];
}

KEY:
foreach my $key (@_) {
my ($hv, $real_key) = ref $key ?
Expand Down

0 comments on commit 3039298

Please sign in to comment.