Blame scripts/memcached-tool

Packit 4e8bc4
#!/usr/bin/perl
Packit 4e8bc4
#
Packit 4e8bc4
# memcached-tool:
Packit 4e8bc4
#   stats/management tool for memcached.
Packit 4e8bc4
#
Packit 4e8bc4
# Author:
Packit 4e8bc4
#   Brad Fitzpatrick <brad@danga.com>
Packit 4e8bc4
#
Packit 4e8bc4
# Contributor:
Packit 4e8bc4
#   Andrey Niakhaichyk <andrey@niakhaichyk.org>
Packit 4e8bc4
#
Packit 4e8bc4
# License:
Packit 4e8bc4
#   public domain.  I give up all rights to this
Packit 4e8bc4
#   tool.  modify and copy at will.
Packit 4e8bc4
#
Packit 4e8bc4
Packit 4e8bc4
use strict;
Packit 4e8bc4
use IO::Socket::INET;
Packit 4e8bc4
Packit 4e8bc4
my $addr = shift;
Packit 4e8bc4
my $mode = shift || "display";
Packit 4e8bc4
my ($from, $to);
Packit 4e8bc4
my $limit;
Packit 4e8bc4
Packit 4e8bc4
if ($mode eq "display") {
Packit 4e8bc4
    undef $mode if @ARGV;
Packit 4e8bc4
} elsif ($mode eq "move") {
Packit 4e8bc4
    $from = shift;
Packit 4e8bc4
    $to = shift;
Packit 4e8bc4
    undef $mode if $from < 6 || $from > 17;
Packit 4e8bc4
    undef $mode if $to   < 6 || $to   > 17;
Packit 4e8bc4
    print STDERR "ERROR: parameters out of range\n\n" unless $mode;
Packit 4e8bc4
} elsif ($mode eq 'dump') {
Packit 4e8bc4
    if (@ARGV) {
Packit 4e8bc4
        $limit = shift;
Packit 4e8bc4
        undef $mode if $limit < 1;
Packit 4e8bc4
        print STDERR "ERROR: invalid limit (should be a positive number)\n\n" unless $mode;
Packit 4e8bc4
    }
Packit 4e8bc4
} elsif ($mode eq 'stats') {
Packit 4e8bc4
    ;
Packit 4e8bc4
} elsif ($mode eq 'settings') {
Packit 4e8bc4
    ;
Packit 4e8bc4
} elsif ($mode eq 'sizes') {
Packit 4e8bc4
    ;
Packit 4e8bc4
} else {
Packit 4e8bc4
    undef $mode;
Packit 4e8bc4
}
Packit 4e8bc4
Packit 4e8bc4
undef $mode if @ARGV;
Packit 4e8bc4
Packit 4e8bc4
die
Packit 4e8bc4
    "Usage: memcached-tool <host[:port] | /path/to/socket> [mode]\n
Packit 4e8bc4
       memcached-tool 10.0.0.5:11211 display        # shows slabs
Packit 4e8bc4
       memcached-tool 10.0.0.5:11211                # same.  (default is display)
Packit 4e8bc4
       memcached-tool 10.0.0.5:11211 stats          # shows general stats
Packit 4e8bc4
       memcached-tool 10.0.0.5:11211 settings       # shows settings stats
Packit 4e8bc4
       memcached-tool 10.0.0.5:11211 sizes          # shows sizes stats
Packit 4e8bc4
       memcached-tool 10.0.0.5:11211 dump [limit]   # dumps keys and values
Packit 4e8bc4
Packit 4e8bc4
WARNING! sizes is a development command.
Packit 4e8bc4
As of 1.4 it is still the only command which will lock your memcached instance for some time.
Packit 4e8bc4
If you have many millions of stored items, it can become unresponsive for several minutes.
Packit 4e8bc4
Run this at your own risk. It is roadmapped to either make this feature optional
Packit 4e8bc4
or at least speed it up.
Packit 4e8bc4
" unless $addr && $mode;
Packit 4e8bc4
Packit 4e8bc4
Packit 4e8bc4
sub server_connect {
Packit 4e8bc4
    my $sock;
Packit 4e8bc4
    if ($addr =~ m:/:) {
Packit 4e8bc4
        $sock = IO::Socket::UNIX->new(
Packit 4e8bc4
            Peer => $addr,
Packit 4e8bc4
        );
Packit 4e8bc4
    }
Packit 4e8bc4
    else {
Packit 4e8bc4
        $addr .= ':11211' unless $addr =~ /:\d+$/;
Packit 4e8bc4
Packit 4e8bc4
        $sock = IO::Socket::INET->new(
Packit 4e8bc4
            PeerAddr => $addr,
Packit 4e8bc4
            Proto    => 'tcp',
Packit 4e8bc4
        );
Packit 4e8bc4
    }
Packit 4e8bc4
    die "Couldn't connect to $addr\n" unless $sock;
Packit 4e8bc4
    return $sock;
Packit 4e8bc4
}
Packit 4e8bc4
Packit 4e8bc4
my $sock = server_connect();
Packit 4e8bc4
Packit 4e8bc4
if ($mode eq 'dump') {
Packit 4e8bc4
    print STDERR "Dumping memcache contents";
Packit 4e8bc4
    print STDERR " (limiting to $limit keys)" unless !$limit;
Packit 4e8bc4
    print STDERR "\n";
Packit 4e8bc4
    print $sock "lru_crawler metadump all\r\n";
Packit 4e8bc4
    my %keyexp;
Packit 4e8bc4
    my $keycount = 0;
Packit 4e8bc4
    while (<$sock>) {
Packit 4e8bc4
        last if /^END/ or ($limit and $keycount == $limit);
Packit 4e8bc4
        # return format looks like this
Packit 4e8bc4
        # key=foo exp=2147483647 la=1521046038 cas=717111 fetch=no cls=13 size=1232
Packit 4e8bc4
        if (/^key=(\S+) exp=(-?\d+) .*/) {
Packit 4e8bc4
            my ($k, $exp) = ($1, $2);
Packit 4e8bc4
            $k =~ s/%(.{2})/chr hex $1/eg;
Packit 4e8bc4
Packit 4e8bc4
            if ($exp == -1) {
Packit 4e8bc4
                $keyexp{$k} = 0;
Packit 4e8bc4
            } else {
Packit 4e8bc4
                $keyexp{$k} = $exp;
Packit 4e8bc4
            }
Packit 4e8bc4
        }
Packit 4e8bc4
        $keycount++;
Packit 4e8bc4
    }
Packit 4e8bc4
Packit 4e8bc4
    if ($limit) {
Packit 4e8bc4
        # Need to reopen the connection here to stop the metadump in
Packit 4e8bc4
        # case the key limit was reached.
Packit 4e8bc4
        #
Packit 4e8bc4
        # XXX: Once a limit on # of keys returned is introduced in
Packit 4e8bc4
        # `lru_crawler metadump`, this should be removed and the proper
Packit 4e8bc4
        # parameter passed in the query above.
Packit 4e8bc4
        close($sock);
Packit 4e8bc4
        $sock = server_connect();
Packit 4e8bc4
    }
Packit 4e8bc4
Packit 4e8bc4
    foreach my $k (keys(%keyexp)) {
Packit 4e8bc4
        print $sock "get $k\r\n";
Packit 4e8bc4
        my $response = <$sock>;
Packit 4e8bc4
        if ($response =~ /VALUE (\S+) (\d+) (\d+)/) {
Packit 4e8bc4
            my $flags = $2;
Packit 4e8bc4
            my $len = $3;
Packit 4e8bc4
            my $val;
Packit 4e8bc4
            read $sock, $val, $len;
Packit 4e8bc4
            print "add $k $flags $keyexp{$k} $len\r\n$val\r\n";
Packit 4e8bc4
            # get the END
Packit 4e8bc4
            $_ = <$sock>;
Packit 4e8bc4
            $_ = <$sock>;
Packit 4e8bc4
        }
Packit 4e8bc4
    }
Packit 4e8bc4
    exit;
Packit 4e8bc4
}
Packit 4e8bc4
Packit 4e8bc4
if ($mode eq 'stats') {
Packit 4e8bc4
    my %items;
Packit 4e8bc4
Packit 4e8bc4
    print $sock "stats\r\n";
Packit 4e8bc4
Packit 4e8bc4
    while (<$sock>) {
Packit 4e8bc4
        last if /^END/;
Packit 4e8bc4
        chomp;
Packit 4e8bc4
        if (/^STAT\s+(\S*)\s+(.*)/) {
Packit 4e8bc4
            $items{$1} = $2;
Packit 4e8bc4
        }
Packit 4e8bc4
    }
Packit 4e8bc4
    printf ("#%-22s %5s %13s\n", $addr, "Field", "Value");
Packit 4e8bc4
    foreach my $name (sort(keys(%items))) {
Packit 4e8bc4
        printf ("%29s %14s\n", $name, $items{$name});
Packit 4e8bc4
Packit 4e8bc4
    }
Packit 4e8bc4
    exit;
Packit 4e8bc4
}
Packit 4e8bc4
Packit 4e8bc4
if ($mode eq 'settings') {
Packit 4e8bc4
    my %items;
Packit 4e8bc4
Packit 4e8bc4
    print $sock "stats settings\r\n";
Packit 4e8bc4
Packit 4e8bc4
    while (<$sock>) {
Packit 4e8bc4
        last if /^END/;
Packit 4e8bc4
        chomp;
Packit 4e8bc4
        if (/^STAT\s+(\S*)\s+(.*)/) {
Packit 4e8bc4
            $items{$1} = $2;
Packit 4e8bc4
        }
Packit 4e8bc4
    }
Packit 4e8bc4
    printf ("#%-17s %5s %11s\n", $addr, "Field", "Value");
Packit 4e8bc4
    foreach my $name (sort(keys(%items))) {
Packit 4e8bc4
        printf ("%24s %12s\n", $name, $items{$name});
Packit 4e8bc4
    }
Packit 4e8bc4
    exit;
Packit 4e8bc4
}
Packit 4e8bc4
Packit 4e8bc4
Packit 4e8bc4
if ($mode eq 'sizes') {
Packit 4e8bc4
    my %items;
Packit 4e8bc4
Packit 4e8bc4
    print $sock "stats sizes\r\n";
Packit 4e8bc4
Packit 4e8bc4
    while (<$sock>) {
Packit 4e8bc4
        last if /^END/;
Packit 4e8bc4
        chomp;
Packit 4e8bc4
        if (/^STAT\s+(\S*)\s+(.*)/) {
Packit 4e8bc4
            $items{$1} = $2;
Packit 4e8bc4
        }
Packit 4e8bc4
    }
Packit 4e8bc4
    printf ("#%-17s %5s %11s\n", $addr, "Size", "Count");
Packit 4e8bc4
    foreach my $name (sort(keys(%items))) {
Packit 4e8bc4
        printf ("%24s %12s\n", $name, $items{$name});
Packit 4e8bc4
    }
Packit 4e8bc4
    exit;
Packit 4e8bc4
}
Packit 4e8bc4
Packit 4e8bc4
# display mode:
Packit 4e8bc4
Packit 4e8bc4
my %items;  # class -> { number, age, chunk_size, chunks_per_page,
Packit 4e8bc4
#            total_pages, total_chunks, used_chunks,
Packit 4e8bc4
#            free_chunks, free_chunks_end }
Packit 4e8bc4
Packit 4e8bc4
print $sock "stats items\r\n";
Packit 4e8bc4
my $max = 0;
Packit 4e8bc4
while (<$sock>) {
Packit 4e8bc4
    last if /^END/;
Packit 4e8bc4
    if (/^STAT items:(\d+):(\w+) (\d+)/) {
Packit 4e8bc4
        $items{$1}{$2} = $3;
Packit 4e8bc4
    }
Packit 4e8bc4
}
Packit 4e8bc4
Packit 4e8bc4
print $sock "stats slabs\r\n";
Packit 4e8bc4
while (<$sock>) {
Packit 4e8bc4
    last if /^END/;
Packit 4e8bc4
    if (/^STAT (\d+):(\w+) (\d+)/) {
Packit 4e8bc4
        $items{$1}{$2} = $3;
Packit 4e8bc4
        $max = $1;
Packit 4e8bc4
    }
Packit 4e8bc4
}
Packit 4e8bc4
Packit 4e8bc4
print "  #  Item_Size  Max_age   Pages   Count   Full?  Evicted Evict_Time OOM\n";
Packit 4e8bc4
foreach my $n (1..$max) {
Packit 4e8bc4
    my $it = $items{$n};
Packit 4e8bc4
    next if (0 == $it->{total_pages});
Packit 4e8bc4
    my $size = $it->{chunk_size} < 1024 ?
Packit 4e8bc4
        "$it->{chunk_size}B" :
Packit 4e8bc4
        sprintf("%.1fK", $it->{chunk_size} / 1024.0);
Packit 4e8bc4
    my $full = $it->{used_chunks} == $it->{total_chunks} ? "yes" : " no";
Packit 4e8bc4
    printf("%3d %8s %9ds %7d %7d %7s %8d %8d %4d\n",
Packit 4e8bc4
           $n, $size, $it->{age}, $it->{total_pages},
Packit 4e8bc4
           $it->{number}, $full, $it->{evicted},
Packit 4e8bc4
           $it->{evicted_time}, $it->{outofmemory});
Packit 4e8bc4
}
Packit 4e8bc4