2010-06-13 24 views
9

L'app My Pylons utilizza il server MySQL locale tramite SQLAlchemy e python-MySQLdb. Quando il server viene riavviato, aperte le connessioni in pool sono apparentemente chiusi, ma l'applicazione non conoscono il problema e a quanto pare quando si tenta di utilizzare tale connessione che riceve "server MySQL è andato via":Gestione mysql restart in SQLAlchemy

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

Questa eccezione non viene catturato da nessuna parte, quindi bolle verso l'utente. Se dovessi gestire questa eccezione da qualche parte nel mio codice, per favore mostra il posto per tale codice in un'app WSGI Pylons. O forse c'è una soluzione in SA stessa?

risposta

6

See Modifica nella parte inferiore per soluzione testata

Non ho provato, ma forse usando PoolListener è un modo per andare?

Si potrebbe fare qualcosa di simile:

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

In questo modo ogni connessione il tempo sta per essere estratto dalla piscina ci prova se è effettivamente collegato al server. In caso contrario, diamo a sqlalchemy una possibilità di riconnettersi. Dopo, se il problema è ancora lì, lo lasciamo andare.

PS: Non ho testato se funziona.

Edit: Per quanto riguarda la piloni, modifiche al motore inizializzazione mostrato sopra avrebbe bisogno di essere fatto in your_app.model.init_model (piloni 0.9.7) o your_app.config.environment.load_environment (piloni 1.0) Funzione - Questi sono questo è il posti in cui viene creata l'istanza del motore .

EDIT

Ok. Sono stato in grado di riprodurre la situazione descritta. Il codice sopra necessita di alcune modifiche per funzionare. Di seguito è come dovrebbe essere fatto. Inoltre non importa se è 0.9.7 o 1.0.

È necessario modificare your_app/config/environment.py. Mettere queste esportazioni nella parte superiore del file:

import sqlalchemy 
import sqlalchemy.interfaces 
import _mysql_exceptions 

E la fine della funzione load_environment dovrebbe essere simile che:

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) 

Questa volta sono stato in grado di provarlo (su piloni 1.0 + SQLAlchemy 0,6. 1) e funziona. :)

+0

Grazie, anima simile è qui: http://www.mail-archive.com/[email protected]/msg15079.html e funziona per me. – wRAR

+0

Non hai visto la tua modifica :) – wRAR

+0

Nota per SQLAlchemy 0.7 - 'PoolListener' è deprecato, ma la stessa soluzione può essere implementata usando il nuovo [sistema degli eventi] (http://docs.sqlalchemy.org/en/latest/ core/pooling.html # disconnect-movimentazione-pessimista). –

3

È possibile utilizzare SQLAlchemy proxy per la movimentazione su ogni query SQL eccezione:

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 

Per collegare questo proxy è necessario farlo in config/ambiente.py

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

O scrivere middleware per la gestione su ogni query http eccezione:

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

di collegare questo middleware, al fine di stack di cui hai bisogno o semplicemente in config/middleware.py:

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