Request timeouts
Web endpoint (a.k.a. webhook) requests should complete quickly, ideally within a
few seconds. All web endpoint function types
(web_endpoint
, asgi_app
, wsgi_app
)
have a maximum HTTP request timeout of 150 seconds enforced. However, the
underlying Modal function can have a longer timeout.
In case the function takes more than 150 seconds to complete, a HTTP status 303 redirect response is returned pointing at the original URL with a special query parameter linking it that request. This is the result URL for your function. Most web browsers allow for up to 20 such redirects, effectively allowing up to 50 minutes (20 * 150 s) for web endpoints before the request times out.
Some libraries and tools might require you to add a flag or option in order to
follow redirects automatically, e.g. curl -L ...
or http --follow ...
.
The result URL can be reloaded without triggering a new request. It will block until the request completes.
wait_for_response=False
In cases when you want a web endpoint to trigger and return the result URL
immediately, you can add the wait_for_response=False
flag to your
web_endpoint/asgi_app/wsgi_app decorator. The decorated function will then be
immediately triggered and a 202 Accepted
status code will be returned, along
with a JSON payload containing the result URL: {"result_url": "..."}
.
This can be useful if you for example want to store the result URL somewhere for later access, or immediately redirect a web browser to a URL that won’t trigger a new function call on manual refresh.
Note that once the body is returned from the result URL and is sent successfully as an HTTP response, it may not be accessed again. This is because HTTP has stream semantics, and we support large response bodies. (For responses less than a few MiB, it may work briefly, but this is not supported.)
Polling solutions
Sometimes it can be useful to be able to poll for results rather than wait for a
long running HTTP request. The easiest way to do this is to have your web
endpoint spawn a modal.Function
call and return the function call id that
another endpoint can use to poll the submitted function’s status. Here is an
example:
import fastapi
from modal import Stub, asgi_app
from modal.functions import FunctionCall
stub = Stub()
web_app = fastapi.FastAPI()
@stub.function()
@asgi_app()
def fastapi_app():
return web_app
@stub.function()
def slow_operation():
...
@web_app.post("/accept")
async def accept_job(request: fastapi.Request):
call = slow_operation.spawn()
return {"call_id": call.object_id}
@web_app.get("/result/{call_id}")
async def poll_results(call_id: str):
function_call = FunctionCall.from_id(call_id)
try:
return function_call.get(timeout=0)
except TimeoutError:
http_accepted_code = 202
return fastapi.responses.JSONResponse({}, status_code=http_accepted_code)
Document OCR Web App is an example that uses this pattern.