memcached-tool.pl 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. #!/usr/bin/perl
  2. #
  3. # memcached-tool:
  4. # stats/management tool for memcached.
  5. #
  6. # Author:
  7. # Brad Fitzpatrick <brad@danga.com>
  8. #
  9. # License:
  10. # public domain. I give up all rights to this
  11. # tool. modify and copy at will.
  12. #
  13. use strict;
  14. use IO::Socket::INET;
  15. my $host = shift;
  16. my $mode = shift || "display";
  17. my ($from, $to);
  18. if ($mode eq "display") {
  19. undef $mode if @ARGV;
  20. } elsif ($mode eq "move") {
  21. $from = shift;
  22. $to = shift;
  23. undef $mode if $from < 6 || $from > 17;
  24. undef $mode if $to < 6 || $to > 17;
  25. print STDERR "ERROR: parameters out of range\n\n" unless $mode;
  26. } elsif ($mode eq 'dump') {
  27. ;
  28. } elsif ($mode eq 'stats') {
  29. ;
  30. } else {
  31. undef $mode;
  32. }
  33. undef $mode if @ARGV;
  34. die
  35. "Usage: memcached-tool <host[:port]> [mode]\n
  36. memcached-tool 10.0.0.5:11211 display # shows slabs
  37. memcached-tool 10.0.0.5:11211 # same. (default is display)
  38. memcached-tool 10.0.0.5:11211 stats # shows general stats
  39. memcached-tool 10.0.0.5:11211 move 7 9 # takes 1MB slab from class #7
  40. # to class #9.
  41. You can only move slabs around once memory is totally allocated, and only
  42. once the target class is full. (So you can't move from #6 to #9 and #7
  43. to #9 at the same itme, since you'd have to wait for #9 to fill from
  44. the first reassigned page)
  45. " unless $host && $mode;
  46. $host .= ":11211" unless $host =~ /:\d+/;
  47. my $sock = IO::Socket::INET->new(PeerAddr => $host,
  48. Proto => 'tcp');
  49. die "Couldn't connect to $host\n" unless $sock;
  50. if ($mode eq "move") {
  51. my $tries = 0;
  52. while (1) {
  53. print $sock "slabs reassign $from $to\r\n";
  54. my $res = <$sock>;
  55. $res =~ s/\s+//;
  56. if ($res eq "DONE") {
  57. print "Success.\n";
  58. exit 0;
  59. } elsif ($res eq "CANT") {
  60. print "Error: can't move from $from to $to. Destination not yet full? See usage docs.\n";
  61. exit;
  62. } elsif ($res eq "BUSY") {
  63. if (++$tries == 3) {
  64. print "Failed to move after 3 tries. Try again later.\n";
  65. exit;
  66. }
  67. print "Page busy, retrying...\n";
  68. sleep 1;
  69. }
  70. }
  71. exit;
  72. }
  73. if ($mode eq 'dump') {
  74. my %items;
  75. my $totalitems;
  76. print $sock "stats items\r\n";
  77. while (<$sock>) {
  78. last if /^END/;
  79. if (/^STAT items:(\d*):number (\d*)/) {
  80. $items{$1} = $2;
  81. $totalitems += $2;
  82. }
  83. }
  84. print STDERR "Dumping memcache contents\n";
  85. print STDERR " Number of buckets: " . scalar(keys(%items)) . "\n";
  86. print STDERR " Number of items : $totalitems\n";
  87. foreach my $bucket (sort(keys(%items))) {
  88. print STDERR "Dumping bucket $bucket - " . $items{$bucket} . " total items\n";
  89. print $sock "stats cachedump $bucket $items{$bucket} 1\r\n";
  90. my %keyexp;
  91. while (<$sock>) {
  92. last if /^END/;
  93. # return format looks like this
  94. # ITEM foo [6 b; 1176415152 s]
  95. if (/^ITEM (\S+) \[.* (\d+) s\]/) {
  96. $keyexp{$1} = $2;
  97. }
  98. }
  99. foreach my $k (keys(%keyexp)) {
  100. my $val;
  101. print $sock "get $k\r\n";
  102. my $response = <$sock>;
  103. $response =~ /VALUE (\S+) (\d+) (\d+)/;
  104. my $flags = $2;
  105. my $len = $3;
  106. read $sock, $val , $len;
  107. # get the END
  108. $_ = <$sock>;
  109. $_ = <$sock>;
  110. print "add $k $flags $keyexp{$k} $len\r\n$val\r\n";
  111. }
  112. }
  113. exit;
  114. }
  115. if ($mode eq 'stats') {
  116. my %items;
  117. print $sock "stats\r\n";
  118. while (<$sock>) {
  119. last if /^END/;
  120. chomp;
  121. if (/^STAT\s+(\S*)\s+(.*)/) {
  122. $items{$1} = $2;
  123. }
  124. }
  125. printf ("#%-17s %5s %11s\n", $host, "Field", "Value");
  126. foreach my $name (sort(keys(%items))) {
  127. printf ("%24s %12s\n", $name, $items{$name});
  128. }
  129. exit;
  130. }
  131. # display mode:
  132. my %items; # class -> { number, age, chunk_size, chunks_per_page,
  133. # total_pages, total_chunks, used_chunks,
  134. # free_chunks, free_chunks_end }
  135. print $sock "stats items\r\n";
  136. while (<$sock>) {
  137. last if /^END/;
  138. if (/^STAT items:(\d+):(\w+) (\d+)/) {
  139. $items{$1}{$2} = $3;
  140. }
  141. }
  142. print $sock "stats slabs\r\n";
  143. while (<$sock>) {
  144. last if /^END/;
  145. if (/^STAT (\d+):(\w+) (\d+)/) {
  146. $items{$1}{$2} = $3;
  147. }
  148. }
  149. print " # Item_Size Max_age 1MB_pages Count Full?\n";
  150. foreach my $n (1..40) {
  151. my $it = $items{$n};
  152. next if (0 == $it->{total_pages});
  153. my $size = $it->{chunk_size} < 1024 ? "$it->{chunk_size} B " :
  154. sprintf("%.1f kB", $it->{chunk_size} / 1024.0);
  155. my $full = $it->{free_chunks_end} == 0 ? "yes" : " no";
  156. printf "%3d %8s %7d s %7d %7d %7s\n",
  157. $n, $size, $it->{age}, $it->{total_pages},
  158. $it->{number}, $full;
  159. }