From fc88b9f2d47c72df0b37438da1b31f3769e5bf6a Mon Sep 17 00:00:00 2001 From: Fei Hu Date: Thu, 11 May 2023 12:44:30 -0700 Subject: proxy: add tests for ustats --- t/proxyustats.lua | 31 ++++++++ t/proxyustats.t | 227 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 258 insertions(+) create mode 100644 t/proxyustats.lua create mode 100644 t/proxyustats.t 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; +} -- cgit v1.2.1