Obsługa restartu mysql w SQLAlchemy

Aplikacja My Pylons korzysta z lokalnego serwera MySQL poprzez SQLAlchemy i python-MySQLdb. Po ponownym uruchomieniu serwera otwarte połączenia zbiorcze są pozornie zamknięte, ale aplikacja o tym nie wie i najwyraźniej, gdy próbuje użyć takiego połączenia, otrzymuje "serwer MySQL zniknął": {]}

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

Ten wyjątek nie jest nigdzie złapany, więc bąbelki do użytkownika. Jeśli mam obsłużyć ten wyjątek gdzieś w moim kodzie, proszę pokazać miejsce dla takiego kodu w aplikacji pylons WSGI. A może jest rozwiązanie w samym SA?

Author: wRAR, 2010-06-13

2 answers

Zobacz edycję na dole testowanego rozwiązania

Nie próbowałem, ale może za pomocą PoolListener jest droga?

Mógłbyś zrobić coś takiego:

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

W ten sposób za każdym razem, gdy połączenie ma być sprawdzane z puli, testujemy, czy rzeczywiście jest podłączone do serwera. Jeśli nie, dajemy sqlalchemy jedną szansę na ponowne połączenie. Potem, jeśli problem nadal istnieje, odpuszczamy.

[[3]] PS: nie testowałem czy to działa.

Edit: jeśli chodzi o pylony, modyfikacje inicjalizacji silnika pokazane powyżej muszą być wykonane w your_app.model.init_model (Pylons 0.9.7) or your_app.config.środowisko.load_environment (Pylons 1.0) function - są to to jest miejsca miejsce, w którym zostanie utworzona instancja silnika.

EDIT

Ok. Udało mi się odtworzyć opisaną sytuację. Powyższy kod wymaga pewnych zmian w celu praca. Poniżej znajduje się sposób, w jaki należy to zrobić. Nie ma też znaczenia, czy jest to 0.9.7, czy 1.0.

Musisz edytować your_app/config/environment.py. umieść te eksportu na górze pliku:

import sqlalchemy
import sqlalchemy.interfaces
import _mysql_exceptions

I koniec funkcji load_environment powinien wyglądać tak:

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)

Tym razem udało mi się go przetestować (na pylonach 1.0 + SQLAlchemy 0.6.1) i działa. :)

 6
Author: zifot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-14 18:26:00

Możesz użyć serwera proxy SQLAlchemy do obsługi wyjątków przy każdym zapytaniu 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

Aby podłączyć ten proxy musisz to zrobić w config/enviroment.py

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

Lub napisać middleware do obsługi wyjątków przy każdym zapytaniu 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']

Aby połączyć ten middleware w kolejności stosu, jak potrzebujesz lub po prostu w config/middleware.py:

# CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
app = MyMiddleware(app)
 3
Author: estin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-14 08:34:33