Make long-running tasks time out in FastAPI

David Y.
jump to solution

The Problem

One of the API endpoints in my FastAPI application executes a data retrieval task that sometimes takes a very long time to complete, making my application feel sluggish. How can I cancel the execution of this task if it takes more than a set amount of time and serve cached data instead?

The Solution

We can do this using the asyncio.wait_for function that takes an awaitable and a timeout value in seconds as parameters. The awaitable is then run as a task. If the task is completed before the timeout is reached, wait_for will return the value the task returns. If the timeout is reached before the task returns, the task will be canceled, and wait_for will raise a TimeoutError. In Python versions before 3.11, it raised an asyncio.TimeoutError instead.

The following example code demonstrates using a simulated data retrieval task that sleeps for 10 seconds before returning a random number and a timeout of 5 seconds. For Python versions below 3.11 replace except TimeoutError with asyncio.exceptions.TimeoutError.

from fastapi import FastAPI
import asyncio, random

app = FastAPI()

cache = {"result": random.random()}  # seed the cache


async def long_running_task():
    await asyncio.sleep(10)  # sleep for 10 seconds
    return random.random()  # return a random number


@app.get("/retrieve-data")
async def retrieve_data():
    try:
        result = await asyncio.wait_for(long_running_task(), timeout=5)  # timeout after five seconds of waiting
        cache["result"] = result  # cache the result
        return {"message": result, "cache": False}
    except TimeoutError:
        return {"message": cache["result"], "cache": True}


# Run FastAPI app
if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

A request to this application’s /retrieve-data endpoint will invoke long_running_task, cancel it after five seconds, and then return the cached random number. If we swap the task’s sleep time and the timeout value, a request to /retrieve-data will wait for five seconds before returning a new random number. In both cases, the API indicates whether the value returned was sourced from the cache.

Considered "not bad" by 4 million developers and more than 150,000 organizations worldwide, Sentry provides code-level observability to many of the world's best-known companies like Disney, Peloton, Cloudflare, Eventbrite, Slack, Supercell, and Rockstar Games. Each month we process billions of exceptions from the most popular products on the internet.

Sentry