Подтвердить что ты не робот

Изящная обработка "MySQL ушел"

Я пишу небольшой адаптер базы данных в Python, в основном для удовольствия. Я пытаюсь получить код, чтобы изящно восстановить из ситуации, когда соединение MySQL "уходит", ака wait_timeout превышено. Я установил wait_timeout в 10, чтобы попробовать.

Здесь мой код:

def select(self, query, params=[]):
        try:
            self.cursor = self.cxn.cursor()
            self.cursor.execute(query, params)
        except MySQLdb.OperationalError, e:
            if e[0] == 2006:
                print "We caught the exception properly!"
                print self.cxn
                self.cxn.close()
                self.cxn = self.db._get_cxn()
                self.cursor = self.cxn.cursor()
                self.cursor.execute(query, params)
                print self.cxn

        return self.cursor.fetchall()

Далее я жду десять секунд и попробую сделать запрос. Вот как выглядит CherryPy:

[31/Dec/2009:20:47:29] ENGINE Bus STARTING
[31/Dec/2009:20:47:29] ENGINE Starting database pool...
[31/Dec/2009:20:47:29] ENGINE POOL Connecting to MySQL...
[31/Dec/2009:20:47:29] ENGINE POOL Connecting to MySQL...
[31/Dec/2009:20:47:29] ENGINE POOL Connecting to MySQL...
[31/Dec/2009:20:47:29] ENGINE POOL Connecting to MySQL...
[31/Dec/2009:20:47:29] ENGINE POOL Connecting to MySQL...
[31/Dec/2009:20:47:29] ENGINE Started monitor thread '_TimeoutMonitor'.
[31/Dec/2009:20:47:29] ENGINE Started monitor thread 'Autoreloader'.
[31/Dec/2009:20:47:30] ENGINE Serving on 0.0.0.0:8888
[31/Dec/2009:20:47:30] ENGINE Bus STARTED
We caught the exception properly!   <====================================== Aaarg!
<_mysql.connection open to 'localhost' at 1ee22b0>
[31/Dec/2009:20:48:25] HTTP Traceback (most recent call last):
  File "/usr/local/lib/python2.6/dist-packages/CherryPy-3.1.2-py2.6.egg/cherrypy/_cprequest.py", line 606, in respond
cherrypy.response.body = self.handler()
  File "/usr/local/lib/python2.6/dist-packages/CherryPy-3.1.2-py2.6.egg/cherrypy/_cpdispatch.py", line 25, in __call__
    return self.callable(*self.args, **self.kwargs)
  File "adp.py", line 69, in reports
    page.sources = sql.GetSources()
  File "/home/swoods/dev/adp/sql.py", line 45, in __call__
    return getattr(self.formatter.cxn, parsefn)(sql, sql_vars)
  File "/home/swoods/dev/adp/database.py", line 96, in select
    self.cursor.execute(query, params)
  File "/usr/lib/pymodules/python2.6/MySQLdb/cursors.py", line 166, in execute
    self.errorhandler(self, exc, value)
  File "/usr/lib/pymodules/python2.6/MySQLdb/connections.py", line 35, in defaulterrorhandler
    raise errorclass, errorvalue
OperationalError: (2006, 'MySQL server has gone away')

[31/Dec/2009:20:48:25] HTTP 
Request Headers:
  COOKIE: session_id=e14f63acc306b26f14d966e606612642af2dd423
  HOST: localhost:8888
  CACHE-CONTROL: max-age=0
  ACCEPT: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
  ACCEPT-CHARSET: ISO-8859-1,utf-8;q=0.7,*;q=0.3
  USER-AGENT: Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.5 (KHTML, like     Gecko) Chrome/4.0.249.43 Safari/532.5
  CONNECTION: keep-alive
  Remote-Addr: 127.0.0.1
  ACCEPT-LANGUAGE: en-US,en;q=0.8
  ACCEPT-ENCODING: gzip,deflate
127.0.0.1 - - [31/Dec/2009:20:48:25] "GET /reports/1 HTTP/1.1" 500 1770 "" "Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.249.43 Safari/532.5"

Почему это не работает? Я четко поймаю исключение, регенерирую как соединение, так и курсор, но он все равно не работает. Связано ли это с тем, как MySQLdb получает соединения?

4b9b3361

Ответ 1

Не могу видеть из кода, но я предполагаю, что метод db._get_cxn() выполняет какой-то пул соединений и возвращает существующий объект соединения вместо создания нового. Нет ли вызова, который вы можете сделать на db, чтобы очистить существующее бесполезное соединение? (И вы действительно должны называть внутренний метод _ -prefixed?)

Для предотвращения MySQL has gone away я обычно предпочитаю сохранять временную метку с соединением последнего используемого ею времени. Затем, прежде чем пытаться использовать его снова, я смотрю на метку времени и закрываю/удаляю соединение, если оно было использовано в последний раз больше, чем несколько часов назад. Это позволяет скрыть все возможные запросы с помощью try...except OperationalError...try again.