I'm having an issue with sending SIGINT's to python scripts which are connecting to a MySQL database using MySQLdb (mysql-python). The python script runs in an infinite loop, and I want to catch the SIGINT and gracefully exit after completing the current loop of the script. However the SIGINT is interrupting any mysql query that is being run, and disconnects from the server. This causes errors as the python script attempts to finish out it's current loop.
I've narrowed the behavior down to the following script:
infinity.py
#! /usr/bin/python
import time
import signal
import MySQLdb
_connection = MySQLdb.connect(host='dummyhost', user='dummyuser', passwd='dummypasswd')
sigint_received = False
def handle_sigint(signal_number, frame):
global sigint_received
print 'handling SIGINT'
sigint_received = True
signal.signal(signal.SIGINT, handle_sigint)
while True:
if sigint_received:
print 'SIGINT was received - ignoring'
sigint_received = False
print 'Starting very long query'
_connection.cursor().execute('Select * from very_large_table')
print 'Finished very long query'
print 'sleeping for 5 seconds'
time.sleep(5)
If I run this script from the command line and then send it a SIGINT from another shell (with sudo kill -INT $PID
) I get the following output:
Starting very long query
handling SIGINT
Finished very long query
sleeping for 5 seconds
SIGINT was received - ignoring
Starting very long query
Traceback (most recent call last):
File "reporting/scripts/infinity.py", line 24, in <module>
_connection.cursor().execute('Select * from agile_clicks_arrivals')
File "/home/chris.palmer/src/adverplex-src/redistributable/MySQLdb/MySQLdb/cursors.py", line 174, in execute
self.errorhandler(self, exc, value)
File "/home/chris.palmer/src/adverplex-src/redistributable/MySQLdb/MySQLdb/connections.py", line 36, in defaulterrorhandler
raise errorclass, errorvalue
_mysql_exceptions.OperationalError: (2006, 'MySQL server has gone away')
The handling SIGINT
is output immediately upon sending the signal, and the Finished very long query
follows right after. So it is clear that the execute
call is being interrupted. I've also debugged the above script with gdb and when I send the SIGINT, gdb stops and gives the following output:
(gdb) run infinity.py
Starting program: python infinity.py
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff53ff700 (LWP 991)]
[Thread 0x7ffff53ff700 (LWP 991) exited]
Starting very long query
Program received signal SIGINT, Interrupt.
0x00007ffff7bcbd2d in read () from /lib/x86_64-linux-gnu/libpthread.so.0
(gdb) c
Continuing.
Finished very long query
sleeping for 5 seconds
Starting very long query
Traceback (most recent call last):
File "reporting/scripts/infinity.py", line 24, in <module>
_connection.cursor().execute('Select * from agile_clicks_arrivals')
File "/home/chris.palmer/src/adverplex-src/redistributable/MySQLdb/MySQLdb/cursors.py", line 174, in execute
self.errorhandler(self, exc, value)
File "/home/chris.palmer/src/adverplex-src/redistributable/MySQLdb/MySQLdb/connections.py", line 36, in defaulterrorhandler
raise errorclass, errorvalue
_mysql_exceptions.OperationalError: (2006, 'MySQL server has gone away')
[Inferior 1 (process 988) exited with code 01]
(gdb)
Interestingly the print line from my python signal handler doesn't ever execute when I run it through gdb, but it does break execution in libpthread.so.0.
Is the C code underlying the MySqldb package registering it's own signal handler? Is the query being run in a separate thread and if so does it receive the signal as well as the python script?
Is there any way I can prevent this behaviour? I have discovered that I can modify the signal handler to reconnect to MySQL (by calling MySQLdb.connect
again. That would prevent any future queries from failing but it would still have the effect of the current query getting aborted.
Thanks Chris