I have a slow HTTP service I'm writing a proxy for. If I hit the service directly using curl, it looks like this:
{"message":"waited 10 seconds"}
I can hit it at 14s:
{"message":"waited 14 seconds"}
And at 16s:
{"message":"waited 16 seconds"}
I wrote a Mojolicious proxy for the slow service:
my $timer = $c->param('timer') // 10;
my $url = Mojo::URL->new('http://localhost:3333');
$url->query(timer => $timer);
app->log->debug("Invoking " . $url->to_string);
$c->ua->inactivity_timeout(60)->connect_timeout(60)->request_timeout(60)->get($url, sub {
$c->render(json => $tx->res->json);
You can see my various *_timeout() invocations on the user agent. If I hit the proxy, I get what I expect at 14s:
Server: Mojolicious (Perl)
Date: Tue, 05 Sep 2017 20:00:04 GMT
Content-Type: application/json;charset=UTF-8
{"message":"waited 14 seconds"}
But at 16s I get a timeout error:
curl: (52) Empty reply from server
Indeed, the proxy's log shows the request and then a timeout 15s after the request:
[Tue Sep 5 14:01:00 2017] [debug] Inactivity timeout
Mojo::Reactor::Poll: I/O watcher failed: Connection already closed at utils/slow-wrap line 15.
There's something magic about 15s. I added the various timeouts on the user agent, so something else is timing out. I suspect it may be the default timeout in the Mojo::IOLoop::Stream constructor:
sub new { shift->SUPER::new(handle => shift, buffer => '', timeout => 15) }
If I start my proxy with MOJO_INACTIVITY_TIMOUT=60, then the proxy does not timeout:
Server: Mojolicious (Perl)
Content-Type: application/json;charset=UTF-8
Date: Tue, 05 Sep 2017 20:04:50 GMT
{"message":"waited 16 seconds"}
And the proxy logs are clean:
$ MOJO_INACTIVITY_TIMEOUT=60 ct utils/slow-wrap daemon
[Tue Sep 5 14:04:28 2017] [info] Listening at "http://*:3000"
[Tue Sep 5 14:04:34 2017] [debug] GET "/slow"
[Tue Sep 5 14:04:34 2017] [debug] Routing to a callback
[Tue Sep 5 14:04:50 2017] [debug] 200 OK (16.012817s, 0.062/s)
My question is: how can I determine what timeout is being triggered at 15s, and how can I change it on a per-request basis? I don't want to set that timeout for the entire proxy (I do want other slow calls to timeout)—just this particular controller. Is that possible? I've seen various email threads that recommend adding something like to the controller (before invoking the user-agent):
Mojo::IOLoop->stream($c->tx->connection)->timeout(60);
which seems to work in this situation, but I'm unsure this is the right approach. Is there a better mechanism for this? Will this hold up for many requests in the event loop at once?
Scott