summaryrefslogtreecommitdiff
path: root/t/proxyconfig.t
diff options
context:
space:
mode:
Diffstat (limited to 't/proxyconfig.t')
-rw-r--r--t/proxyconfig.t177
1 files changed, 176 insertions, 1 deletions
diff --git a/t/proxyconfig.t b/t/proxyconfig.t
index 8c9332c..73b0169 100644
--- a/t/proxyconfig.t
+++ b/t/proxyconfig.t
@@ -60,7 +60,7 @@ sub wait_reload {
}
my @mocksrvs = ();
-diag "making mock servers";
+#diag "making mock servers";
for my $port (11511, 11512, 11513) {
my $srv = mock_server($port);
ok(defined $srv, "mock server created");
@@ -267,7 +267,182 @@ is(<$watcher>, "OK\r\n", "watcher enabled");
}
+###
+# diag "starting proxy again from scratch"
+###
+
+# TODO: probably time to abstract the entire "start the server with mocked
+# listeners" routine.
+$watcher = undef;
+write_modefile('return "reqlimit"');
+$p_srv->stop;
+while (1) {
+ if ($p_srv->is_running) {
+ sleep 1;
+ } else {
+ ok(!$p_srv->is_running, "stopped proxy");
+ last;
+ }
+}
+
+@mocksrvs = ();
+# re-create the mock servers so we get clean connects, the previous
+# backends could be reconnecting still.
+for my $port (11511, 11512, 11513) {
+ my $srv = mock_server($port);
+ ok(defined $srv, "mock server created");
+ push(@mocksrvs, $srv);
+}
+
+# Start up a clean server.
+# Since limits are per worker thread, cut the worker threads down to 1 to ease
+# testing.
+$p_srv = new_memcached('-o proxy_config=./t/proxyconfig.lua -l 127.0.0.1 -t 1', 11510);
+$ps = $p_srv->sock;
+$ps->autoflush(1);
+
+{
+ for my $msrv ($mocksrvs[0], $mocksrvs[1], $mocksrvs[2]) {
+ my $be = $msrv->accept();
+ $be->autoflush(1);
+ ok(defined $be, "mock backend created");
+ push(@mbe, $be);
+ }
+
+ for my $be (@mbe) {
+ like(<$be>, qr/version/, "received version command");
+ print $be "VERSION 1.0.0-mock\r\n";
+ }
+
+ my $stats = mem_stats($ps, 'proxy');
+ isnt($stats->{active_req_limit}, 0, "active request limit is set");
+
+ # active request limit is 4, pipeline 6 requests and ensure the last two
+ # get junked
+ my $cmd = '';
+ for ('a', 'b', 'c', 'd', 'e', 'f') {
+ $cmd .= "mg /test/$_\r\n";
+ }
+ print $ps $cmd;
+
+ # Lua config only sends commands to the first backend for this test.
+ my $be = $mbe[0];
+ for (1 .. 4) {
+ like(<$be>, qr/^mg \/test\/\w\r\n$/, "backend received mg");
+ print $be "EN\r\n";
+ }
+ my $s = IO::Select->new();
+ $s->add($be);
+ my @readable = $s->can_read(0.25);
+ is(scalar @readable, 0, "no more pending reads on backend");
+
+ for (1 .. 4) {
+ is(scalar <$ps>, "EN\r\n", "received miss from backend");
+ }
+
+ is(scalar <$ps>, "SERVER_ERROR active request limit reached\r\n", "got error back");
+ is(scalar <$ps>, "SERVER_ERROR active request limit reached\r\n", "got two limit errors");
+
+ # Test turning the limit back off.
+ write_modefile('return "noreqlimit"');
+ $watcher = $p_srv->new_sock;
+ print $watcher "watch proxyevents\n";
+ is(<$watcher>, "OK\r\n", "watcher enabled");
+ $p_srv->reload();
+ wait_reload($watcher);
+
+ $stats = mem_stats($ps, 'proxy');
+ is($stats->{active_req_limit}, 0, "active request limit unset");
+
+ $cmd = '';
+ for ('a', 'b', 'c', 'd', 'e', 'f') {
+ $cmd .= "mg /test/$_\r\n";
+ }
+ print $ps $cmd;
+ for (1 .. 6) {
+ like(<$be>, qr/^mg \/test\/\w\r\n$/, "backend received mg");
+ print $be "EN\r\n";
+ }
+ for (1 .. 6) {
+ is(scalar <$ps>, "EN\r\n", "received miss from backend");
+ }
+}
+
+{
+ # Test the buffer memory limiter.
+ # - limit per worker will be 1/t global limit
+ write_modefile('return "buflimit"');
+ $p_srv->reload();
+ wait_reload($watcher);
+ # Get a secondary client to trample limit.
+ my $sps = $p_srv->new_sock;
+
+ my $stats = mem_stats($ps, 'proxy');
+ isnt($stats->{buffer_memory_limit}, 0, "buf mem limit is set");
+
+ # - test SET commands with values, but nothing being read on backend
+ my $data = 'x' x 30000;
+ my $cmd = "ms foo 30000 T30\r\n" . $data . "\r\n";
+ print $ps $cmd;
+
+ my $be = $mbe[0];
+ my $s = IO::Select->new;
+ $s->add($be);
+ # Wait until the backend has the request queued, then send the second one.
+ my @readable = $s->can_read(1);
+ print $sps $cmd;
+
+ my $res;
+ is(scalar <$be>, "ms foo 30000 T30\r\n", "received first ms");
+ $res = scalar <$be>;
+ print $be "HD\r\n";
+
+ # The second request should have been caught by the memory limiter
+ is(scalar <$sps>, "SERVER_ERROR out of memory\r\n", "got server error");
+ # FIXME: The original response cannot succeed because we cannot allocate
+ # enough memory to read the response from the backend.
+ # This is conveniently testing both paths right here but I would prefer
+ # something better.
+ # TODO: need to see if it's possible to surface an OOM from the backend
+ # handler, but that requires more rewiring.
+ is(scalar <$ps>, "SERVER_ERROR backend failure\r\n", "first request succeeded");
+
+ # Backend gets killed from a read OOM, so we need to re-establish.
+ $mbe[0] = $mocksrvs[0]->accept();
+ $be = $mbe[0];
+ $be->autoflush(1);
+ like(<$be>, qr/version/, "received version command");
+ print $be "VERSION 1.0.0-mock\r\n";
+ like(<$watcher>, qr/error=outofmemory/, "OOM log line");
+
+ # Memory limits won't drop until the garbage collectors run, which
+ # requires a bit more push, so instead we raise the limits here so we can
+ # retry from the pre-existing connections to test swallow mode.
+ write_modefile('return "buflimit2"');
+ $p_srv->reload();
+ wait_reload($watcher);
+
+ # Test sending another request down both pipes to ensure they still work.
+ $cmd = "ms foo 2 T30\r\nhi\r\n";
+ print $ps $cmd;
+ is(scalar <$be>, "ms foo 2 T30\r\n", "client works after oom");
+ is(scalar <$be>, "hi\r\n", "client works after oom");
+ print $be "HD\r\n";
+ is(scalar <$ps>, "HD\r\n", "client received resp after oom");
+ print $sps $cmd;
+ is(scalar <$be>, "ms foo 2 T30\r\n", "client works after oom");
+ is(scalar <$be>, "hi\r\n", "client works after oom");
+ print $be "HD\r\n";
+ is(scalar <$sps>, "HD\r\n", "client received resp after oom");
+
+ # - test GET commands but don't read back, large backend values
+ # - test disabling the limiter
+ # extended testing:
+ # - create errors while holding the buffers?
+}
+
# TODO:
+# check reqlimit/bwlimit counters
# remove backends
# do dead sockets close?
# adding user stats