source: script/ketama-distr.pl @ dbde51

Revision dbde51, 2.8 KB checked in by Tomash Brechko <tomash.brechko@…>, 3 years ago (diff)

Better distribution of ketama points.

Feed previous point instead of sequential index.

  • Property mode set to 100755
Line 
1#! /usr/bin/perl
2# -*- cperl -*-
3#
4# Copyright (C) 2009 Tomash Brechko.  All rights reserved.
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the same terms as Perl itself, either Perl version 5.8.8
8# or, at your option, any later version of Perl 5 you may have
9# available.
10#
11use warnings;
12use strict;
13
14=head1 NAME
15
16ketama-distr.pl - compute relative distribution of keys.
17
18=head1 SYNOPSIS
19
20  ketama-distr.pl OPTIONS
21
22=head1 OPTIONS
23
24=over
25
26=item C<--ketama_points, -k NUM>
27
28B<Required, greater than zero.> Number of ketama points per server of
29weight 1.
30
31=item C<--server, -s HOST:PORT[:WEIGHT]>
32
33B<Two or more.>  Specifies a server.  May be given multiple
34times.  Default I<WEIGHT> is 1.
35
36=back
37
38=cut
39
40
41use Getopt::Long qw(:config gnu_getopt);
42use Pod::Usage;
43
44my %options;
45if (! GetOptions(\%options,
46                 qw(ketama_points|k=i server|s=s@))
47    || @ARGV || grep({ not defined } @options{qw(ketama_points server)})
48    || $options{ketama_points} <= 0 || @{$options{server}} < 2) {
49    pod2usage(1);
50}
51
52
53use String::CRC32;
54
55
56sub compute_old {
57    my ($server, $index, $prev) = @_;
58
59    $server =~ s/:/\0/;
60
61    my $point = crc32($server . pack("V", $index));
62
63    return $point;
64}
65
66
67sub compute_new {
68    my ($server, $index, $prev) = @_;
69
70    $server =~ s/:/\0/;
71
72    my $point = crc32($server . pack("V", $prev));
73
74    return $point;
75}
76
77
78sub compute {
79    my ($compute_point) = @_;
80
81    my @continuum;
82
83    my $j = 0;
84    foreach my $s (@{$options{server}}) {
85        ++$j;
86        my ($server, $weight) = $s =~ /^([^:]+:[^:]+)(?::(.+))?$/;
87
88        die "$s should be HOST:PORT" unless defined $server;
89
90        $weight = 1 unless defined $weight;
91
92        my $prev = 0;
93        for (my $i = 0; $i < $options{ketama_points} * $weight; ++$i) {
94            my $point = $compute_point->($server, $i, $prev);
95            push @continuum, [$point, "$j: $server"];
96            $prev = $point;
97        }
98    }
99
100    use sort 'stable';
101    @continuum = sort {$a->[0] <=> $b->[0]} @continuum;
102
103    my $prev_point = 0;
104    my $first_server = '';
105    my %server_share;
106    foreach my $c (@continuum) {
107        $first_server = $c->[1] unless $first_server;
108        $server_share{$c->[1]} += $c->[0] - $prev_point;
109        $prev_point = $c->[0];
110    }
111    # Wraparound case.
112    $server_share{$first_server} += 2**32 - 1 - $prev_point;
113
114    foreach my $s (sort keys %server_share) {
115        my $share = $server_share{$s};
116        printf("server %s  total = % 10u (%.2f%%)\n",
117               $s, $share, $share * 100 / (2**32 - 1));
118    }
119
120    return @continuum;
121}
122
123
124print "Old:\n";
125compute(\&compute_old);
126print "\n";
127print "New:\n";
128my $total_points = compute(\&compute_new);
129print "\n";
130my $int_size = 4;
131print "Continuum array size = ", $total_points * $int_size * 2, " bytes\n";
Note: See TracBrowser for help on using the repository browser.