Handle SIGTERM for cleaner shutdowns (#737)

pull/808/head
dgtlmoon 2 years ago committed by GitHub
parent 26f5c56ba4
commit f0f2fe94ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -6,6 +6,34 @@
# Read more https://github.com/dgtlmoon/changedetection.io/wiki # Read more https://github.com/dgtlmoon/changedetection.io/wiki
from changedetectionio import changedetection from changedetectionio import changedetection
import multiprocessing
import signal
import os
def sigterm_handler(_signo, _stack_frame):
import sys
print('Shutdown: Got SIGCHLD')
# https://stackoverflow.com/questions/40453496/python-multiprocessing-capturing-signals-to-restart-child-processes-or-shut-do
pid, status = os.waitpid(-1, os.WNOHANG | os.WUNTRACED | os.WCONTINUED)
print('Sub-process: pid %d status %d' % (pid, status))
if status != 0:
sys.exit(1)
raise SystemExit
if __name__ == '__main__': if __name__ == '__main__':
changedetection.main() signal.signal(signal.SIGCHLD, sigterm_handler)
# The only way I could find to get Flask to shutdown, is to wrap it and then rely on the subsystem issuing SIGTERM/SIGKILL
parse_process = multiprocessing.Process(target=changedetection.main)
parse_process.daemon = True
parse_process.start()
import time
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
#parse_process.terminate() not needed, because this process will issue it to the sub-process anyway
print ("Exited - CTRL+C")

@ -1259,7 +1259,6 @@ def notification_runner():
global notification_debug_log global notification_debug_log
from datetime import datetime from datetime import datetime
import json import json
while not app.config.exit.is_set(): while not app.config.exit.is_set():
try: try:
# At the moment only one thread runs (single runner) # At the moment only one thread runs (single runner)

@ -4,6 +4,7 @@
import getopt import getopt
import os import os
import signal
import sys import sys
import eventlet import eventlet
@ -11,7 +12,22 @@ import eventlet.wsgi
from . import store, changedetection_app, content_fetcher from . import store, changedetection_app, content_fetcher
from . import __version__ from . import __version__
# Only global so we can access it in the signal handler
datastore = None
app = None
def sigterm_handler(_signo, _stack_frame):
global app
global datastore
app.config.exit.set()
datastore.sync_to_json()
print('Shutdown: Got SIGTERM, DB saved to disk')
raise SystemExit
def main(): def main():
global datastore
global app
ssl_mode = False ssl_mode = False
host = '' host = ''
port = os.environ.get('PORT') or 5000 port = os.environ.get('PORT') or 5000
@ -72,8 +88,10 @@ def main():
"Or use the -C parameter to create the directory.".format(app_config['datastore_path']), file=sys.stderr) "Or use the -C parameter to create the directory.".format(app_config['datastore_path']), file=sys.stderr)
sys.exit(2) sys.exit(2)
datastore = store.ChangeDetectionStore(datastore_path=app_config['datastore_path'], version_tag=__version__) datastore = store.ChangeDetectionStore(datastore_path=app_config['datastore_path'], version_tag=__version__)
app = changedetection_app(app_config, datastore) app = changedetection_app(app_config, datastore)
signal.signal(signal.SIGTERM, sigterm_handler)
# Go into cleanup mode # Go into cleanup mode
if do_cleanup: if do_cleanup:
@ -111,4 +129,3 @@ def main():
else: else:
eventlet.wsgi.server(eventlet.listen((host, int(port))), app) eventlet.wsgi.server(eventlet.listen((host, int(port))), app)

Loading…
Cancel
Save