5

I'm using python on google app engine, and keep getting google.appengine.api.urlfetch_errors.DeadlineExceededError on requests made from a machine which does some backend processing. The requests take approximately 60s, sometimes a little longer, so I've attempted to increase the deadline.

The requests are wrapped in a retry, and from the logs I can see that the time between retries is always ~60s. I assume this is either because I've configured things incorrectly, or misunderstand the limitations of the deadline.

The machine config is:

instance_class: B8
basic_scaling:
  max_instances: 1
  idle_timeout: 10m

The code I'm using is (redacted for simplicity):

from google.appengine.api import urlfetch
from retrying import retry

timeout = 600
retries = 10

@retry(
    stop_max_attempt_number=retries,
    wait_exponential_multiplier=1000,
    wait_exponential_max=1000*60*5
)
def fetch(url):
    """Fetch remote data, retrying as necessary"""
    urlfetch.set_default_fetch_deadline(timeout)
    result = urlfetch.fetch(url)
    if result.status_code != 200:
        raise IOError("Did not receive OK response from server")
    return result.content

data = fetch(config['url'])

I've tried setting the deadline explicitly as urlfetch.fetch(url, deadline=timeout) but setting the default seems to be the approach most people suggest.

Can anyone clarify whether there is a maximum value which can be set for deadline?

  • 1
    I'm pretty sure 60 seconds is the max for urlfetch (and standard interactive requests), but it doesn't seem to be documented any longer. 60 seconds is a long time to keep an connection open - you might be better polling the other side? – snakecharmerb Jan 11 '17 at 10:01
  • 1
    Thanks snakecharmerb. To be honest I've found conflicting information (some of it may be from older versions of GAE). I've found sources claiming 60s is the max, and others saying this only applies to the `automatic_scaling` classes. Certainly a search on github finds plenty of people calling `urlfetch.set_default_fetch_deadline()` with values higher than 60, and given I can't find the limit documented anywhere in the docs I'd love for someone to tell me I'm just missing something obvious! – Jamie Collinson Jan 11 '17 at 10:24
  • Yes, unfortunately the docstring for `set_default_fetch_deadline` just says "This function doesn't do any range or type checking of the value", which isn't very helpful. Regardless of the limit though, I'd be thinking of trying a different approach to avoid tying up an instance for so long, unless it takes that long to transfer the data? FWIW its worth [this java doc](https://cloud.google.com/appengine/docs/java/outbound-requests#request_timeouts) says 60s for an outbound request, you'd imagine the same limits apply in java-world., – snakecharmerb Jan 11 '17 at 10:35
  • Thanks for that link - hadn't seen that one before. That does sound *almost* conclusive. This is a machine running a backend task via a cron. The task takes ~45mins of data processing after fetching the data file, so taking some processing time is not an issue. Unfortunately the raw data it fetches is quite large (and the server quite slow) so it takes 60s +/- 10s for the request. I guess I need to look at splitting the requests up but I'm not sure that's offered by the server I'm requesting from. – Jamie Collinson Jan 11 '17 at 11:43
  • Maybe you could use [sockets](https://cloud.google.com/appengine/docs/python/sockets/) instead of http, if the remote supports them? Looks like they might not time out for at least two minutes. Or use a compute engine instance instead of a backend. – snakecharmerb Jan 11 '17 at 11:57
  • Possible duplicate of [How to set timeout for urlfetch in Google App Engine?](http://stackoverflow.com/questions/2162115/how-to-set-timeout-for-urlfetch-in-google-app-engine) – Dan Cornilescu Jan 11 '17 at 14:03
  • @snakecharmerb - thanks for the suggestions and help. – Jamie Collinson Jan 11 '17 at 14:28
  • @DanCornilescu - I don't think it's a duplicate as that question is asking how to set the timeout, and I'm asking if there's a maximum which can be set. – Jamie Collinson Jan 11 '17 at 14:28
  • @JamieCollinson: fair enough. – Dan Cornilescu Jan 11 '17 at 14:39

1 Answers1

2

The Request Timer

The Google App Engine request timer (Java/Python/Go) ensures that requests have a finite lifespan and do not get caught in an infinite loop. Currently, the deadline for requests to frontend instances is 60 seconds. (Backend instances have no corresponding limit.) Every request, including warmup (request to /_ah/warmup) and loading requests ("loading_request=1" log header), is subject to this restriction.

If a request fails to return within 60 seconds and a DeadlineExceededError is thrown and not caught, the request is aborted and a 500 internal server error is returned. If the DeadlineExceededError is caught but a response is not produced quickly enough (you have less than a second), the request is aborted and a 500 internal server error is returned.

As far as i read the document i think the maximum request timeout is 60 seconds in the app engine. Here is the link to the documentation

  • Thanks Bravin, I've read that documentation and am using what it recommends, but when I change the deadline (e.g. to 600s) it still seems to timeout after a maximum of 60s.What I'm trying to establish is whether there's a maximum value serving as a limit or whether I'm making a mistake in my code. – Jamie Collinson Jan 11 '17 at 10:04
  • appreciate you taking the time to respond, but that's the `google.appengine.runtime.DeadlineExceededError` rather than `google.appengine.api.urlfetch_errors.DeadlineExceededError`. Also the request deadline only refers to instances which are automatic scaling. Both manual and basic scaling instances can [run indefinitely per the docs](https://cloud.google.com/appengine/docs/python/an-overview-of-app-engine#scaling_types_and_instance_classes) – Jamie Collinson Jan 11 '17 at 11:37
  • @JamieCollinson I'm pretty sure 60 seconds is the max for urlfetch (and standard interactive requests), but it doesn't seem to be documented any longer. 60 seconds is a long time to keep an connection open - you might be better polling the other side? for an example app engine queues. – Bravin Balasubramaniam Jan 11 '17 at 11:51