Anybody interested in testing patch to allow weighted nodes?

Brett G. Durrett brett at imvu.com
Wed May 16 23:05:35 UTC 2007


I have attached a patch to 1.57 that will allow weighting of nodes 
specified in a nodefile.  This might be handy if your servers have a 
wide  range of performance and you want to direct less traffic to less 
powerful servers.

I am putting this out in case anybody has use for it and is willing to 
test it  :)

Some notes:

- It only works using nodefiles, not the config file node definitions or 
the configuration interface "add" or "remove" commands.
- I am running about 5% of our traffic through my production test 
server... it appears to be working well but I would not feel comfortable 
asking others to try it in production.  I have done very little 
testing... I am not sure about the behavior in failure cases, especially 
in regards to failing-out bad nodes.

- To weight a node, add whitespace and weight:<int> to the nodefile... 
for example:

10.10.10.78         weight:1
10.10.10.79         weight:1
10.10.10.124        weight:5
10.10.10.125        weight:5

- If you do not specify a weight for any node the weighting code is 
ignored. 
- If you specify a weight for some (but not all) nodes, it will assume a 
weight of "1" for nodes that did not have a weight.
- If weights are assigned, the "show pool <pool>" command will add the 
weights of all nodes to the output... if you did not use weighting, the 
standard output will be displayed.  For example:

show pool virtual_web
10.10.10.78:80 765 weight:1
10.10.10.79:80 770 weight:1
10.10.10.124:80 4007 weight:5
10.10.10.125:80 3921 weight:5

Again, this was a quick hack and I am providing this for testing... if 
your Perlbal is controlling a nuclear reactor, please don't try this.

Thanks in advance for any testing / feedback you can provide,

B-





-------------- next part --------------
Index: lib/Perlbal/Pool.pm
===================================================================
--- lib/Perlbal/Pool.pm	(revision 674)
+++ lib/Perlbal/Pool.pm	(working copy)
@@ -19,6 +19,9 @@
 use constant BM_ROUNDROBIN => 2;
 use constant BM_RANDOM => 3;
 
+# if weights used, defualt weight for non-weighted node
+use constant NODE_DEFAULT_WEIGHT => 1;
+
 use fields (
             'name',            # string; name of this pool
             'use_count',       # int; number of services using us
@@ -32,6 +35,11 @@
             'nodefile.lastmod',   # unix time nodefile was last modified
             'nodefile.lastcheck', # unix time nodefile was last stated
             'nodefile.checking',  # boolean; if true AIO is stating the file for us
+            
+            # used for weighted random
+            'node_weight',     # hashref; { ip:port => int weight }
+            'weighted_nodes',  # array FIXME 
+            'node_weight_total', # int; sum of weight of all nodes, used to flag weighted
             );
 
 sub new {
@@ -46,6 +54,8 @@
     $self->{nodes} = [];
     $self->{node_count} = 0;
     $self->{node_used} = {};
+    $self->{node_weight} = {};
+    $self->{node_weight_total} = 0;
 
     $self->{nodefile} = undef;
     $self->{balance_method} = BM_RANDOM;
@@ -120,6 +130,7 @@
     my $dataref = shift;
 
     my @nodes = split(/\r?\n/, $$dataref);
+    my $totalnodeweight = 0;
 
     # prepare for adding nodes
     $self->{nodes} = [];
@@ -130,6 +141,14 @@
         if (/(\d+\.\d+\.\d+\.\d+)(?::(\d+))?/) {
             my ($ip, $port) = ($1, $2);
             $port ||= 80;
+            if(/weight:(\d+)/){
+              my $weight = $1;
+              $self->{node_weight}->{"$ip:$port"} = $weight;
+              $totalnodeweight += $weight;
+            }
+            else{
+              $self->{node_weight}->{"$ip:$port"} = 0;
+            }
             $self->{node_used}->{"$ip:$port"} ||= 0; # set to 0 if not set
             push @{$self->{nodes}}, [ $ip, $port ];
         }
@@ -137,6 +156,30 @@
 
     # setup things using new data
     $self->{node_count} = scalar @{$self->{nodes}};
+
+    # if weight used, build array for weights, assign default to unweighted
+    if( $totalnodeweight ){
+        $self->{weighted_nodes} = [];
+        my $nodeindex = 0;
+        foreach ( @{$self->{nodes}} ){
+            my ( $ip, $port ) = @{ $_ };
+            if( not( $self->{node_weight}->{"$ip:$port"} ) ){
+                # FIXME: consider warning that some nodes not weighted
+                $self->{node_weight}->{"$ip:$port"} = NODE_DEFAULT_WEIGHT;
+                $totalnodeweight += NODE_DEFAULT_WEIGHT;
+            }
+            my $nodeweight = $self->{node_weight}->{"$ip:$port"};
+            while( $nodeweight-- > 0 ){
+              push @{$self->{weighted_nodes}}, $nodeindex;
+            }
+            $nodeindex++;
+        }
+        $self->{node_weight_total} = $totalnodeweight;
+    }
+    else{
+        # Set total weight to zero to disable weighting
+        $self->{node_weight_total} = 0;
+    }
 }
 
 sub _load_nodefile_sync {
@@ -225,8 +268,14 @@
     # no nodes?
     return () unless $self->{node_count};
 
-    # pick one randomly
-    return @{$self->{nodes}[int(rand($self->{node_count}))]};
+    if( not $self->{node_weight_total} ){
+        # pick one randomly
+        return @{$self->{nodes}[int(rand($self->{node_count}))]};
+    }
+    else{
+        # use weighted random
+        return @{$self->{nodes}[$self->{weighted_nodes}[int(rand($self->{node_weight_total}))]]};
+    }
 }
 
 sub backend_should_live {
@@ -270,6 +319,16 @@
     $self->{use_count}--;
 }
 
+sub node_weight {
+    my Perlbal::Pool $self = $_[0];
+    return $self->{node_weight}->{$_[1]};
+}
+
+sub node_weight_total {
+    my Perlbal::Pool $self = $_[0];
+    return $self->{node_weight_total};
+}
+
 sub name {
     my Perlbal::Pool $self = $_[0];
     return $self->{name};
Index: lib/Perlbal.pm
===================================================================
--- lib/Perlbal.pm	(revision 674)
+++ lib/Perlbal.pm	(working copy)
@@ -807,7 +807,14 @@
 
             foreach my $node (@{ $pl->nodes }) {
                 my $ipport = "$node->[0]:$node->[1]";
-                $mc->out($ipport . " " . $pl->node_used($ipport));
+                if( not $pl->node_weight_total() ){
+                    # Not using weights, standard output
+                    $mc->out($ipport . " " . $pl->node_used($ipport));
+                }
+                else{
+                    # Weights in use, add weight to output
+                    $mc->out($ipport . " " . $pl->node_used($ipport) . " weight:" . $pl->node_weight($ipport));
+                }
             }
         } else {
             foreach my $name (sort keys %pool) {


More information about the perlbal mailing list