4

I currently have a route in a Flask app that pulls data from an external server and then pushes the results to the front end. The external server is occasionally slow or unresponsive. What's the best way to place a timeout on the route call, so that the front end doesn't hang if the external server is lagging? Or is there a more appropriate way to handle this situation in Flask (not Apache, nginx, etc)?

My goal is to timeout a route call, not keep an arbitrary long process alive like this SO question: Time out issues with chrome and flask. Options like websockets run background processes/threads until they finish; however, I want to stop a slow route call after some fixed amount of time has elapsed. Like Timeout on a function call and Python Timeout but within a Flask context. Celery's task decorator (Concurrent asynchronous processes with Python, Flask and Celery) seems like a great solution, but I don't want to require a large dependency to only use a small amount of its functionality.

Community
  • 1
  • 1
Jay B. Martin
  • 499
  • 4
  • 11
  • How is your app being served? That's really where the blocking behavior happens, and the best way forward depends on the thing that is running flask, rather than anything within flask itself. – AdamKG Aug 10 '13 at 17:17
  • My app is being served by gunicorn. I'm actually not experiencing any blocking problems because all of the the calls are asynchronous. I sometimes get the spinning wheels of death, and placing timeouts on the ajax calls causes Flask to throw broken-pipe errors. – Jay B. Martin Aug 10 '13 at 17:22
  • Er, why do you think the calls are async? If so you shouldn't get the spins-of-death. Kind of unclear on what the problem is - are you trying to avoid the broken-pipe issue, handle the broken pipe thing gracefully, or avoid the spins of death? – AdamKG Aug 10 '13 at 17:24
  • And what worker class are you using with gunicorn? Actually, can you include your whole gunicorn cmdline? – AdamKG Aug 10 '13 at 17:25
  • My goal is to have a spinner display while content is loading async from the back end (currently does). If the external request takes too long, I'd like flask to signal the front end that it couldn't complete the request. Currently, when the internet connection is either slow or down, Flask waits indefinitely to get a response from the external server. Because this is all happening async, the rest of the web app still functions well (no blocking), I just want the front end to correctly display what's going on. – Jay B. Martin Aug 10 '13 at 17:32
  • Gotcha. I think a bg (stdlib-threading.Thread) thread + a .join() in your request-handling code will do it, will add answer w/ deets. – AdamKG Aug 10 '13 at 17:34
  • Do you still need the gunicorn info? – Jay B. Martin Aug 10 '13 at 17:36
  • Don't think so, but might be an edge-case with greenthreads + regular threads if the simple approach doesn't work/is buggy. – AdamKG Aug 10 '13 at 17:37

1 Answers1

2

Not entirely sure if I'm right about all this, but my understanding is that if the thread (or greenthread) handling the request does the network call in it's own "foreground", and that call times out, the borken pipe is pretty much going to happen. But what you can do is spin off a fully-separate thread that does the network request, then call Thread.join() with a timeout in your request-handling code.

http://docs.python.org/2/library/threading.html#threading.Thread.join

At that point, call Thread.isAlive() (still in your request-handling code path) and if True, the network call didn't return in time, and you return your error state.

If it's False - and you'll need to have the "worker" thread update some (thread-safe) data structure with the response data - you get that response data and go on your way.

AdamKG
  • 13,678
  • 3
  • 38
  • 46