2012-04-18 28 views
12

Me encuentro con un problema de caché cuando uso sqlalchemy. Yo uso sqlalchemy inserto datos en la base de datos mysql. Tengo la otra aplicación procesa estos datos y luego actualizo esta información directamente. Pero mi sqlalchemy siempre obtuvo datos viejos en lugar de datos actualizados ... Creo que sqlalchemy almacenó en caché mi solicitud ... así que ... ¿Cómo desactivarlo?Cómo deshabilitar el almacenamiento en caché de SQLAlchemy?

+1

¿Tienes un problema? http://stackoverflow.com/questions/16586114/sqlalchemy-returns-stale-rows –

Respuesta

34

La causa habitual para las personas que piensan que hay un "caché" en juego, además del habitual mapa de identidad SQLAlchemy que es local para una transacción, es que están observando los efectos del aislamiento de la transacción. La sesión de SQLAlchemy funciona de manera predeterminada en modo transaccional, lo que significa que espera hasta que se llame a session.commit() para conservar los datos en la base de datos. Durante este tiempo, otras transacciones en curso en otros lugares no verán estos datos.

Sin embargo, debido a la naturaleza aislada de las transacciones, hay un giro adicional. Esas otras transacciones en curso no solo no verán los datos de su transacción hasta que se confirmen, sino que tampoco podrán verla en algunos casos hasta que se confirmen o se retrotraigan también (que es el mismo efecto que su cierre() está teniendo aquí). Una transacción con un grado promedio de aislamiento se mantendrá en el estado que se ha cargado hasta el momento, y seguirá proporcionándole ese mismo estado local a la transacción aunque los datos reales hayan cambiado - esto se llama lecturas repetibles en transacción Lenguaje de aislamiento.

http://en.wikipedia.org/wiki/Isolation_%28database_systems%29

-1

Como sé SQLAlchemy does not store caches, por lo que necesita mirar logging de salida.

+0

Creo que sí. Abrí 'echo = True' pero no obtuve nada útil. –

+0

¿ha actualizado la sesión con una confirmación? –

+0

Actualizo los datos sin usar sqlalchemy .. uso 'MySQLdb' .. Me aseguro de que los datos se hayan actualizado en MySQL ... –

0

En primer lugar, no hay caché para SQLAlchemy. Según su método para obtener datos de DB, debe hacer algunas pruebas después de que otros actualicen la base de datos, vea si puede obtener nuevos datos.

(1) use connection: 
connection = engine.connect() 
result = connection.execute("select username from users") 
for row in result: 
    print "username:", row['username'] 
connection.close() 
(2) use Engine ... 
(3) use MegaData... 

favor folowing el paso en: http://docs.sqlalchemy.org/en/latest/core/connections.html

Otra posible razón es su base de datos MySQL y no se actualiza de forma permanente. Reinicie el servicio MySQL y haga una comprobación.

+0

Gracias por su respuesta. Lo he resuelto Me olvido de 'session.close' cuando uso' scoped_session' ... –

3

Adicionalmente a zzzeek respuesta excelente,

tuve un problema similar. Resolví el problema usando sesiones de vida cortas.

with closing(new_session()) as sess: 
    # do your stuff 

Utilicé una nueva sesión por tarea, grupo de tareas o solicitud (en el caso de la aplicación web). Eso resolvió el problema de "almacenamiento en caché" para mí.

Este material era muy útil para mí:

When do I construct a Session, when do I commit it, and when do I close it

+1

El enlace de arriba va a los documentos para la sesión. El título implica que debe señalar aquí: http://docs.sqlalchemy.org/en/rel_0_8/orm/session.html#session-faq-whentocreate – mozey

11

Este tema ha sido muy frustrante para mí, pero por fin se ha dado cuenta.

Tengo una aplicación Flask/SQLAlchemy que se ejecuta junto con un sitio PHP anterior. El sitio PHP escribiría en la base de datos y SQLAlchemy no estaría al tanto de ningún cambio.

Probé el establecimiento de autoflush sessionmaker = True sin éxito Probé db_session.flush(), db_session.expire_all(), y db_session.commit() antes de consultar y ninguno funcionó. Todavía mostró datos obsoletos.

Finalmente me encontré con esta sección de la documentación SQLAlchemy: http://docs.sqlalchemy.org/en/latest/dialects/postgresql.html#transaction-isolation-level

Ajuste del isolation_level funcionó muy bien. Ahora mi aplicación Flask está "hablando" a la aplicación PHP. Aquí está el código:

engine = create_engine(
       "postgresql+pg8000://scott:[email protected]/test", 
       isolation_level="READ UNCOMMITTED" 
      ) 

Cuando el motor SQLAlchemy se inicia con el "READ uncommited" isolation_level se llevará a cabo "sucio lee" lo que significa que leerá cambios uncommited directamente de la base de datos.

Esperanza esto ayuda


Aquí es una posible cortesía solución de AaronD en los comentarios

from flask.ext.sqlalchemy import SQLAlchemy 

class UnlockedAlchemy(SQLAlchemy): 
    def apply_driver_hacks(self, app, info, options): 
     if "isolation_level" not in options: 
      options["isolation_level"] = "READ COMMITTED" 
    return super(UnlockedAlchemy, self).apply_driver_hacks(app, info, options) 
+0

Si está utilizando Flask-SQLAlchemy, puede subclase 'flask.ext .sqlalchemy.SQLAlchemy' y anula la función 'apply_driver_hacks' para establecer el nivel de aislamiento, manteniendo toda la integración de Flask. Además, probablemente el nivel de aislamiento 'READ COMMITTED' sea suficiente, siempre que ambas aplicaciones estén cometiendo sus escrituras una vez que las hayan realizado y no hayan esperado durante mucho tiempo. De esta manera, no tiene que preocuparse por las lecturas sucias, solo le ofrece una nueva instantánea de base de datos cada vez que lee. –

+0

@AaronD ¿Podría publicar su código en la subclase 'flask.ext.sqlalchemy.SQLAlchemy' como mencionó? –

+1

Sólo tengo esto en mi código: 'UnlockedAlchemy clase (SQLAlchemy): def apply_driver_hacks (auto, aplicación, información, opciones): si no "isolation_level" en las opciones: opciones [ "isolation_level"] = "lectura confirmada " return super (UnlockedAlchemy, self) .apply_driver_hacks (aplicación, información, opciones)' –

3

esto sucedía en mi solicitud frasco, y mi solución fue expirará todos los objetos del sesión después de cada solicitud.

from flask.signals import request_finished 
def expire_session(sender, response, **extra): 
    app.db.session.expire_all() 
request_finished.connect(expire_session, flask_app) 

Funcionó como un encanto.

Cuestiones relacionadas