summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFei Hu <feihu@stripe.com>2023-05-11 12:44:30 -0700
committerdormando <dormando@rydia.net>2023-05-11 17:45:32 -0700
commitfc88b9f2d47c72df0b37438da1b31f3769e5bf6a (patch)
tree87b4c17246d6406e0fe678a0bf16c67a86932103
parent279eec1f1d8d9be059d693ff367d55f0f33fe2ae (diff)
downloadmemcached-fc88b9f2d47c72df0b37438da1b31f3769e5bf6a.tar.gz
proxy: add tests for ustats
-rw-r--r--t/proxyustats.lua31
-rw-r--r--t/proxyustats.t227
2 files changed, 258 insertions, 0 deletions
diff --git a/t/proxyustats.lua b/t/proxyustats.lua
new file mode 100644
index 0000000..e0302cb
--- /dev/null
+++ b/t/proxyustats.lua
@@ -0,0 +1,31 @@
+-- get some information about the test being run from an external file
+-- so we can modify ourselves.
+local config = dofile("/tmp/proxyustats.lua")
+
+local idx = 1
+
+function mcp_config_pools(old)
+ mcp.backend_read_timeout(4)
+ mcp.backend_connect_timeout(5)
+ local pfx, f, l = string.match(config, "(%S+)%s*(%S+)%s*(%S+)")
+ local first = tonumber(f)
+ local last = tonumber(l)
+ while first <= last do
+ mcp.add_stat(first, pfx .. first)
+ first = first + 1
+ end
+
+ return {}
+end
+
+function route_fn(zones)
+ return function(r)
+ local key = r:key()
+ mcp.stat(math.abs(tonumber(key)), tonumber(key))
+ return "HD\r\n"
+ end
+end
+
+function mcp_config_routes(zones)
+ mcp.attach(mcp.CMD_MG, route_fn(zones))
+end
diff --git a/t/proxyustats.t b/t/proxyustats.t
new file mode 100644
index 0000000..0d5e340
--- /dev/null
+++ b/t/proxyustats.t
@@ -0,0 +1,227 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use Test::More;
+use FindBin qw($Bin);
+use lib "$Bin/lib";
+use Carp qw(croak);
+use MemcachedTest;
+use IO::Select;
+use IO::Socket qw(AF_INET SOCK_STREAM);
+
+# TODO: possibly... set env var to a generated temp filename before starting
+# the server so we can pass that in?
+my $configfile = "/tmp/proxyustats.lua";
+
+if (!supports_proxy()) {
+ plan skip_all => 'proxy not enabled';
+ exit 0;
+}
+
+# Set up some server sockets.
+sub mock_server {
+ my $port = shift;
+ my $srv = IO::Socket->new(
+ Domain => AF_INET,
+ Type => SOCK_STREAM,
+ Proto => 'tcp',
+ LocalHost => '127.0.0.1',
+ LocalPort => $port,
+ ReusePort => 1,
+ Listen => 5) || die "IO::Socket: $@";
+ return $srv;
+}
+
+sub accept_backend {
+ my $srv = shift;
+ my $be = $srv->accept();
+ $be->autoflush(1);
+ ok(defined $be, "mock backend created");
+ like(<$be>, qr/version/, "received version command");
+ print $be "VERSION 1.0.0-mock\r\n";
+
+ return $be;
+}
+
+# Put a version command down the pipe to ensure the socket is clear.
+# client version commands skip the proxy code
+sub check_version {
+ my $ps = shift;
+ print $ps "version\r\n";
+ like(<$ps>, qr/VERSION /, "version received");
+}
+
+# A single line containing prefix, start ustat and end ustat index.
+sub write_config {
+ my $cmd = shift;
+ open(my $fh, "> $configfile") or die "Couldn't overwrite $configfile: $!";
+ print $fh $cmd;
+ close($fh);
+}
+
+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");
+}
+
+my ($p_srv, $ps, $watcher);
+sub restart_memcached {
+ if ($p_srv) {
+ $p_srv->stop();
+ }
+ write_config('return "a 1 0"');
+ $p_srv = new_memcached('-o proxy_config=./t/proxyustats.lua');
+ $ps = $p_srv->sock;
+ $ps->autoflush(1);
+
+ $watcher = $p_srv->new_sock;
+ print $watcher "watch proxyevents\n";
+ is(<$watcher>, "OK\r\n", "watcher enabled");
+}
+
+diag "testing failure to start";
+write_config("invalid");
+eval {
+ $p_srv = new_memcached('-o proxy_config=./t/proxyustats.lua');
+};
+ok($@ && $@ =~ m/Failed to connect/, "server successfully not started");
+
+restart_memcached();
+
+subtest 'succeeded to allocate 1024 ustats' => sub {
+ my $pfx = "a";
+ my $first = 1;
+ my $last = 1024;
+ write_config('return "' . $pfx . ' ' . $first . ' ' . $last . '"');
+ $p_srv->reload();
+ wait_reload($watcher);
+
+ my $stats = mem_stats($ps, 'proxy');
+ for my $i ($first..$last) {
+ my $ustat = "user_" . $pfx . $i;
+ is($stats->{$ustat}, 0, $ustat . " found");
+ }
+};
+
+subtest 'failed to allocate 1025 ustats' => sub {
+ my $pfx = "a";
+ my $first = 1025;
+ my $last = 1025;
+ write_config('return "' . $pfx . ' ' . $first . ' ' . $last . '"');
+ $p_srv->reload();
+ unlike(<$watcher>, qr/ts=(\S+) gid=\d+ type=proxy_conf status=start/, "reload not started");
+ unlike(<$watcher>, qr/ts=(\S+) gid=\d+ type=proxy_conf status=done/, "reload not completed");
+
+ restart_memcached();
+};
+
+subtest 'failed to allocate ustat at index 0' => sub {
+ my $pfx = "a";
+ my $first = 0;
+ my $last = 0;
+ write_config('return "' . $pfx . ' ' . $first . ' ' . $last . '"');
+ $p_srv->reload();
+ unlike(<$watcher>, qr/ts=(\S+) gid=\d+ type=proxy_conf status=start/, "reload not started");
+ unlike(<$watcher>, qr/ts=(\S+) gid=\d+ type=proxy_conf status=done/, "reload not completed");
+
+ restart_memcached();
+};
+
+
+subtest 'succeeded to allocate ustat at 1024 only' => sub {
+ # restart memcached to clear any ustats.
+ restart_memcached();
+
+ my $pfx = "a";
+ my $first = 1024;
+ my $last = 1024;
+ write_config('return "' . $pfx . ' ' . $first . ' ' . $last . '"');
+ $p_srv->reload();
+ wait_reload($watcher);
+
+ my $stats = mem_stats($ps, 'proxy');
+ while (my ($ustat, $value) = each %{$stats}) {
+ if (index($ustat, "user_") == 0) {
+ is($ustat, "user_a1024", "user_a1024 found");
+ }
+ }
+};
+
+subtest 'ustats incr/decr and perseverance over reload' => sub {
+ # restart memcached to clear any ustats.
+ restart_memcached();
+
+ my $pfx = "a";
+ my $first = 1;
+ my $last = 3;
+ write_config('return "' . $pfx . ' ' . $first . ' ' . $last . '"');
+ $p_srv->reload();
+ wait_reload($watcher);
+
+ print $ps "mg 1\r\n";
+ is(scalar <$ps>, "HD\r\n", "mg 1 hit");
+ print $ps "mg 2\r\n";
+ is(scalar <$ps>, "HD\r\n", "mg 2 hit");
+ print $ps "mg 2\r\n";
+ is(scalar <$ps>, "HD\r\n", "mg 2 hit");
+
+ my $stats = mem_stats($ps, 'proxy');
+ is($stats->{user_a1}, 1, "user_a1 is 1");
+ is($stats->{user_a2}, 4, "user_a2 is 4");
+ is($stats->{user_a3}, 0, "user_a3 is 0");
+
+ $pfx = "b";
+ $first = 2;
+ $last = 4;
+ write_config('return "' . $pfx . ' ' . $first . ' ' . $last . '"');
+ $p_srv->reload();
+ wait_reload($watcher);
+
+ # subtract 2 at idx 2.
+ print $ps "mg -2\r\n";
+ is(scalar <$ps>, "HD\r\n", "mg -2 hit");
+
+ $stats = mem_stats($ps, 'proxy');
+ is($stats->{user_a1}, 1, "user_a1 is 1");
+ is($stats->{user_b2}, 2, "user_b2 is 2");
+ is($stats->{user_b3}, 0, "user_b3 is 0");
+ is($stats->{user_b4}, 0, "user_b4 is 0");
+};
+
+subtest 'failed to allocate ustat longer than 128 chars' => sub {
+ my $pfx = '*' x 128;
+ my $first = 1;
+ my $last = 1;
+ write_config('return "' . $pfx . ' ' . $first . ' ' . $last . '"');
+ $p_srv->reload();
+ unlike(<$watcher>, qr/ts=(\S+) gid=\d+ type=proxy_conf status=start/, "reload not started");
+ unlike(<$watcher>, qr/ts=(\S+) gid=\d+ type=proxy_conf status=done/, "reload not completed");
+
+ restart_memcached();
+};
+
+subtest 'negative ustat value underflow' => sub {
+ # restart memcached to clear any ustats.
+ restart_memcached();
+
+ my $pfx = "a";
+ my $first = 1;
+ my $last = 1;
+ write_config('return "' . $pfx . ' ' . $first . ' ' . $last . '"');
+ $p_srv->reload();
+ wait_reload($watcher);
+
+ print $ps "mg -1\r\n";
+ is(scalar <$ps>, "HD\r\n", "mg -1 hit");
+
+ my $stats = mem_stats($ps, 'proxy');
+ isnt($stats->{user_a1}, -1, "user_a1 is not -1");
+};
+
+done_testing();
+
+END {
+ unlink $configfile;
+}