2012-04-18 16 views
12

Ho riscontrato un problema con la cache quando utilizzo sqlalchemy. Io uso sqlalchemy inserire un dato nel database mysql. Ho l'altra applicazione a elaborare questi dati e ad aggiornare questi dati direttamente. Ma il mio sqlalchemy ha sempre avuto dati vecchi piuttosto che dati aggiornati .. Penso che sqlalchemy abbia inserito nella cache la mia richiesta .. quindi .. Come disabilitarlo?Come disabilitare la memorizzazione nella cache di SQLAlchemy?

+1

Correlati? http://stackoverflow.com/questions/16586114/sqlalchemy-returns-stale-rows –

risposta

34

La solita causa per le persone che pensano che ci sia una "cache" in gioco, oltre alla solita mappa di identità SQLAlchemy che è locale per una transazione, è che stanno osservando gli effetti dell'isolamento della transazione. La sessione di SQLAlchemy funziona di default in una modalità transazionale, il che significa che attende fino al session.commit() per mantenere i dati nel database. Durante questo periodo, le altre transazioni in corso altrove non vedranno questi dati.

Tuttavia, a causa della natura isolata delle transazioni, c'è una svolta in più. Quelle altre transazioni in corso non solo non visualizzeranno i dati della tua transazione fino a quando non verranno confermati, ma non potranno vederli in alcuni casi fino al che sono stati commessi o annullati (che è lo stesso effetto che il tuo close() sta avendo Qui). Una transazione con un livello medio di isolamento si manterrà sullo stato che ha caricato fino a quel momento e continuerà a fornire lo stesso stato locale alla transazione anche se i dati reali sono cambiati - questo si chiama letture ripetibili nella transazione linguaggio di isolamento.

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

-1

Come noto SQLAlchemy does not store caches, quindi è necessario esaminare l'output logging.

+0

Lo penso. Ho aperto 'echo = True' ma non ho trovato nulla di utile. –

+0

hai aggiornato la sessione con un commit? –

+0

Aggiorno i dati senza usare sqlalchemy .. uso 'MySQLdb' .. Mi assicuro che i dati siano aggiornati in MySQL .. –

0

Innanzitutto, non esiste una cache per SQLAlchemy. In base al metodo utilizzato per recuperare i dati dal DB, è necessario eseguire alcuni test dopo che il database è stato aggiornato da altri, verificare se è possibile ottenere nuovi dati.

(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... 

prega folowing il passo: http://docs.sqlalchemy.org/en/latest/core/connections.html

Un'altra possibile ragione è il vostro DB MySQL non viene aggiornato in modo permanente. Riavvia il servizio MySQL e verifica.

+0

Grazie per la risposta. L'ho risolto. Ho appena dimenticato 'session.close' quando uso' scoped_session' ... –

3

In aggiunta a zzzeek risposta eccellente,

ho avuto un problema simile. Ho risolto il problema utilizzando sessioni di breve durata.

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

Ho utilizzato una nuova sessione per attività, gruppo di attività o richiesta (in caso di app Web). Questo ha risolto il problema del "caching" per me.

Questo materiale è stato molto utile per me:

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

+1

Il link sopra sta andando ai documenti per la sessione. Il titolo implica che dovrebbe indicare qui: http://docs.sqlalchemy.org/en/rel_0_8/orm/session.html#session-faq-whentocreate – mozey

11

Questo problema è stato davvero frustrante per me, ma ho finalmente capito.

Possiedo un'applicazione Flask/SQLAlchemy in esecuzione su un sito PHP precedente. Il sito PHP dovrebbe scrivere sul database e SQLAlchemy non sarebbe a conoscenza di eventuali modifiche.

ho provato l'impostazione autoflush sessionmaker = True senza successo ho provato db_session.flush(), db_session.expire_all(), e db_session.commit() prima interrogazione e nessuno ha lavorato. Mostrava comunque dati obsoleti.

Infine mi sono imbattuto in questa sezione la documentazione SQLAlchemy: http://docs.sqlalchemy.org/en/latest/dialects/postgresql.html#transaction-isolation-level

Impostazione del ISOLATION_LEVEL funzionato alla grande. Ora la mia app Flask sta "parlando" con l'app PHP. Ecco il codice:

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

Quando il motore SQLAlchemy è iniziato con il "READ UNCOMMITED" ISOLATION_LEVEL esso deve svolgere "sporco legge" il che significa che leggerà cambiamenti uncommited direttamente dal database.

Spero che questo aiuti


Ecco una possibile soluzione per gentile concessione di AaronD nei commenti

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

Se si utilizza Flask-SQLAlchemy, è possibile sottoclasse 'flask.ext .sqlalchemy.SQLAlchemy' e sovrascrive la funzione 'apply_driver_hacks' per impostare il livello di isolamento, mantenendo comunque tutta l'integrazione di Flask. Inoltre, probabilmente il livello di isolamento 'READ COMMITTED' è sufficiente a garantire che entrambe le applicazioni eseguano le loro scritture dopo averle eseguite e non abbiano aspettato per molto tempo. In questo modo non ti devi preoccupare delle letture sporche, ti dà solo una nuova istantanea del DB ogni volta che leggi. –

+0

@AaronD Potresti postare il tuo codice nella sottoclasse 'flask.ext.sqlalchemy.SQLAlchemy' come hai menzionato? –

+1

Ho solo questo nel mio codice: classe 'UnlockedAlchemy (SQLAlchemy): def apply_driver_hacks (self, app, informazioni, opzioni): se non "ISOLATION_LEVEL" nelle opzioni: opzioni [ "ISOLATION_LEVEL"] = "Read Committed " return super (UnlockedAlchemy, self) .apply_driver_hacks (app, informazioni, opzioni)' –

3

questo stava accadendo nella mia richiesta Flask, e la mia soluzione era quella di scadenza tutti gli oggetti del sessione dopo ogni richiesta.

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

Ha funzionato come un fascino.

Problemi correlati