File: proxyslotcache.t

package info (click to toggle)
memcached 1.6.39-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,320 kB
  • sloc: ansic: 62,281; perl: 12,500; sh: 4,569; makefile: 468; python: 402; xml: 59
file content (185 lines) | stat: -rw-r--r-- 5,763 bytes parent folder | download | duplicates (2)
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#!/usr/bin/env perl
# Check funcgen and memory accounting.

use strict;
use warnings;
use Test::More;
use FindBin qw($Bin);
use lib "$Bin/lib";
use Carp qw(croak);
use MemcachedTest;
use IO::Socket qw(AF_INET SOCK_STREAM);
use IO::Select;
use Data::Dumper qw/Dumper/;

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

# Set up the listeners _before_ starting the proxy.
# the fourth listener is only occasionally used.
my $t = Memcached::ProxyTest->new(servers => [12101]);

my $p_srv = new_memcached('-o proxy_config=./t/proxyslotcache.lua -t 1');
my $ps = $p_srv->sock;
$ps->autoflush(1);

$t->set_c($ps);
$t->accept_backends();

sub get_mem {
    $t->c_send("mg one/collect\r\n");
    # TODO: func to just read the client data back out?
    my $mem = scalar <$ps>;
    like($mem, qr/^SERVER_ERROR \d+/, "got garbage size back");
    # get the beginning total memory usage
    $mem =~ s/^SERVER_ERROR (\d+).+$/$1/s;

    return $mem;
}

sub slot_test {
    my $prefix = shift;
    my $etotal = shift; # expected slot total

    my $mem_start = get_mem();
    my $cmd = "mg $prefix/go N50\r\n";
    my $fcmd = '';
    my $cnt = 100;
    for (1 .. $cnt) {
        $fcmd .= $cmd;
    }
    $t->c_send($fcmd);
    # first receive all requests but don't send responses to ensure all
    # coroutines are generated.
    for (1 .. $cnt) {
        $t->be_recv(0, $cmd, "received request $_");
    }
    ok("received all requests");
    for (1 .. $cnt) {
        $t->be_send(0, "HD\r\n");
    }
    # once all responses are received the client gets woken up again.
    for (1 .. $cnt) {
        $t->c_recv_be("received response $_");
    }
    $t->clear();

    my $stats = mem_stats($ps, "proxyfuncs");
    is($stats->{funcs_foo}, 6, "six total function");
    is($stats->{slots_foo}, $etotal, "$etotal total slots in use");
    my $mem_middle = get_mem();
    cmp_ok($mem_start * 1.5, '<', $mem_middle, "memory usage grew a bit: $mem_start - $mem_middle");

    # run some non-pipelined requests and check that slots are still max
    for (1 .. 50) {
        $t->c_send("mg $prefix/go C$_\r\n");
        $t->be_recv_c(0, "received request $_");
        $t->be_send(0, "HD\r\n");
        $t->c_recv_be("received response $_");
    }

    $stats = mem_stats($ps, "proxyfuncs");
    is($stats->{funcs_foo}, 6, "after batch: six total function");
    is($stats->{slots_foo}, $etotal, "after batch: $etotal total slots in use");

    # run some more and check that slots have dropped
    for (1 .. 100) {
        $t->c_send("mg $prefix/go C$_\r\n");
        $t->be_recv_c(0, "received request $_");
        $t->be_send(0, "HD\r\n");
        $t->c_recv_be("received response $_");
    }

    $stats = mem_stats($ps, "proxyfuncs");
    is($stats->{funcs_foo}, 6, "after batch 2: six total function");
    cmp_ok($stats->{slots_foo}, '<', $etotal, "after batch 2: fewer total slots in use");
    cmp_ok($stats->{slots_foo}, '>', 1, "after batch 2: ... but more than one slot");

    # run enough to drop below 1 and check that it's 1
    for (1 .. (12 * $cnt)) {
        $t->c_send("mg $prefix/go C$_\r\n");
        $t->be_recv_c(0, "received request $_");
        $t->be_send(0, "HD\r\n");
        $t->c_recv_be("received response $_");
    }

    $stats = mem_stats($ps, "proxyfuncs");
    is($stats->{funcs_foo}, 6, "final batch: six total function");
    # algorithm will reduce slot cache count down to 2, not one. so we have
    # one more slot than functions.
    is($stats->{slots_foo}, 6, "final batch: 6 slots");
    my $mem_end = get_mem();
    cmp_ok($mem_start * 1.5, '>', $mem_end, "memory usage dropped back: $mem_start - $mem_end");

}

# minimum slot count is 6.
# "two" is two total slots per top level slot.
# "three" is three total slots per top level slot"
# so "three" * 100 + 3 extra base slots from two and one being unused
# ... is where that weird 303 comes from.
subtest 'excess slot freeing depth 1' => sub {
    slot_test("one", 105);
};

subtest 'excess slot freeing depth 2' => sub {
    slot_test("two", 204);
};

subtest 'excess slot freeing depth 3' => sub {
    slot_test("three", 303);
};

sub wait_reload {
    my $w = shift;
    like(<$w>, qr/ts=(\S+) gid=\d+ type=proxy_conf status=start/, "reload started");
    like(<$w>, qr/ts=(\S+) gid=\d+ type=proxy_conf status=done/, "reload completed");
}

# reload a bunch of times and check that the proxyfuncs slots aren't
# leaking and the total memory usage isn't consistently creeping up
subtest 'reload memory leaks' => sub {
    my $mem_start = get_mem();
    my $watcher = $p_srv->new_sock;
    print $watcher "watch proxyevents\n";
    is(<$watcher>, "OK\r\n", "watcher enabled");

    my $mem_end = 0;
    my $mem_end_last = 0;
    for (1 .. 20) {
        my $cmd = "mg three/go N50\r\n";
        my $fcmd = '';
        my $cnt = 100;
        for (1 .. $cnt) {
            $fcmd .= $cmd;
        }
        $t->c_send($fcmd);
        for (1 .. $cnt) {
            $t->be_recv(0, $cmd, "received request $_");
            $t->be_send(0, "HD\r\n");
        }
        for (1 .. $cnt) {
            $t->c_recv_be("received response $_");
        }

        $p_srv->reload();
        wait_reload($watcher);
        # start tracking end memory after looping once.
        my $mem_end = get_mem();
        if ($mem_end_last) {
            is($mem_end, $mem_end_last, "post-collect $_: $mem_end is the same");
        }
        $mem_end_last = $mem_end;
    }

    $mem_end = get_mem();

    cmp_ok($mem_end, '<', $mem_start * 2, "memory didn't bloat");
    my $stats = mem_stats($ps, "proxyfuncs");
    is($stats->{funcs_foo}, 6, "post reload: function count");
    is($stats->{slots_foo}, 6, "post reload: slot count matches func count");
};

done_testing();