package Net::Paraget::AssignmentManager; # # $Id: AssignmentManager.pm,v 1.7 2001/05/10 04:33:48 lrclause Exp $ # use strict; use Carp; use Net::Paraget::Assignment; use Net::Paraget::Interval; use Class::MethodMaker get_set => [ qw( context file min_splittable_eta ) ], new_hash_init => [ qw( new hash_init ) ]; sub state { my ( $self, $string, $priority ) = @_; main::state( "assignment manager: " . $string, $priority ); } sub get_n_assignments { my ( $self, $n ) = @_; my $context = $self->context(); my $server_queue = $context->server_queue(); my @assignments = (); while ( $n > 0 ) { my ( $server, $interval ) = $self->find_best_match(); last unless defined $server and defined $interval; my $split_at = $self->find_split_offset( $interval, $server ); $self->state( "Best match " . $server->info() . " with interval " . $interval->info() . " starting at $split_at", 3 ); last if $split_at >= $interval->end(); my $to_assign = $interval->split( $split_at ); my $mirror = $server->get_mirror(); $server_queue->remove( $server ); my $assignment = Net::Paraget::Assignment->new( mirror => $mirror, interval => $to_assign, file => $self->file, server => $server, ); $self->state( "created assignment " . $assignment->info(), 3 ); $to_assign->hash_init( assignment => $assignment, completed => 1, ); push @assignments, $assignment; $n--; } return @assignments; } sub find_split_offset { my ( $self, $interval, $server ) = @_; my $prev = $interval->prev(); return 0 unless $prev->assignment(); my $prev_speed = 0; my $prev_assignment = $prev->assignment(); $prev_speed = $prev_assignment->server->a_speed() if $prev_assignment; my $time = $server->time_over_interval( $interval ); my $offset = int ( $interval->start() + $time * $prev_speed ); return $offset; } sub interval_can_be_split { my ( $self, $interval ) = @_; my $estimation = $interval->eta(); my $min_eta = $self->min_splittable_eta(); return 1 if not defined $estimation; my $x = $min_eta <= $estimation; if ( not $x ) { $self->state( "interval cannot be split: eta of $estimation is less than minimal $min_eta", 3 ); } return $min_eta <= $estimation; } sub pick_untried_server { my ($self) = @_; foreach my $server ( $self->context()->server_queue()->servers() ) { return $server if not $server->tries() and not $server->disabled(); } croak "Internal error: No untried servers in pick_untried_server"; } sub pick_server_to_refresh { my ($self) = @_; my $oldest; foreach my $server ( $self->context->server_queue()->servers() ) { $oldest = $server if (not defined $oldest or not defined $server->last_try() or (defined $oldest->last_try() and $server->last_try() < $oldest->last_try())); } return $oldest; } sub pick_fastest_server { my ($self) = @_; my $best_server; foreach my $server ( $self->context->server_queue->servers() ) { $best_server = $server if (not defined $best_server and defined $server->speed()); if ($server->speed() and $server->speed() > $best_server->speed()) { $best_server = $server; } } return $best_server; } sub pick_slowest_interval { my ($self) = @_; my $best_interval; foreach my $interval ( $self->context->interval_manager->uncompleted() ) { next unless $self->interval_can_be_split($interval); $best_interval = $interval if not defined $best_interval; if (not $interval->prev()->assignment()) { if ($best_interval->assignment()) { if ($best_interval->size() < $interval->size()) { $best_interval = $interval; } } else { $best_interval = $interval; } } else { next if not $best_interval->prev()->assignment(); next if not defined $interval->prev()->assignment()->client->estimated_time_to_completion(); next if not defined $best_interval->prev()->assignment()->client->estimated_time_to_completion(); if ($interval->prev()->assignment()->client()->estimated_time_to_completion() > $best_interval->prev()->assignment()->client()->estimated_time_to_completion()) { $best_interval = $interval; } } } return $best_interval; } sub pick_server_select_method { my ($self) = @_; my $num_untried = 0; foreach my $server ( $self->context->server_queue->servers() ) { $num_untried++ if not $server->disabled() and not $server->tries(); } if ($num_untried > 0) { my $probability = $num_untried / $self->context->server_queue->servers(); if ($probability < 0.25) { $probability = 0.25; } return "pick_untried_server" if $probability > rand(); } return "pick_server_to_refresh" if rand() > 0.10; return "pick_fastest_server"; } sub find_best_match { my ( $self ) = @_; my $best_server; my $best_interval; $best_interval = $self->pick_slowest_interval(); return undef, undef unless defined $best_interval; my $server_method = $self->pick_server_select_method(); $best_server = $self->$server_method(); return undef, undef if not defined $best_server; main::state("Found ".$best_server->as_string()." using $server_method", 1); return $best_server, $best_interval; } sub complex_find_best_match { my ( $self ) = @_; my $optimal_time = $self->optimal_time(); return unless defined $optimal_time; my $best_diff = undef; my $best_server; my $best_interval; foreach my $server ( $self->context->server_queue->servers() ) { foreach my $interval ( $self->context->interval_manager->uncompleted() ) { $self->state ("Testing for " . $server->id() . " vs interval " . $interval->info(), 4 ); next unless $self->interval_can_be_split($interval); my $my_time = $server->time_over_interval( $interval ); $self->state( "est time is $my_time", 4 ); my $diff = abs( $optimal_time - $my_time ); if ( not defined $best_diff or $diff < $best_diff ) { $best_diff = $diff; $best_server = $server; $best_interval = $interval; } } } return $best_server, $best_interval; } sub optimal_time { my ( $self ) = @_; my $total_server_speed = $self->total_server_speed() or return undef; my $total_uncompleted_size = $self->context->interval_manager->total_uncompleted_size(); return $total_uncompleted_size / $total_server_speed; } sub total_server_speed { my ( $self ) = @_; my $server_list = $self->context->server_list(); my $speed = 0; foreach my $server ( $self->context->server_queue->servers() ) { $speed += $server->a_speed() unless $server->disabled(); } return $speed; } 1;