3

I'm trying to add background tasks to my middleware, but haven't found any official way to do it in their docs, this is what I have tried so far:

async def task1():
    logging.info("Waiting ...")
    time.sleep(5)
    logging.info("Waited.")

@app.middleware("http")
async def add_process_time_header(request, call_next, bt=Depends(BackgroundTasks)):
    a = bt.dependency()
    a.add_task(task1)
    a()

    return await call_next(request)

This is blocking my requests, also shouldn't I be able to call async functions without await? I want to completely ignore the results of the background tasks, I dont need results from bg tasks

redigaffi
  • 429
  • 6
  • 22
  • what's your background task ? IO/bound or CPU/bound ? – milad_vayani May 25 '22 at 05:02
  • @milad_vayani I/O bound - will read/write from database. – redigaffi May 25 '22 at 06:22
  • Does this answer your question? [How to log raw HTTP request/response in Python FastAPI?](https://stackoverflow.com/questions/69670125/how-to-log-raw-http-request-response-in-python-fastapi) – Chris Mar 07 '23 at 18:03
  • Related answers can be found [here](https://stackoverflow.com/a/73283272/17865804) and [here](https://stackoverflow.com/a/70899261/17865804) as well. – Chris Mar 07 '23 at 18:04
  • Please have a look at [this answer](https://stackoverflow.com/a/71517830/17865804) as well, which explains the difference between defining an endpoint/background task in FastAPI with `async def` and `def`, and which provides solutions when one needs to run blocking operations (such as `time.sleep()`) inside an `async def` endpoint/background task (`task1()` demonstrated in your question would block the event loop - see the linked answer above for more details). – Chris Mar 08 '23 at 12:28

3 Answers3

2

You can attach a BackgroundTask instance to a response to instruct Starlette to perform a background task after the response is returned.

Code taken directly from this github issue thread and worked for me perfectly:

from starlette.background import BackgroundTask
from somewhere import your_background_task

@app.middleware("http")
async def middleware(request: Request, call_next):

    # middleware code

    response.background = BackgroundTask(your_background_task, arg)
    return response
alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
1

I found some way to solve your problem, i think you can find better way,but for now you can use like this:

import logging
import time
import asyncio
from fastapi import FastAPI, Request

app = FastAPI()
@app.get("/")
async def call_api():
    await asyncio.sleep(5)
    return True

async def task1():
    logging.info("Waiting ...")
    await asyncio.sleep(5)
    logging.info("Waited.")

@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    start_time = time.time()

    response , res__task1 = await asyncio.gather(*[call_next(request),task1()])
    process_time = time.time() - start_time
    response.headers["X-Process-Time"] = str(process_time)
    print(process_time)

    return response

A total of 5 seconds are spent on all tasks

milad_vayani
  • 398
  • 1
  • 4
  • 14
  • 3
    The github link you sent previously did the job: github.com/tiangolo/fastapi/issues/2215#issuecomment-931411005 – redigaffi May 25 '22 at 08:59
0

You are using a blocking function for your background task.

time.sleep(5)

Eventhough you are using an async fuction, it doesn't contain any non-blocking i/o. So in effect, it cannot be executed asynchronously.

You should use the following sleep function for a non-blocking timeout.

import asyncio
   await asyncio.sleep(5)

In this way, your sleepwill not block requests being processed. Read more here:Python 3.7 - asyncio.sleep() and time.sleep()

John S John
  • 292
  • 2
  • 9
  • According to this video, my code should work fine https://www.youtube.com/watch?v=_yXOJvr5vOM only difference is he is using a controller instead of a middleware. The problem here is injecting the BackgroundTask service to my middleware afaik – redigaffi May 25 '22 at 06:45
  • I managed to get it to work with github.com/tiangolo/fastapi/issues/2215#issuecomment-931411005 also uvicorn running with 1 worker – redigaffi May 25 '22 at 09:51
  • I see that you still have use asyncio.sleep() as I suggested. Good that it's working. – John S John May 25 '22 at 11:14
  • I dont need to use async.sleep, I can use a normal sleep and it works as expected, but thanks for the help – redigaffi May 25 '22 at 11:25