3

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

Chris Palmer
  • 193
  • 5
  • Have you tried setting the [sigint-ignore](http://dev.mysql.com/doc/refman/5.0/en/mysql-command-options.html#option_mysql_sigint-ignore) option in MySQL? – Air May 23 '14 at 16:32
  • 1
    I think the problem is that your script is in a system call (namely read) when the signal arrives. Every signal interrupts system call execution, so the read call within execute() is probably interrupted which may make the underlying mysql client sad. I'm not familiar with Python, i'm just trying to guess the direction... hopefully helps – Géza Török Sep 30 '14 at 14:23

0 Answers0