camelobserver has asked for the wisdom of the Perl Monks concerning the following question:

Hi,

I'm making an API request to another server and trying to enforce a hard timeout so I don't stall if the server isn't responsive, or trickles a response back. The timeout I want to use is very short (250ms).

Is it possible to combine LWP::UserAgent with an alarm call to force a timeout? I've tried this but w/o success.

I've also looked at LWPx::ParanoidAgent but in my tests with that I was still able to get a request that took twice as long as my timeout.

I'm hoping Monks can point me in the right direction, surely someone here has run into a similar problem and found the right solution for handling it.

Thanks in advance,

One humble camelobserver

  • Comment on Hard timeout for LWP::UserAgent or similar

Replies are listed 'Best First'.
Re: Hard timeout for LWP::UserAgent or similar
by davido (Cardinal) on Mar 30, 2011 at 17:06 UTC

    I'm sure you're aware of the LWP::UserAgent timeout() method, but its granularity is in seconds, not milliseconds. Plus, it has a rather telling description of the timeout() function:

    $ua->timeout( $secs )

    Get/set the timeout value in seconds. The default timeout() value is 180 seconds, i.e. 3 minutes.

    The requests is aborted if no activity on the connection to the server is observed for timeout seconds. This means that the time it takes for the complete transaction and the request() method to actually return might be longer.

    As it says, the time it takes might be longer than the actual timeout setting.

    LWPx::ParanoidAgent's documentation doesn't discuss finer granularity than 'seconds' either. So I don't see that as getting you any closer to your 250ms turnaround.

    There is an interesting module: LWP::Parallel::UserAgent. At first when I looked at it I was thinking of your problem in terms of attacking it in parallel so that it isn't as important that any single thread takes longer than you wanted. But as I was reading over the module, I discovered that it supports a non-blocking mode, which can be set with the $ua->nonblock( $ok ) method. A non-blocking approach with a callback might be so much nicer than trying to constrain your timeouts to 250ms. You may want to have a look at that technique.


    Dave

      You can use fractional seconds
      #!/usr/bin/perl -- use strict; use warnings; use LWP::Simple qw! $ua !; use Time::HiRes qw! time !; Main( @ARGV ); exit( 0 ); sub Main { print time, "\n"; $ua->timeout( 1/1000000 ); $ua->show_progress(1); $ua->get(q!http://example.com/!); print time, "\n"; } __END__ 1301533357.73438 ** GET http://example.com/ ==> 500 Can't connect to example.com:80 (ti +meout) 1301533357.96767
Re: Hard timeout for LWP::UserAgent or similar
by Eliya (Vicar) on Mar 30, 2011 at 18:25 UTC
    Is it possible to combine LWP::UserAgent with an alarm call to force a timeout? I've tried this but w/o success.

    When alarm doesn't work (for whatever reason), I usually fork another process and have the parent kill the child (which would do the actual work) after a predefined time.

    Something like this:

    use LWP; for (...) { my $pid = fork(); die "couldn't fork" unless defined $pid; unless ($pid) { my $ua = LWP::UserAgent->new(); my $response = $ua->get('http://...'); ... exit; } else { select undef, undef, undef, 0.25; kill 15, $pid; } }

    (you might want to use a more sophisticated timing/killing method...)

Re: Hard timeout for LWP::UserAgent or similar
by BrowserUk (Patriarch) on Mar 30, 2011 at 17:19 UTC

    What's wrong with the built-in timeout?

    #! perl -slw use strict; use Time::HiRes qw[ time ]; use LWP; my $ua = LWP::UserAgent->new; $ua->timeout( 0.1 ); print time; my $response = $ua->get('http://192.168.1.31/noexist.html'); print time; if ($response->is_success) { print $response->decoded_content; # or whatever } else { die $response->status_line; } __END__ C:\test>junk76 1301504675.567 1301504675.74173 500 Can't connect to 192.168.1.31:80 (connect: timeout) at C:\test\jun +k76.pl line 17.

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      From the docs
      $ua->timeout( $secs )
      
          Get/set the timeout value in seconds. The default timeout() value is 180 seconds, i.e. 3 minutes.
      
          The requests is aborted if no activity on the connection to the server is observed for timeout seconds. This means that the time it takes for the complete transaction and the request() method to actually return might be longer.
      
      So a trickling stream of data coming in could mean the timeout never getting triggered, or getting triggered way later than timeout.
        So a trickling stream of data coming in could mean the timeout never getting triggered, or getting triggered way later than timeout.

        Remember that tcp/ip coalesces small writes into larger packets before transmission. So the slower the trickle, the longer the time between packets.

        In my simplistic testing, a 250 millisecond timeout never took longer than 315 milliseconds to actually return. If 315 is too long for you, try scaling back the value you set to say 200.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.