I'm currently trying to understand the signal handling in Django when receiving a SIGTERM.
Background information
I have an application with potentially long running requests, running in a Docker container. When Docker wants to stop a container, it first sends a SIGTERM signal, waits for a while, and then sends a SIGKILL. Normally, on the SIGTERM, you stop receiving new requests, and hope that the currently running requests finish before Docker decides to send a SIGKILL. However, in my application, I want to save which requests have been tried, and find that more important than finishing the request right now. So I'd prefer for the current requests to shutdown on SIGTERM, so that I can gracefully end them (and saving their state), rather than waiting for the SIGKILL.
My attempt
My theory is that you can register a signal listener for SIGTERM, that performs a sys.exit()
, so that a SystemExit
exception is raised. I then want to catch that exception in my request handler, and save my state. As a first experiment I've created a mock project for the Django development server.
I registered the signal in the Appconfig.ready()
function:
import signal
import sys
from django.apps import AppConfig
import logging
logger = logging.getLogger(__name__)
def signal_handler(signal_num, frame):
sys.exit()
class TesterConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'tester'
def ready(self):
logger.info('starting ready')
signal.signal(signal.SIGTERM, signal_handler)
and have created a request handler that catches Exceptions and BaseExceptions:
import logging
import sys
import time
from django.http import HttpResponse
def handler(request):
try:
logger.info('start')
while True:
time.sleep(1)
except Exception:
logger.info('exception')
except BaseException:
logger.info('baseexception')
return HttpResponse('hallo')
But when I start the development server user python manage.py runserver
and then send a kill signal using kill -n 15 <pid>
, no 'baseexception' message gets logged ('start' does get logged).
The full code can be foud here.
My question
My hypothesis is that the SIGTERM signal is handled in the main thread, so the sys.exit()
call is handled in the main thread. So the exception is not raised in the thread running the request handler, and nothing is caught.
How do I change my code to have the SystemError
raised in the request handler thread? I need some information from that thread to log, so I can't just 'log' something in the signal handler directly.