summaryrefslogtreecommitdiff
path: root/t/error-extstore.t
blob: a9584d03a1c7623e4a93d8bdd223371e739e026d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#!/usr/bin/env perl
# Test the "Error on get" path for extstore.
# the entire error handling code for process_get_command() never worked, and
# would infinite loop. get_extstore() can hit it sometimes.

use strict;
use warnings;

use Test::More;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;

my $ext_path;

if (!supports_extstore()) {
    plan skip_all => 'extstore not enabled';
    exit 0;
}

$ext_path = "/tmp/extstore.$$";

my $server = new_memcached("-m 64 -I 4m -U 0 -o ext_page_size=8,ext_wbuf_size=8,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,ext_max_sleep=100000");
my $sock = $server->sock;

# Wait until all items have flushed
sub wait_for_ext {
    my $sum = 1;
    while ($sum != 0) {
        my $s = mem_stats($sock, "items");
        $sum = 0;
        for my $key (keys %$s) {
            if ($key =~ m/items:(\d+):number/) {
                # Ignore classes which can contain extstore items
                next if $1 < 3;
                $sum += $s->{$key};
            }
        }
        sleep 1 if $sum != 0;
    }
}

# We're testing to ensure item chaining doesn't corrupt or poorly overlap
# data, so create a non-repeating pattern.
my @parts = ();
for (1 .. 8000) {
    push(@parts, $_);
}
my $pattern = join(':', @parts);
my $plen = length($pattern);

# Set some large items and let them flush to extstore.
for (1..5) {
    my $size = 3000 * 1024;
    my $data = "x" x $size;
    print $sock "set foo$_ 0 0 $size\r\n$data\r\n";
    my $res = <$sock>;
    is($res, "STORED\r\n", "stored some big items");
}

wait_for_ext();

{
    my $long_key = "f" x 512;
    print $sock "get foo1 foo2 foo3 $long_key\r\n";
    ok(scalar <$sock> =~ /CLIENT_ERROR bad command line format/, 'long key fails');
    my $stats = mem_stats($sock);
    cmp_ok($stats->{get_aborted_extstore}, '>', 1, 'some extstore queries aborted');
}

# Infinite loop: if we aborted some extstore requests, the next request would hang
# the daemon.
{
    my $size = 3000 * 1024;
    my $data = "x" x $size;
    mem_get_is($sock, "foo1", $data);
}

# Disable automatic page balancing, then move enough pages that the large
# items can no longer be loaded from extstore
{
    print $sock "slabs automove 0\r\n";
    my $res = <$sock>;
    my $source = 0;
    while (1) {
        print $sock "slabs reassign $source 1\r\n";
        $res = <$sock>;
        if ($res =~ m/NOSPARE/) {
            $source = -1;
            my $stats = mem_stats($sock, 'slabs');
            for my $key (grep { /total_pages/ } keys %$stats) {
                if ($key =~ m/(\d+):total_pages/) {
                    next if $1 < 3;
                    $source = $1 if $stats->{$key} > 1;
                }
            }
            last if $source == -1;
        }
        select undef, undef, undef, 0.10;
    }
}

# fetching the large keys should now fail.
{
    print $sock "get foo1\r\n";
    my $res = <$sock>;
    $res =~ s/[\r\n]//g;
    is($res, 'SERVER_ERROR out of memory writing get response', 'can no longer read back item');
    my $stats = mem_stats($sock);
    is($stats->{get_oom_extstore}, 1, 'check extstore oom counter');
}

# Leaving this for future generations.
# The process_get_command() function had several memory leaks.
my $LEAK_TEST = 0;
if ($LEAK_TEST) {
    my $tries = 0;
    while ($tries) {
        print $sock "slabs reassign 1 39\r\n";
        my $res = <$sock>;
        if ($res =~ m/BUSY/) {
            select undef, undef, undef, 0.10;
        } else {
            $tries--;
        }
    }
    my $long_key = "f" x 512;
    while (1) {
        print $sock "get foo1 foo2 foo3 $long_key\r\n";
        my $res = <$sock>;
    }
}

done_testing();

END {
    unlink $ext_path if $ext_path;
}