diff --git a/Makefile.PL b/Makefile.PL index 56a26b7..8c49386 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -21,6 +21,7 @@ WriteMakefile( 'LWP::Simple' => 0, fields => 0, 'Test::More' => 0, + 'List::Util' => 0, }, ); diff --git a/lib/MogileFS/Backend.pm b/lib/MogileFS/Backend.pm index df30090..fc89ba3 100644 --- a/lib/MogileFS/Backend.pm +++ b/lib/MogileFS/Backend.pm @@ -9,6 +9,7 @@ use Socket qw( MSG_NOSIGNAL PF_INET IPPROTO_TCP SOCK_STREAM ); use Errno qw( EINPROGRESS EWOULDBLOCK EISCONN ); use POSIX (); use MogileFS::Client; +use List::Util qw/ shuffle /; use fields ('hosts', # arrayref of "$host:$port" of mogilefsd servers 'host_dead', # "$host:$port" -> $time (of last connect failure) @@ -18,6 +19,7 @@ use fields ('hosts', # arrayref of "$host:$port" of mogilefsd servers 'pref_ip', # hashref; { ip => preferred ip } 'timeout', # time in seconds to allow sockets to become readable 'last_host_connected', # "ip:port" of last host connected to + 'last_host_idx', # array index of the last host we connected to 'hooks', # hash: hookname -> coderef ); @@ -59,6 +61,8 @@ sub _init { $self->{timeout} = $args{timeout} || 3; } + $self->{hosts} = [ shuffle(@{ $self->{hosts} }) ]; + $self->{host_dead} = {}; return $self; @@ -220,6 +224,12 @@ sub err { return $self->{lasterr} ? 1 : 0; } +sub force_disconnect { + my MogileFS::Backend $self = shift; + undef $self->{sock_cache}; + return; +} + ################################################################################ # MogileFS::Backend class methods # @@ -310,12 +320,16 @@ sub _get_sock { my $size = scalar(@{$self->{hosts}}); my $tries = $size > 15 ? 15 : $size; - my $idx = int(rand() * $size); + + unless (defined($self->{last_host_idx})) { + $self->{last_host_idx} = int(rand() * $size); + } my $now = time(); my $sock; foreach (1..$tries) { - my $host = $self->{hosts}->[$idx++ % $size]; + $self->{last_host_idx} = ($self->{last_host_idx}+1) % $size; + my $host = $self->{hosts}->[$self->{last_host_idx}]; # try dead hosts every 5 seconds next if $self->{host_dead}->{$host} && diff --git a/lib/MogileFS/Client.pm b/lib/MogileFS/Client.pm index ec61c87..ec162b7 100644 --- a/lib/MogileFS/Client.pm +++ b/lib/MogileFS/Client.pm @@ -185,6 +185,20 @@ sub errcode { return $self->{backend}->errcode; } +=head2 force_disconnect + +Forces the client to disconnect from the tracker, causing it to reconnect +when the next request is made. It will reconnect to a different tracker if +possible. A paranoid application may wish to do to this before retrying a +failed command, on the off chance that another tracker may be working better. + +=cut + +sub force_disconnect { + my MogileFS::Client $self = shift; + return $self->{backend}->force_disconnect(); +} + =head2 readonly $is_readonly = $mogc->readonly diff --git a/t/30-disconnect.t b/t/30-disconnect.t new file mode 100644 index 0000000..6f2e3f5 --- /dev/null +++ b/t/30-disconnect.t @@ -0,0 +1,20 @@ +#!/usr/bin/perl -w + +use strict; +use Test::More; +use MogileFS::Client; + +my $obj = bless({ + backend => bless({ + }, 'MogileFS::Backend') +}, 'MogileFS::Client'); + +isa_ok($obj, 'MogileFS::Client'); + +$obj->{backend}->{sock_cache} = 'x'; +is($obj->{backend}->{sock_cache}, 'x'); + +$obj->force_disconnect(); +is($obj->{backend}->{sock_cache}, undef); + +done_testing();