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?
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. :)
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)
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