3

MySQLdb (mysql-python) を使用して MySQL データベースに接続している Python スクリプトに SIGINT を送信する際に問題が発生しています。Python スクリプトは無限ループで実行されます。スクリプトの現在のループが完了した後、SIGINT をキャッチして正常に終了したいと考えています。ただし、SIGINT は実行中の mysql クエリを中断し、サーバーから切断します。これにより、Python スクリプトが現在のループを終了しようとするため、エラーが発生します。

動作を次のスクリプトに絞り込みました。

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)

このスクリプトをコマンド ラインから実行し、別のシェルから SIGINT を送信すると (を使用sudo kill -INT $PID)、次の出力が得られます。

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')

handling SIGINTシグナルを送信するとすぐに が出力され、直後に が続きFinished very long queryます。executeしたがって、通話が中断されていることは明らかです。また、上記のスクリプトを gdb でデバッグしました。SIGINT を送信すると、gdb が停止し、次の出力が表示されます。

(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) 

興味深いことに、私の Python シグナル ハンドラーからの出力行は、gdb を介して実行すると実行されませんが、libpthread.so.0 では実行が中断されます。

MySqldb パッケージの基礎となる C コードは、独自のシグナル ハンドラを登録していますか? クエリは別のスレッドで実行されていますか? その場合、Python スクリプトだけでなくシグナルも受信しますか?

この動作を防ぐ方法はありますか? シグナル ハンドラーを変更して MySQL に再接続できることを発見しました (MySQLdb.connect再度呼び出すことにより)。これにより、今後のクエリが失敗するのを防ぐことができますが、現在のクエリが中止されるという影響がまだあります。

ありがとうクリス

4

0 に答える 0