I wonder if the there is a canonical approach to this in Django?
Essentially the way views are structured, they are functions that return a result that is delivered to the client. It can be as simple as this:
from django.http import HttpResponse
def my_view(request):
return HttpResponse('This is my view!')
But let us suppose that when my_view is loaded, there is some meat in it:
from django.http import HttpResponse
def my_view(request):
result = do_stuff()
return HttpResponse(result)
And imagine that do_stuff
has some moderately time-consuming wind down code to run, that is code that the result does not depend on, but that it would like to run all the same (perhaps to release resources, return some stuff to a clean state etc. it matters not, the principle is that do_stuff
has some code it wants to run, it's a little slow, and hence running it delays delivery of the result, but the result in no way whatsoever depends upon it, so it would prefer to return the result and then run this code).
In a ridiculous imaginary and nonsensical world, this might resemble:
from django.http import HttpResponse
def my_view(request):
result = do_stuff()
return HttpResponse(result)
do_more_stuff()
But of course do_more_stuff
never runs in the real world because of the very nature of returning.
Now, I can imagine solutions here that spins do_more_stuff()
off onto a background thread or into a child process for completion, allowing my_view
to return.
The reason I am asking, though, is twofold:
Regarding the lifespan of such a spun-off thread of process, there is a slight mystery here, that I could laboriously test or study deeply, but others may have a handle on. Is it guaranteed to run to completion (assuming modest run time and not something exceptional like a hang, deadlock or month long run time ..., but maybe 1–5 seconds real time and delaying delivery of the view this long is radically noticeable in the page view lag). The view function is run within a process, and so there are questions regarding the lifecycle of that process and any threads or children it spawns. I sort of imagine it's robust that if Django is running under uwsgi say that views are not handled in ephemeral processes but in processes that linger and continue to handle views and as such are around for the long haul and any reasonable background thread or child process will be able to complete comfortably.
Perhaps there is a canonical Django way to do this. I am curious because it can't be that I'm the first or only person to have struck such a need (i.e. found code in a view that is slowing down delivery but upon which the response does not depend) and that it must be a fairly common thing for developers to encounter and may have a canonical solution I have not found. I've checked middleware options, but it is similarly constrained, i.e. like a view returns a response.
I have a suspicion a solution may reside in the request-finished signal:
https://docs.djangoproject.com/en/3.0/ref/signals/#request-finished
and that a function hooked to that signal runs after the response is delivered and does not delay delivery of the response. But again, short of testing it I don't know, and so am happy for input (while I run off and test it).