7

Ho una funzione, che fa più query sullo stesso set di dati e voglio assicurarmi che tutte le query vedano esattamente gli stessi dati.In Django, come ottenere letture ripetibili per una transazione?

In termini di SQL, questo significa REPEATABLE READ livello di isolamento per i database che lo supportano. Non mi dispiace avere un livello più alto o anche un blocco completo se il database non è in grado.

Per quanto vedo, questo non è il caso. Cioè se corro qualcosa di simile a questo codice in una shell Python:

with transaction.atomic(): 
    for t in range(0, 60): 
     print("{0}: {1}".format(t, MyModel.objects.count())) 
     time.sleep(1) 

Non appena faccio MyModel.objects.create(...) in un altro, il valore visto con l'aumento del ciclo in esecuzione immediatamente. È esattamente quello che voglio evitare. Ulteriori test mostrano che il comportamento corrisponde al livello READ COMMITTED, che è troppo lassista per i miei gusti.

Vorrei anche sottolineare il punto, voglio un livello di isolamento più rigoroso solo per una singola funzione, non per l'intero progetto.

Quali sono le opzioni migliori per raggiungere questo obiettivo?

Nel mio caso particolare, l'unico database a cui tengo è PostgreSQL 9.3+, ma voglio anche una certa compatibilità con SQLite3 nel qual caso anche il blocco completo dell'intero database va bene per me. Eppure, ovviamente, più la soluzione è generale, più è preferita.

+0

la memorizzazione nella cache di questi metadati è un approccio accettabile? –

+0

Purtroppo no. Le query che faccio calcolano varie statistiche sui dati degli eventi grezzi e per avere una vista coerente, dovrò trascinare l'intero set di dati in memoria, che è qualcosa che non voglio davvero fare. – drdaeman

risposta

6

Hai ragione, il livello di isolamento della transazione predefinito in postgres è READ COMMITTED. Si può facilmente cambiare nelle impostazioni per verificare se sarebbe adatta alle vostre esigenze: https://docs.djangoproject.com/en/1.8/ref/databases/#isolation-level

Inoltre dubito si dovranno affrontare alcuni problemi di prestazioni perché Postgres opera in modo molto efficiente durante il lavoro con le transazioni. Anche in modalità SERIALIZABLE. Anche mysql ha il livello di isolamento predefinito REPEATABLE READ e, poiché lo vediamo, non danneggia le prestazioni.

In ogni caso è possibile impostare manualmente la modalità di isolamento ogni volta che è necessario in questo modo: http://initd.org/psycopg/docs/extensions.html#isolation-level-constants

per impostare il livello di isolamento delle transazioni su misura si può provare dovrebbe occupare come:

from django.db import connection 

with transaction.atomic(): 
    cursor = connection.cursor() 
    cursor.execute('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ') 
    # logic 

Anche io ti suggerirei di cambiare impostazione predefinita prima la modalità nelle impostazioni (se possibile). Quindi se si adatta alle tue esigenze puoi rimuoverlo e modificare il codice in posti speciali.

+0

Grazie per la risposta. Ho avuto problemi di prestazioni prima (credo che fosse circa cinque anni fa) e il cambiamento del livello di isolamento predefinito da SERIALIZABLE a READ COMMITTED (che era un progetto non-Django) ha avuto un impatto ragionevole sulle prestazioni. Forse era solo il mio caso particolare, o avevo colpito qualche bug. In ogni caso, sono riluttante a impostare un livello di isolamento troppo rigido a livello globale e mi piacerebbe davvero sapere se c'è un modo per cambiare questo solo per la durata dell'esecuzione di una singola funzione o di un blocco di codice. O sapere che questo non è fattibile perché qualcosa nel design ORM lo impedisce. – drdaeman

+0

Ovviamente ci sono alcuni scenari in cui le severe modalità di isolamento sono eccessive. Comunque postgres farà del suo meglio :) L'impostazione di un livello di isolamento non predefinito per parti particolari del codice è assolutamente ok.Usare lo stesso in tutto il progetto è più coerente in quanto garantisce che non ti dimentichi almeno di farlo da qualche parte. Aggiornato la risposta con un esempio di codice. – alTus

+1

il problema è che devi chiamare 'SET TRANSACTION ISOLATION LEVEL REPEATABLE READ' prima di qualsiasi query e non è semplice da fare in Django –

Problemi correlati