Обработайте перезапуск MySQL в SQLAlchemy - PullRequest
8 голосов
/ 13 июня 2010

Приложение My Pylons использует локальный сервер MySQL через SQLAlchemy и python-MySQLdb.Когда сервер перезапускается, соединения с открытым пулом, по-видимому, закрываются, но приложение об этом не знает и, очевидно, когда оно пытается использовать такое соединение, оно получает сообщение «Сервер MySQL исчез»:

File '/usr/lib/pymodules/python2.6/sqlalchemy/engine/default.py', line 277 in do_execute
  cursor.execute(statement, parameters)
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: (OperationalError) (2006, 'MySQL server has gone away')

Это исключение нигде не поймано, поэтому оно всплывает перед пользователем.Если мне нужно обработать это исключение где-нибудь в моем коде, пожалуйста, покажите место для такого кода в приложении Pylons WSGI.Или, может быть, есть решение в самой SA?

Ответы [ 2 ]

6 голосов
/ 14 июня 2010

См. РЕДАКТИРОВАТЬ внизу для протестированного решения

Я не пробовал, но, возможно, с помощью PoolListener это способgo?

Вы можете сделать что-то вроде этого:

class MyListener(sqlalchemy.interfaces.PoolListener):
    def __init__(self):
       self.retried = False
    def checkout(self, dbapi_con, con_record, con_proxy):
       try:
           dbapi_con.info() # is there any better way to simply check if connection to mysql is alive?
       except sqlalchemy.exc.OperationalError:
           if self.retried:
               self.retried = False
               raise # we do nothing
           self.retried = True
           raise sqlalchemy.exc.DisconnectionError

# next, code according to documentation linked above follows

e = create_engine("url://", listeners=[MyListener()])

Таким образом, каждый раз, когда соединение собирается проверяться из пула, мы проверяем, действительно ли оно подключено к серверу.Если нет, мы даем sqlalchemy один шанс восстановить соединение.После этого, если проблема все еще существует, мы отпускаем ее.

PS: я не проверял, работает ли это.

Редактировать: Что касается пилонов, изменения в инициализации двигателя показаливыше должно быть сделано в your_app.model.init_model (Pylons 0.9.7) или your_app.config.environment.load_environment (Pylons 1.0) функция - это это места места, где создается экземпляр движка.

EDIT

ОК.Я смог воспроизвести описанную ситуацию.Код выше нуждается в некоторых изменениях, чтобы работать.Ниже описано, как это сделать.Также не имеет значения, 0.9.7 или 1.0.

Вам необходимо отредактировать your_app / config / environment.py.Поместите эти экспорты в начало файла:

import sqlalchemy
import sqlalchemy.interfaces
import _mysql_exceptions

И конец функции load_environment должен выглядеть следующим образом:

class MyListener(sqlalchemy.interfaces.PoolListener):
    def __init__(self):
       self.retried = False
    def checkout(self, dbapi_con, con_record, con_proxy):
       try:
           dbapi_con.cursor().execute('select now()')
       except _mysql_exceptions.OperationalError:
           if self.retried:
               self.retried = False
               raise
           self.retried = True
           raise sqlalchemy.exc.DisconnectionError

config['sqlalchemy.listeners'] = [MyListener()]

engine = engine_from_config(config, 'sqlalchemy.')
init_model(engine)

На этот раз я смог протестировать его (на Pylons 1.0+ SQLAlchemy 0.6.1) и это работает. :)

3 голосов
/ 14 июня 2010

Вы можете использовать прокси SQLAlchemy для обработки исключений для каждого запроса sql:

from sqlalchemy.interfaces import ConnectionProxy
class MyProxy(ConnectionProxy):
    def cursor_execute(self, execute, cursor, statement, parameters, context, executemany):
        try:
            return execute(cursor, statement, parameters, context)
        except sqlalchemy.exc.OperationalError:
            # Handle this exception
            pass

Для подключения этого прокси вы должны сделать это в config / enviroment.py

engine = engine_from_config(config, 'sqlalchemy.', proxy=MyProxy())

Или напишите промежуточное программное обеспечение для обработки исключений для каждого http-запроса:

class MyMiddleware(object):
    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        try:
            return self.app(environ, start_response)
        except sqlalchemy.exc.OperationalError:
            start_response(
                '500 Internal Server Error',
                [('content-type', 'text/html')])
            return ['error page\n']

Чтобы подключить это промежуточное ПО в нужном вам порядке или просто в config / middleware.py:

# CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
app = MyMiddleware(app)
...