| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package App::Memcached::CLI::Main; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 2 |  |  | 2 |  | 54360 | use strict; | 
|  | 2 |  |  |  |  | 12 |  | 
|  | 2 |  |  |  |  | 53 |  | 
| 4 | 2 |  |  | 2 |  | 8 | use warnings; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 39 |  | 
| 5 | 2 |  |  | 2 |  | 28 | use 5.008_001; | 
|  | 2 |  |  |  |  | 6 |  | 
| 6 |  |  |  |  |  |  |  | 
| 7 | 2 |  |  | 2 |  | 9 | use Carp; | 
|  | 2 |  |  |  |  | 2 |  | 
|  | 2 |  |  |  |  | 97 |  | 
| 8 | 2 |  |  | 2 |  | 11 | use File::Basename 'basename'; | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 131 |  | 
| 9 | 2 |  |  | 2 |  | 1175 | use Getopt::Long qw(:config posix_default no_ignore_case no_ignore_case_always); | 
|  | 2 |  |  |  |  | 18305 |  | 
|  | 2 |  |  |  |  | 7 |  | 
| 10 | 2 |  |  | 2 |  | 1196 | use IO::Socket::INET; | 
|  | 2 |  |  |  |  | 32173 |  | 
|  | 2 |  |  |  |  | 9 |  | 
| 11 | 2 |  |  | 2 |  | 721 | use List::Util qw(first); | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 156 |  | 
| 12 | 2 |  |  | 2 |  | 813 | use Term::ReadLine; | 
|  | 2 |  |  |  |  | 4105 |  | 
|  | 2 |  |  |  |  | 53 |  | 
| 13 | 2 |  |  | 2 |  | 465 | use Time::HiRes; | 
|  | 2 |  |  |  |  | 1033 |  | 
|  | 2 |  |  |  |  | 7 |  | 
| 14 |  |  |  |  |  |  |  | 
| 15 | 2 |  |  | 2 |  | 496 | use App::Memcached::CLI; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 49 |  | 
| 16 | 2 |  |  | 2 |  | 694 | use App::Memcached::CLI::DataSource; | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 62 |  | 
| 17 | 2 |  |  | 2 |  | 676 | use App::Memcached::CLI::Help; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 55 |  | 
| 18 | 2 |  |  | 2 |  | 335 | use App::Memcached::CLI::Item; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 43 |  | 
| 19 | 2 |  |  | 2 |  | 26 | use App::Memcached::CLI::Util ':all'; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 164 |  | 
| 20 |  |  |  |  |  |  |  | 
| 21 | 2 |  |  | 2 |  | 11 | use version; our $VERSION = 'v0.9.5'; | 
|  | 2 |  |  |  |  | 2 |  | 
|  | 2 |  |  |  |  | 8 |  | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | my $PROGRAM = basename $0; | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | my %COMMAND2ALIASES = ( | 
| 26 |  |  |  |  |  |  | help         => ['\h'], | 
| 27 |  |  |  |  |  |  | version      => ['\v'], | 
| 28 |  |  |  |  |  |  | quit         => [qw(\q exit)], | 
| 29 |  |  |  |  |  |  | display      => [qw(\d)], | 
| 30 |  |  |  |  |  |  | stats        => [qw(\s)], | 
| 31 |  |  |  |  |  |  | settings     => [qw(\c config)], | 
| 32 |  |  |  |  |  |  | cachedump    => [qw(\cd)], | 
| 33 |  |  |  |  |  |  | detaildump   => [qw(\dd)], | 
| 34 |  |  |  |  |  |  | detail       => [], | 
| 35 |  |  |  |  |  |  | dump_all     => [], | 
| 36 |  |  |  |  |  |  | restore_dump => [], | 
| 37 |  |  |  |  |  |  | randomset    => [qw(sample)], | 
| 38 |  |  |  |  |  |  | get          => [], | 
| 39 |  |  |  |  |  |  | gets         => [], | 
| 40 |  |  |  |  |  |  | set          => [], | 
| 41 |  |  |  |  |  |  | add          => [], | 
| 42 |  |  |  |  |  |  | replace      => [], | 
| 43 |  |  |  |  |  |  | append       => [], | 
| 44 |  |  |  |  |  |  | prepend      => [], | 
| 45 |  |  |  |  |  |  | cas          => [], | 
| 46 |  |  |  |  |  |  | incr         => [], | 
| 47 |  |  |  |  |  |  | decr         => [], | 
| 48 |  |  |  |  |  |  | touch        => [], | 
| 49 |  |  |  |  |  |  | delete       => [], | 
| 50 |  |  |  |  |  |  | flush_all    => [qw(flush)], | 
| 51 |  |  |  |  |  |  | call         => [], | 
| 52 |  |  |  |  |  |  | ); | 
| 53 |  |  |  |  |  |  | my %COMMAND_OF; | 
| 54 |  |  |  |  |  |  | while (my ($cmd, $aliases) = each %COMMAND2ALIASES) { | 
| 55 |  |  |  |  |  |  | $COMMAND_OF{$cmd} = $cmd; | 
| 56 |  |  |  |  |  |  | $COMMAND_OF{$_}   = $cmd for @$aliases; | 
| 57 |  |  |  |  |  |  | } | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | my $DEFAULT_CACHEDUMP_SIZE = 20; | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | sub new { | 
| 62 | 0 |  |  | 0 | 0 | 0 | my $class  = shift; | 
| 63 | 0 |  |  |  |  | 0 | my %params = @_; | 
| 64 |  |  |  |  |  |  |  | 
| 65 | 0 |  |  |  |  | 0 | eval { | 
| 66 |  |  |  |  |  |  | $params{ds} | 
| 67 |  |  |  |  |  |  | = App::Memcached::CLI::DataSource->connect( | 
| 68 |  |  |  |  |  |  | $params{addr}, timeout => $params{timeout} | 
| 69 | 0 |  |  |  |  | 0 | ); | 
| 70 |  |  |  |  |  |  | }; | 
| 71 | 0 | 0 |  |  |  | 0 | if ($@) { | 
| 72 | 0 |  |  |  |  | 0 | warn "Can't connect to Memcached server! Addr=$params{addr}"; | 
| 73 | 0 |  |  |  |  | 0 | debug "ERROR: " . $@; | 
| 74 | 0 |  |  |  |  | 0 | return; | 
| 75 |  |  |  |  |  |  | } | 
| 76 |  |  |  |  |  |  |  | 
| 77 | 0 |  |  |  |  | 0 | bless \%params, $class; | 
| 78 |  |  |  |  |  |  | } | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | sub parse_args { | 
| 81 | 6 |  |  | 6 | 0 | 7435 | my $class = shift; | 
| 82 |  |  |  |  |  |  |  | 
| 83 | 6 |  |  |  |  | 8 | my %params; # will be passed to new() | 
| 84 | 6 | 100 | 66 |  |  | 38 | if (defined $ARGV[0] and looks_like_addr($ARGV[0])) { | 
| 85 | 4 |  |  |  |  | 19 | $params{addr} = shift @ARGV; | 
| 86 |  |  |  |  |  |  | } | 
| 87 |  |  |  |  |  |  | GetOptions( | 
| 88 | 6 | 50 |  |  |  | 38 | \my %opts, 'addr|a=s', 'timeout|t=i', | 
| 89 |  |  |  |  |  |  | 'debug|d', 'help|h', 'man', | 
| 90 |  |  |  |  |  |  | ) or return +{}; | 
| 91 |  |  |  |  |  |  |  | 
| 92 | 6 | 50 |  |  |  | 2213 | if (defined $opts{debug}) { | 
| 93 | 0 |  |  |  |  | 0 | $App::Memcached::CLI::DEBUG = 1; | 
| 94 |  |  |  |  |  |  | } | 
| 95 |  |  |  |  |  |  |  | 
| 96 | 6 |  |  |  |  | 25 | %params = (%opts, %params); | 
| 97 | 6 |  |  |  |  | 21 | $params{addr} = create_addr($params{addr}); | 
| 98 |  |  |  |  |  |  |  | 
| 99 | 6 |  |  |  |  | 23 | return \%params; | 
| 100 |  |  |  |  |  |  | } | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | sub run { | 
| 103 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 104 | 0 | 0 |  |  |  |  | if (@ARGV) { | 
| 105 | 0 |  |  |  |  |  | $self->run_batch; | 
| 106 |  |  |  |  |  |  | } else { | 
| 107 | 0 |  |  |  |  |  | $self->run_interactive; | 
| 108 |  |  |  |  |  |  | } | 
| 109 |  |  |  |  |  |  | } | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | sub run_batch { | 
| 112 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 113 | 0 | 0 |  |  |  |  | debug "Run batch mode with @ARGV" if (@ARGV); | 
| 114 | 0 |  |  |  |  |  | my ($_command, @args) = @ARGV; | 
| 115 | 0 |  |  |  |  |  | my $command = $COMMAND_OF{$_command}; | 
| 116 | 0 | 0 | 0 |  |  |  | unless ($command) { | 
| 117 | 0 |  |  |  |  |  | print "Unknown command - $_command\n"; | 
| 118 | 0 |  |  |  |  |  | return; | 
| 119 |  |  |  |  |  |  | } elsif ($command eq 'quit') { | 
| 120 |  |  |  |  |  |  | print "Nothing to do with $_command\n"; | 
| 121 |  |  |  |  |  |  | return; | 
| 122 |  |  |  |  |  |  | } | 
| 123 |  |  |  |  |  |  |  | 
| 124 | 0 |  |  |  |  |  | my $ret = $self->$command(@args); | 
| 125 | 0 | 0 |  |  |  |  | unless ($ret) { | 
| 126 | 0 |  |  |  |  |  | print qq[Command seems failed. Run \`$PROGRAM help\` or \`$PROGRAM help $command\` for usage.\n]; | 
| 127 |  |  |  |  |  |  | } | 
| 128 |  |  |  |  |  |  | } | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | sub run_interactive { | 
| 131 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 132 | 0 |  |  |  |  |  | debug "Start interactive mode. $self->{addr}"; | 
| 133 | 0 |  | 0 |  |  |  | my $isa_tty = -t STDIN && (-t STDOUT || !(-f STDOUT || -c STDOUT)); | 
| 134 | 0 | 0 |  |  |  |  | unless ($isa_tty) { | 
| 135 | 0 |  |  |  |  |  | croak "TTY Not Found! Quit."; | 
| 136 |  |  |  |  |  |  | } | 
| 137 | 0 |  |  |  |  |  | my $exit_loop = 0; | 
| 138 |  |  |  |  |  |  | local $SIG{INT} = local $SIG{QUIT} = sub { | 
| 139 | 0 |  |  | 0 |  |  | $exit_loop = 1; | 
| 140 | 0 |  |  |  |  |  | warn "Caught INT or QUIT. Exiting..."; | 
| 141 | 0 |  |  |  |  |  | }; | 
| 142 |  |  |  |  |  |  |  | 
| 143 | 0 |  |  |  |  |  | $self->{term} = Term::ReadLine->new($PROGRAM); | 
| 144 | 0 |  |  |  |  |  | print "Type '\\h' or 'help' to show help.\n\n"; | 
| 145 | 0 |  |  |  |  |  | while (! $exit_loop) { | 
| 146 | 0 |  |  |  |  |  | my ($command, @args) = $self->prompt; | 
| 147 | 0 | 0 |  |  |  |  | next unless $command; | 
| 148 | 0 | 0 |  |  |  |  | if ($command eq 'quit') { | 
| 149 | 0 |  |  |  |  |  | $exit_loop = 1; | 
| 150 | 0 |  |  |  |  |  | next; | 
| 151 |  |  |  |  |  |  | } | 
| 152 | 0 | 0 |  |  |  |  | unless ($self->{ds}->ping) { | 
| 153 | 0 |  |  |  |  |  | print "Server has gone away.\n"; | 
| 154 | 0 |  |  |  |  |  | $exit_loop = 1; | 
| 155 | 0 |  |  |  |  |  | next; | 
| 156 |  |  |  |  |  |  | } | 
| 157 |  |  |  |  |  |  |  | 
| 158 | 0 |  |  |  |  |  | my $ret = $self->$command(@args); | 
| 159 | 0 | 0 |  |  |  |  | unless ($ret) { | 
| 160 | 0 |  |  |  |  |  | print "Command seems failed. Type \\h $command for help.\n\n"; | 
| 161 |  |  |  |  |  |  | } | 
| 162 |  |  |  |  |  |  | } | 
| 163 | 0 |  |  |  |  |  | debug "Finish interactive mode. $self->{addr}"; | 
| 164 |  |  |  |  |  |  | } | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | sub prompt { | 
| 167 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 168 |  |  |  |  |  |  |  | 
| 169 | 0 |  |  |  |  |  | local $| = 1; | 
| 170 | 0 |  |  |  |  |  | local $\; | 
| 171 |  |  |  |  |  |  |  | 
| 172 | 0 |  |  |  |  |  | my $input = $self->{term}->readline("memcached\@$self->{addr}> "); | 
| 173 | 0 |  |  |  |  |  | chomp($input); | 
| 174 | 0 | 0 |  |  |  |  | return unless $input; | 
| 175 | 0 | 0 |  |  |  |  | $self->{term}->addhistory($input) if ($input =~ m/\S/); | 
| 176 |  |  |  |  |  |  |  | 
| 177 | 0 |  |  |  |  |  | my ($_command, @args) = split(m/\s+/, $input); | 
| 178 | 0 |  |  |  |  |  | my $command = $COMMAND_OF{$_command}; | 
| 179 | 0 | 0 |  |  |  |  | print "Unknown command - $input\n" unless $command; | 
| 180 |  |  |  |  |  |  |  | 
| 181 | 0 |  |  |  |  |  | return $command, @args; | 
| 182 |  |  |  |  |  |  | } | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | sub help { | 
| 185 | 0 |  |  | 0 | 0 |  | my $self    = shift; | 
| 186 | 0 |  | 0 |  |  |  | my $command = shift || q{}; | 
| 187 |  |  |  |  |  |  |  | 
| 188 | 0 |  |  |  |  |  | my @command_info = @App::Memcached::CLI::Help::COMMANDS_INFO; | 
| 189 |  |  |  |  |  |  |  | 
| 190 | 0 |  |  |  |  |  | my $body   = q{}; | 
| 191 | 0 |  |  |  |  |  | my $space  = ' ' x 4; | 
| 192 |  |  |  |  |  |  |  | 
| 193 |  |  |  |  |  |  | # Help for specified command | 
| 194 | 0 | 0 |  |  |  |  | if (my $function = $COMMAND_OF{$command}) { | 
|  |  | 0 |  |  |  |  |  | 
| 195 | 0 |  |  |  |  |  | my $aliases = join(q{, }, _sorted_aliases_of($function)); | 
| 196 | 0 |  |  |  |  |  | my $info = (grep { $_->{command} eq $function } @command_info)[0]; | 
|  | 0 |  |  |  |  |  |  | 
| 197 | 0 |  |  |  |  |  | $body .= sprintf qq{\n[Command "%s"]\n\n}, $command; | 
| 198 | 0 |  |  |  |  |  | $body .= "Summary:\n"; | 
| 199 | 0 |  |  |  |  |  | $body .= sprintf "%s%s\n\n", $space, $info->{summary}; | 
| 200 | 0 |  |  |  |  |  | $body .= "Aliases:\n"; | 
| 201 | 0 |  |  |  |  |  | $body .= sprintf "%s%s\n\n", $space, $aliases; | 
| 202 | 0 | 0 |  |  |  |  | if ($info->{description}) { | 
| 203 | 0 |  |  |  |  |  | $body .= $info->{description}; | 
| 204 | 0 |  |  |  |  |  | $body .= "\n"; | 
| 205 |  |  |  |  |  |  | } | 
| 206 | 0 |  |  |  |  |  | print $body; | 
| 207 | 0 |  |  |  |  |  | return 1; | 
| 208 |  |  |  |  |  |  | } | 
| 209 |  |  |  |  |  |  | # Command not found, but continue | 
| 210 |  |  |  |  |  |  | elsif ($command) { | 
| 211 | 0 |  |  |  |  |  | $body .= "Unknown command: $command\n"; | 
| 212 |  |  |  |  |  |  | } | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | # General help | 
| 215 | 0 |  |  |  |  |  | $body .= "\n[Available Commands]\n"; | 
| 216 | 0 |  |  |  |  |  | for my $info (@command_info) { | 
| 217 | 0 |  |  |  |  |  | my $cmd = $info->{command}; | 
| 218 | 0 |  |  |  |  |  | my $commands = join(q{, }, _sorted_aliases_of($cmd)); | 
| 219 |  |  |  |  |  |  | $body .= sprintf "%s%-20s%s%s\n", | 
| 220 | 0 |  |  |  |  |  | $space, $commands, $space x 2, $info->{summary}; | 
| 221 |  |  |  |  |  |  | } | 
| 222 | 0 |  |  |  |  |  | $body .= "\nType \\h  for each.\n\n"; | 
| 223 | 0 |  |  |  |  |  | print $body; | 
| 224 | 0 |  |  |  |  |  | return 1; | 
| 225 |  |  |  |  |  |  | } | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | sub _sorted_aliases_of { | 
| 228 | 0 |  |  | 0 |  |  | my $command = shift; | 
| 229 | 0 |  |  |  |  |  | my @aliases = @{$COMMAND2ALIASES{$command}}; | 
|  | 0 |  |  |  |  |  |  | 
| 230 | 0 | 0 |  |  |  |  | return (shift @aliases, $command, @aliases) if @aliases; | 
| 231 | 0 |  |  |  |  |  | return ($command); | 
| 232 |  |  |  |  |  |  | } | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | sub call { | 
| 235 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 236 | 0 |  |  |  |  |  | my @commands = @_; | 
| 237 | 0 | 0 |  |  |  |  | unless (@commands) { | 
| 238 | 0 |  |  |  |  |  | print "No COMMAND given.\n"; | 
| 239 | 0 |  |  |  |  |  | return; | 
| 240 |  |  |  |  |  |  | } | 
| 241 | 0 |  |  |  |  |  | my $response = $self->{ds}->query_any(join q{ }, @commands); | 
| 242 | 0 |  |  |  |  |  | print $response; | 
| 243 | 0 |  |  |  |  |  | return 1; | 
| 244 |  |  |  |  |  |  | } | 
| 245 |  |  |  |  |  |  |  | 
| 246 |  |  |  |  |  |  | sub get { | 
| 247 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 248 | 0 |  |  |  |  |  | return $self->_retrieve('get', @_); | 
| 249 |  |  |  |  |  |  | } | 
| 250 |  |  |  |  |  |  |  | 
| 251 |  |  |  |  |  |  | sub gets { | 
| 252 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 253 | 0 |  |  |  |  |  | return $self->_retrieve('gets', @_); | 
| 254 |  |  |  |  |  |  | } | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | sub _retrieve { | 
| 257 | 0 |  |  | 0 |  |  | my $self = shift; | 
| 258 | 0 |  |  |  |  |  | my ($command, @keys) = @_; | 
| 259 | 0 | 0 |  |  |  |  | unless (@keys) { | 
| 260 | 0 |  |  |  |  |  | print "No KEY specified.\n"; | 
| 261 | 0 |  |  |  |  |  | return; | 
| 262 |  |  |  |  |  |  | } | 
| 263 |  |  |  |  |  |  | my $items = App::Memcached::CLI::Item->find( | 
| 264 | 0 |  |  |  |  |  | \@keys, $self->{ds}, command => $command, | 
| 265 |  |  |  |  |  |  | ); | 
| 266 | 0 | 0 |  |  |  |  | unless (@$items) { | 
| 267 | 0 |  |  |  |  |  | print "Not found - @keys\n"; | 
| 268 | 0 |  |  |  |  |  | return 1; | 
| 269 |  |  |  |  |  |  | } | 
| 270 | 0 | 0 |  |  |  |  | if (@$items == 1) { | 
| 271 | 0 |  |  |  |  |  | $items->[0]->output; | 
| 272 | 0 |  |  |  |  |  | return 1; | 
| 273 |  |  |  |  |  |  | } | 
| 274 | 0 |  |  |  |  |  | for (my $i=0; $i < scalar(@$items); $i++) { | 
| 275 | 0 |  |  |  |  |  | my $item = $items->[$i]; | 
| 276 | 0 |  |  |  |  |  | $item->output_line; | 
| 277 |  |  |  |  |  |  | } | 
| 278 | 0 |  |  |  |  |  | return 1; | 
| 279 |  |  |  |  |  |  | } | 
| 280 |  |  |  |  |  |  |  | 
| 281 | 0 |  |  | 0 | 0 |  | sub set     { return &_store(shift, 'set', @_); } | 
| 282 | 0 |  |  | 0 | 0 |  | sub add     { return &_store(shift, 'add', @_); } | 
| 283 | 0 |  |  | 0 | 0 |  | sub replace { return &_store(shift, 'replace', @_); } | 
| 284 | 0 |  |  | 0 | 0 |  | sub append  { return &_store(shift, 'append',  @_); } | 
| 285 | 0 |  |  | 0 | 0 |  | sub prepend { return &_store(shift, 'prepend', @_); } | 
| 286 |  |  |  |  |  |  |  | 
| 287 |  |  |  |  |  |  | sub _store { | 
| 288 | 0 |  |  | 0 |  |  | my $self    = shift; | 
| 289 | 0 |  |  |  |  |  | my $command = shift; | 
| 290 | 0 |  |  |  |  |  | my ($key, $value, $expire, $flags) = @_; | 
| 291 | 0 | 0 | 0 |  |  |  | unless ($key and $value) { | 
| 292 | 0 |  |  |  |  |  | print "KEY or VALUE not specified.\n"; | 
| 293 | 0 |  |  |  |  |  | return; | 
| 294 |  |  |  |  |  |  | } | 
| 295 | 0 |  |  |  |  |  | my $item = App::Memcached::CLI::Item->new( | 
| 296 |  |  |  |  |  |  | key    => $key, | 
| 297 |  |  |  |  |  |  | value  => $value, | 
| 298 |  |  |  |  |  |  | expire => $expire, | 
| 299 |  |  |  |  |  |  | flags  => $flags, | 
| 300 |  |  |  |  |  |  | ); | 
| 301 | 0 | 0 |  |  |  |  | unless ($item->save($self->{ds}, command => $command)) { | 
| 302 | 0 |  |  |  |  |  | print "Failed to $command item. KEY $key, VALUE $value\n"; | 
| 303 | 0 |  |  |  |  |  | return 1; | 
| 304 |  |  |  |  |  |  | } | 
| 305 | 0 |  |  |  |  |  | print "OK\n"; | 
| 306 | 0 |  |  |  |  |  | return 1; | 
| 307 |  |  |  |  |  |  | } | 
| 308 |  |  |  |  |  |  |  | 
| 309 |  |  |  |  |  |  | sub cas { | 
| 310 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 311 | 0 |  |  |  |  |  | my ($key, $value, $cas, $expire, $flags) = @_; | 
| 312 | 0 | 0 | 0 |  |  |  | unless ($key and $value and $cas) { | 
|  |  |  | 0 |  |  |  |  | 
| 313 | 0 |  |  |  |  |  | print "KEY or VALUE or CAS not specified.\n"; | 
| 314 | 0 |  |  |  |  |  | return; | 
| 315 |  |  |  |  |  |  | } | 
| 316 | 0 |  |  |  |  |  | my $item = App::Memcached::CLI::Item->new( | 
| 317 |  |  |  |  |  |  | key    => $key, | 
| 318 |  |  |  |  |  |  | value  => $value, | 
| 319 |  |  |  |  |  |  | expire => $expire, | 
| 320 |  |  |  |  |  |  | flags  => $flags, | 
| 321 |  |  |  |  |  |  | cas    => $cas, | 
| 322 |  |  |  |  |  |  | ); | 
| 323 | 0 | 0 |  |  |  |  | unless ($item->save($self->{ds}, command => 'cas')) { | 
| 324 | 0 |  |  |  |  |  | print "Failed to cas item. KEY $key, VALUE $value\n"; | 
| 325 | 0 |  |  |  |  |  | return 1; | 
| 326 |  |  |  |  |  |  | } | 
| 327 | 0 |  |  |  |  |  | print "OK\n"; | 
| 328 | 0 |  |  |  |  |  | return 1; | 
| 329 |  |  |  |  |  |  | } | 
| 330 |  |  |  |  |  |  |  | 
| 331 | 0 |  |  | 0 | 0 |  | sub incr { return &_incr_decr(shift, 'incr', @_); } | 
| 332 | 0 |  |  | 0 | 0 |  | sub decr { return &_incr_decr(shift, 'decr', @_); } | 
| 333 |  |  |  |  |  |  |  | 
| 334 |  |  |  |  |  |  | sub _incr_decr { | 
| 335 | 0 |  |  | 0 |  |  | my $self   = shift; | 
| 336 | 0 |  |  |  |  |  | my $cmd    = shift; | 
| 337 | 0 |  |  |  |  |  | my $key    = shift; | 
| 338 | 0 |  |  |  |  |  | my $number = shift; | 
| 339 | 0 | 0 | 0 |  |  |  | unless ($key and defined $number) { | 
| 340 | 0 |  |  |  |  |  | print "No KEY or VALUE specified.\n"; | 
| 341 | 0 |  |  |  |  |  | return; | 
| 342 |  |  |  |  |  |  | } | 
| 343 | 0 | 0 |  |  |  |  | unless ($number =~ m/^\d+$/) { | 
| 344 | 0 |  |  |  |  |  | print "Give numeric number for $cmd VALUE.\n"; | 
| 345 | 0 |  |  |  |  |  | return; | 
| 346 |  |  |  |  |  |  | } | 
| 347 | 0 |  |  |  |  |  | my $new_value = $self->{ds}->$cmd($key, $number); | 
| 348 | 0 | 0 |  |  |  |  | unless (defined $new_value) { | 
| 349 | 0 |  |  |  |  |  | print "FAILED - $cmd\n"; | 
| 350 | 0 |  |  |  |  |  | return; | 
| 351 |  |  |  |  |  |  | } | 
| 352 | 0 |  |  |  |  |  | print "OK. New VALUE is $new_value\n"; | 
| 353 | 0 |  |  |  |  |  | return 1; | 
| 354 |  |  |  |  |  |  | } | 
| 355 |  |  |  |  |  |  |  | 
| 356 |  |  |  |  |  |  | sub touch { | 
| 357 | 0 |  |  | 0 | 0 |  | my $self   = shift; | 
| 358 | 0 |  |  |  |  |  | my $key    = shift; | 
| 359 | 0 |  |  |  |  |  | my $expire = shift; | 
| 360 | 0 | 0 | 0 |  |  |  | unless ($key and defined $expire) { | 
| 361 | 0 |  |  |  |  |  | print "No KEY or EXPIRE specified.\n"; | 
| 362 | 0 |  |  |  |  |  | return; | 
| 363 |  |  |  |  |  |  | } | 
| 364 | 0 | 0 |  |  |  |  | unless ($expire =~ m/^\d+$/) { | 
| 365 | 0 |  |  |  |  |  | print "Give numeric number for EXPIRE.\n"; | 
| 366 | 0 |  |  |  |  |  | return; | 
| 367 |  |  |  |  |  |  | } | 
| 368 | 0 | 0 |  |  |  |  | unless ($self->{ds}->touch($key, $expire)) { | 
| 369 | 0 |  |  |  |  |  | print "Failed to touch. KEY $key maybe missing\n"; | 
| 370 | 0 |  |  |  |  |  | return; | 
| 371 |  |  |  |  |  |  | } | 
| 372 | 0 |  |  |  |  |  | print "OK\n"; | 
| 373 | 0 |  |  |  |  |  | return 1; | 
| 374 |  |  |  |  |  |  | } | 
| 375 |  |  |  |  |  |  |  | 
| 376 |  |  |  |  |  |  | sub delete { | 
| 377 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 378 | 0 |  |  |  |  |  | my $key  = shift; | 
| 379 | 0 | 0 |  |  |  |  | unless ($key) { | 
| 380 | 0 |  |  |  |  |  | print "No KEY specified.\n"; | 
| 381 | 0 |  |  |  |  |  | return; | 
| 382 |  |  |  |  |  |  | } | 
| 383 | 0 |  |  |  |  |  | my $item = App::Memcached::CLI::Item->new(key => $key); | 
| 384 | 0 | 0 |  |  |  |  | unless ($item->remove($self->{ds})) { | 
| 385 | 0 |  |  |  |  |  | warn "Failed to delete item. KEY $key"; | 
| 386 | 0 |  |  |  |  |  | return; | 
| 387 |  |  |  |  |  |  | } | 
| 388 | 0 |  |  |  |  |  | print "OK\n"; | 
| 389 | 0 |  |  |  |  |  | return 1; | 
| 390 |  |  |  |  |  |  | } | 
| 391 |  |  |  |  |  |  |  | 
| 392 |  |  |  |  |  |  | sub version { | 
| 393 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 394 | 0 |  |  |  |  |  | my $version = $self->{ds}->query_one('version'); | 
| 395 | 0 |  |  |  |  |  | print "$version\n"; | 
| 396 | 0 |  |  |  |  |  | return 1; | 
| 397 |  |  |  |  |  |  | } | 
| 398 |  |  |  |  |  |  |  | 
| 399 |  |  |  |  |  |  | sub cachedump { | 
| 400 | 0 |  |  | 0 | 0 |  | my $self  = shift; | 
| 401 | 0 |  |  |  |  |  | my $class = shift; | 
| 402 | 0 |  | 0 |  |  |  | my $num   = shift || $DEFAULT_CACHEDUMP_SIZE; | 
| 403 |  |  |  |  |  |  |  | 
| 404 | 0 | 0 |  |  |  |  | unless ($class) { | 
| 405 | 0 |  |  |  |  |  | print "No slab class specified.\n"; | 
| 406 | 0 |  |  |  |  |  | return; | 
| 407 |  |  |  |  |  |  | } | 
| 408 | 0 |  |  |  |  |  | my $response = $self->{ds}->query("stats cachedump $class $num"); | 
| 409 | 0 |  |  |  |  |  | my %expires; | 
| 410 | 0 |  |  |  |  |  | for my $line (@$response) { | 
| 411 | 0 | 0 |  |  |  |  | if ($line !~ m/^ITEM (\S+) \[(\d+) b; (\d+) s\]/) { | 
| 412 | 0 |  |  |  |  |  | warn "Unknown response: $line"; | 
| 413 | 0 |  |  |  |  |  | next; | 
| 414 |  |  |  |  |  |  | } | 
| 415 | 0 |  |  |  |  |  | my %data = (key => $1, length => $2, expire => $3); | 
| 416 | 0 |  |  |  |  |  | $expires{$data{key}} = \%data; | 
| 417 |  |  |  |  |  |  | } | 
| 418 | 0 | 0 |  |  |  |  | return 1 unless %expires; | 
| 419 |  |  |  |  |  |  |  | 
| 420 | 0 |  |  |  |  |  | my @keys = keys %expires; | 
| 421 |  |  |  |  |  |  | my $items = App::Memcached::CLI::Item->find( | 
| 422 | 0 |  |  |  |  |  | \@keys, $self->{ds}, command => 'gets', | 
| 423 |  |  |  |  |  |  | ); | 
| 424 | 0 |  |  |  |  |  | for (my $i=0; $i < scalar(@$items); $i++) { | 
| 425 | 0 |  |  |  |  |  | my $item = $items->[$i]; | 
| 426 | 0 |  |  |  |  |  | $item->{expire} = $expires{$item->{key}}{expire}; | 
| 427 | 0 |  |  |  |  |  | $item->output_line; | 
| 428 |  |  |  |  |  |  | } | 
| 429 | 0 |  |  |  |  |  | return 1; | 
| 430 |  |  |  |  |  |  | } | 
| 431 |  |  |  |  |  |  |  | 
| 432 |  |  |  |  |  |  | sub display { | 
| 433 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 434 |  |  |  |  |  |  |  | 
| 435 | 0 |  |  |  |  |  | my %stats; | 
| 436 | 0 |  |  |  |  |  | my $max = 1; | 
| 437 |  |  |  |  |  |  |  | 
| 438 | 0 |  |  |  |  |  | my $resp_items = $self->{ds}->query('stats items'); | 
| 439 | 0 |  |  |  |  |  | for my $line (@$resp_items) { | 
| 440 | 0 | 0 |  |  |  |  | if ($line =~ m/^STAT items:(\d+):(\w+) (\d+)/) { | 
| 441 | 0 |  |  |  |  |  | $stats{$1}{$2} = $3; | 
| 442 |  |  |  |  |  |  | } | 
| 443 |  |  |  |  |  |  | } | 
| 444 |  |  |  |  |  |  |  | 
| 445 | 0 |  |  |  |  |  | my $resp_slabs = $self->{ds}->query('stats slabs'); | 
| 446 | 0 |  |  |  |  |  | for my $line (@$resp_slabs) { | 
| 447 | 0 | 0 |  |  |  |  | if ($line =~ m/^STAT (\d+):(\w+) (\d+)/) { | 
| 448 | 0 |  |  |  |  |  | $stats{$1}{$2} = $3; | 
| 449 | 0 |  |  |  |  |  | $max = $1; | 
| 450 |  |  |  |  |  |  | } | 
| 451 |  |  |  |  |  |  | } | 
| 452 |  |  |  |  |  |  |  | 
| 453 | 0 |  |  |  |  |  | print "  #  Item_Size  Max_age   Pages   Count   Full?  Evicted Evict_Time OOM\n"; | 
| 454 | 0 |  |  |  |  |  | for my $class (1..$max) { | 
| 455 | 0 |  |  |  |  |  | my $slab = $stats{$class}; | 
| 456 | 0 | 0 |  |  |  |  | next unless $slab->{total_pages}; | 
| 457 |  |  |  |  |  |  |  | 
| 458 |  |  |  |  |  |  | my $size | 
| 459 |  |  |  |  |  |  | = $slab->{chunk_size} < 1024 ? "$slab->{chunk_size}B" | 
| 460 | 0 | 0 |  |  |  |  | : sprintf("%.1fK", $slab->{chunk_size} / 1024.0) ; | 
| 461 |  |  |  |  |  |  |  | 
| 462 | 0 | 0 |  |  |  |  | my $full = ($slab->{free_chunks_end} == 0) ? 'yes' : 'no'; | 
| 463 |  |  |  |  |  |  | printf( | 
| 464 |  |  |  |  |  |  | "%3d %8s %9ds %7d %7d %7s %8d %8d %4d\n", | 
| 465 |  |  |  |  |  |  | $class, $size, $slab->{age} || 0, $slab->{total_pages}, | 
| 466 |  |  |  |  |  |  | $slab->{number} || 0, $full, $slab->{evicted} || 0, | 
| 467 | 0 |  | 0 |  |  |  | $slab->{evicted_time} || 0, $slab->{outofmemory} || 0, | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 468 |  |  |  |  |  |  | ); | 
| 469 |  |  |  |  |  |  | } | 
| 470 |  |  |  |  |  |  |  | 
| 471 | 0 |  |  |  |  |  | return 1; | 
| 472 |  |  |  |  |  |  | } | 
| 473 |  |  |  |  |  |  |  | 
| 474 |  |  |  |  |  |  | sub stats { | 
| 475 | 0 |  |  | 0 | 0 |  | my $self   = shift; | 
| 476 | 0 |  |  |  |  |  | my $filter = shift; | 
| 477 | 0 |  |  |  |  |  | my $response = $self->{ds}->query('stats'); | 
| 478 | 0 |  |  |  |  |  | _print_stats_of_response('stats', $filter, @$response); | 
| 479 | 0 |  |  |  |  |  | return 1; | 
| 480 |  |  |  |  |  |  | } | 
| 481 |  |  |  |  |  |  |  | 
| 482 |  |  |  |  |  |  | sub settings { | 
| 483 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 484 | 0 |  |  |  |  |  | my $filter = shift; | 
| 485 | 0 |  |  |  |  |  | my $response = $self->{ds}->query('stats settings'); | 
| 486 | 0 |  |  |  |  |  | _print_stats_of_response('stats settings', $filter, @$response); | 
| 487 | 0 |  |  |  |  |  | return 1; | 
| 488 |  |  |  |  |  |  | } | 
| 489 |  |  |  |  |  |  |  | 
| 490 |  |  |  |  |  |  | sub _print_stats_of_response { | 
| 491 | 0 |  |  | 0 |  |  | my $title  = shift; | 
| 492 | 0 |  |  |  |  |  | my $filter = shift; | 
| 493 | 0 |  |  |  |  |  | my @lines  = @_; | 
| 494 |  |  |  |  |  |  |  | 
| 495 | 0 |  |  |  |  |  | my %stats; | 
| 496 | 0 |  |  |  |  |  | my ($max_key_l, $max_val_l) = (0, 0); | 
| 497 |  |  |  |  |  |  |  | 
| 498 | 0 |  |  |  |  |  | for my $line (@lines) { | 
| 499 | 0 | 0 |  |  |  |  | next if ($line !~ m/^STAT\s+(\S*)\s+(.*)/); | 
| 500 | 0 |  |  |  |  |  | my ($key, $value) = ($1, $2); | 
| 501 | 0 | 0 |  |  |  |  | if (length $key   > $max_key_l) { $max_key_l = length $key; } | 
|  | 0 |  |  |  |  |  |  | 
| 502 | 0 | 0 |  |  |  |  | if (length $value > $max_val_l) { $max_val_l = length $value; } | 
|  | 0 |  |  |  |  |  |  | 
| 503 | 0 | 0 | 0 |  |  |  | next if ($filter && $key !~ m/$filter/); | 
| 504 | 0 |  |  |  |  |  | $stats{$key} = $value; | 
| 505 |  |  |  |  |  |  | } | 
| 506 |  |  |  |  |  |  |  | 
| 507 | 0 |  |  |  |  |  | print  "# $title\n"; | 
| 508 | 0 |  |  |  |  |  | printf "#%${max_key_l}s  %${max_val_l}s\n", 'Field', 'Value'; | 
| 509 | 0 |  |  |  |  |  | for my $field (sort {$a cmp $b} (keys %stats)) { | 
|  | 0 |  |  |  |  |  |  | 
| 510 | 0 |  |  |  |  |  | printf (" %${max_key_l}s  %${max_val_l}s\n", $field, $stats{$field}); | 
| 511 |  |  |  |  |  |  | } | 
| 512 |  |  |  |  |  |  | } | 
| 513 |  |  |  |  |  |  |  | 
| 514 |  |  |  |  |  |  | sub detaildump { | 
| 515 | 0 |  |  | 0 | 0 |  | my $self  = shift; | 
| 516 | 0 |  |  |  |  |  | my $response = $self->{ds}->query("stats detail dump"); | 
| 517 | 0 |  |  |  |  |  | print "$_\n" for @$response; | 
| 518 | 0 |  |  |  |  |  | return 1; | 
| 519 |  |  |  |  |  |  | } | 
| 520 |  |  |  |  |  |  |  | 
| 521 |  |  |  |  |  |  | sub detail { | 
| 522 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 523 | 0 |  | 0 |  |  |  | my $mode = shift || q{}; | 
| 524 | 0 | 0 |  | 0 |  |  | unless (first { $_ eq $mode } qw/on off/) { | 
|  | 0 |  |  |  |  |  |  | 
| 525 | 0 |  |  |  |  |  | print "Mode must be 'on' or 'off'!\n"; | 
| 526 | 0 |  |  |  |  |  | return; | 
| 527 |  |  |  |  |  |  | } | 
| 528 | 0 |  |  |  |  |  | my $response = $self->{ds}->query_one("stats detail $mode"); | 
| 529 | 0 |  |  |  |  |  | my %result = ( | 
| 530 |  |  |  |  |  |  | on  => 'Enabled', | 
| 531 |  |  |  |  |  |  | off => 'Disabled', | 
| 532 |  |  |  |  |  |  | ); | 
| 533 | 0 | 0 |  |  |  |  | if ($response !~ m/^OK/) { | 
| 534 | 0 |  |  |  |  |  | print "Seems failed to set detail $mode\n"; | 
| 535 | 0 |  |  |  |  |  | return; | 
| 536 |  |  |  |  |  |  | } | 
| 537 | 0 |  |  |  |  |  | print "OK - $result{$mode} stats collection for detail dump.\n"; | 
| 538 | 0 |  |  |  |  |  | return 1; | 
| 539 |  |  |  |  |  |  | } | 
| 540 |  |  |  |  |  |  |  | 
| 541 |  |  |  |  |  |  | sub dump_all { | 
| 542 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 543 | 0 |  |  |  |  |  | my %items; | 
| 544 |  |  |  |  |  |  | my $total; | 
| 545 |  |  |  |  |  |  |  | 
| 546 | 0 |  |  |  |  |  | my $response = $self->{ds}->query('stats items'); | 
| 547 | 0 |  |  |  |  |  | for my $line (@$response) { | 
| 548 | 0 | 0 |  |  |  |  | if ($line =~ m/^STAT items:(\d*):number (\d*)/) { | 
| 549 | 0 |  |  |  |  |  | $items{$1} = $2; | 
| 550 | 0 |  |  |  |  |  | $total += $2; | 
| 551 |  |  |  |  |  |  | } | 
| 552 |  |  |  |  |  |  | } | 
| 553 |  |  |  |  |  |  |  | 
| 554 | 0 |  |  |  |  |  | print  STDERR "Dumping memcache contents\n"; | 
| 555 | 0 |  |  |  |  |  | printf STDERR "  Number of buckets: %d\n", scalar(keys(%items)); | 
| 556 | 0 |  |  |  |  |  | print  STDERR "  Number of items  : $total\n"; | 
| 557 |  |  |  |  |  |  |  | 
| 558 | 0 |  |  |  |  |  | for my $bucket (sort(keys %items)) { | 
| 559 | 0 |  |  |  |  |  | print STDERR "Dumping bucket $bucket - " . $items{$bucket} . " total items\n"; | 
| 560 | 0 |  |  |  |  |  | $response = $self->{ds}->query("stats cachedump $bucket $items{$bucket}"); | 
| 561 |  |  |  |  |  |  |  | 
| 562 | 0 |  |  |  |  |  | my %expires; | 
| 563 | 0 |  |  |  |  |  | for my $line (@$response) { | 
| 564 |  |  |  |  |  |  | # Ex) ITEM foo [6 b; 1176415152 s] | 
| 565 | 0 | 0 |  |  |  |  | if ($line =~ m/^ITEM (\S+) \[.* (\d+) s\]/) { | 
| 566 | 0 |  |  |  |  |  | $expires{$1} = $2; | 
| 567 |  |  |  |  |  |  | } | 
| 568 |  |  |  |  |  |  | } | 
| 569 |  |  |  |  |  |  |  | 
| 570 | 0 |  |  |  |  |  | my $now = time(); | 
| 571 | 0 |  |  |  |  |  | my @keys_bucket = keys %expires; | 
| 572 | 0 |  |  |  |  |  | while (my @keys = splice(@keys_bucket, 0, 20)) { | 
| 573 | 0 |  |  |  |  |  | my $list = $self->{ds}->get(\@keys); | 
| 574 | 0 |  |  |  |  |  | for my $d (@$list) { | 
| 575 | 0 | 0 |  |  |  |  | my $expire = ($expires{$d->{key}} < $now) ? 0 : $expires{$d->{key}}; | 
| 576 | 0 |  |  |  |  |  | print "add $d->{key} $d->{flags} $expire $d->{length}\r\n"; | 
| 577 | 0 |  |  |  |  |  | print "$d->{value}\r\n"; | 
| 578 |  |  |  |  |  |  | } | 
| 579 | 0 |  |  |  |  |  | Time::HiRes::sleep(0.01); | 
| 580 |  |  |  |  |  |  | } | 
| 581 |  |  |  |  |  |  | } | 
| 582 |  |  |  |  |  |  |  | 
| 583 | 0 |  |  |  |  |  | return 1; | 
| 584 |  |  |  |  |  |  | } | 
| 585 |  |  |  |  |  |  |  | 
| 586 |  |  |  |  |  |  | sub restore_dump { | 
| 587 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 588 | 0 |  |  |  |  |  | my $file = shift; | 
| 589 | 0 | 0 | 0 |  |  |  | unless ($file and -r $file) { | 
| 590 | 0 |  |  |  |  |  | print "Dump FILE not found.\n"; | 
| 591 | 0 |  |  |  |  |  | return; | 
| 592 |  |  |  |  |  |  | } | 
| 593 |  |  |  |  |  |  |  | 
| 594 | 0 | 0 |  |  |  |  | open my $fh, '<', $file or die "Can't read $file!"; | 
| 595 | 0 |  |  |  |  |  | my $i = 0; | 
| 596 | 0 |  |  |  |  |  | while (my $input = $fh->getline) { | 
| 597 | 0 |  |  |  |  |  | $i++; | 
| 598 | 0 |  |  |  |  |  | $self->{ds}->{socket}->write($input); | 
| 599 | 0 | 0 |  |  |  |  | if ($i%200 == 0) { | 
| 600 | 0 |  |  |  |  |  | printf "%s...loaded $i lines\n", q[ ] x 4; | 
| 601 | 0 |  |  |  |  |  | $self->{ds}->{socket}->flush; | 
| 602 | 0 |  |  |  |  |  | Time::HiRes::sleep(0.03); | 
| 603 |  |  |  |  |  |  | } | 
| 604 |  |  |  |  |  |  | } | 
| 605 | 0 |  |  |  |  |  | print "Complete.\n"; | 
| 606 | 0 |  |  |  |  |  | return 1; | 
| 607 |  |  |  |  |  |  | } | 
| 608 |  |  |  |  |  |  |  | 
| 609 |  |  |  |  |  |  | sub flush_all { | 
| 610 | 0 |  |  | 0 | 0 |  | my $self  = shift; | 
| 611 | 0 |  |  |  |  |  | my $delay = shift; | 
| 612 | 0 |  |  |  |  |  | my $query = 'flush_all'; | 
| 613 | 0 | 0 |  |  |  |  | if ($delay) { $query .= " $delay"; } | 
|  | 0 |  |  |  |  |  |  | 
| 614 | 0 |  |  |  |  |  | my $response = $self->{ds}->query_one($query); | 
| 615 | 0 |  |  |  |  |  | print "$response\n"; | 
| 616 | 0 |  |  |  |  |  | return 1; | 
| 617 |  |  |  |  |  |  | } | 
| 618 |  |  |  |  |  |  |  | 
| 619 |  |  |  |  |  |  | sub randomset { | 
| 620 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 621 | 0 |  |  |  |  |  | my ($num, $max, $min, $namespace) = @_; | 
| 622 | 0 |  | 0 |  |  |  | $num    ||= 100; | 
| 623 | 0 |  | 0 |  |  |  | $max    ||= 1_000_000; | 
| 624 | 0 |  | 0 |  |  |  | $min    ||= 1; | 
| 625 | 0 |  | 0 |  |  |  | $namespace ||= 'memcached-cli:sample'; | 
| 626 |  |  |  |  |  |  |  | 
| 627 | 0 |  |  |  |  |  | local $| = 1; # disable output buffering | 
| 628 | 0 |  |  |  |  |  | print "Random Generate. ["; | 
| 629 | 0 |  |  |  |  |  | my $pos = 0; | 
| 630 | 0 |  |  |  |  |  | for my $i (1..$num) { | 
| 631 | 0 |  |  |  |  |  | my $key    = $namespace . ":data$i"; | 
| 632 | 0 |  |  |  |  |  | my $length = int( rand() * ($max - $min) ) + $min; | 
| 633 | 0 |  |  |  |  |  | my $value  = '.' x $length; | 
| 634 | 0 | 0 |  |  |  |  | unless ($self->{ds}->set($key, $value)) { | 
| 635 | 0 |  |  |  |  |  | warn "Failed to set KEY $key, length: $length B"; | 
| 636 |  |  |  |  |  |  | } | 
| 637 | 0 | 0 |  |  |  |  | if ( (my $_pos = int($i*20 / $num)) > $pos ) { | 
| 638 | 0 |  |  |  |  |  | $pos = $_pos; | 
| 639 | 0 |  |  |  |  |  | print '.'; | 
| 640 |  |  |  |  |  |  | } | 
| 641 | 0 | 0 |  |  |  |  | Time::HiRes::sleep(0.1) if ($i % 100 == 0); | 
| 642 |  |  |  |  |  |  | } | 
| 643 | 0 |  |  |  |  |  | print "]\n"; | 
| 644 | 0 |  |  |  |  |  | print "Complete.\n"; | 
| 645 | 0 |  |  |  |  |  | return 1; | 
| 646 |  |  |  |  |  |  | } | 
| 647 |  |  |  |  |  |  |  | 
| 648 |  |  |  |  |  |  | 1; | 
| 649 |  |  |  |  |  |  | __END__ |