Blame t/metaget.t

Packit Service 584ef9
#!/usr/bin/perl
Packit Service 584ef9
Packit Service 584ef9
use strict;
Packit Service 584ef9
use warnings;
Packit Service 584ef9
use Test::More;
Packit Service 584ef9
use FindBin qw($Bin);
Packit Service 584ef9
use lib "$Bin/lib";
Packit Service 584ef9
use MemcachedTest;
Packit Service 584ef9
Packit Service 584ef9
my $server = new_memcached();
Packit Service 584ef9
my $sock = $server->sock;
Packit Service 584ef9
Packit Service 584ef9
# command syntax:
Packit Service 584ef9
# mg [key] [flags] [tokens]\r\n
Packit Service 584ef9
# response:
Packit Service 584ef9
# VA [flags] [tokens]\r\n
Packit Service 584ef9
# data\r\n
Packit Service 584ef9
# EN\r\n
Packit Service 584ef9
#
Packit Service 584ef9
# flags:
Packit Service 584ef9
# - s: item size
Packit Service 584ef9
# - v: return item value
Packit Service 584ef9
# - c: return item cas
Packit Service 584ef9
# - t: return item TTL remaining (-1 for unlimited)
Packit Service 584ef9
# - f: client flags
Packit Service 584ef9
# - l: last access time
Packit Service 584ef9
# - h: whether item has been hit before
Packit Service 584ef9
# - O: opaque to copy back.
Packit Service 584ef9
# - k: return key
Packit Service 584ef9
# - q: noreply semantics.
Packit Service 584ef9
# - u: don't bump the item
Packit Service 584ef9
# updaters:
Packit Service 584ef9
# - N (token): vivify on miss, takes TTL as a argument
Packit Service 584ef9
# - R (token): if token is less than item TTL win for recache
Packit Service 584ef9
# - T (token): update remaining TTL
Packit Service 584ef9
# FIXME: do I need a "if stale and no token sent, flip" explicit flag?
Packit Service 584ef9
# extra response flags:
Packit Service 584ef9
# - W: client has "won" the token
Packit Service 584ef9
# - X: object is stale
Packit Service 584ef9
# - Z: object has sent a winning token
Packit Service 584ef9
#
Packit Service 584ef9
# ms [key] [flags] [tokens]\r\n
Packit Service 584ef9
# value\r\n
Packit Service 584ef9
# response:
Packit Service 584ef9
# ST [flags] [tokens]\r\n
Packit Service 584ef9
# ST STORED, NS NOT_STORED, EX EXISTS, NF NOT_FOUND
Packit Service 584ef9
#
Packit Service 584ef9
# flags:
Packit Service 584ef9
# - q: noreply
Packit Service 584ef9
# - F (token): set client flags
Packit Service 584ef9
# - C (token): compare CAS value
Packit Service 584ef9
# - S (token): item size
Packit Service 584ef9
# - T (token): TTL
Packit Service 584ef9
# - O: opaque to copy back.
Packit Service 584ef9
# - k: return key
Packit Service 584ef9
# - I: invalid. set-to-invalid if CAS is older than it should be.
Packit Service 584ef9
# Not implemented:
Packit Service 584ef9
# - E: add if not exists (influences other options)
Packit Service 584ef9
# - A: append (exclusive)
Packit Service 584ef9
# - P: prepend (exclusive)
Packit Service 584ef9
# - L: replace (exclusive)
Packit Service 584ef9
# - incr/decr? pushing it, I guess.
Packit Service 584ef9
#
Packit Service 584ef9
# md [key] [flags] [tokens]\r\n
Packit Service 584ef9
# response:
Packit Service 584ef9
# DE [flags] [tokens]
Packit Service 584ef9
# flags:
Packit Service 584ef9
# - q: noreply
Packit Service 584ef9
# - T (token): updates TTL
Packit Service 584ef9
# - C (token): compare CAS value
Packit Service 584ef9
# - I: invalidate. mark as stale, bumps CAS.
Packit Service 584ef9
# - O: opaque to copy back.
Packit Service 584ef9
# - k: return key
Packit Service 584ef9
#
Packit Service 584ef9
# mn\r\n
Packit Service 584ef9
# response:
Packit Service 584ef9
# EN
Packit Service 584ef9
Packit Service 584ef9
# metaget tests
Packit Service 584ef9
Packit Service 584ef9
# basic test
Packit Service 584ef9
# - raw mget
Packit Service 584ef9
# - raw mget miss
Packit Service 584ef9
# - raw mget bad key
Packit Service 584ef9
Packit Service 584ef9
{
Packit Service 584ef9
    print $sock "set foo 0 0 2\r\nhi\r\n";
Packit Service 584ef9
    is(scalar <$sock>, "STORED\r\n", "stored test value");
Packit Service 584ef9
Packit Service 584ef9
    print $sock "me none\r\n";
Packit Service 584ef9
    is(scalar <$sock>, "EN\r\n", "raw mget miss");
Packit Service 584ef9
Packit Service 584ef9
    print $sock "me foo\r\n";
Packit Service 584ef9
    like(scalar <$sock>, qr/^ME foo /, "raw mget result");
Packit Service 584ef9
    # bleed the EN off the socket.
Packit Service 584ef9
    my $dud = scalar <$sock>;
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
# mget with arguments
Packit Service 584ef9
# - set some specific TTL and get it back (within reason)
Packit Service 584ef9
# - get cas
Packit Service 584ef9
# - autovivify and bit-win
Packit Service 584ef9
Packit Service 584ef9
{
Packit Service 584ef9
    print $sock "set foo2 0 90 2\r\nho\r\n";
Packit Service 584ef9
    is(scalar <$sock>, "STORED\r\n", "stored test value");
Packit Service 584ef9
Packit Service 584ef9
    mget_is({ sock => $sock,
Packit Service 584ef9
              flags => 'sv',
Packit Service 584ef9
              etokens => [2] },
Packit Service 584ef9
            'foo2', 'ho', "retrieved test value");
Packit Service 584ef9
Packit Service 584ef9
    my $res = mget($sock, 'foo2', 'stv');
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
# lease-test, use two sockets? one socket should be fine, actually.
Packit Service 584ef9
# - get a win on autovivify
Packit Service 584ef9
# - get a loss on the same command
Packit Service 584ef9
# - have a set/cas fail
Packit Service 584ef9
# - have a cas succeed
Packit Service 584ef9
# - repeat for "triggered on TTL"
Packit Service 584ef9
# - test just modifying the TTL (touch)
Packit Service 584ef9
# - test fetching without value
Packit Service 584ef9
{
Packit Service 584ef9
    my $res = mget($sock, 'needwin', 'scvNt 30');
Packit Service 584ef9
    like($res->{flags}, qr/scvNt/, "got main flags back");
Packit Service 584ef9
    like($res->{flags}, qr/W/, "got a win result");
Packit Service 584ef9
    unlike($res->{flags}, qr/Z/, "no token already sent warning");
Packit Service 584ef9
Packit Service 584ef9
    # asked for size and TTL. size should be 0, TTL should be > 0 and < 30
Packit Service 584ef9
    is($res->{tokens}->[0], 0, "got zero size: autovivified response");
Packit Service 584ef9
    my $ttl = $res->{tokens}->[1];
Packit Service 584ef9
    ok($ttl > 0 && $ttl <= 30, "auto TTL is within requested window");
Packit Service 584ef9
Packit Service 584ef9
    # try to fail this time.
Packit Service 584ef9
    {
Packit Service 584ef9
        my $res = mget($sock, 'needwin', 'stcvN 30');
Packit Service 584ef9
        ok(keys %$res, "got a non-empty response");
Packit Service 584ef9
        unlike($res->{flags}, qr/W/, "not a win result");
Packit Service 584ef9
        like($res->{flags}, qr/Z/, "object already sent win result");
Packit Service 584ef9
    }
Packit Service 584ef9
Packit Service 584ef9
    # set back with the wrong CAS
Packit Service 584ef9
    print $sock "ms needwin CST 5000 2 120\r\nnu\r\n";
Packit Service 584ef9
    like(scalar <$sock>, qr/^EX /, "failed to SET: CAS didn't match");
Packit Service 584ef9
Packit Service 584ef9
    # again, but succeed.
Packit Service 584ef9
    # TODO: the actual CAS command should work here too?
Packit Service 584ef9
    my $cas = $res->{tokens}->[1];
Packit Service 584ef9
    print $sock "ms needwin CST $cas 2 120\r\nmu\r\n";
Packit Service 584ef9
    like(scalar <$sock>, qr/^ST /, "SET: CAS matched");
Packit Service 584ef9
Packit Service 584ef9
    # now we repeat the original mget, but the data should be different.
Packit Service 584ef9
    $res = mget($sock, 'needwin', 'sktcvN 30');
Packit Service 584ef9
    ok(keys %$res, "not a miss");
Packit Service 584ef9
    like($res->{flags}, qr/sktcvN/, "got main flags back");
Packit Service 584ef9
    unlike($res->{flags}, qr/[WZ]/, "not a win or token result");
Packit Service 584ef9
    is($res->{tokens}->[1], 'needwin', "key matches");
Packit Service 584ef9
    $ttl = $res->{tokens}->[2];
Packit Service 584ef9
    ok($ttl > 100 && $ttl <= 120, "TTL is within requested window: $ttl");
Packit Service 584ef9
    is($res->{val}, "mu", "value matches");
Packit Service 584ef9
Packit Service 584ef9
    # now we do the whole routine again, but for "triggered on TTL being low"
Packit Service 584ef9
    # TTL was set to 120 just now, so anything lower than this should trigger.
Packit Service 584ef9
    $res = mget($sock, 'needwin', 'stcvNR 30 130');
Packit Service 584ef9
    like($res->{flags}, qr/stcvNR/, "got main flags back");
Packit Service 584ef9
    like($res->{flags}, qr/W/, "got a win result");
Packit Service 584ef9
    unlike($res->{flags}, qr/Z/, "no token already sent warning");
Packit Service 584ef9
    is($res->{val}, "mu", "value matches");
Packit Service 584ef9
Packit Service 584ef9
    # try to fail this time.
Packit Service 584ef9
    {
Packit Service 584ef9
        my $res = mget($sock, 'needwin', 'stcvNR 30 130');
Packit Service 584ef9
        ok(keys %$res, "got a non-empty response");
Packit Service 584ef9
        unlike($res->{flags}, qr/W/, "not a win result");
Packit Service 584ef9
        like($res->{flags}, qr/Z/, "object already sent win result");
Packit Service 584ef9
        is($res->{val}, "mu", "value matches");
Packit Service 584ef9
    }
Packit Service 584ef9
Packit Service 584ef9
    # again, but succeed.
Packit Service 584ef9
    $cas = $res->{tokens}->[2];
Packit Service 584ef9
    print $sock "ms needwin CST $cas 4 300\r\nzuuu\r\n";
Packit Service 584ef9
    like(scalar <$sock>, qr/^ST /, "SET: CAS matched");
Packit Service 584ef9
Packit Service 584ef9
    # now we repeat the original mget, but the data should be different.
Packit Service 584ef9
    $res = mget($sock, 'needwin', 'stcvN 30');
Packit Service 584ef9
    ok(keys %$res, "not a miss");
Packit Service 584ef9
    like($res->{flags}, qr/stcvN/, "got main flags back");
Packit Service 584ef9
    unlike($res->{flags}, qr/[WZ]/, "not a win or token result");
Packit Service 584ef9
    $ttl = $res->{tokens}->[1];
Packit Service 584ef9
    ok($ttl > 250 && $ttl <= 300, "TTL is within requested window");
Packit Service 584ef9
    ok($res->{tokens}->[0] == 4, "Size returned correctly");
Packit Service 584ef9
    is($res->{val}, "zuuu", "value matches: " . $res->{val});
Packit Service 584ef9
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
# test get-and-touch mode
Packit Service 584ef9
{
Packit Service 584ef9
    # Set key with lower initial TTL.
Packit Service 584ef9
    print $sock "ms gatkey ST 4 100\r\nooom\r\n";
Packit Service 584ef9
    like(scalar <$sock>, qr/^ST /, "set gatkey");
Packit Service 584ef9
Packit Service 584ef9
    # Coolish side feature and/or bringer of bugs: 't' before 'T' gives TTL
Packit Service 584ef9
    # before adjustment. 'T' before 't' gives TTL after adjustment.
Packit Service 584ef9
    # Here we want 'T' before 't' to ensure we did adjust the value.
Packit Service 584ef9
    my $res = mget($sock, 'gatkey', 'svTt 300');
Packit Service 584ef9
    ok(keys %$res, "not a miss");
Packit Service 584ef9
    unlike($res->{flags}, qr/[WZ]/, "not a win or token result");
Packit Service 584ef9
    my $ttl = $res->{tokens}->[1];
Packit Service 584ef9
    ok($ttl > 280 && $ttl <= 300, "TTL is within requested window: $ttl");
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
# test no-value mode
Packit Service 584ef9
{
Packit Service 584ef9
    # Set key with lower initial TTL.
Packit Service 584ef9
    print $sock "ms hidevalue ST 4 100\r\nhide\r\n";
Packit Service 584ef9
    like(scalar <$sock>, qr/^ST /, "set hidevalue");
Packit Service 584ef9
Packit Service 584ef9
    my $res = mget($sock, 'hidevalue', 'st');
Packit Service 584ef9
    ok(keys %$res, "not a miss");
Packit Service 584ef9
    is($res->{val}, '', "no value returned");
Packit Service 584ef9
Packit Service 584ef9
    $res = mget($sock, 'hidevalue', 'stv');
Packit Service 584ef9
    ok(keys %$res, "not a miss");
Packit Service 584ef9
    is($res->{val}, 'hide', "real value returned");
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
# test hit-before? flag
Packit Service 584ef9
{
Packit Service 584ef9
    print $sock "ms hitflag ST 3 100\r\nhit\r\n";
Packit Service 584ef9
    like(scalar <$sock>, qr/^ST /, "set hitflag");
Packit Service 584ef9
Packit Service 584ef9
    my $res = mget($sock, 'hitflag', 'sth');
Packit Service 584ef9
    ok(keys %$res, "not a miss");
Packit Service 584ef9
    is($res->{tokens}->[2], 0, "not been hit before");
Packit Service 584ef9
Packit Service 584ef9
    $res = mget($sock, 'hitflag', 'sth');
Packit Service 584ef9
    ok(keys %$res, "not a miss");
Packit Service 584ef9
    is($res->{tokens}->[2], 1, "been hit before");
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
# test no-update flag
Packit Service 584ef9
{
Packit Service 584ef9
    print $sock "ms noupdate ST 3 100\r\nhit\r\n";
Packit Service 584ef9
    like(scalar <$sock>, qr/^ST /, "set noupdate");
Packit Service 584ef9
Packit Service 584ef9
    my $res = mget($sock, 'noupdate', 'stuh');
Packit Service 584ef9
    ok(keys %$res, "not a miss");
Packit Service 584ef9
    is($res->{tokens}->[2], 0, "not been hit before");
Packit Service 584ef9
Packit Service 584ef9
    # _next_ request should show a hit.
Packit Service 584ef9
    # gets modified here but returns previous state.
Packit Service 584ef9
    $res = mget($sock, 'noupdate', 'sth');
Packit Service 584ef9
    is($res->{tokens}->[2], 0, "still not a hit");
Packit Service 584ef9
Packit Service 584ef9
    $res = mget($sock, 'noupdate', 'stuh');
Packit Service 584ef9
    is($res->{tokens}->[2], 1, "finally a hit");
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
# test last-access time
Packit Service 584ef9
{
Packit Service 584ef9
    print $sock "ms la_test ST 2 100\r\nla\r\n";
Packit Service 584ef9
    like(scalar <$sock>, qr/^ST /, "set la_test");
Packit Service 584ef9
    sleep 2;
Packit Service 584ef9
Packit Service 584ef9
    my $res = mget($sock, 'la_test', 'stl');
Packit Service 584ef9
    ok(keys %$res, "not a miss");
Packit Service 584ef9
    print STDERR "Last access is: ", $res->{tokens}->[2], "\n";
Packit Service 584ef9
    isnt($res->{tokens}->[2], 0, "been over a second since most recently accessed");
Packit Service 584ef9
Packit Service 584ef9
    # TODO: Can't test re-accessing since it requires a long wait right now.
Packit Service 584ef9
    # I want to adjust the LA time accuracy in a deliberate change.
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
# high level tests:
Packit Service 584ef9
# - mget + mset with serve-stale
Packit Service 584ef9
# - set a value
Packit Service 584ef9
# - mget it back. should be no XZW tokens
Packit Service 584ef9
# - invalidate via mdelete and mget/revalidate with mset
Packit Service 584ef9
#   - remember failure scenarios!
Packit Service 584ef9
#     - TTL timed out?
Packit Service 584ef9
#     - CAS too high?
Packit Service 584ef9
#   - also test re-setting as stale (CAS is below requested)
Packit Service 584ef9
#     - this should probably be conditional.
Packit Service 584ef9
Packit Service 584ef9
{
Packit Service 584ef9
    diag "starting serve stale with mdelete";
Packit Service 584ef9
    my ($ttl, $cas, $res);
Packit Service 584ef9
    print $sock "set toinv 0 0 3\r\nmoo\r\n";
Packit Service 584ef9
    is(scalar <$sock>, "STORED\r\n", "stored key 'toinv'");
Packit Service 584ef9
Packit Service 584ef9
    $res = mget($sock, 'toinv', 'sv');
Packit Service 584ef9
    unlike($res->{flags}, qr/[XWZ]/, "no extra flags");
Packit Service 584ef9
Packit Service 584ef9
    # Lets mark the sucker as invalid, and drop its TTL to 30s
Packit Service 584ef9
    diag "running mdelete";
Packit Service 584ef9
    print $sock "md toinv IT 30\r\n";
Packit Service 584ef9
    like(scalar <$sock>, qr/^DE /, "mdelete'd key");
Packit Service 584ef9
Packit Service 584ef9
    # TODO: decide on if we need an explicit flag for "if I fetched a stale
Packit Service 584ef9
    # value, does winning matter?
Packit Service 584ef9
    # I think it's probably fine. clients can always ignore the win, or we can
Packit Service 584ef9
    # add an option later to "don't try to revalidate if stale", perhaps.
Packit Service 584ef9
    $res = mget($sock, 'toinv', 'stcv');
Packit Service 584ef9
    ok(keys %$res, "not a miss");
Packit Service 584ef9
    like($res->{flags}, qr/stcv/, "got main flags back");
Packit Service 584ef9
    like($res->{flags}, qr/W/, "won the recache");
Packit Service 584ef9
    like($res->{flags}, qr/X/, "item is marked stale");
Packit Service 584ef9
    $ttl = $res->{tokens}->[1];
Packit Service 584ef9
    ok($ttl > 0 && $ttl <= 30, "TTL is within requested window");
Packit Service 584ef9
    ok($res->{tokens}->[0] == 3, "Size returned correctly");
Packit Service 584ef9
    is($res->{val}, "moo", "value matches");
Packit Service 584ef9
Packit Service 584ef9
    diag "trying to fail then stale set via mset";
Packit Service 584ef9
    print $sock "ms toinv STC 1 90 0\r\nf\r\n";
Packit Service 584ef9
    like(scalar <$sock>, qr/^EX /, "failed to SET: low CAS didn't match");
Packit Service 584ef9
Packit Service 584ef9
    print $sock "ms toinv SITC 1 90 0\r\nf\r\n";
Packit Service 584ef9
    like(scalar <$sock>, qr/^ST /, "SET an invalid/stale item");
Packit Service 584ef9
Packit Service 584ef9
    diag "confirm item still stale, and TTL wasn't raised.";
Packit Service 584ef9
    $res = mget($sock, 'toinv', 'stcv');
Packit Service 584ef9
    like($res->{flags}, qr/X/, "item is marked stale");
Packit Service 584ef9
    like($res->{flags}, qr/Z/, "win token already sent");
Packit Service 584ef9
    unlike($res->{flags}, qr/W/, "didn't win: token already sent");
Packit Service 584ef9
    $ttl = $res->{tokens}->[1];
Packit Service 584ef9
    ok($ttl > 0 && $ttl <= 30, "TTL wasn't modified");
Packit Service 584ef9
Packit Service 584ef9
    # TODO: CAS too high?
Packit Service 584ef9
Packit Service 584ef9
    diag "do valid mset";
Packit Service 584ef9
    $cas = $res->{tokens}->[2];
Packit Service 584ef9
    print $sock "ms toinv STC 1 90 $cas\r\ng\r\n";
Packit Service 584ef9
    like(scalar <$sock>, qr/^ST /, "SET over the stale item");
Packit Service 584ef9
Packit Service 584ef9
    $res = mget($sock, 'toinv', 'stcv');
Packit Service 584ef9
    ok(keys %$res, "not a miss");
Packit Service 584ef9
    unlike($res->{flags}, qr/[WXZ]/, "no stale, win, or tokens");
Packit Service 584ef9
Packit Service 584ef9
    $ttl = $res->{tokens}->[1];
Packit Service 584ef9
    ok($ttl > 30 && $ttl <= 90, "TTL was modified");
Packit Service 584ef9
    ok($cas != $res->{tokens}->[2], "CAS was updated");
Packit Service 584ef9
    is($res->{tokens}->[0], 1, "size updated");
Packit Service 584ef9
    is($res->{val}, "g", "value was updated");
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
# Quiet flag suppresses most output. Badly invalid commands will still
Packit Service 584ef9
# generate something. Not weird to parse like 'noreply' token was...
Packit Service 584ef9
# mget's with hits should return real data.
Packit Service 584ef9
{
Packit Service 584ef9
    diag "testing quiet flag";
Packit Service 584ef9
    print $sock "ms quiet Sq 2\r\nmo\r\n";
Packit Service 584ef9
    print $sock "md quiet q\r\n";
Packit Service 584ef9
    print $sock "mg quiet svq\r\n";
Packit Service 584ef9
    diag "now purposefully cause an error\r\n";
Packit Service 584ef9
    print $sock "ms quiet S\r\n";
Packit Service 584ef9
    like(scalar <$sock>, qr/^CLIENT_ERROR/, "resp not ST, DE, or EN");
Packit Service 584ef9
Packit Service 584ef9
    # Now try a pipelined get. Throw an mnop at the end
Packit Service 584ef9
    print $sock "ms quiet Sq 2\r\nbo\r\n";
Packit Service 584ef9
    print $sock "mg quiet svq\r\nmg quiet svq\r\nmg quietmiss svq\r\nmn\r\n";
Packit Service 584ef9
    # Should get back VA/data/VA/data/EN
Packit Service 584ef9
    like(scalar <$sock>, qr/^VA svq 2/, "get response");
Packit Service 584ef9
    like(scalar <$sock>, qr/^bo/, "get value");
Packit Service 584ef9
    like(scalar <$sock>, qr/^VA svq 2/, "get response");
Packit Service 584ef9
    like(scalar <$sock>, qr/^bo/, "get value");
Packit Service 584ef9
    like(scalar <$sock>, qr/^EN/, "end token");
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
{
Packit Service 584ef9
    my $k = 'otest';
Packit Service 584ef9
    diag "testing mget opaque";
Packit Service 584ef9
    print $sock "ms $k ST 2 100\r\nra\r\n";
Packit Service 584ef9
    like(scalar <$sock>, qr/^ST /, "set $k");
Packit Service 584ef9
Packit Service 584ef9
    my $res = mget($sock, $k, 'stvO opaque');
Packit Service 584ef9
    is($res->{tokens}->[2], 'opaque', "O flag returned opaque");
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
{
Packit Service 584ef9
    diag "flag and token count errors";
Packit Service 584ef9
    print $sock "mg foo sv extratoken\r\n";
Packit Service 584ef9
    like(scalar <$sock>, qr/^CLIENT_ERROR incorrect number of tokens/, "too many tokens");
Packit Service 584ef9
    print $sock "mg foo svN\r\n";
Packit Service 584ef9
    like(scalar <$sock>, qr/^CLIENT_ERROR incorrect number of tokens/, "too few tokens");
Packit Service 584ef9
    print $sock "mg foo mooooo\r\n";
Packit Service 584ef9
    like(scalar <$sock>, qr/^CLIENT_ERROR invalid or duplicate flag/, "gone silly with flags");
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
# TODO: move wait_for_ext into Memcached.pm
Packit Service 584ef9
sub wait_for_ext {
Packit Service 584ef9
    my $sock = shift;
Packit Service 584ef9
    my $target = shift || 0;
Packit Service 584ef9
    my $sum = $target + 1;
Packit Service 584ef9
    while ($sum > $target) {
Packit Service 584ef9
        my $s = mem_stats($sock, "items");
Packit Service 584ef9
        $sum = 0;
Packit Service 584ef9
        for my $key (keys %$s) {
Packit Service 584ef9
            if ($key =~ m/items:(\d+):number/) {
Packit Service 584ef9
                # Ignore classes which can contain extstore items
Packit Service 584ef9
                next if $1 < 3;
Packit Service 584ef9
                $sum += $s->{$key};
Packit Service 584ef9
            }
Packit Service 584ef9
        }
Packit Service 584ef9
        sleep 1 if $sum > $target;
Packit Service 584ef9
    }
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
my $ext_path;
Packit Service 584ef9
# Do a basic extstore test if enabled.
Packit Service 584ef9
if (supports_extstore()) {
Packit Service 584ef9
    diag "mget + extstore tests";
Packit Service 584ef9
    $ext_path = "/tmp/extstore.$$";
Packit Service 584ef9
    my $server = new_memcached("-m 64 -U 0 -o ext_page_size=8,ext_wbuf_size=2,ext_threads=1,ext_io_depth=2,ext_item_size=512,ext_item_age=2,ext_recache_rate=10000,ext_max_frag=0.9,ext_path=$ext_path:64m,slab_automove=0,ext_compact_under=1,no_lru_crawler");
Packit Service 584ef9
    my $sock = $server->sock;
Packit Service 584ef9
Packit Service 584ef9
    my $value;
Packit Service 584ef9
    {
Packit Service 584ef9
        my @chars = ("C".."Z");
Packit Service 584ef9
        for (1 .. 20000) {
Packit Service 584ef9
            $value .= $chars[rand @chars];
Packit Service 584ef9
        }
Packit Service 584ef9
    }
Packit Service 584ef9
Packit Service 584ef9
    my $keycount = 10;
Packit Service 584ef9
    for (1 .. $keycount) {
Packit Service 584ef9
        print $sock "set nfoo$_ 0 0 20000 noreply\r\n$value\r\n";
Packit Service 584ef9
    }
Packit Service 584ef9
Packit Service 584ef9
    wait_for_ext($sock);
Packit Service 584ef9
    mget_is({ sock => $sock,
Packit Service 584ef9
              flags => 'sv',
Packit Service 584ef9
              etokens => [20000] },
Packit Service 584ef9
            'nfoo1', $value, "retrieved test value");
Packit Service 584ef9
    my $stats = mem_stats($sock);
Packit Service 584ef9
    cmp_ok($stats->{get_extstore}, '>', 0, 'one object was fetched');
Packit Service 584ef9
Packit Service 584ef9
    my $ovalue = $value;
Packit Service 584ef9
    for (1 .. 4) {
Packit Service 584ef9
        $value .= $ovalue;
Packit Service 584ef9
    }
Packit Service 584ef9
    # Fill to eviction.
Packit Service 584ef9
    $keycount = 1000;
Packit Service 584ef9
    for (1 .. $keycount) {
Packit Service 584ef9
        print $sock "set mfoo$_ 0 0 100000 noreply\r\n$value\r\n";
Packit Service 584ef9
        # wait to avoid memory evictions
Packit Service 584ef9
        wait_for_ext($sock, 1) if ($_ % 250 == 0);
Packit Service 584ef9
    }
Packit Service 584ef9
Packit Service 584ef9
    print $sock "mg mfoo1 sv\r\n";
Packit Service 584ef9
    is(scalar <$sock>, "EN\r\n");
Packit Service 584ef9
    print $sock "mg mfoo1 svq\r\nmn\r\n";
Packit Service 584ef9
    is(scalar <$sock>, "EN\r\n");
Packit Service 584ef9
    $stats = mem_stats($sock);
Packit Service 584ef9
    cmp_ok($stats->{miss_from_extstore}, '>', 0, 'at least one miss');
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
###
Packit Service 584ef9
Packit Service 584ef9
# takes hash:
Packit Service 584ef9
# - sock
Packit Service 584ef9
# - args (metaget flags)
Packit Service 584ef9
# - array of tokens
Packit Service 584ef9
# - array of expected response tokens
Packit Service 584ef9
Packit Service 584ef9
# returns hash:
Packit Service 584ef9
# - win (if won a condition)
Packit Service 584ef9
# - array of tokens
Packit Service 584ef9
# - value, etc?
Packit Service 584ef9
# useful to chain together for further requests.
Packit Service 584ef9
# works only with single line values. no newlines in value.
Packit Service 584ef9
# FIXME: some workaround for super long values :|
Packit Service 584ef9
# TODO: move this to lib/MemcachedTest.pm
Packit Service 584ef9
sub mget_is {
Packit Service 584ef9
    # single line values only
Packit Service 584ef9
    my ($o, $key, $val, $msg) = @_;
Packit Service 584ef9
Packit Service 584ef9
    my $dval = defined $val ? "'$val'" : "<undef>";
Packit Service 584ef9
    $msg ||= "$key == $dval";
Packit Service 584ef9
Packit Service 584ef9
    my $s = $o->{sock};
Packit Service 584ef9
    my $flags = $o->{flags};
Packit Service 584ef9
    # sometimes response flags can differ from request flags.
Packit Service 584ef9
    my $eflags = $o->{eflags} || $flags;
Packit Service 584ef9
    my $tokens = exists $o->{tokens} ? join(' ', @{$o->{tokens}}) : '';
Packit Service 584ef9
    my $etokens = exists $o->{etokens} ? join(' ', @{$o->{etokens}}) : '';
Packit Service 584ef9
Packit Service 584ef9
    print $s "mg $key $flags $tokens\r\n";
Packit Service 584ef9
    if (! defined $val) {
Packit Service 584ef9
        my $line = scalar <$s>;
Packit Service 584ef9
        if ($line =~ /^VA/) {
Packit Service 584ef9
            $line .= scalar(<$s>) . scalar(<$s>);
Packit Service 584ef9
        }
Packit Service 584ef9
        Test::More::is($line, "EN\r\n", $msg);
Packit Service 584ef9
    } else {
Packit Service 584ef9
        my $len = length($val);
Packit Service 584ef9
        my $body = scalar(<$s>);
Packit Service 584ef9
        my $expected = "VA $eflags $etokens\r\n$val\r\nEN\r\n";
Packit Service 584ef9
        if (!$body || $body =~ /^EN/) {
Packit Service 584ef9
            Test::More::is($body, $expected, $msg);
Packit Service 584ef9
            return;
Packit Service 584ef9
        }
Packit Service 584ef9
        $body .= scalar(<$s>) . scalar(<$s>);
Packit Service 584ef9
        Test::More::is($body, $expected, $msg);
Packit Service 584ef9
        return mget_res($body);
Packit Service 584ef9
    }
Packit Service 584ef9
    return {};
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
sub mget {
Packit Service 584ef9
    my $s = shift;
Packit Service 584ef9
    my $key = shift;
Packit Service 584ef9
    my $flags = shift;
Packit Service 584ef9
    my $tokens = join(' ', @_);
Packit Service 584ef9
Packit Service 584ef9
    print $s "mg $key $flags ", $tokens, "\r\n";
Packit Service 584ef9
    my $header = scalar(<$s>);
Packit Service 584ef9
    my $val = "\r\n";
Packit Service 584ef9
    if ($flags =~ m/v/) {
Packit Service 584ef9
        $val = scalar(<$s>);
Packit Service 584ef9
    }
Packit Service 584ef9
    my $end = scalar(<$s>);
Packit Service 584ef9
Packit Service 584ef9
    return mget_res($header . $val);
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
# parse out a response
Packit Service 584ef9
sub mget_res {
Packit Service 584ef9
    my $resp = shift;
Packit Service 584ef9
    my %r = ();
Packit Service 584ef9
Packit Service 584ef9
    if ($resp =~ m/^VA ([^\s]+) ([^\r]+)\r\n(.*)\r\n/gm) {
Packit Service 584ef9
        $r{flags} = $1;
Packit Service 584ef9
        $r{val} = $3;
Packit Service 584ef9
        $r{tokens} = [split(/ /, $2)];
Packit Service 584ef9
    }
Packit Service 584ef9
Packit Service 584ef9
    return \%r;
Packit Service 584ef9
}
Packit Service 584ef9
Packit Service 584ef9
done_testing();
Packit Service 584ef9
Packit Service 584ef9
END {
Packit Service 584ef9
    unlink $ext_path if $ext_path;
Packit Service 584ef9
}