2010-06-13 20 views
9

La aplicación My Pylons utiliza un servidor MySQL local a través de SQLAlchemy y python-MySQLdb. Cuando se reinicia el servidor, conexiones agrupadas abiertos son aparentemente cerradas, pero la aplicación no sabe nada de esto y al parecer, cuando se trata de usar dicha conexión se recibe "servidor MySQL se ha apagado":Manejar el reinicio de mysql en 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') 

Esta excepción no está atrapado en ninguna parte, por lo que burbujea hasta el usuario. Si debo manejar esta excepción en algún lugar de mi código, muestre el lugar para dicho código en una aplicación Pylons WSGI. ¿O tal vez hay una solución en SA en sí?

Respuesta

6

Ver EDITAR en la parte inferior para la solución probada

Yo no lo probamos, pero tal vez usando PoolListener es un camino a seguir?

se podría hacer algo como esto:

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

De esta manera cada conexión de tiempo está a punto de ser retirado de la piscina comprobamos si en realidad está conectado al servidor. Si no, le damos a sqlalchemy una oportunidad de volver a conectar. Después de eso, si el problema sigue ahí, lo dejamos ir.

PD: No he probado si esto funciona.

Editar: En cuanto a los pilones, las modificaciones a la inicialización del motor mostró anteriormente tendría que ser hecho en your_app.model.init_model (Pilones 0.9.7) o your_app.config.environment.load_environment (Pilones 1.0) función - estos son este es el lugares lugar donde se crea la instancia del motor.

EDITAR

Ok. Pude reproducir la situación descrita. El código anterior necesita algunos cambios para poder funcionar. A continuación se muestra cómo se debe hacer. Además, no importa si es 0.9.7 o 1.0.

Necesita editar su_app/config/environment.py. Poner estas exportaciones en la parte superior del archivo:

import sqlalchemy 
import sqlalchemy.interfaces 
import _mysql_exceptions 

Y el final de la función load_environment debe ser similar a lo siguiente:

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) 

Esta vez tuve la oportunidad de probarlo (en los pilones 1.0 + SQLAlchemy 0.6. 1) y funciona. :)

+0

Gracias, alma similar está aquí: http://www.mail-archive.com/[email protected]/msg15079.html y funciona para mí. – wRAR

+0

No vi tu edición :) – wRAR

+0

Nota para SQLAlchemy 0.7 - 'PoolListener' está en desuso, pero la misma solución se puede implementar usando el nuevo [sistema de eventos] (http://docs.sqlalchemy.org/en/latest/ core/pooling.html # disconnect-handling-pesimista). –

3

Puede utilizar SQLAlchemy proxy para el manejo en cada consulta SQL excepción:

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 

Para conectar este proxy debe hacer eso en config/ambiente.py

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

O escribir middleware para el manejo de cada petición HTTP de excepción:

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

Para conectar este middleware con el fin de pila como sea necesario o simplemente en config/middleware.py:

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