Hi there,
I would like to introduce asynchronous behavior into my django application.
I’m trying to write an api endpoint which is called from a webhook.
However, processing the request is very I/O intensive and will cause the webhook to fail if it takes more that 10 seconds to complete.
I did a little bit of research and found that using django’s sync_to_async()
might solve my problem.
My goal now is to return immediately with the response code 200 and start the heavy processing in a new thread.
Currently it looks like this:
@require_POST
@csrf_exempt
async def hook(request):
payload = json.loads(request.body)
event = payload['event_name']
if event:
logger.info(f"received event {event}")
# run database update in a new thread
loop = asyncio.get_event_loop()
async_function = sync_to_async(
fetch.handle_systemhook,
thread_sensitive=False,)
loop.create_task(async_function(payload=payload))
# return 200 immediately to prevent the systemhook from failing
return HttpResponse()
else:
logger.info(f"the received request did not contain the field 'event_name' and will be ignored")
logger.debug(f"payload: {payload}")
return HttpResponseBadRequest()
However, when the hook triggers, I get the following error:
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/asgiref/sync.py", line 486, in thread_handler
raise exc_info[1]
File "/usr/local/lib/python3.9/site-packages/django/core/handlers/exception.py", line 42, in inner
response = await get_response(request)
File "/usr/local/lib/python3.9/site-packages/django/core/handlers/base.py", line 265, in _get_response_async
self.check_response(response, callback)
File "/usr/local/lib/python3.9/site-packages/django/core/handlers/base.py", line 337, in check_response
raise ValueError(
ValueError: The view submodules.views.hook didn't return an HttpResponse object. It returned an unawaited coroutine instead. You may need to add an 'await' into your view.
I’m not really sure what to make of the error message, as I’m not explicitly returning a coroutine.
Could you tell me what I’m missing?
Is this the best way to achieve what I’m trying to do or should I take a different approach?
django is being run within docker with niginx and gunicorn/uvicorn:
gunicorn -w 3 -k uvicorn.workers.UvicornWorker siteroot.asgi:application --bind 0.0.0.0:8000