2011-07-21 23 views
20

Me he encontrado con una situación extraña. Estoy escribiendo algunos casos de prueba para mi programa. El programa está escrito para trabajar en sqllite o postgresqul dependiendo de las preferencias. Ahora estoy escribiendo mi código de prueba usando unittest. Muy básicamente lo que estoy haciendo:python sqlalchemy + postgresql programa congela

def setUp(self): 
    """ 
     Reset the database before each test. 
    """ 
    if os.path.exists(root_storage): 
     shutil.rmtree(root_storage) 
    reset_database() 
    initialize_startup() 
    self.project_service = ProjectService() 
    self.structure_helper = FilesHelper() 
    user = model.User("test_user", "test_pass", "[email protected]", 
         True, "user") 
    self.test_user = dao.store_entity(user) 

en la configuración elimino todas las carpetas que existen (creados por algunas pruebas) entonces restablecer mi base de datos (tablas de caída en cascada básicamente) entonces inicializar la base de datos de nuevo y crear un poco servicios que se usarán para las pruebas.

def tearDown(self): 
    """ 
     Remove project folders and clean up database. 
    """ 
    created_projects = dao.get_projects_for_user(self.test_user.id) 
    for project in created_projects: 
     self.structure_helper.remove_project_structure(project.name) 
    reset_database() 

derribar hace lo mismo, excepto la creación de los servicios, ya que este módulo de prueba es parte de un mismo conjunto con otros módulos y no quiero que las cosas se quedan atrás por algunas pruebas.

Ahora todas mis pruebas se ejecutan bien con SQLLite. Con postgresql me encuentro con una situación muy extraña: en algún punto de la ejecución, que en realidad difiere de ejecución a ejecución por un pequeño margen (por ejemplo, una o dos llamadas adicionales), el programa simplemente se detiene. Quiero decir que no se genera ningún error, no se lanza ninguna excepción, el programa simplemente se detiene.

Ahora lo único que se me ocurre es que de alguna manera me olvido de una conexión abierta en algún lugar y después de que haya transcurrido el tiempo y algo sucede. Pero tengo MUCHAS conexiones, así que antes de comenzar a recorrer todo ese código, agradecería algunas sugerencias/opiniones.

¿Qué podría causar este tipo de comportamiento? ¿Por dónde empezar a buscar?

Saludos, Bogdan

Respuesta

27

aplicaciones basadas en PostgreSQL congelación debido PG bloquea tablas bastante agresiva, en particular, que no permitirá que un comando DROP continúe si las conexiones están abiertas en una transacción pendiente, que han tenido acceso a esa mesa en de cualquier manera (SELECT incluido).

Si está en un sistema unix, el comando "ps -ef | grep 'post'" le mostrará todos los procesos de Postgresql y verá el estado de los comandos actuales, incluida su "DROP TABLE" colgada o lo que sea, eso es frío. También puede verlo si selecciona desde la vista pg_stat_activity.

Por lo tanto, la clave es garantizar que no queden transacciones pendientes, esto significa a nivel DBAPI que los cursores de resultados están cerrados, y cualquier conexión abierta actualmente tiene rollback() o está cerrado explícitamente. En SQLAlchemy, esto significa que cualquier conjunto de resultados (es decir, ResultProxy) con filas pendientes se agotan por completo y cualquier objeto Connection ha sido close() d, que los devuelve al grupo y llama al rollback() en la conexión DBAPI subyacente. querrá asegurarse de que existe algún tipo de código de desmontaje incondicional que asegure que esto ocurra antes de que se emita cualquier tipo de comando DROP TABLE.

Por lo que "tengo muchas conexiones", usted debe conseguir eso bajo control. Cuando el conjunto de pruebas SQLA corre a través de sus 3.000 pruebas de algo, nos aseguramos de que estamos absolutamente en control de conexiones y por lo general sólo se abre una conexión a la vez (aún así, se ejecuta en PyPy tiene algunos comportamientos que todavía causa cuelga con PG ..es duro). Hay una clase de grupo llamada AssertionPool que puede usar para esto, lo que asegura que solo una conexión se desprotege a la vez, de lo contrario se genera un error informativo (muestra dónde se guardó).

+0

Sería bueno mencionar esto en algún lugar de la documentación. Encontré exactamente el mismo problema después de cambiar de sqlite a Postgre. – schlamar

+1

aquí va, estado allí desde hace años: http://www.sqlalchemy.org/trac/wiki/FAQ#MyprogramishangingwhenIsaytable.dropmetadata.drop_all – zzzeek

+0

"... que han accedido a esa tabla de alguna manera (SELECT incluido)." Bueno, ¡esto salvó mi salud mental! ¡Muchas gracias! – Tregoreg

8

Una solución que encontré para este problema fue llamar al db.session.close() antes de intentar llamar al db.drop_all().Esto cerrará la conexión antes de eliminar las tablas, lo que impedirá que Postgres bloquee las tablas.

Vea una discusión mucho más detallada del problema here.

+1

¡Esta solución resolvió mi problema de inmediato! –

+0

Esta respuesta proporcionó mi solución, y la respuesta zzzeek explicó el por qué. Gracias a ambos. – zerocog

+0

Todavía tenía bloqueos, aunque se usó 'close_all()' para desmontar un dispositivo en Pytest, que era la única fuente de una sesión para todas las pruebas. Entonces hay otra parte de las congelaciones, que no se soluciona con esto. – Zelphir

Cuestiones relacionadas