~yolanda.robla/ubuntu/trusty/memcached/add_distribution

« back to all changes in this revision

Viewing changes to t/binary.t

  • Committer: Bazaar Package Importer
  • Author(s): David Martínez Moreno
  • Date: 2009-08-01 23:26:45 UTC
  • mto: (3.3.1 squeeze) (1.4.2 upstream)
  • mto: This revision was merged to the branch mainline in revision 8.
  • Revision ID: james.westby@ubuntu.com-20090801232645-g57xdaf3zal53qcl
Tags: upstream-1.4.0
Import upstream version 1.4.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/perl
 
2
 
 
3
use strict;
 
4
use warnings;
 
5
use Test::More tests => 880;
 
6
use FindBin qw($Bin);
 
7
use lib "$Bin/lib";
 
8
use MemcachedTest;
 
9
 
 
10
my $server = new_memcached();
 
11
ok($server, "started the server");
 
12
 
 
13
# Based almost 100% off testClient.py which is:
 
14
# Copyright (c) 2007  Dustin Sallings <dustin@spy.net>
 
15
 
 
16
# Command constants
 
17
use constant CMD_GET        => 0x00;
 
18
use constant CMD_SET        => 0x01;
 
19
use constant CMD_ADD        => 0x02;
 
20
use constant CMD_REPLACE    => 0x03;
 
21
use constant CMD_DELETE     => 0x04;
 
22
use constant CMD_INCR       => 0x05;
 
23
use constant CMD_DECR       => 0x06;
 
24
use constant CMD_QUIT       => 0x07;
 
25
use constant CMD_FLUSH      => 0x08;
 
26
use constant CMD_GETQ       => 0x09;
 
27
use constant CMD_NOOP       => 0x0A;
 
28
use constant CMD_VERSION    => 0x0B;
 
29
use constant CMD_GETK       => 0x0C;
 
30
use constant CMD_GETKQ      => 0x0D;
 
31
use constant CMD_APPEND     => 0x0E;
 
32
use constant CMD_PREPEND    => 0x0F;
 
33
use constant CMD_STAT       => 0x10;
 
34
use constant CMD_SETQ       => 0x11;
 
35
use constant CMD_ADDQ       => 0x12;
 
36
use constant CMD_REPLACEQ   => 0x13;
 
37
use constant CMD_DELETEQ    => 0x14;
 
38
use constant CMD_INCREMENTQ => 0x15;
 
39
use constant CMD_DECREMENTQ => 0x16;
 
40
use constant CMD_QUITQ      => 0x17;
 
41
use constant CMD_FLUSHQ     => 0x18;
 
42
use constant CMD_APPENDQ    => 0x19;
 
43
use constant CMD_PREPENDQ   => 0x1A;
 
44
 
 
45
# REQ and RES formats are divided even though they currently share
 
46
# the same format, since they _could_ differ in the future.
 
47
use constant REQ_PKT_FMT      => "CCnCCnNNNN";
 
48
use constant RES_PKT_FMT      => "CCnCCnNNNN";
 
49
use constant INCRDECR_PKT_FMT => "NNNNN";
 
50
use constant MIN_RECV_BYTES   => length(pack(RES_PKT_FMT));
 
51
use constant REQ_MAGIC        => 0x80;
 
52
use constant RES_MAGIC        => 0x81;
 
53
 
 
54
my $mc = MC::Client->new;
 
55
 
 
56
# Let's turn on detail stats for all this stuff
 
57
 
 
58
$mc->stats('detail on');
 
59
 
 
60
my $check = sub {
 
61
    my ($key, $orig_flags, $orig_val) = @_;
 
62
    my ($flags, $val, $cas) = $mc->get($key);
 
63
    is($flags, $orig_flags, "Flags is set properly");
 
64
    ok($val eq $orig_val || $val == $orig_val, $val . " = " . $orig_val);
 
65
};
 
66
 
 
67
my $set = sub {
 
68
    my ($key, $exp, $orig_flags, $orig_value) = @_;
 
69
    $mc->set($key, $orig_value, $orig_flags, $exp);
 
70
    $check->($key, $orig_flags, $orig_value);
 
71
};
 
72
 
 
73
my $empty = sub {
 
74
    my $key = shift;
 
75
    my $rv =()= eval { $mc->get($key) };
 
76
    is($rv, 0, "Didn't get a result from get");
 
77
    ok($@->not_found, "We got a not found error when we expected one");
 
78
};
 
79
 
 
80
my $delete = sub {
 
81
    my ($key, $when) = @_;
 
82
    $mc->delete($key, $when);
 
83
    $empty->($key);
 
84
};
 
85
 
 
86
# diag "Test Version";
 
87
my $v = $mc->version;
 
88
ok(defined $v && length($v), "Proper version: $v");
 
89
 
 
90
# diag "Flushing...";
 
91
$mc->flush;
 
92
 
 
93
# diag "Noop";
 
94
$mc->noop;
 
95
 
 
96
# diag "Simple set/get";
 
97
$set->('x', 5, 19, "somevalue");
 
98
 
 
99
# diag "Delete";
 
100
$delete->('x');
 
101
 
 
102
# diag "Flush";
 
103
$set->('x', 5, 19, "somevaluex");
 
104
$set->('y', 5, 17, "somevaluey");
 
105
$mc->flush;
 
106
$empty->('x');
 
107
$empty->('y');
 
108
 
 
109
{
 
110
    # diag "Add";
 
111
    $empty->('i');
 
112
    $mc->add('i', 'ex', 5, 10);
 
113
    $check->('i', 5, "ex");
 
114
 
 
115
    my $rv =()= eval { $mc->add('i', "ex2", 10, 5) };
 
116
    is($rv, 0, "Add didn't return anything");
 
117
    ok($@->exists, "Expected exists error received");
 
118
    $check->('i', 5, "ex");
 
119
}
 
120
 
 
121
{
 
122
    # diag "Too big.";
 
123
    $empty->('toobig');
 
124
    $mc->set('toobig', 'not too big', 10, 10);
 
125
    eval {
 
126
        my $bigval = ("x" x (1024*1024)) . "x";
 
127
        $mc->set('toobig', $bigval, 10, 10);
 
128
    };
 
129
    ok($@->too_big, "Was too big");
 
130
    $empty->('toobig');
 
131
}
 
132
 
 
133
{
 
134
    # diag "Replace";
 
135
    $empty->('j');
 
136
 
 
137
    my $rv =()= eval { $mc->replace('j', "ex", 19, 5) };
 
138
    is($rv, 0, "Replace didn't return anything");
 
139
    ok($@->not_found, "Expected not_found error received");
 
140
    $empty->('j');
 
141
    $mc->add('j', "ex2", 14, 5);
 
142
    $check->('j', 14, "ex2");
 
143
    $mc->replace('j', "ex3", 24, 5);
 
144
    $check->('j', 24, "ex3");
 
145
}
 
146
 
 
147
{
 
148
    # diag "MultiGet";
 
149
    $mc->add('xx', "ex", 1, 5);
 
150
    $mc->add('wye', "why", 2, 5);
 
151
    my $rv = $mc->get_multi(qw(xx wye zed));
 
152
 
 
153
    # CAS is returned with all gets.
 
154
    $rv->{xx}->[2]  = 0;
 
155
    $rv->{wye}->[2] = 0;
 
156
    is_deeply($rv->{xx}, [1, 'ex', 0], "X is correct");
 
157
    is_deeply($rv->{wye}, [2, 'why', 0], "Y is correct");
 
158
    is(keys(%$rv), 2, "Got only two answers like we expect");
 
159
}
 
160
 
 
161
# diag "Test increment";
 
162
$mc->flush;
 
163
is($mc->incr("x"), 0, "First incr call is zero");
 
164
is($mc->incr("x"), 1, "Second incr call is one");
 
165
is($mc->incr("x", 211), 212, "Adding 211 gives you 212");
 
166
is($mc->incr("x", 2**33), 8589934804, "Blast the 32bit border");
 
167
 
 
168
# diag "Issue 48 - incrementing plain text.";
 
169
{
 
170
    $mc->set("issue48", "text", 0, 0);
 
171
    my $rv =()= eval { $mc->incr('issue48'); };
 
172
    ok($@ && $@->delta_badval, "Expected invalid value when incrementing text.");
 
173
    $check->('issue48', 0, "text");
 
174
 
 
175
    my $rv =()= eval { $mc->decr('issue48'); };
 
176
    ok($@ && $@->delta_badval, "Expected invalid value when decrementing text.");
 
177
    $check->('issue48', 0, "text");
 
178
}
 
179
 
 
180
 
 
181
# diag "Test decrement";
 
182
$mc->flush;
 
183
is($mc->incr("x", undef, 5), 5, "Initial value");
 
184
is($mc->decr("x"), 4, "Decrease by one");
 
185
is($mc->decr("x", 211), 0, "Floor is zero");
 
186
 
 
187
{
 
188
    # diag "bug21";
 
189
    $mc->add("bug21", "9223372036854775807", 0, 0);
 
190
    is($mc->incr("bug21"), 9223372036854775808, "First incr for bug21.");
 
191
    is($mc->incr("bug21"), 9223372036854775809, "Second incr for bug21.");
 
192
    is($mc->decr("bug21"), 9223372036854775808, "Decr for bug21.");
 
193
}
 
194
 
 
195
{
 
196
    # diag "CAS";
 
197
    $mc->flush;
 
198
 
 
199
    {
 
200
        my $rv =()= eval { $mc->set("x", "bad value", 19, 5, 0x7FFFFFFFFF) };
 
201
        is($rv, 0, "Empty return on expected failure");
 
202
        ok($@->not_found, "Error was 'not found' as expected");
 
203
    }
 
204
 
 
205
    $mc->add("x", "original value", 5, 19);
 
206
 
 
207
    my ($flags, $val, $i) = $mc->get("x");
 
208
    is($val, "original value", "->gets returned proper value");
 
209
 
 
210
    {
 
211
        my $rv =()= eval { $mc->set("x", "broken value", 19, 5, $i+1) };
 
212
        is($rv, 0, "Empty return on expected failure (1)");
 
213
        ok($@->exists, "Expected error state of 'exists' (1)");
 
214
    }
 
215
 
 
216
    $mc->set("x", "new value", 19, 5, $i);
 
217
 
 
218
    my ($newflags, $newval, $newi) = $mc->get("x");
 
219
    is($newval, "new value", "CAS properly overwrote value");
 
220
 
 
221
    {
 
222
        my $rv =()= eval { $mc->set("x", "replay value", 19, 5,  $i) };
 
223
        is($rv, 0, "Empty return on expected failure (2)");
 
224
        ok($@->exists, "Expected error state of 'exists' (2)");
 
225
    }
 
226
}
 
227
 
 
228
# diag "Silent set.";
 
229
$mc->silent_mutation(::CMD_SETQ, 'silentset', 'silentsetval');
 
230
 
 
231
# diag "Silent add.";
 
232
$mc->silent_mutation(::CMD_ADDQ, 'silentadd', 'silentaddval');
 
233
 
 
234
# diag "Silent replace.";
 
235
{
 
236
    my $key = "silentreplace";
 
237
    my $extra = pack "NN", 829, 0;
 
238
    $empty->($key);
 
239
    # $mc->send_silent(::CMD_REPLACEQ, $key, 'somevalue', 7278552, $extra, 0);
 
240
    # $empty->($key);
 
241
 
 
242
    $mc->add($key, "xval", 831, 0);
 
243
    $check->($key, 831, 'xval');
 
244
 
 
245
    $mc->send_silent(::CMD_REPLACEQ, $key, 'somevalue', 7278552, $extra, 0);
 
246
    $check->($key, 829, 'somevalue');
 
247
}
 
248
 
 
249
# diag "Silent delete";
 
250
{
 
251
    my $key = "silentdelete";
 
252
    $empty->($key);
 
253
    $mc->set($key, "some val", 19, 0);
 
254
    $mc->send_silent(::CMD_DELETEQ, $key, '', 772);
 
255
    $empty->($key);
 
256
}
 
257
 
 
258
# diag "Silent increment";
 
259
{
 
260
    my $key = "silentincr";
 
261
    my $opaque = 98428747;
 
262
    $empty->($key);
 
263
    $mc->silent_incrdecr(::CMD_INCREMENTQ, $key, 0, 0, 0);
 
264
    is($mc->incr($key, 0), 0, "First call is 0");
 
265
 
 
266
    $mc->silent_incrdecr(::CMD_INCREMENTQ, $key, 8, 0, 0);
 
267
    is($mc->incr($key, 0), 8);
 
268
}
 
269
 
 
270
# diag "Silent decrement";
 
271
{
 
272
    my $key = "silentdecr";
 
273
    my $opaque = 98428147;
 
274
    $empty->($key);
 
275
    $mc->silent_incrdecr(::CMD_DECREMENTQ, $key, 0, 185, 0);
 
276
    is($mc->incr($key, 0), 185);
 
277
 
 
278
    $mc->silent_incrdecr(::CMD_DECREMENTQ, $key, 8, 0, 0);
 
279
    is($mc->incr($key, 0), 177);
 
280
}
 
281
 
 
282
# diag "Silent flush";
 
283
{
 
284
    $set->('x', 5, 19, "somevaluex");
 
285
    $set->('y', 5, 17, "somevaluey");
 
286
    $mc->send_silent(::CMD_FLUSHQ, '', '', 2775256);
 
287
    $empty->('x');
 
288
    $empty->('y');
 
289
}
 
290
 
 
291
# diag "Append";
 
292
{
 
293
    my $key = "appendkey";
 
294
    my $value = "some value";
 
295
    $set->($key, 8, 19, $value);
 
296
    $mc->_append_prepend(::CMD_APPEND, $key, " more");
 
297
    $check->($key, 19, $value . " more");
 
298
}
 
299
 
 
300
# diag "Prepend";
 
301
{
 
302
    my $key = "prependkey";
 
303
    my $value = "some value";
 
304
    $set->($key, 8, 19, $value);
 
305
    $mc->_append_prepend(::CMD_PREPEND, $key, "prefixed ");
 
306
    $check->($key, 19, "prefixed " . $value);
 
307
}
 
308
 
 
309
# diag "Silent append";
 
310
{
 
311
    my $key = "appendqkey";
 
312
    my $value = "some value";
 
313
    $set->($key, 8, 19, $value);
 
314
    $mc->send_silent(::CMD_APPENDQ, $key, " more", 7284492);
 
315
    $check->($key, 19, $value . " more");
 
316
}
 
317
 
 
318
# diag "Silent prepend";
 
319
{
 
320
    my $key = "prependqkey";
 
321
    my $value = "some value";
 
322
    $set->($key, 8, 19, $value);
 
323
    $mc->send_silent(::CMD_PREPENDQ, $key, "prefixed ", 7284492);
 
324
    $check->($key, 19, "prefixed " . $value);
 
325
}
 
326
 
 
327
# diag "Leaky binary get test.";
 
328
# # http://code.google.com/p/memcached/issues/detail?id=16
 
329
{
 
330
    # Get a new socket so we can speak text to it.
 
331
    my $sock = $server->new_sock;
 
332
    my $max = 1024 * 1024;
 
333
    my $big = "a big value that's > .5M and < 1M. ";
 
334
    while (length($big) * 2 < $max) {
 
335
        $big = $big . $big;
 
336
    }
 
337
    my $biglen = length($big);
 
338
 
 
339
    for(1..100) {
 
340
        my $key = "some_key_$_";
 
341
        # print STDERR "Key is $key\n";
 
342
        # print $sock "set $key 0 0 $vallen\r\n$value\r\n";
 
343
        print $sock "set $key 0 0 $biglen\r\n$big\r\n";
 
344
        is(scalar <$sock>, "STORED\r\n", "stored big");
 
345
        my ($f, $v, $c) = $mc->get($key);
 
346
    }
 
347
}
 
348
 
 
349
# diag "Test stats settings."
 
350
{
 
351
    my %stats = $mc->stats('settings');
 
352
 
 
353
    is(1024, $stats{'maxconns'});
 
354
    is('NULL', $stats{'domain_socket'});
 
355
    is('on', $stats{'evictions'});
 
356
    is('yes', $stats{'cas_enabled'});
 
357
}
 
358
 
 
359
# Along with the assertion added to the code to verify we're staying
 
360
# within bounds when we do a stats detail dump (detail turned on at
 
361
# the top).
 
362
my %stats = $mc->stats('detail dump');
 
363
 
 
364
# ######################################################################
 
365
# Test ends around here.
 
366
# ######################################################################
 
367
 
 
368
package MC::Client;
 
369
 
 
370
use strict;
 
371
use warnings;
 
372
use fields qw(socket);
 
373
use IO::Socket::INET;
 
374
 
 
375
sub new {
 
376
    my $self = shift;
 
377
    my $sock = $server->sock;
 
378
    $self = fields::new($self);
 
379
    $self->{socket} = $sock;
 
380
    return $self;
 
381
}
 
382
 
 
383
sub send_command {
 
384
    my $self = shift;
 
385
    die "Not enough args to send_command" unless @_ >= 4;
 
386
    my ($cmd, $key, $val, $opaque, $extra_header, $cas) = @_;
 
387
 
 
388
    $extra_header = '' unless defined $extra_header;
 
389
    my $keylen    = length($key);
 
390
    my $vallen    = length($val);
 
391
    my $extralen  = length($extra_header);
 
392
    my $datatype  = 0;  # field for future use
 
393
    my $reserved  = 0;  # field for future use
 
394
    my $totallen  = $keylen + $vallen + $extralen;
 
395
    my $ident_hi  = 0;
 
396
    my $ident_lo  = 0;
 
397
 
 
398
    if ($cas) {
 
399
        $ident_hi = int($cas / 2 ** 32);
 
400
        $ident_lo = int($cas % 2 ** 32);
 
401
    }
 
402
 
 
403
    my $msg = pack(::REQ_PKT_FMT, ::REQ_MAGIC, $cmd, $keylen, $extralen,
 
404
                   $datatype, $reserved, $totallen, $opaque, $ident_hi,
 
405
                   $ident_lo);
 
406
 
 
407
    my $full_msg = $msg . $extra_header . $key . $val;
 
408
    my $sent = $self->{socket}->send($full_msg);
 
409
    if($sent != length($full_msg)) {
 
410
        die("only sent $sent of " . length($full_msg) . " bytes");
 
411
    }
 
412
}
 
413
 
 
414
# Send a silent command and ensure it doesn't respond.
 
415
sub send_silent {
 
416
    my $self = shift;
 
417
    die "Not enough args to send_silent" unless @_ >= 4;
 
418
    my ($cmd, $key, $val, $opaque, $extra_header, $cas) = @_;
 
419
 
 
420
    $self->send_command($cmd, $key, $val, $opaque, $extra_header, $cas);
 
421
    $self->send_command(::CMD_NOOP, '', '', $opaque + 1);
 
422
 
 
423
    my ($ropaque, $data) = $self->_handle_single_response;
 
424
    Test::More::is($ropaque, $opaque + 1);
 
425
}
 
426
 
 
427
sub silent_mutation {
 
428
    my $self = shift;
 
429
    my ($cmd, $key, $value) = @_;
 
430
 
 
431
    $empty->($key);
 
432
    my $extra = pack "NN", 82, 0;
 
433
    $mc->send_silent($cmd, $key, $value, 7278552, $extra, 0);
 
434
    $check->($key, 82, $value);
 
435
}
 
436
 
 
437
sub _handle_single_response {
 
438
    my $self = shift;
 
439
    my $myopaque = shift;
 
440
 
 
441
    $self->{socket}->recv(my $response, ::MIN_RECV_BYTES);
 
442
    Test::More::is(length($response), ::MIN_RECV_BYTES, "Expected read length");
 
443
 
 
444
    my ($magic, $cmd, $keylen, $extralen, $datatype, $status, $remaining,
 
445
        $opaque, $ident_hi, $ident_lo) = unpack(::RES_PKT_FMT, $response);
 
446
    Test::More::is($magic, ::RES_MAGIC, "Got proper response magic");
 
447
 
 
448
    return ($opaque, '', '', 0) if($remaining == 0);
 
449
 
 
450
    # fetch the value
 
451
    my $rv="";
 
452
    while($remaining - length($rv) > 0) {
 
453
        $self->{socket}->recv(my $buf, $remaining - length($rv));
 
454
        $rv .= $buf;
 
455
    }
 
456
    if(length($rv) != $remaining) {
 
457
        my $found = length($rv);
 
458
        die("Expected $remaining bytes, got $found");
 
459
    }
 
460
 
 
461
    if (defined $myopaque) {
 
462
        Test::More::is($opaque, $myopaque, "Expected opaque");
 
463
    } else {
 
464
        Test::More::pass("Implicit pass since myopaque is undefined");
 
465
    }
 
466
 
 
467
    my $cas = ($ident_hi * 2 ** 32) + $ident_lo;
 
468
 
 
469
    if ($status) {
 
470
        die MC::Error->new($status, $rv);
 
471
    }
 
472
 
 
473
    return ($opaque, $rv, $cas, $keylen);
 
474
}
 
475
 
 
476
sub _do_command {
 
477
    my $self = shift;
 
478
    die unless @_ >= 3;
 
479
    my ($cmd, $key, $val, $extra_header, $cas) = @_;
 
480
 
 
481
    $extra_header = '' unless defined $extra_header;
 
482
    my $opaque = int(rand(2**32));
 
483
    $self->send_command($cmd, $key, $val, $opaque, $extra_header, $cas);
 
484
    my (undef, $rv, $rcas) = $self->_handle_single_response($opaque);
 
485
    return ($rv, $rcas);
 
486
}
 
487
 
 
488
sub _incrdecr_header {
 
489
    my $self = shift;
 
490
    my ($amt, $init, $exp) = @_;
 
491
 
 
492
    my $amt_hi = int($amt / 2 ** 32);
 
493
    my $amt_lo = int($amt % 2 ** 32);
 
494
 
 
495
    my $init_hi = int($init / 2 ** 32);
 
496
    my $init_lo = int($init % 2 ** 32);
 
497
 
 
498
    my $extra_header = pack(::INCRDECR_PKT_FMT, $amt_hi, $amt_lo, $init_hi,
 
499
                            $init_lo, $exp);
 
500
 
 
501
    return $extra_header;
 
502
}
 
503
 
 
504
sub _incrdecr {
 
505
    my $self = shift;
 
506
    my ($cmd, $key, $amt, $init, $exp) = @_;
 
507
 
 
508
    my ($data, undef) = $self->_do_command($cmd, $key, '',
 
509
                                           $self->_incrdecr_header($amt, $init, $exp));
 
510
 
 
511
    my $header = substr $data, 0, 8, '';
 
512
    my ($resp_hi, $resp_lo) = unpack "NN", $header;
 
513
    my $resp = ($resp_hi * 2 ** 32) + $resp_lo;
 
514
 
 
515
    return $resp;
 
516
}
 
517
 
 
518
sub silent_incrdecr {
 
519
    my $self = shift;
 
520
    my ($cmd, $key, $amt, $init, $exp) = @_;
 
521
    my $opaque = 8275753;
 
522
 
 
523
    $mc->send_silent($cmd, $key, '', $opaque,
 
524
                     $mc->_incrdecr_header($amt, $init, $exp));
 
525
}
 
526
 
 
527
sub stats {
 
528
    my $self = shift;
 
529
    my $key  = shift;
 
530
    my $cas = 0;
 
531
    my $opaque = int(rand(2**32));
 
532
    $self->send_command(::CMD_STAT, $key, '', $opaque, '', $cas);
 
533
 
 
534
    my %rv = ();
 
535
    my $found_key = '';
 
536
    my $found_val = '';
 
537
    do {
 
538
        my ($op, $data, $cas, $keylen) = $self->_handle_single_response($opaque);
 
539
        if($keylen > 0) {
 
540
            $found_key = substr($data, 0, $keylen);
 
541
            $found_val = substr($data, $keylen);
 
542
            $rv{$found_key} = $found_val;
 
543
        } else {
 
544
            $found_key = '';
 
545
        }
 
546
    } while($found_key ne '');
 
547
    return %rv;
 
548
}
 
549
 
 
550
sub get {
 
551
    my $self = shift;
 
552
    my $key  = shift;
 
553
    my ($rv, $cas) = $self->_do_command(::CMD_GET, $key, '', '');
 
554
 
 
555
    my $header = substr $rv, 0, 4, '';
 
556
    my $flags  = unpack("N", $header);
 
557
 
 
558
    return ($flags, $rv, $cas);
 
559
}
 
560
 
 
561
sub get_multi {
 
562
    my $self = shift;
 
563
    my @keys = @_;
 
564
 
 
565
    for (my $i = 0; $i < @keys; $i++) {
 
566
        $self->send_command(::CMD_GETQ, $keys[$i], '', $i, '', 0);
 
567
    }
 
568
 
 
569
    my $terminal = @keys + 10;
 
570
    $self->send_command(::CMD_NOOP, '', '', $terminal);
 
571
 
 
572
    my %return;
 
573
    while (1) {
 
574
        my ($opaque, $data) = $self->_handle_single_response;
 
575
        last if $opaque == $terminal;
 
576
 
 
577
        my $header = substr $data, 0, 4, '';
 
578
        my $flags  = unpack("N", $header);
 
579
 
 
580
        $return{$keys[$opaque]} = [$flags, $data];
 
581
    }
 
582
 
 
583
    return %return if wantarray;
 
584
    return \%return;
 
585
}
 
586
 
 
587
sub version {
 
588
    my $self = shift;
 
589
    return $self->_do_command(::CMD_VERSION, '', '');
 
590
}
 
591
 
 
592
sub flush {
 
593
    my $self = shift;
 
594
    return $self->_do_command(::CMD_FLUSH, '', '');
 
595
}
 
596
 
 
597
sub add {
 
598
    my $self = shift;
 
599
    my ($key, $val, $flags, $expire) = @_;
 
600
    my $extra_header = pack "NN", $flags, $expire;
 
601
    my $cas = 0;
 
602
    return $self->_do_command(::CMD_ADD, $key, $val, $extra_header, $cas);
 
603
}
 
604
 
 
605
sub set {
 
606
    my $self = shift;
 
607
    my ($key, $val, $flags, $expire, $cas) = @_;
 
608
    my $extra_header = pack "NN", $flags, $expire;
 
609
    return $self->_do_command(::CMD_SET, $key, $val, $extra_header, $cas);
 
610
}
 
611
 
 
612
sub _append_prepend {
 
613
    my $self = shift;
 
614
    my ($cmd, $key, $val, $cas) = @_;
 
615
    return $self->_do_command($cmd, $key, $val, '', $cas);
 
616
}
 
617
 
 
618
sub replace {
 
619
    my $self = shift;
 
620
    my ($key, $val, $flags, $expire) = @_;
 
621
    my $extra_header = pack "NN", $flags, $expire;
 
622
    my $cas = 0;
 
623
    return $self->_do_command(::CMD_REPLACE, $key, $val, $extra_header, $cas);
 
624
}
 
625
 
 
626
sub delete {
 
627
    my $self = shift;
 
628
    my ($key) = @_;
 
629
    return $self->_do_command(::CMD_DELETE, $key, '');
 
630
}
 
631
 
 
632
sub incr {
 
633
    my $self = shift;
 
634
    my ($key, $amt, $init, $exp) = @_;
 
635
    $amt = 1 unless defined $amt;
 
636
    $init = 0 unless defined $init;
 
637
    $exp = 0 unless defined $exp;
 
638
 
 
639
    return $self->_incrdecr(::CMD_INCR, $key, $amt, $init, $exp);
 
640
}
 
641
 
 
642
sub decr {
 
643
    my $self = shift;
 
644
    my ($key, $amt, $init, $exp) = @_;
 
645
    $amt = 1 unless defined $amt;
 
646
    $init = 0 unless defined $init;
 
647
    $exp = 0 unless defined $exp;
 
648
 
 
649
    return $self->_incrdecr(::CMD_DECR, $key, $amt, $init, $exp);
 
650
}
 
651
 
 
652
sub noop {
 
653
    my $self = shift;
 
654
    return $self->_do_command(::CMD_NOOP, '', '');
 
655
}
 
656
 
 
657
package MC::Error;
 
658
 
 
659
use strict;
 
660
use warnings;
 
661
 
 
662
use constant ERR_UNKNOWN_CMD  => 0x81;
 
663
use constant ERR_NOT_FOUND    => 0x1;
 
664
use constant ERR_EXISTS       => 0x2;
 
665
use constant ERR_TOO_BIG      => 0x3;
 
666
use constant ERR_EINVAL       => 0x4;
 
667
use constant ERR_NOT_STORED   => 0x5;
 
668
use constant ERR_DELTA_BADVAL => 0x6;
 
669
 
 
670
use overload '""' => sub {
 
671
    my $self = shift;
 
672
    return "Memcache Error ($self->[0]): $self->[1]";
 
673
};
 
674
 
 
675
sub new {
 
676
    my $class = shift;
 
677
    my $error = [@_];
 
678
    my $self = bless $error, (ref $class || $class);
 
679
 
 
680
    return $self;
 
681
}
 
682
 
 
683
sub not_found {
 
684
    my $self = shift;
 
685
    return $self->[0] == ERR_NOT_FOUND;
 
686
}
 
687
 
 
688
sub exists {
 
689
    my $self = shift;
 
690
    return $self->[0] == ERR_EXISTS;
 
691
}
 
692
 
 
693
sub too_big {
 
694
    my $self = shift;
 
695
    return $self->[0] == ERR_TOO_BIG;
 
696
}
 
697
 
 
698
sub delta_badval {
 
699
    my $self = shift;
 
700
    return $self->[0] == ERR_DELTA_BADVAL;
 
701
}
 
702
 
 
703
# vim: filetype=perl
 
704