| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
# $Id$ |
|
2
|
|
|
|
|
|
|
# |
|
3
|
|
|
|
|
|
|
# Copyright (c) 2003, 2004 Brad Fitzpatrick |
|
4
|
|
|
|
|
|
|
# |
|
5
|
|
|
|
|
|
|
# See COPYRIGHT section in pod text below for usage and distribution rights. |
|
6
|
|
|
|
|
|
|
# |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
package Cache::Memcached; |
|
9
|
|
|
|
|
|
|
|
|
10
|
9
|
|
|
9
|
|
320439
|
use strict; |
|
|
9
|
|
|
|
|
21
|
|
|
|
9
|
|
|
|
|
385
|
|
|
11
|
9
|
|
|
9
|
|
77
|
use warnings; |
|
|
9
|
|
|
|
|
22
|
|
|
|
9
|
|
|
|
|
332
|
|
|
12
|
|
|
|
|
|
|
|
|
13
|
9
|
|
|
9
|
|
58
|
no strict 'refs'; |
|
|
9
|
|
|
|
|
18
|
|
|
|
9
|
|
|
|
|
230
|
|
|
14
|
9
|
|
|
9
|
|
21212
|
use Storable (); |
|
|
9
|
|
|
|
|
52534
|
|
|
|
9
|
|
|
|
|
296
|
|
|
15
|
9
|
|
|
9
|
|
10495
|
use Socket qw( MSG_NOSIGNAL PF_INET PF_UNIX IPPROTO_TCP SOCK_STREAM ); |
|
|
9
|
|
|
|
|
43865
|
|
|
|
9
|
|
|
|
|
2200
|
|
|
16
|
9
|
|
|
9
|
|
9485
|
use IO::Handle (); |
|
|
9
|
|
|
|
|
76513
|
|
|
|
9
|
|
|
|
|
210
|
|
|
17
|
9
|
|
|
9
|
|
9644
|
use Time::HiRes (); |
|
|
9
|
|
|
|
|
19375
|
|
|
|
9
|
|
|
|
|
264
|
|
|
18
|
9
|
|
|
9
|
|
8064
|
use String::CRC32; |
|
|
9
|
|
|
|
|
5198
|
|
|
|
9
|
|
|
|
|
665
|
|
|
19
|
9
|
|
|
9
|
|
7816
|
use Errno qw( EINPROGRESS EWOULDBLOCK EISCONN ); |
|
|
9
|
|
|
|
|
10976
|
|
|
|
9
|
|
|
|
|
1386
|
|
|
20
|
9
|
|
|
9
|
|
8279
|
use Cache::Memcached::GetParser; |
|
|
9
|
|
|
|
|
27
|
|
|
|
9
|
|
|
|
|
247
|
|
|
21
|
9
|
|
|
9
|
|
9028
|
use Encode (); |
|
|
9
|
|
|
|
|
156273
|
|
|
|
9
|
|
|
|
|
366
|
|
|
22
|
9
|
|
|
|
|
114
|
use fields qw{ |
|
23
|
|
|
|
|
|
|
debug no_rehash stats compress_threshold compress_enable stat_callback |
|
24
|
|
|
|
|
|
|
readonly select_timeout namespace namespace_len servers active buckets |
|
25
|
|
|
|
|
|
|
pref_ip |
|
26
|
|
|
|
|
|
|
bucketcount _single_sock _stime |
|
27
|
|
|
|
|
|
|
connect_timeout cb_connect_fail |
|
28
|
|
|
|
|
|
|
parser_class |
|
29
|
|
|
|
|
|
|
buck2sock buck2sock_generation |
|
30
|
9
|
|
|
9
|
|
8822
|
}; |
|
|
9
|
|
|
|
|
14483
|
|
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
# flag definitions |
|
33
|
9
|
|
|
9
|
|
1499
|
use constant F_STORABLE => 1; |
|
|
9
|
|
|
|
|
18
|
|
|
|
9
|
|
|
|
|
605
|
|
|
34
|
9
|
|
|
9
|
|
47
|
use constant F_COMPRESS => 2; |
|
|
9
|
|
|
|
|
18
|
|
|
|
9
|
|
|
|
|
351
|
|
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# size savings required before saving compressed value |
|
37
|
9
|
|
|
9
|
|
43
|
use constant COMPRESS_SAVINGS => 0.20; # percent |
|
|
9
|
|
|
|
|
13
|
|
|
|
9
|
|
|
|
|
347
|
|
|
38
|
|
|
|
|
|
|
|
|
39
|
9
|
|
|
9
|
|
51
|
use vars qw($VERSION $HAVE_ZLIB $FLAG_NOSIGNAL $HAVE_SOCKET6); |
|
|
9
|
|
|
|
|
14
|
|
|
|
9
|
|
|
|
|
845
|
|
|
40
|
|
|
|
|
|
|
$VERSION = "1.30"; |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
BEGIN { |
|
43
|
9
|
|
|
9
|
|
594
|
$HAVE_ZLIB = eval "use Compress::Zlib (); 1;"; |
|
|
9
|
|
|
9
|
|
12084
|
|
|
|
9
|
|
|
|
|
731863
|
|
|
|
9
|
|
|
|
|
96
|
|
|
44
|
9
|
|
|
9
|
|
583
|
$HAVE_SOCKET6 = eval "use Socket6 qw(AF_INET6 PF_INET6); 1;"; |
|
|
9
|
|
|
|
|
23501
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
45
|
|
|
|
|
|
|
} |
|
46
|
|
|
|
|
|
|
|
|
47
|
9
|
|
|
9
|
|
4242
|
my $HAVE_XS = eval "use Cache::Memcached::GetParserXS; 1;"; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
48
|
|
|
|
|
|
|
$HAVE_XS = 0 if $ENV{NO_XS}; |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
my $parser_class = $HAVE_XS ? "Cache::Memcached::GetParserXS" : "Cache::Memcached::GetParser"; |
|
51
|
|
|
|
|
|
|
if ($ENV{XS_DEBUG}) { |
|
52
|
|
|
|
|
|
|
print "using parser: $parser_class\n"; |
|
53
|
|
|
|
|
|
|
} |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
$FLAG_NOSIGNAL = 0; |
|
56
|
|
|
|
|
|
|
eval { $FLAG_NOSIGNAL = MSG_NOSIGNAL; }; |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
my %host_dead; # host -> unixtime marked dead until |
|
59
|
|
|
|
|
|
|
my %cache_sock; # host -> socket |
|
60
|
|
|
|
|
|
|
my $socket_cache_generation = 1; # Set to 1 here because below the buck2sock_generation is set to 0, keep them in order. |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
my $PROTO_TCP; |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
our $SOCK_TIMEOUT = 2.6; # default timeout in seconds |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
sub new { |
|
67
|
2
|
|
|
2
|
1
|
3007403
|
my Cache::Memcached $self = shift; |
|
68
|
2
|
50
|
|
|
|
152
|
$self = fields::new( $self ) unless ref $self; |
|
69
|
|
|
|
|
|
|
|
|
70
|
2
|
50
|
|
|
|
10365
|
my $args = (@_ == 1) ? shift : { @_ }; # hashref-ify args |
|
71
|
|
|
|
|
|
|
|
|
72
|
2
|
|
|
|
|
8
|
$self->{'buck2sock'}= []; |
|
73
|
2
|
|
|
|
|
6
|
$self->{'buck2sock_generation'} = 0; |
|
74
|
2
|
|
|
|
|
13
|
$self->set_servers($args->{'servers'}); |
|
75
|
2
|
|
50
|
|
|
31
|
$self->{'debug'} = $args->{'debug'} || 0; |
|
76
|
2
|
|
|
|
|
6
|
$self->{'no_rehash'} = $args->{'no_rehash'}; |
|
77
|
2
|
|
|
|
|
6
|
$self->{'stats'} = {}; |
|
78
|
2
|
|
50
|
|
|
20
|
$self->{'pref_ip'} = $args->{'pref_ip'} || {}; |
|
79
|
2
|
|
|
|
|
5
|
$self->{'compress_threshold'} = $args->{'compress_threshold'}; |
|
80
|
2
|
|
|
|
|
5
|
$self->{'compress_enable'} = 1; |
|
81
|
2
|
|
50
|
|
|
22
|
$self->{'stat_callback'} = $args->{'stat_callback'} || undef; |
|
82
|
2
|
|
|
|
|
6
|
$self->{'readonly'} = $args->{'readonly'}; |
|
83
|
2
|
|
33
|
|
|
21
|
$self->{'parser_class'} = $args->{'parser_class'} || $parser_class; |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
# TODO: undocumented |
|
86
|
2
|
|
50
|
|
|
17
|
$self->{'connect_timeout'} = $args->{'connect_timeout'} || 0.25; |
|
87
|
2
|
|
50
|
|
|
20
|
$self->{'select_timeout'} = $args->{'select_timeout'} || 1.0; |
|
88
|
2
|
|
100
|
|
|
21
|
$self->{namespace} = $args->{namespace} || ''; |
|
89
|
2
|
|
|
|
|
11
|
$self->{namespace_len} = length $self->{namespace}; |
|
90
|
|
|
|
|
|
|
|
|
91
|
2
|
|
|
|
|
10
|
return $self; |
|
92
|
|
|
|
|
|
|
} |
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
sub set_pref_ip { |
|
95
|
0
|
|
|
0
|
0
|
0
|
my Cache::Memcached $self = shift; |
|
96
|
0
|
|
|
|
|
0
|
$self->{'pref_ip'} = shift; |
|
97
|
|
|
|
|
|
|
} |
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
sub set_servers { |
|
100
|
2
|
|
|
2
|
1
|
5
|
my Cache::Memcached $self = shift; |
|
101
|
2
|
|
|
|
|
6
|
my ($list) = @_; |
|
102
|
2
|
|
50
|
|
|
12
|
$self->{'servers'} = $list || []; |
|
103
|
2
|
|
|
|
|
4
|
$self->{'active'} = scalar @{$self->{'servers'}}; |
|
|
2
|
|
|
|
|
8
|
|
|
104
|
2
|
|
|
|
|
5
|
$self->{'buckets'} = undef; |
|
105
|
2
|
|
|
|
|
13
|
$self->{'bucketcount'} = 0; |
|
106
|
2
|
|
|
|
|
33
|
$self->init_buckets; |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# We didn't close any sockets, so we reset the buck2sock generation, not increment the global socket cache generation. |
|
109
|
2
|
|
|
|
|
4
|
$self->{'buck2sock_generation'} = 0; |
|
110
|
|
|
|
|
|
|
|
|
111
|
2
|
|
|
|
|
4
|
$self->{'_single_sock'} = undef; |
|
112
|
2
|
50
|
|
|
|
4
|
if (@{$self->{'servers'}} == 1) { |
|
|
2
|
|
|
|
|
11
|
|
|
113
|
2
|
|
|
|
|
5
|
$self->{'_single_sock'} = $self->{'servers'}[0]; |
|
114
|
|
|
|
|
|
|
} |
|
115
|
|
|
|
|
|
|
|
|
116
|
2
|
|
|
|
|
4
|
return $self; |
|
117
|
|
|
|
|
|
|
} |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
sub set_cb_connect_fail { |
|
120
|
0
|
|
|
0
|
0
|
0
|
my Cache::Memcached $self = shift; |
|
121
|
0
|
|
|
|
|
0
|
$self->{'cb_connect_fail'} = shift; |
|
122
|
|
|
|
|
|
|
} |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
sub set_connect_timeout { |
|
125
|
0
|
|
|
0
|
1
|
0
|
my Cache::Memcached $self = shift; |
|
126
|
0
|
|
|
|
|
0
|
$self->{'connect_timeout'} = shift; |
|
127
|
|
|
|
|
|
|
} |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
sub set_debug { |
|
130
|
0
|
|
|
0
|
1
|
0
|
my Cache::Memcached $self = shift; |
|
131
|
0
|
|
|
|
|
0
|
my ($dbg) = @_; |
|
132
|
0
|
|
0
|
|
|
0
|
$self->{'debug'} = $dbg || 0; |
|
133
|
|
|
|
|
|
|
} |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
sub set_readonly { |
|
136
|
0
|
|
|
0
|
1
|
0
|
my Cache::Memcached $self = shift; |
|
137
|
0
|
|
|
|
|
0
|
my ($ro) = @_; |
|
138
|
0
|
|
|
|
|
0
|
$self->{'readonly'} = $ro; |
|
139
|
|
|
|
|
|
|
} |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
sub set_norehash { |
|
142
|
0
|
|
|
0
|
1
|
0
|
my Cache::Memcached $self = shift; |
|
143
|
0
|
|
|
|
|
0
|
my ($val) = @_; |
|
144
|
0
|
|
|
|
|
0
|
$self->{'no_rehash'} = $val; |
|
145
|
|
|
|
|
|
|
} |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
sub set_compress_threshold { |
|
148
|
0
|
|
|
0
|
1
|
0
|
my Cache::Memcached $self = shift; |
|
149
|
0
|
|
|
|
|
0
|
my ($thresh) = @_; |
|
150
|
0
|
|
|
|
|
0
|
$self->{'compress_threshold'} = $thresh; |
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
sub enable_compress { |
|
154
|
0
|
|
|
0
|
1
|
0
|
my Cache::Memcached $self = shift; |
|
155
|
0
|
|
|
|
|
0
|
my ($enable) = @_; |
|
156
|
0
|
|
|
|
|
0
|
$self->{'compress_enable'} = $enable; |
|
157
|
|
|
|
|
|
|
} |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
sub forget_dead_hosts { |
|
160
|
0
|
|
|
0
|
0
|
0
|
my Cache::Memcached $self = shift; |
|
161
|
0
|
|
|
|
|
0
|
%host_dead = (); |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
# We need to globally recalculate our buck2sock in all objects, so we increment the global generation. |
|
164
|
0
|
|
|
|
|
0
|
$socket_cache_generation++; |
|
165
|
|
|
|
|
|
|
|
|
166
|
0
|
|
|
|
|
0
|
return 1; |
|
167
|
|
|
|
|
|
|
} |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
sub set_stat_callback { |
|
170
|
0
|
|
|
0
|
0
|
0
|
my Cache::Memcached $self = shift; |
|
171
|
0
|
|
|
|
|
0
|
my ($stat_callback) = @_; |
|
172
|
0
|
|
|
|
|
0
|
$self->{'stat_callback'} = $stat_callback; |
|
173
|
|
|
|
|
|
|
} |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
my %sock_map; # stringified-$sock -> "$ip:$port" |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
sub _dead_sock { |
|
178
|
1
|
|
|
1
|
|
4
|
my ($self, $sock, $ret, $dead_for) = @_; |
|
179
|
1
|
50
|
|
|
|
9
|
if (my $ipport = $sock_map{$sock}) { |
|
180
|
1
|
|
|
|
|
16
|
my $now = time(); |
|
181
|
1
|
50
|
|
|
|
9
|
$host_dead{$ipport} = $now + $dead_for |
|
182
|
|
|
|
|
|
|
if $dead_for; |
|
183
|
1
|
|
|
|
|
4
|
delete $cache_sock{$ipport}; |
|
184
|
1
|
|
|
|
|
4
|
delete $sock_map{$sock}; |
|
185
|
|
|
|
|
|
|
} |
|
186
|
|
|
|
|
|
|
# We need to globally recalculate our buck2sock in all objects, so we increment the global generation. |
|
187
|
1
|
|
|
|
|
3
|
$socket_cache_generation++; |
|
188
|
|
|
|
|
|
|
|
|
189
|
1
|
|
|
|
|
95
|
return $ret; # 0 or undef, probably, depending on what caller wants |
|
190
|
|
|
|
|
|
|
} |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
sub _close_sock { |
|
193
|
0
|
|
|
0
|
|
0
|
my ($self, $sock) = @_; |
|
194
|
0
|
0
|
|
|
|
0
|
if (my $ipport = $sock_map{$sock}) { |
|
195
|
0
|
|
|
|
|
0
|
close $sock; |
|
196
|
0
|
|
|
|
|
0
|
delete $cache_sock{$ipport}; |
|
197
|
0
|
|
|
|
|
0
|
delete $sock_map{$sock}; |
|
198
|
|
|
|
|
|
|
} |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
# We need to globally recalculate our buck2sock in all objects, so we increment the global generation. |
|
201
|
0
|
|
|
|
|
0
|
$socket_cache_generation++; |
|
202
|
|
|
|
|
|
|
|
|
203
|
0
|
|
|
|
|
0
|
return 1; |
|
204
|
|
|
|
|
|
|
} |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
sub _connect_sock { # sock, sin, timeout |
|
207
|
2
|
|
|
2
|
|
6
|
my ($sock, $sin, $timeout) = @_; |
|
208
|
2
|
50
|
|
|
|
7
|
$timeout = 0.25 if not defined $timeout; |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# make the socket non-blocking from now on, |
|
211
|
|
|
|
|
|
|
# except if someone wants 0 timeout, meaning |
|
212
|
|
|
|
|
|
|
# a blocking connect, but even then turn it |
|
213
|
|
|
|
|
|
|
# non-blocking at the end of this function |
|
214
|
|
|
|
|
|
|
|
|
215
|
2
|
50
|
|
|
|
6
|
if ($timeout) { |
|
216
|
2
|
|
|
|
|
19
|
IO::Handle::blocking($sock, 0); |
|
217
|
|
|
|
|
|
|
} else { |
|
218
|
0
|
|
|
|
|
0
|
IO::Handle::blocking($sock, 1); |
|
219
|
|
|
|
|
|
|
} |
|
220
|
|
|
|
|
|
|
|
|
221
|
2
|
|
|
|
|
294
|
my $ret = connect($sock, $sin); |
|
222
|
|
|
|
|
|
|
|
|
223
|
2
|
50
|
33
|
|
|
82
|
if (!$ret && $timeout && $!==EINPROGRESS) { |
|
|
|
|
33
|
|
|
|
|
|
224
|
|
|
|
|
|
|
|
|
225
|
2
|
|
|
|
|
10
|
my $win=''; |
|
226
|
2
|
|
|
|
|
9
|
vec($win, fileno($sock), 1) = 1; |
|
227
|
|
|
|
|
|
|
|
|
228
|
2
|
100
|
|
|
|
250355
|
if (select(undef, $win, undef, $timeout) > 0) { |
|
229
|
1
|
|
|
|
|
5
|
$ret = connect($sock, $sin); |
|
230
|
|
|
|
|
|
|
# EISCONN means connected & won't re-connect, so success |
|
231
|
1
|
50
|
33
|
|
|
5
|
$ret = 1 if !$ret && $!==EISCONN; |
|
232
|
|
|
|
|
|
|
} |
|
233
|
|
|
|
|
|
|
} |
|
234
|
|
|
|
|
|
|
|
|
235
|
2
|
50
|
|
|
|
20
|
unless ($timeout) { # socket was temporarily blocking, now revert |
|
236
|
0
|
|
|
|
|
0
|
IO::Handle::blocking($sock, 0); |
|
237
|
|
|
|
|
|
|
} |
|
238
|
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
# from here on, we use non-blocking (async) IO for the duration |
|
240
|
|
|
|
|
|
|
# of the socket's life |
|
241
|
|
|
|
|
|
|
|
|
242
|
2
|
|
|
|
|
13
|
return $ret; |
|
243
|
|
|
|
|
|
|
} |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
sub sock_to_host { # (host) #why is this public? I wouldn't have to worry about undef $self if it weren't. |
|
246
|
9
|
50
|
|
9
|
0
|
28
|
my Cache::Memcached $self = ref $_[0] ? shift : undef; |
|
247
|
9
|
|
|
|
|
13
|
my $host = $_[0]; |
|
248
|
9
|
100
|
|
|
|
34
|
return $cache_sock{$host} if $cache_sock{$host}; |
|
249
|
|
|
|
|
|
|
|
|
250
|
3
|
|
|
|
|
11
|
my $now = time(); |
|
251
|
3
|
|
|
|
|
34
|
my ($ip, $port) = $host =~ /(.*):(\d+)$/; |
|
252
|
3
|
50
|
|
|
|
18
|
if (defined($ip)) { |
|
253
|
3
|
|
|
|
|
10
|
$ip =~ s/[\[\]]//g; # get rid of optional IPv6 brackets |
|
254
|
|
|
|
|
|
|
} |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
return undef if |
|
257
|
3
|
100
|
66
|
|
|
59
|
$host_dead{$host} && $host_dead{$host} > $now; |
|
258
|
2
|
|
|
|
|
4
|
my $sock; |
|
259
|
|
|
|
|
|
|
|
|
260
|
2
|
|
|
|
|
9
|
my $connected = 0; |
|
261
|
2
|
|
|
|
|
12
|
my $sin; |
|
262
|
2
|
|
33
|
|
|
1688
|
my $proto = $PROTO_TCP ||= getprotobyname('tcp'); |
|
263
|
|
|
|
|
|
|
|
|
264
|
2
|
50
|
|
|
|
15
|
if ( index($host, '/') != 0 ) |
|
265
|
|
|
|
|
|
|
{ |
|
266
|
|
|
|
|
|
|
# if a preferred IP is known, try that first. |
|
267
|
2
|
50
|
33
|
|
|
68
|
if ($self && $self->{pref_ip}{$ip}) { |
|
268
|
0
|
|
|
|
|
0
|
my $prefip = $self->{pref_ip}{$ip}; |
|
269
|
0
|
0
|
0
|
|
|
0
|
if ($HAVE_SOCKET6 && index($prefip, ':') != -1) { |
|
270
|
9
|
|
|
9
|
|
58
|
no strict 'subs'; # for PF_INET6 and AF_INET6, weirdly imported |
|
|
9
|
|
|
|
|
17
|
|
|
|
9
|
|
|
|
|
2094
|
|
|
271
|
0
|
|
|
|
|
0
|
socket($sock, PF_INET6, SOCK_STREAM, $proto); |
|
272
|
0
|
|
|
|
|
0
|
$sock_map{$sock} = $host; |
|
273
|
0
|
|
|
|
|
0
|
$sin = Socket6::pack_sockaddr_in6($port, |
|
274
|
|
|
|
|
|
|
Socket6::inet_pton(AF_INET6, $prefip)); |
|
275
|
|
|
|
|
|
|
} else { |
|
276
|
0
|
|
|
|
|
0
|
socket($sock, PF_INET, SOCK_STREAM, $proto); |
|
277
|
0
|
|
|
|
|
0
|
$sock_map{$sock} = $host; |
|
278
|
0
|
|
|
|
|
0
|
$sin = Socket::sockaddr_in($port, Socket::inet_aton($prefip)); |
|
279
|
|
|
|
|
|
|
} |
|
280
|
|
|
|
|
|
|
|
|
281
|
0
|
0
|
|
|
|
0
|
if (_connect_sock($sock,$sin,$self->{connect_timeout})) { |
|
282
|
0
|
|
|
|
|
0
|
$connected = 1; |
|
283
|
|
|
|
|
|
|
} else { |
|
284
|
0
|
0
|
|
|
|
0
|
if (my $cb = $self->{cb_connect_fail}) { |
|
285
|
0
|
|
|
|
|
0
|
$cb->($prefip); |
|
286
|
|
|
|
|
|
|
} |
|
287
|
0
|
|
|
|
|
0
|
close $sock; |
|
288
|
|
|
|
|
|
|
} |
|
289
|
|
|
|
|
|
|
} |
|
290
|
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
# normal path, or fallback path if preferred IP failed |
|
292
|
2
|
50
|
|
|
|
9
|
unless ($connected) { |
|
293
|
2
|
50
|
33
|
|
|
16
|
if ($HAVE_SOCKET6 && index($ip, ':') != -1) { |
|
294
|
9
|
|
|
9
|
|
49
|
no strict 'subs'; # for PF_INET6 and AF_INET6, weirdly imported |
|
|
9
|
|
|
|
|
15
|
|
|
|
9
|
|
|
|
|
17000
|
|
|
295
|
0
|
|
|
|
|
0
|
socket($sock, PF_INET6, SOCK_STREAM, $proto); |
|
296
|
0
|
|
|
|
|
0
|
$sock_map{$sock} = $host; |
|
297
|
0
|
|
|
|
|
0
|
$sin = Socket6::pack_sockaddr_in6($port, |
|
298
|
|
|
|
|
|
|
Socket6::inet_pton(AF_INET6, $ip)); |
|
299
|
|
|
|
|
|
|
} else { |
|
300
|
2
|
|
|
|
|
83
|
socket($sock, PF_INET, SOCK_STREAM, $proto); |
|
301
|
2
|
|
|
|
|
95
|
$sock_map{$sock} = $host; |
|
302
|
2
|
|
|
|
|
49
|
$sin = Socket::sockaddr_in($port, Socket::inet_aton($ip)); |
|
303
|
|
|
|
|
|
|
} |
|
304
|
|
|
|
|
|
|
|
|
305
|
2
|
50
|
|
|
|
34
|
my $timeout = $self ? $self->{connect_timeout} : 0.25; |
|
306
|
2
|
100
|
|
|
|
8
|
unless (_connect_sock($sock, $sin, $timeout)) { |
|
307
|
1
|
50
|
|
|
|
10
|
my $cb = $self ? $self->{cb_connect_fail} : undef; |
|
308
|
1
|
50
|
|
|
|
4
|
$cb->($ip) if $cb; |
|
309
|
1
|
|
|
|
|
83
|
return _dead_sock($self, $sock, undef, 20 + int(rand(10))); |
|
310
|
|
|
|
|
|
|
} |
|
311
|
|
|
|
|
|
|
} |
|
312
|
|
|
|
|
|
|
} else { # it's a unix domain/local socket |
|
313
|
0
|
|
|
|
|
0
|
socket($sock, PF_UNIX, SOCK_STREAM, 0); |
|
314
|
0
|
|
|
|
|
0
|
$sock_map{$sock} = $host; |
|
315
|
0
|
|
|
|
|
0
|
$sin = Socket::sockaddr_un($host); |
|
316
|
0
|
0
|
|
|
|
0
|
my $timeout = $self ? $self->{connect_timeout} : 0.25; |
|
317
|
0
|
0
|
|
|
|
0
|
unless (_connect_sock($sock,$sin,$timeout)) { |
|
318
|
0
|
0
|
|
|
|
0
|
my $cb = $self ? $self->{cb_connect_fail} : undef; |
|
319
|
0
|
0
|
|
|
|
0
|
$cb->($host) if $cb; |
|
320
|
0
|
|
|
|
|
0
|
return _dead_sock($self, $sock, undef, 20 + int(rand(10))); |
|
321
|
|
|
|
|
|
|
} |
|
322
|
|
|
|
|
|
|
} |
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
# make the new socket not buffer writes. |
|
325
|
1
|
|
|
|
|
6
|
my $old = select($sock); |
|
326
|
1
|
|
|
|
|
7
|
$| = 1; |
|
327
|
1
|
|
|
|
|
3
|
select($old); |
|
328
|
|
|
|
|
|
|
|
|
329
|
1
|
|
|
|
|
4
|
$cache_sock{$host} = $sock; |
|
330
|
|
|
|
|
|
|
|
|
331
|
1
|
|
|
|
|
2
|
return $sock; |
|
332
|
|
|
|
|
|
|
} |
|
333
|
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
sub get_sock { # (key) |
|
335
|
2
|
|
|
2
|
0
|
5
|
my Cache::Memcached $self = $_[0]; |
|
336
|
2
|
|
|
|
|
4
|
my $key = $_[1]; |
|
337
|
2
|
50
|
|
|
|
13
|
return $self->sock_to_host($self->{'_single_sock'}) if $self->{'_single_sock'}; |
|
338
|
0
|
0
|
|
|
|
0
|
return undef unless $self->{'active'}; |
|
339
|
0
|
0
|
|
|
|
0
|
my $hv = ref $key ? int($key->[0]) : _hashfunc($key); |
|
340
|
|
|
|
|
|
|
|
|
341
|
0
|
0
|
|
|
|
0
|
my $real_key = ref $key ? $key->[1] : $key; |
|
342
|
0
|
|
|
|
|
0
|
my $tries = 0; |
|
343
|
0
|
|
|
|
|
0
|
while ($tries++ < 20) { |
|
344
|
0
|
|
|
|
|
0
|
my $host = $self->{'buckets'}->[$hv % $self->{'bucketcount'}]; |
|
345
|
0
|
|
|
|
|
0
|
my $sock = $self->sock_to_host($host); |
|
346
|
0
|
0
|
|
|
|
0
|
return $sock if $sock; |
|
347
|
0
|
0
|
|
|
|
0
|
return undef if $self->{'no_rehash'}; |
|
348
|
0
|
|
|
|
|
0
|
$hv += _hashfunc($tries . $real_key); # stupid, but works |
|
349
|
|
|
|
|
|
|
} |
|
350
|
0
|
|
|
|
|
0
|
return undef; |
|
351
|
|
|
|
|
|
|
} |
|
352
|
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
sub init_buckets { |
|
354
|
2
|
|
|
2
|
0
|
16
|
my Cache::Memcached $self = shift; |
|
355
|
2
|
50
|
|
|
|
11
|
return if $self->{'buckets'}; |
|
356
|
2
|
|
|
|
|
5
|
my $bu = $self->{'buckets'} = []; |
|
357
|
2
|
|
|
|
|
5
|
foreach my $v (@{$self->{'servers'}}) { |
|
|
2
|
|
|
|
|
13
|
|
|
358
|
2
|
50
|
|
|
|
9
|
if (ref $v eq "ARRAY") { |
|
359
|
0
|
|
|
|
|
0
|
for (1..$v->[1]) { push @$bu, $v->[0]; } |
|
|
0
|
|
|
|
|
0
|
|
|
360
|
|
|
|
|
|
|
} else { |
|
361
|
2
|
|
|
|
|
7
|
push @$bu, $v; |
|
362
|
|
|
|
|
|
|
} |
|
363
|
|
|
|
|
|
|
} |
|
364
|
2
|
|
|
|
|
4
|
$self->{'bucketcount'} = scalar @{$self->{'buckets'}}; |
|
|
2
|
|
|
|
|
7
|
|
|
365
|
|
|
|
|
|
|
} |
|
366
|
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
sub disconnect_all { |
|
368
|
0
|
|
|
0
|
1
|
0
|
my Cache::Memcached $self = shift; |
|
369
|
0
|
|
|
|
|
0
|
my $sock; |
|
370
|
0
|
|
|
|
|
0
|
foreach $sock (values %cache_sock) { |
|
371
|
0
|
|
|
|
|
0
|
close $sock; |
|
372
|
|
|
|
|
|
|
} |
|
373
|
0
|
|
|
|
|
0
|
%cache_sock = (); |
|
374
|
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
# We need to globally recalculate our buck2sock in all objects, so we increment the global generation. |
|
376
|
0
|
|
|
|
|
0
|
$socket_cache_generation++; |
|
377
|
|
|
|
|
|
|
} |
|
378
|
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
# writes a line, then reads result. by default stops reading after a |
|
380
|
|
|
|
|
|
|
# single line, but caller can override the $check_complete subref, |
|
381
|
|
|
|
|
|
|
# which gets passed a scalarref of buffer read thus far. |
|
382
|
|
|
|
|
|
|
sub _write_and_read { |
|
383
|
7
|
|
|
7
|
|
10
|
my Cache::Memcached $self = shift; |
|
384
|
7
|
|
|
|
|
13
|
my ($sock, $line, $check_complete) = @_; |
|
385
|
7
|
|
|
|
|
8
|
my $res; |
|
386
|
7
|
|
|
|
|
12
|
my ($ret, $offset) = (undef, 0); |
|
387
|
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
$check_complete ||= sub { |
|
389
|
7
|
|
|
7
|
|
31
|
return (rindex($ret, "\r\n") + 2 == length($ret)); |
|
390
|
7
|
|
50
|
|
|
64
|
}; |
|
391
|
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
# state: 0 - writing, 1 - reading, 2 - done |
|
393
|
7
|
|
|
|
|
9
|
my $state = 0; |
|
394
|
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
# the bitsets for select |
|
396
|
7
|
|
|
|
|
9
|
my ($rin, $rout, $win, $wout); |
|
397
|
0
|
|
|
|
|
0
|
my $nfound; |
|
398
|
|
|
|
|
|
|
|
|
399
|
7
|
|
|
|
|
9
|
my $copy_state = -1; |
|
400
|
7
|
50
|
|
|
|
18
|
local $SIG{'PIPE'} = "IGNORE" unless $FLAG_NOSIGNAL; |
|
401
|
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
# the select loop |
|
403
|
7
|
|
|
|
|
10
|
while(1) { |
|
404
|
21
|
50
|
|
|
|
52
|
if ($copy_state!=$state) { |
|
405
|
21
|
100
|
|
|
|
47
|
last if $state==2; |
|
406
|
14
|
|
|
|
|
26
|
($rin, $win) = ('', ''); |
|
407
|
14
|
100
|
|
|
|
40
|
vec($rin, fileno($sock), 1) = 1 if $state==1; |
|
408
|
14
|
100
|
|
|
|
48
|
vec($win, fileno($sock), 1) = 1 if $state==0; |
|
409
|
14
|
|
|
|
|
22
|
$copy_state = $state; |
|
410
|
|
|
|
|
|
|
} |
|
411
|
14
|
|
|
|
|
371
|
$nfound = select($rout=$rin, $wout=$win, undef, |
|
412
|
|
|
|
|
|
|
$self->{'select_timeout'}); |
|
413
|
14
|
50
|
|
|
|
38
|
last unless $nfound; |
|
414
|
|
|
|
|
|
|
|
|
415
|
14
|
100
|
|
|
|
36
|
if (vec($wout, fileno($sock), 1)) { |
|
416
|
7
|
|
|
|
|
443
|
$res = send($sock, $line, $FLAG_NOSIGNAL); |
|
417
|
|
|
|
|
|
|
next |
|
418
|
7
|
50
|
33
|
|
|
26
|
if not defined $res and $!==EWOULDBLOCK; |
|
419
|
7
|
50
|
|
|
|
16
|
unless ($res > 0) { |
|
420
|
0
|
|
|
|
|
0
|
$self->_close_sock($sock); |
|
421
|
0
|
|
|
|
|
0
|
return undef; |
|
422
|
|
|
|
|
|
|
} |
|
423
|
7
|
50
|
|
|
|
16
|
if ($res == length($line)) { # all sent |
|
424
|
7
|
|
|
|
|
12
|
$state = 1; |
|
425
|
|
|
|
|
|
|
} else { # we only succeeded in sending some of it |
|
426
|
0
|
|
|
|
|
0
|
substr($line, 0, $res, ''); # delete the part we sent |
|
427
|
|
|
|
|
|
|
} |
|
428
|
|
|
|
|
|
|
} |
|
429
|
|
|
|
|
|
|
|
|
430
|
14
|
100
|
|
|
|
40
|
if (vec($rout, fileno($sock), 1)) { |
|
431
|
7
|
|
|
|
|
52
|
$res = sysread($sock, $ret, 255, $offset); |
|
432
|
|
|
|
|
|
|
next |
|
433
|
7
|
50
|
33
|
|
|
24
|
if !defined($res) and $!==EWOULDBLOCK; |
|
434
|
7
|
50
|
|
|
|
18
|
if ($res == 0) { # catches 0=conn closed or undef=error |
|
435
|
0
|
|
|
|
|
0
|
$self->_close_sock($sock); |
|
436
|
0
|
|
|
|
|
0
|
return undef; |
|
437
|
|
|
|
|
|
|
} |
|
438
|
7
|
|
|
|
|
8
|
$offset += $res; |
|
439
|
7
|
50
|
|
|
|
19
|
$state = 2 if $check_complete->(\$ret); |
|
440
|
|
|
|
|
|
|
} |
|
441
|
|
|
|
|
|
|
} |
|
442
|
|
|
|
|
|
|
|
|
443
|
7
|
50
|
|
|
|
15
|
unless ($state == 2) { |
|
444
|
0
|
|
|
|
|
0
|
$self->_dead_sock($sock); # improperly finished |
|
445
|
0
|
|
|
|
|
0
|
return undef; |
|
446
|
|
|
|
|
|
|
} |
|
447
|
|
|
|
|
|
|
|
|
448
|
7
|
|
|
|
|
53
|
return $ret; |
|
449
|
|
|
|
|
|
|
} |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
sub delete { |
|
452
|
0
|
|
|
0
|
1
|
0
|
my Cache::Memcached $self = shift; |
|
453
|
0
|
|
|
|
|
0
|
my ($key, $time) = @_; |
|
454
|
0
|
0
|
0
|
|
|
0
|
return 0 if ! $self->{'active'} || $self->{'readonly'}; |
|
455
|
0
|
0
|
|
|
|
0
|
my $stime = Time::HiRes::time() if $self->{'stat_callback'}; |
|
456
|
0
|
|
|
|
|
0
|
my $sock = $self->get_sock($key); |
|
457
|
0
|
0
|
|
|
|
0
|
return 0 unless $sock; |
|
458
|
|
|
|
|
|
|
|
|
459
|
0
|
|
|
|
|
0
|
$self->{'stats'}->{"delete"}++; |
|
460
|
0
|
0
|
|
|
|
0
|
$key = ref $key ? $key->[1] : $key; |
|
461
|
0
|
0
|
|
|
|
0
|
$time = $time ? " $time" : ""; |
|
462
|
0
|
|
|
|
|
0
|
my $cmd = "delete $self->{namespace}$key$time\r\n"; |
|
463
|
0
|
|
|
|
|
0
|
my $res = _write_and_read($self, $sock, $cmd); |
|
464
|
|
|
|
|
|
|
|
|
465
|
0
|
0
|
|
|
|
0
|
if ($self->{'stat_callback'}) { |
|
466
|
0
|
|
|
|
|
0
|
my $etime = Time::HiRes::time(); |
|
467
|
0
|
|
|
|
|
0
|
$self->{'stat_callback'}->($stime, $etime, $sock, 'delete'); |
|
468
|
|
|
|
|
|
|
} |
|
469
|
|
|
|
|
|
|
|
|
470
|
0
|
|
0
|
|
|
0
|
return defined $res && $res eq "DELETED\r\n"; |
|
471
|
|
|
|
|
|
|
} |
|
472
|
|
|
|
|
|
|
*remove = \&delete; |
|
473
|
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
sub add { |
|
475
|
0
|
|
|
0
|
1
|
0
|
_set("add", @_); |
|
476
|
|
|
|
|
|
|
} |
|
477
|
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
sub replace { |
|
479
|
0
|
|
|
0
|
1
|
0
|
_set("replace", @_); |
|
480
|
|
|
|
|
|
|
} |
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
sub set { |
|
483
|
2
|
|
|
2
|
1
|
930
|
_set("set", @_); |
|
484
|
|
|
|
|
|
|
} |
|
485
|
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
sub append { |
|
487
|
0
|
|
|
0
|
0
|
0
|
_set("append", @_); |
|
488
|
|
|
|
|
|
|
} |
|
489
|
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
sub prepend { |
|
491
|
0
|
|
|
0
|
0
|
0
|
_set("prepend", @_); |
|
492
|
|
|
|
|
|
|
} |
|
493
|
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
sub _set { |
|
495
|
2
|
|
|
2
|
|
4
|
my $cmdname = shift; |
|
496
|
2
|
|
|
|
|
5
|
my Cache::Memcached $self = shift; |
|
497
|
2
|
|
|
|
|
5
|
my ($key, $val, $exptime) = @_; |
|
498
|
2
|
50
|
33
|
|
|
19
|
return 0 if ! $self->{'active'} || $self->{'readonly'}; |
|
499
|
2
|
50
|
|
|
|
8
|
my $stime = Time::HiRes::time() if $self->{'stat_callback'}; |
|
500
|
2
|
|
|
|
|
9
|
my $sock = $self->get_sock($key); |
|
501
|
2
|
50
|
|
|
|
14
|
return 0 unless $sock; |
|
502
|
|
|
|
|
|
|
|
|
503
|
9
|
|
|
9
|
|
71
|
use bytes; # return bytes from length() |
|
|
9
|
|
|
|
|
17
|
|
|
|
9
|
|
|
|
|
83
|
|
|
504
|
|
|
|
|
|
|
|
|
505
|
0
|
0
|
0
|
|
|
0
|
my $app_or_prep = $cmdname eq 'append' || $cmdname eq 'prepend' ? 1 : 0; |
|
506
|
0
|
|
|
|
|
0
|
$self->{'stats'}->{$cmdname}++; |
|
507
|
0
|
|
|
|
|
0
|
my $flags = 0; |
|
508
|
0
|
0
|
|
|
|
0
|
$key = ref $key ? $key->[1] : $key; |
|
509
|
|
|
|
|
|
|
|
|
510
|
0
|
0
|
|
|
|
0
|
if (ref $val) { |
|
511
|
0
|
0
|
|
|
|
0
|
die "append or prepend cannot take a reference" if $app_or_prep; |
|
512
|
0
|
|
|
|
|
0
|
local $Carp::CarpLevel = 2; |
|
513
|
0
|
|
|
|
|
0
|
$val = Storable::nfreeze($val); |
|
514
|
0
|
|
|
|
|
0
|
$flags |= F_STORABLE; |
|
515
|
|
|
|
|
|
|
} |
|
516
|
0
|
0
|
|
|
|
0
|
warn "value for memkey:$key is not defined" unless defined $val; |
|
517
|
|
|
|
|
|
|
|
|
518
|
0
|
|
|
|
|
0
|
my $len = length($val); |
|
519
|
|
|
|
|
|
|
|
|
520
|
0
|
0
|
0
|
|
|
0
|
if ($self->{'compress_threshold'} && $HAVE_ZLIB && $self->{'compress_enable'} && |
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
521
|
|
|
|
|
|
|
$len >= $self->{'compress_threshold'} && !$app_or_prep) { |
|
522
|
|
|
|
|
|
|
|
|
523
|
0
|
|
|
|
|
0
|
my $c_val = Compress::Zlib::memGzip($val); |
|
524
|
0
|
|
|
|
|
0
|
my $c_len = length($c_val); |
|
525
|
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
# do we want to keep it? |
|
527
|
0
|
0
|
|
|
|
0
|
if ($c_len < $len*(1 - COMPRESS_SAVINGS)) { |
|
528
|
0
|
|
|
|
|
0
|
$val = $c_val; |
|
529
|
0
|
|
|
|
|
0
|
$len = $c_len; |
|
530
|
0
|
|
|
|
|
0
|
$flags |= F_COMPRESS; |
|
531
|
|
|
|
|
|
|
} |
|
532
|
|
|
|
|
|
|
} |
|
533
|
|
|
|
|
|
|
|
|
534
|
0
|
|
0
|
|
|
0
|
$exptime = int($exptime || 0); |
|
535
|
|
|
|
|
|
|
|
|
536
|
0
|
0
|
|
|
|
0
|
local $SIG{'PIPE'} = "IGNORE" unless $FLAG_NOSIGNAL; |
|
537
|
0
|
|
|
|
|
0
|
my $line = "$cmdname $self->{namespace}$key $flags $exptime $len\r\n$val\r\n"; |
|
538
|
|
|
|
|
|
|
|
|
539
|
0
|
|
|
|
|
0
|
my $res = _write_and_read($self, $sock, $line); |
|
540
|
|
|
|
|
|
|
|
|
541
|
0
|
0
|
0
|
|
|
0
|
if ($self->{'debug'} && $line) { |
|
542
|
0
|
|
|
|
|
0
|
chop $line; chop $line; |
|
|
0
|
|
|
|
|
0
|
|
|
543
|
0
|
|
|
|
|
0
|
print STDERR "Cache::Memcache: $cmdname $self->{namespace}$key = $val ($line)\n"; |
|
544
|
|
|
|
|
|
|
} |
|
545
|
|
|
|
|
|
|
|
|
546
|
0
|
0
|
|
|
|
0
|
if ($self->{'stat_callback'}) { |
|
547
|
0
|
|
|
|
|
0
|
my $etime = Time::HiRes::time(); |
|
548
|
0
|
|
|
|
|
0
|
$self->{'stat_callback'}->($stime, $etime, $sock, $cmdname); |
|
549
|
|
|
|
|
|
|
} |
|
550
|
|
|
|
|
|
|
|
|
551
|
0
|
|
0
|
|
|
0
|
return defined $res && $res eq "STORED\r\n"; |
|
552
|
|
|
|
|
|
|
} |
|
553
|
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
sub incr { |
|
555
|
0
|
|
|
0
|
1
|
0
|
_incrdecr("incr", @_); |
|
556
|
|
|
|
|
|
|
} |
|
557
|
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
sub decr { |
|
559
|
0
|
|
|
0
|
1
|
0
|
_incrdecr("decr", @_); |
|
560
|
|
|
|
|
|
|
} |
|
561
|
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
sub _incrdecr { |
|
563
|
0
|
|
|
0
|
|
0
|
my $cmdname = shift; |
|
564
|
0
|
|
|
|
|
0
|
my Cache::Memcached $self = shift; |
|
565
|
0
|
|
|
|
|
0
|
my ($key, $value) = @_; |
|
566
|
0
|
0
|
0
|
|
|
0
|
return undef if ! $self->{'active'} || $self->{'readonly'}; |
|
567
|
0
|
0
|
|
|
|
0
|
my $stime = Time::HiRes::time() if $self->{'stat_callback'}; |
|
568
|
0
|
|
|
|
|
0
|
my $sock = $self->get_sock($key); |
|
569
|
0
|
0
|
|
|
|
0
|
return undef unless $sock; |
|
570
|
0
|
0
|
|
|
|
0
|
$key = $key->[1] if ref $key; |
|
571
|
0
|
|
|
|
|
0
|
$self->{'stats'}->{$cmdname}++; |
|
572
|
0
|
0
|
|
|
|
0
|
$value = 1 unless defined $value; |
|
573
|
|
|
|
|
|
|
|
|
574
|
0
|
|
|
|
|
0
|
my $line = "$cmdname $self->{namespace}$key $value\r\n"; |
|
575
|
0
|
|
|
|
|
0
|
my $res = _write_and_read($self, $sock, $line); |
|
576
|
|
|
|
|
|
|
|
|
577
|
0
|
0
|
|
|
|
0
|
if ($self->{'stat_callback'}) { |
|
578
|
0
|
|
|
|
|
0
|
my $etime = Time::HiRes::time(); |
|
579
|
0
|
|
|
|
|
0
|
$self->{'stat_callback'}->($stime, $etime, $sock, $cmdname); |
|
580
|
|
|
|
|
|
|
} |
|
581
|
|
|
|
|
|
|
|
|
582
|
0
|
0
|
0
|
|
|
0
|
return undef unless defined $res && $res =~ /^(\d+)/; |
|
583
|
0
|
|
|
|
|
0
|
return $1; |
|
584
|
|
|
|
|
|
|
} |
|
585
|
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
sub get { |
|
587
|
0
|
|
|
0
|
1
|
0
|
my Cache::Memcached $self = $_[0]; |
|
588
|
0
|
|
|
|
|
0
|
my $key = $_[1]; |
|
589
|
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
# TODO: make a fast path for this? or just keep using get_multi? |
|
591
|
0
|
|
|
|
|
0
|
my $r = $self->get_multi($key); |
|
592
|
0
|
0
|
|
|
|
0
|
my $kval = ref $key ? $key->[1] : $key; |
|
593
|
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
# key reconstituted from server won't have utf8 on, so turn it off on input |
|
595
|
|
|
|
|
|
|
# scalar to allow hash lookup to succeed |
|
596
|
0
|
0
|
|
|
|
0
|
Encode::_utf8_off($kval) if Encode::is_utf8($kval); |
|
597
|
|
|
|
|
|
|
|
|
598
|
0
|
|
|
|
|
0
|
return $r->{$kval}; |
|
599
|
|
|
|
|
|
|
} |
|
600
|
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
sub get_multi { |
|
602
|
0
|
|
|
0
|
1
|
0
|
my Cache::Memcached $self = shift; |
|
603
|
0
|
0
|
|
|
|
0
|
return {} unless $self->{'active'}; |
|
604
|
0
|
0
|
|
|
|
0
|
$self->{'_stime'} = Time::HiRes::time() if $self->{'stat_callback'}; |
|
605
|
0
|
|
|
|
|
0
|
$self->{'stats'}->{"get_multi"}++; |
|
606
|
|
|
|
|
|
|
|
|
607
|
0
|
|
|
|
|
0
|
my %val; # what we'll be returning a reference to (realkey -> value) |
|
608
|
|
|
|
|
|
|
my %sock_keys; # sockref_as_scalar -> [ realkeys ] |
|
609
|
0
|
|
|
|
|
0
|
my $sock; |
|
610
|
|
|
|
|
|
|
|
|
611
|
0
|
0
|
|
|
|
0
|
if ($self->{'_single_sock'}) { |
|
612
|
0
|
|
|
|
|
0
|
$sock = $self->sock_to_host($self->{'_single_sock'}); |
|
613
|
0
|
0
|
|
|
|
0
|
unless ($sock) { |
|
614
|
0
|
|
|
|
|
0
|
return {}; |
|
615
|
|
|
|
|
|
|
} |
|
616
|
0
|
|
|
|
|
0
|
foreach my $key (@_) { |
|
617
|
0
|
0
|
|
|
|
0
|
my $kval = ref $key ? $key->[1] : $key; |
|
618
|
0
|
|
|
|
|
0
|
push @{$sock_keys{$sock}}, $kval; |
|
|
0
|
|
|
|
|
0
|
|
|
619
|
|
|
|
|
|
|
} |
|
620
|
|
|
|
|
|
|
} else { |
|
621
|
0
|
|
|
|
|
0
|
my $bcount = $self->{'bucketcount'}; |
|
622
|
0
|
|
|
|
|
0
|
my $sock; |
|
623
|
|
|
|
|
|
|
|
|
624
|
0
|
0
|
|
|
|
0
|
if ($self->{'buck2sock_generation'} != $socket_cache_generation) { |
|
625
|
0
|
|
|
|
|
0
|
$self->{'buck2sock_generation'} = $socket_cache_generation; |
|
626
|
0
|
|
|
|
|
0
|
$self->{'buck2sock'} = []; |
|
627
|
|
|
|
|
|
|
} |
|
628
|
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
KEY: |
|
630
|
0
|
|
|
|
|
0
|
foreach my $key (@_) { |
|
631
|
0
|
0
|
|
|
|
0
|
my ($hv, $real_key) = ref $key ? |
|
632
|
|
|
|
|
|
|
(int($key->[0]), $key->[1]) : |
|
633
|
|
|
|
|
|
|
((crc32($key) >> 16) & 0x7fff, $key); |
|
634
|
|
|
|
|
|
|
|
|
635
|
0
|
|
|
|
|
0
|
my $tries; |
|
636
|
0
|
|
|
|
|
0
|
while (1) { |
|
637
|
0
|
|
|
|
|
0
|
my $bucket = $hv % $bcount; |
|
638
|
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
# this segfaults perl 5.8.4 (and others?) if sock_to_host returns undef... wtf? |
|
640
|
|
|
|
|
|
|
#$sock = $buck2sock[$bucket] ||= $self->sock_to_host($self->{buckets}[ $bucket ]) |
|
641
|
|
|
|
|
|
|
# and last; |
|
642
|
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
# but this variant doesn't crash: |
|
644
|
0
|
|
0
|
|
|
0
|
$sock = $self->{'buck2sock'}->[$bucket] || $self->sock_to_host($self->{buckets}[ $bucket ]); |
|
645
|
0
|
0
|
|
|
|
0
|
if ($sock) { |
|
646
|
0
|
|
|
|
|
0
|
$self->{'buck2sock'}->[$bucket] = $sock; |
|
647
|
0
|
|
|
|
|
0
|
last; |
|
648
|
|
|
|
|
|
|
} |
|
649
|
|
|
|
|
|
|
|
|
650
|
0
|
0
|
|
|
|
0
|
next KEY if $tries++ >= 20; |
|
651
|
0
|
|
|
|
|
0
|
$hv += _hashfunc($tries . $real_key); |
|
652
|
|
|
|
|
|
|
} |
|
653
|
|
|
|
|
|
|
|
|
654
|
0
|
|
|
|
|
0
|
push @{$sock_keys{$sock}}, $real_key; |
|
|
0
|
|
|
|
|
0
|
|
|
655
|
|
|
|
|
|
|
} |
|
656
|
|
|
|
|
|
|
} |
|
657
|
|
|
|
|
|
|
|
|
658
|
0
|
|
|
|
|
0
|
$self->{'stats'}->{"get_keys"} += @_; |
|
659
|
0
|
|
|
|
|
0
|
$self->{'stats'}->{"get_socks"} += keys %sock_keys; |
|
660
|
|
|
|
|
|
|
|
|
661
|
0
|
0
|
|
|
|
0
|
local $SIG{'PIPE'} = "IGNORE" unless $FLAG_NOSIGNAL; |
|
662
|
|
|
|
|
|
|
|
|
663
|
0
|
|
|
|
|
0
|
_load_multi($self, \%sock_keys, \%val); |
|
664
|
|
|
|
|
|
|
|
|
665
|
0
|
0
|
|
|
|
0
|
if ($self->{'debug'}) { |
|
666
|
0
|
|
|
|
|
0
|
while (my ($k, $v) = each %val) { |
|
667
|
0
|
|
|
|
|
0
|
print STDERR "MemCache: got $k = $v\n"; |
|
668
|
|
|
|
|
|
|
} |
|
669
|
|
|
|
|
|
|
} |
|
670
|
0
|
|
|
|
|
0
|
return \%val; |
|
671
|
|
|
|
|
|
|
} |
|
672
|
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
sub _load_multi { |
|
674
|
9
|
|
|
9
|
|
15524
|
use bytes; # return bytes from length() |
|
|
9
|
|
|
|
|
18
|
|
|
|
9
|
|
|
|
|
43
|
|
|
675
|
0
|
|
|
0
|
|
0
|
my Cache::Memcached $self; |
|
676
|
0
|
|
|
|
|
0
|
my ($sock_keys, $ret); |
|
677
|
|
|
|
|
|
|
|
|
678
|
0
|
|
|
|
|
0
|
($self, $sock_keys, $ret) = @_; |
|
679
|
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
# all keyed by $sockstr: |
|
681
|
0
|
|
|
|
|
0
|
my %reading; # $sockstr -> $sock. bool, whether we're reading from this socket |
|
682
|
|
|
|
|
|
|
my %writing; # $sockstr -> $sock. bool, whether we're writing to this socket |
|
683
|
0
|
|
|
|
|
0
|
my %buf; # buffers, for writing |
|
684
|
|
|
|
|
|
|
|
|
685
|
0
|
|
|
|
|
0
|
my %parser; # $sockstr -> Cache::Memcached::GetParser |
|
686
|
|
|
|
|
|
|
|
|
687
|
0
|
|
|
|
|
0
|
my $active_changed = 1; # force rebuilding of select sets |
|
688
|
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
my $dead = sub { |
|
690
|
0
|
|
|
0
|
|
0
|
my $sock = shift; |
|
691
|
0
|
0
|
|
|
|
0
|
print STDERR "killing socket $sock\n" if $self->{'debug'} >= 2; |
|
692
|
0
|
|
|
|
|
0
|
delete $reading{$sock}; |
|
693
|
0
|
|
|
|
|
0
|
delete $writing{$sock}; |
|
694
|
|
|
|
|
|
|
|
|
695
|
0
|
0
|
|
|
|
0
|
if (my $p = $parser{$sock}) { |
|
696
|
0
|
|
|
|
|
0
|
my $key = $p->current_key; |
|
697
|
0
|
0
|
|
|
|
0
|
delete $ret->{$key} if $key; |
|
698
|
|
|
|
|
|
|
} |
|
699
|
|
|
|
|
|
|
|
|
700
|
0
|
0
|
|
|
|
0
|
if ($self->{'stat_callback'}) { |
|
701
|
0
|
|
|
|
|
0
|
my $etime = Time::HiRes::time(); |
|
702
|
0
|
|
|
|
|
0
|
$self->{'stat_callback'}->($self->{'_stime'}, $etime, $sock, 'get_multi'); |
|
703
|
|
|
|
|
|
|
} |
|
704
|
|
|
|
|
|
|
|
|
705
|
0
|
|
|
|
|
0
|
close $sock; |
|
706
|
0
|
|
|
|
|
0
|
$self->_dead_sock($sock); |
|
707
|
0
|
|
|
|
|
0
|
}; |
|
708
|
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
# $finalize->($key, $flags) |
|
710
|
|
|
|
|
|
|
# $finalize->({ $key => $flags, $key => $flags }); |
|
711
|
|
|
|
|
|
|
my $finalize = sub { |
|
712
|
0
|
|
|
0
|
|
0
|
my $map = $_[0]; |
|
713
|
0
|
0
|
|
|
|
0
|
$map = {@_} unless ref $map; |
|
714
|
|
|
|
|
|
|
|
|
715
|
0
|
|
|
|
|
0
|
while (my ($k, $flags) = each %$map) { |
|
716
|
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
# remove trailing \r\n |
|
718
|
0
|
|
|
|
|
0
|
chop $ret->{$k}; chop $ret->{$k}; |
|
|
0
|
|
|
|
|
0
|
|
|
719
|
|
|
|
|
|
|
|
|
720
|
0
|
0
|
0
|
|
|
0
|
$ret->{$k} = Compress::Zlib::memGunzip($ret->{$k}) |
|
721
|
|
|
|
|
|
|
if $HAVE_ZLIB && $flags & F_COMPRESS; |
|
722
|
0
|
0
|
|
|
|
0
|
if ($flags & F_STORABLE) { |
|
723
|
|
|
|
|
|
|
# wrapped in eval in case a perl 5.6 Storable tries to |
|
724
|
|
|
|
|
|
|
# unthaw data from a perl 5.8 Storable. (5.6 is stupid |
|
725
|
|
|
|
|
|
|
# and dies if the version number changes at all. in 5.8 |
|
726
|
|
|
|
|
|
|
# they made it only die if it unencounters a new feature) |
|
727
|
0
|
|
|
|
|
0
|
eval { |
|
728
|
0
|
|
|
|
|
0
|
$ret->{$k} = Storable::thaw($ret->{$k}); |
|
729
|
|
|
|
|
|
|
}; |
|
730
|
|
|
|
|
|
|
# so if there was a problem, just treat it as a cache miss. |
|
731
|
0
|
0
|
|
|
|
0
|
if ($@) { |
|
732
|
0
|
|
|
|
|
0
|
delete $ret->{$k}; |
|
733
|
|
|
|
|
|
|
} |
|
734
|
|
|
|
|
|
|
} |
|
735
|
|
|
|
|
|
|
} |
|
736
|
0
|
|
|
|
|
0
|
}; |
|
737
|
|
|
|
|
|
|
|
|
738
|
0
|
|
|
|
|
0
|
foreach (keys %$sock_keys) { |
|
739
|
0
|
0
|
|
|
|
0
|
my $ipport = $sock_map{$_} or die "No map found matching for $_"; |
|
740
|
0
|
0
|
|
|
|
0
|
my $sock = $cache_sock{$ipport} or die "No sock found for $ipport"; |
|
741
|
0
|
0
|
|
|
|
0
|
print STDERR "processing socket $_\n" if $self->{'debug'} >= 2; |
|
742
|
0
|
|
|
|
|
0
|
$writing{$_} = $sock; |
|
743
|
0
|
0
|
|
|
|
0
|
if ($self->{namespace}) { |
|
744
|
0
|
|
|
|
|
0
|
$buf{$_} = join(" ", 'get', (map { "$self->{namespace}$_" } @{$sock_keys->{$_}}), "\r\n"); |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
745
|
|
|
|
|
|
|
} else { |
|
746
|
0
|
|
|
|
|
0
|
$buf{$_} = join(" ", 'get', @{$sock_keys->{$_}}, "\r\n"); |
|
|
0
|
|
|
|
|
0
|
|
|
747
|
|
|
|
|
|
|
} |
|
748
|
|
|
|
|
|
|
|
|
749
|
0
|
|
|
|
|
0
|
$parser{$_} = $self->{parser_class}->new($ret, $self->{namespace_len}, $finalize); |
|
750
|
|
|
|
|
|
|
} |
|
751
|
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
my $read = sub { |
|
753
|
0
|
|
|
0
|
|
0
|
my $sockstr = "$_[0]"; # $sock is $_[0]; |
|
754
|
0
|
0
|
|
|
|
0
|
my $p = $parser{$sockstr} or die; |
|
755
|
0
|
|
|
|
|
0
|
my $rv = $p->parse_from_sock($_[0]); |
|
756
|
0
|
0
|
|
|
|
0
|
if ($rv > 0) { |
|
|
|
0
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
# okay, finished with this socket |
|
758
|
0
|
|
|
|
|
0
|
delete $reading{$sockstr}; |
|
759
|
|
|
|
|
|
|
} elsif ($rv < 0) { |
|
760
|
0
|
|
|
|
|
0
|
$dead->($_[0]); |
|
761
|
|
|
|
|
|
|
} |
|
762
|
0
|
|
|
|
|
0
|
return $rv; |
|
763
|
0
|
|
|
|
|
0
|
}; |
|
764
|
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
# returns 1 when it's done, for success or error. 0 if still working. |
|
766
|
|
|
|
|
|
|
my $write = sub { |
|
767
|
0
|
|
|
0
|
|
0
|
my ($sock, $sockstr) = ($_[0], "$_[0]"); |
|
768
|
0
|
|
|
|
|
0
|
my $res; |
|
769
|
|
|
|
|
|
|
|
|
770
|
0
|
|
|
|
|
0
|
$res = send($sock, $buf{$sockstr}, $FLAG_NOSIGNAL); |
|
771
|
|
|
|
|
|
|
|
|
772
|
0
|
0
|
0
|
|
|
0
|
return 0 |
|
773
|
|
|
|
|
|
|
if not defined $res and $!==EWOULDBLOCK; |
|
774
|
0
|
0
|
|
|
|
0
|
unless ($res > 0) { |
|
775
|
0
|
|
|
|
|
0
|
$dead->($sock); |
|
776
|
0
|
|
|
|
|
0
|
return 1; |
|
777
|
|
|
|
|
|
|
} |
|
778
|
0
|
0
|
|
|
|
0
|
if ($res == length($buf{$sockstr})) { # all sent |
|
779
|
0
|
|
|
|
|
0
|
$buf{$sockstr} = ""; |
|
780
|
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
# switch the socket from writing to reading |
|
782
|
0
|
|
|
|
|
0
|
delete $writing{$sockstr}; |
|
783
|
0
|
|
|
|
|
0
|
$reading{$sockstr} = $sock; |
|
784
|
0
|
|
|
|
|
0
|
return 1; |
|
785
|
|
|
|
|
|
|
} else { # we only succeeded in sending some of it |
|
786
|
0
|
|
|
|
|
0
|
substr($buf{$sockstr}, 0, $res, ''); # delete the part we sent |
|
787
|
|
|
|
|
|
|
} |
|
788
|
0
|
|
|
|
|
0
|
return 0; |
|
789
|
0
|
|
|
|
|
0
|
}; |
|
790
|
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
# the bitsets for select |
|
792
|
0
|
|
|
|
|
0
|
my ($rin, $rout, $win, $wout); |
|
793
|
0
|
|
|
|
|
0
|
my $nfound; |
|
794
|
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
# the big select loop |
|
796
|
0
|
|
|
|
|
0
|
while(1) { |
|
797
|
0
|
0
|
|
|
|
0
|
if ($active_changed) { |
|
798
|
0
|
0
|
0
|
|
|
0
|
last unless %reading or %writing; # no sockets left? |
|
799
|
0
|
|
|
|
|
0
|
($rin, $win) = ('', ''); |
|
800
|
0
|
|
|
|
|
0
|
foreach (values %reading) { |
|
801
|
0
|
|
|
|
|
0
|
vec($rin, fileno($_), 1) = 1; |
|
802
|
|
|
|
|
|
|
} |
|
803
|
0
|
|
|
|
|
0
|
foreach (values %writing) { |
|
804
|
0
|
|
|
|
|
0
|
vec($win, fileno($_), 1) = 1; |
|
805
|
|
|
|
|
|
|
} |
|
806
|
0
|
|
|
|
|
0
|
$active_changed = 0; |
|
807
|
|
|
|
|
|
|
} |
|
808
|
|
|
|
|
|
|
# TODO: more intelligent cumulative timeout? |
|
809
|
|
|
|
|
|
|
# TODO: select is interruptible w/ ptrace attach, signal, etc. should note that. |
|
810
|
0
|
|
|
|
|
0
|
$nfound = select($rout=$rin, $wout=$win, undef, |
|
811
|
|
|
|
|
|
|
$self->{'select_timeout'}); |
|
812
|
0
|
0
|
|
|
|
0
|
last unless $nfound; |
|
813
|
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
# TODO: possible robustness improvement: we could select |
|
815
|
|
|
|
|
|
|
# writing sockets for reading also, and raise hell if they're |
|
816
|
|
|
|
|
|
|
# ready (input unread from last time, etc.) |
|
817
|
|
|
|
|
|
|
# maybe do that on the first loop only? |
|
818
|
0
|
|
|
|
|
0
|
foreach (values %writing) { |
|
819
|
0
|
0
|
|
|
|
0
|
if (vec($wout, fileno($_), 1)) { |
|
820
|
0
|
0
|
|
|
|
0
|
$active_changed = 1 if $write->($_); |
|
821
|
|
|
|
|
|
|
} |
|
822
|
|
|
|
|
|
|
} |
|
823
|
0
|
|
|
|
|
0
|
foreach (values %reading) { |
|
824
|
0
|
0
|
|
|
|
0
|
if (vec($rout, fileno($_), 1)) { |
|
825
|
0
|
0
|
|
|
|
0
|
$active_changed = 1 if $read->($_); |
|
826
|
|
|
|
|
|
|
} |
|
827
|
|
|
|
|
|
|
} |
|
828
|
|
|
|
|
|
|
} |
|
829
|
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
# if there're active sockets left, they need to die |
|
831
|
0
|
|
|
|
|
0
|
foreach (values %writing) { |
|
832
|
0
|
|
|
|
|
0
|
$dead->($_); |
|
833
|
|
|
|
|
|
|
} |
|
834
|
0
|
|
|
|
|
0
|
foreach (values %reading) { |
|
835
|
0
|
|
|
|
|
0
|
$dead->($_); |
|
836
|
|
|
|
|
|
|
} |
|
837
|
|
|
|
|
|
|
|
|
838
|
0
|
|
|
|
|
0
|
return; |
|
839
|
|
|
|
|
|
|
} |
|
840
|
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
sub _hashfunc { |
|
842
|
0
|
|
|
0
|
|
0
|
return (crc32($_[0]) >> 16) & 0x7fff; |
|
843
|
|
|
|
|
|
|
} |
|
844
|
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
sub flush_all { |
|
846
|
7
|
|
|
7
|
1
|
4117
|
my Cache::Memcached $self = shift; |
|
847
|
|
|
|
|
|
|
|
|
848
|
7
|
|
|
|
|
12
|
my $success = 1; |
|
849
|
|
|
|
|
|
|
|
|
850
|
7
|
|
|
|
|
9
|
my @hosts = @{$self->{'buckets'}}; |
|
|
7
|
|
|
|
|
20
|
|
|
851
|
7
|
|
|
|
|
20
|
foreach my $host (@hosts) { |
|
852
|
7
|
|
|
|
|
25
|
my $sock = $self->sock_to_host($host); |
|
853
|
7
|
|
|
|
|
26
|
my @res = $self->run_command($sock, "flush_all\r\n"); |
|
854
|
7
|
100
|
50
|
|
|
79
|
$success = 0 unless (scalar @res == 1 && (($res[0] || "") eq "OK\r\n")); |
|
|
|
|
100
|
|
|
|
|
|
855
|
|
|
|
|
|
|
} |
|
856
|
|
|
|
|
|
|
|
|
857
|
7
|
|
|
|
|
65
|
return $success; |
|
858
|
|
|
|
|
|
|
} |
|
859
|
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
# returns array of lines, or () on failure. |
|
861
|
|
|
|
|
|
|
sub run_command { |
|
862
|
7
|
|
|
7
|
0
|
11
|
my Cache::Memcached $self = shift; |
|
863
|
7
|
|
|
|
|
11
|
my ($sock, $cmd) = @_; |
|
864
|
7
|
50
|
|
|
|
23
|
return () unless $sock; |
|
865
|
7
|
|
|
|
|
7
|
my $ret; |
|
866
|
7
|
|
|
|
|
10
|
my $line = $cmd; |
|
867
|
7
|
|
|
|
|
16
|
while (my $res = _write_and_read($self, $sock, $line)) { |
|
868
|
7
|
|
|
|
|
10
|
undef $line; |
|
869
|
7
|
|
|
|
|
10
|
$ret .= $res; |
|
870
|
7
|
50
|
|
|
|
66
|
last if $ret =~ /(?:OK|END|ERROR)\r\n$/; |
|
871
|
|
|
|
|
|
|
} |
|
872
|
7
|
|
|
|
|
12
|
chop $ret; chop $ret; |
|
|
7
|
|
|
|
|
8
|
|
|
873
|
7
|
|
|
|
|
21
|
return map { "$_\r\n" } split(/\r\n/, $ret); |
|
|
13
|
|
|
|
|
41
|
|
|
874
|
|
|
|
|
|
|
} |
|
875
|
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
sub stats { |
|
877
|
0
|
|
|
0
|
1
|
|
my Cache::Memcached $self = shift; |
|
878
|
0
|
|
|
|
|
|
my ($types) = @_; |
|
879
|
0
|
0
|
|
|
|
|
return 0 unless $self->{'active'}; |
|
880
|
0
|
0
|
0
|
|
|
|
return 0 unless !ref($types) || ref($types) eq 'ARRAY'; |
|
881
|
0
|
0
|
|
|
|
|
if (!ref($types)) { |
|
882
|
0
|
0
|
|
|
|
|
if (!$types) { |
|
883
|
|
|
|
|
|
|
# I don't much care what the default is, it should just |
|
884
|
|
|
|
|
|
|
# be something reasonable. Obviously "reset" should not |
|
885
|
|
|
|
|
|
|
# be on the list :) but other types that might go in here |
|
886
|
|
|
|
|
|
|
# include maps, cachedump, slabs, or items. Note that |
|
887
|
|
|
|
|
|
|
# this does NOT include 'sizes' anymore, as that can freeze |
|
888
|
|
|
|
|
|
|
# bug servers for a couple seconds. |
|
889
|
0
|
|
|
|
|
|
$types = [ qw( misc malloc self ) ]; |
|
890
|
|
|
|
|
|
|
} else { |
|
891
|
0
|
|
|
|
|
|
$types = [ $types ]; |
|
892
|
|
|
|
|
|
|
} |
|
893
|
|
|
|
|
|
|
} |
|
894
|
|
|
|
|
|
|
|
|
895
|
0
|
|
|
|
|
|
my $stats_hr = { }; |
|
896
|
|
|
|
|
|
|
|
|
897
|
|
|
|
|
|
|
# The "self" stat type is special, it only applies to this very |
|
898
|
|
|
|
|
|
|
# object. |
|
899
|
0
|
0
|
|
|
|
|
if (grep /^self$/, @$types) { |
|
900
|
0
|
|
|
|
|
|
$stats_hr->{'self'} = \%{ $self->{'stats'} }; |
|
|
0
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
} |
|
902
|
|
|
|
|
|
|
|
|
903
|
0
|
|
|
|
|
|
my %misc_keys = map { $_ => 1 } |
|
|
0
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
qw/ bytes bytes_read bytes_written |
|
905
|
|
|
|
|
|
|
cmd_get cmd_set connection_structures curr_items |
|
906
|
|
|
|
|
|
|
get_hits get_misses |
|
907
|
|
|
|
|
|
|
total_connections total_items |
|
908
|
|
|
|
|
|
|
/; |
|
909
|
|
|
|
|
|
|
|
|
910
|
|
|
|
|
|
|
# Now handle the other types, passing each type to each host server. |
|
911
|
0
|
|
|
|
|
|
my @hosts = @{$self->{'buckets'}}; |
|
|
0
|
|
|
|
|
|
|
|
912
|
0
|
|
|
|
|
|
HOST: foreach my $host (@hosts) { |
|
913
|
0
|
|
|
|
|
|
my $sock = $self->sock_to_host($host); |
|
914
|
0
|
0
|
|
|
|
|
next HOST unless $sock; |
|
915
|
0
|
|
|
|
|
|
TYPE: foreach my $typename (grep !/^self$/, @$types) { |
|
916
|
0
|
0
|
|
|
|
|
my $type = $typename eq 'misc' ? "" : " $typename"; |
|
917
|
|
|
|
|
|
|
my $lines = _write_and_read($self, $sock, "stats$type\r\n", sub { |
|
918
|
0
|
|
|
0
|
|
|
my $bref = shift; |
|
919
|
0
|
|
|
|
|
|
return $$bref =~ /^(?:END|ERROR)\r?\n/m; |
|
920
|
0
|
|
|
|
|
|
}); |
|
921
|
0
|
0
|
|
|
|
|
unless ($lines) { |
|
922
|
0
|
|
|
|
|
|
$self->_dead_sock($sock); |
|
923
|
0
|
|
|
|
|
|
next HOST; |
|
924
|
|
|
|
|
|
|
} |
|
925
|
|
|
|
|
|
|
|
|
926
|
0
|
|
|
|
|
|
$lines =~ s/\0//g; # 'stats sizes' starts with NULL? |
|
927
|
|
|
|
|
|
|
|
|
928
|
|
|
|
|
|
|
# And, most lines end in \r\n but 'stats maps' (as of |
|
929
|
|
|
|
|
|
|
# July 2003 at least) ends in \n. ?? |
|
930
|
0
|
|
|
|
|
|
my @lines = split(/\r?\n/, $lines); |
|
931
|
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
# Some stats are key-value, some are not. malloc, |
|
933
|
|
|
|
|
|
|
# sizes, and the empty string are key-value. |
|
934
|
|
|
|
|
|
|
# ("self" was handled separately above.) |
|
935
|
0
|
0
|
|
|
|
|
if ($typename =~ /^(malloc|sizes|misc)$/) { |
|
936
|
|
|
|
|
|
|
# This stat is key-value. |
|
937
|
0
|
|
|
|
|
|
foreach my $line (@lines) { |
|
938
|
0
|
|
|
|
|
|
my ($key, $value) = $line =~ /^(?:STAT )?(\w+)\s(.*)/; |
|
939
|
0
|
0
|
|
|
|
|
if ($key) { |
|
940
|
0
|
|
|
|
|
|
$stats_hr->{'hosts'}{$host}{$typename}{$key} = $value; |
|
941
|
|
|
|
|
|
|
} |
|
942
|
0
|
0
|
0
|
|
|
|
$stats_hr->{'total'}{$key} += $value |
|
|
|
|
0
|
|
|
|
|
|
943
|
|
|
|
|
|
|
if $typename eq 'misc' && $key && $misc_keys{$key}; |
|
944
|
0
|
0
|
0
|
|
|
|
$stats_hr->{'total'}{"malloc_$key"} += $value |
|
945
|
|
|
|
|
|
|
if $typename eq 'malloc' && $key; |
|
946
|
|
|
|
|
|
|
} |
|
947
|
|
|
|
|
|
|
} else { |
|
948
|
|
|
|
|
|
|
# This stat is not key-value so just pull it |
|
949
|
|
|
|
|
|
|
# all out in one blob. |
|
950
|
0
|
|
|
|
|
|
$lines =~ s/^END\r?\n//m; |
|
951
|
0
|
|
0
|
|
|
|
$stats_hr->{'hosts'}{$host}{$typename} ||= ""; |
|
952
|
0
|
|
|
|
|
|
$stats_hr->{'hosts'}{$host}{$typename} .= "$lines"; |
|
953
|
|
|
|
|
|
|
} |
|
954
|
|
|
|
|
|
|
} |
|
955
|
|
|
|
|
|
|
} |
|
956
|
|
|
|
|
|
|
|
|
957
|
0
|
|
|
|
|
|
return $stats_hr; |
|
958
|
|
|
|
|
|
|
} |
|
959
|
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
sub stats_reset { |
|
961
|
0
|
|
|
0
|
0
|
|
my Cache::Memcached $self = shift; |
|
962
|
0
|
|
|
|
|
|
my ($types) = @_; |
|
963
|
0
|
0
|
|
|
|
|
return 0 unless $self->{'active'}; |
|
964
|
|
|
|
|
|
|
|
|
965
|
0
|
|
|
|
|
|
HOST: foreach my $host (@{$self->{'buckets'}}) { |
|
|
0
|
|
|
|
|
|
|
|
966
|
0
|
|
|
|
|
|
my $sock = $self->sock_to_host($host); |
|
967
|
0
|
0
|
|
|
|
|
next HOST unless $sock; |
|
968
|
0
|
|
|
|
|
|
my $ok = _write_and_read($self, $sock, "stats reset"); |
|
969
|
0
|
0
|
0
|
|
|
|
unless (defined $ok && $ok eq "RESET\r\n") { |
|
970
|
0
|
|
|
|
|
|
$self->_dead_sock($sock); |
|
971
|
|
|
|
|
|
|
} |
|
972
|
|
|
|
|
|
|
} |
|
973
|
0
|
|
|
|
|
|
return 1; |
|
974
|
|
|
|
|
|
|
} |
|
975
|
|
|
|
|
|
|
|
|
976
|
|
|
|
|
|
|
1; |
|
977
|
|
|
|
|
|
|
__END__ |