File Coverage

blib/lib/MogileFS/ReplicationPolicy/MultipleDevices.pm
Criterion Covered Total %
statement 38 42 90.4
branch 10 16 62.5
condition 8 18 44.4
subroutine 6 8 75.0
pod 0 4 0.0
total 62 88 70.4


line stmt bran cond sub pod time code
1             package MogileFS::ReplicationPolicy::MultipleDevices;
2 1     1   420 use strict;
  1         1  
  1         30  
3 1     1   3 use base 'MogileFS::ReplicationPolicy';
  1         1  
  1         66  
4 1     1   5 use MogileFS::Util qw(weighted_list);
  1         1  
  1         37  
5 1     1   4 use MogileFS::ReplicationRequest qw(ALL_GOOD TOO_GOOD TEMP_NO_ANSWER);
  1         1  
  1         356  
6              
7             sub new {
8 15     15 0 121 my ($class, $mindevcount) = @_;
9 15         50 return bless {
10             mindevcount => $mindevcount,
11             }, $class;
12             }
13              
14             sub new_from_policy_args {
15 0     0 0 0 my ($class, $argref) = @_;
16             # Note: "MultipleDevices()" is okay, in which case the 'mindevcount'
17             # on the class is used. (see below)
18 0 0       0 $$argref =~ s/^\s* \( \s* (\d*) \s* \) \s*//x
19             or die "$class failed to parse args: $$argref";
20 0         0 return $class->new($1)
21             }
22              
23 0     0 0 0 sub mindevcount { $_[0]{mindevcount} }
24              
25             sub replicate_to {
26 15     15 0 72 my ($self, %args) = @_;
27              
28 15         27 my $fid = delete $args{fid}; # fid scalar to copy
29 15         21 my $on_devs = delete $args{on_devs}; # arrayref of device objects
30 15         16 my $all_devs = delete $args{all_devs}; # hashref of { devid => MogileFS::Device }
31 15         16 my $failed = delete $args{failed}; # hashref of { devid => 1 } of failed attempts this round
32              
33             # this is the per-class mindevcount (the old way), which is passed in automatically
34             # from the replication worker. but if we have our own configured mindevcount
35             # in class.replpolicy, like "MultipleHosts(3)", then we use the explicit one. otherwise,
36             # if blank, or zero, like "MultipleHosts()", then we use the builtin on
37 15         18 my $min = delete $args{min};
38 15   33     51 $min = $self->{mindevcount} || $min;
39              
40 15 50       29 warn "Unknown parameters: " . join(", ", sort keys %args) if %args;
41 15 50 33     93 die "Missing parameters" unless $on_devs && $all_devs && $failed && $fid;
      33        
      33        
42              
43             # number of devices we currently live on
44 15         14 my $already_on = @$on_devs;
45              
46 15 100       38 return ALL_GOOD if $min == $already_on;
47 11 100       27 return TOO_GOOD if $already_on > $min;
48              
49             # total disks available which are candidates for having files on them
50 8         18 my $total_disks = scalar grep { $_->dstate->should_have_files } values %$all_devs;
  36         61  
51              
52 8         10 my %on_dev = map { $_->id => 1 } @$on_devs;
  10         16  
53              
54             # if we have two copies and that's all the disks there are
55             # anywhere, be happy enough, even if mindevcount is higher. in
56             # that case, when they add more disks later, they'll need to fsck
57             # to make files replicate more.
58             # this is here instead of above in case an over replication error causes
59             # the file to be on all disks (where more than necessary)
60 8 50 66     29 return ALL_GOOD if $already_on >= 2 && $already_on == $total_disks;
61              
62 16         30 my @all_dests = sort {
63 36 100 66     63 $b->percent_free <=> $a->percent_free
64             } grep {
65 8         13 ! $on_dev{$_->devid} &&
66             ! $failed->{$_->devid} &&
67             $_->should_get_replicated_files
68             } values %$all_devs;
69              
70 8 50       15 return TEMP_NO_ANSWER unless @all_dests;
71              
72             # Do this little dance to only weight-shuffle the top end of empty devices
73 8         14 @all_dests = weighted_list(map { [$_, 100 * $_->percent_free] }
  20         29  
74             splice(@all_dests, 0, 20));
75              
76 8         33 return MogileFS::ReplicationRequest->new(
77             ideal => \@all_dests,
78             desperate => [],
79             );
80             }
81              
82             1;
83              
84             # Local Variables:
85             # mode: perl
86             # c-basic-indent: 4
87             # indent-tabs-mode: nil
88             # End:
89              
90             __END__