DatabaseError: la transazione corrente viene interrotta, i comandi vengono ignorati fino alla fine del blocco della transazione?


252

Ho ricevuto molti errori con il messaggio:

"DatabaseError: current transaction is aborted, commands ignored until end of transaction block"

dopo essere cambiato da python-psycopg a python-psycopg2 come motore di database del progetto Django.

Il codice rimane lo stesso, ma non so da dove provengano quegli errori.


2
Sono curioso di sapere qual è stata la tua risoluzione finale a questo problema? Sto riscontrando lo stesso problema, ma dal momento che il mio provider di hosting non registra errori di query, finora è stato impossibile capire cosa non va.
Gerdemb,

2
Ho finalmente rintracciato il mio problema fino a un bug quando si utilizzava una tabella di database come backend della cache. Django bug: code.djangoproject.com/ticket/11569 StackOverflow discussione: stackoverflow.com/questions/1189541/...
gerdemb

7
Cordiali saluti Se stai solo usando psycopg2 senza django, conn.rollback()(dove conn è il tuo oggetto di connessione) cancellerai l'errore in modo da poter eseguire altre query
Utente

Risposte:


177

Questo è ciò che fa postgres quando una query produce un errore e si tenta di eseguire un'altra query senza prima eseguire il rollback della transazione. (Potresti pensarlo come una funzionalità di sicurezza, per impedirti di corrompere i tuoi dati.)

Per risolvere questo problema, ti consigliamo di capire dove nel codice viene eseguita quella query errata. Potrebbe essere utile utilizzare le opzioni log_statement e log_min_error_statement nel tuo server postgresql.


il problema è che quando stavo usando python-psycopg, non sono emersi errori del genere. psycopg2 ha implementato un meccanismo diverso parlando con Postgres?
jack

4
Il metodo di comunicazione con il server probabilmente non ha importanza, ma è possibile che la versione che hai usato prima fosse in qualche modo predefinita alla modalità autocommit mentre la nuova versione non lo fa. L'errore potrebbe ancora essersi verificato, ma potresti averlo perso più facilmente. È anche possibile che la conversione del tipo di dati o qualcos'altro sia cambiata rispetto alla versione precedente. Indipendentemente da ciò, la soluzione migliore è quella di rintracciare la query errata in modo da poter vedere cosa c'è che non va.
ʇsәɹoɈ,

133

Per eliminare l'errore, ripristinare l'ultima (errata) transazione dopo aver corretto il codice:

from django.db import transaction
transaction.rollback()

Puoi usare try-tranne per evitare che si verifichi l'errore:

from django.db import transaction, DatabaseError
try:
    a.save()
except DatabaseError:
    transaction.rollback()

Consultare: documentazione Django


3
Questo risolve il problema principale e consente di recuperare dopo un'istruzione che ha causato la transazione interrotta.
RichVel,

questo, combinato con try / tranne.
tomwolber

3
Perché usare IntegrityErrore non la classe base DatabaseError?
Jonathan,

Per qualche motivo ho dovuto spostare il rollback al di fuori della sezione "tranne". Stavo usando .bulk_create () e non .save ()
nu everest

Ha lavorato con Django 1.4.16 dopo aver seguito questo stackoverflow.com/a/15753000/573034
Paolo

50

Quindi, ho riscontrato questo stesso problema. Il problema che stavo avendo qui era che il mio database non era sincronizzato correttamente. I problemi semplici sembrano sempre causare la maggior angoscia ...

Per sincronizzare il tuo django db, dalla directory della tua app, all'interno del terminale, digita:

$ python manage.py syncdb

Modifica: Nota che se stai usando django-south, anche l'esecuzione del comando '$ python manage.py migrate' potrebbe risolvere questo problema.

Buona programmazione!


3
Votato per aver dichiarato l'ovvio. Non darei questo più di un voto però perché probabilmente non era la risposta cercata.
Jameson Quinn,

5
L'ho risolto in modo simile da python manage.py migrate <app>... per tutte le mie app.
Clayton,

3
@Clayton - non dici, ma suppongo che tu stia usando django-south - il migratecomando non è integrato in Django.
Greg Ball,

@ GregBall- Esatto ... Sto usando Django-Sud. Ci scusiamo per non aver specificato.
Clayton,

Ricevo questo errore quando eseguo syncdb - penso che abbia a che fare con l'ordine in cui django passa attraverso i tavoli.
Stuart Axon,


34

Nella mia esperienza, questi errori si verificano in questo modo:

try:
    code_that_executes_bad_query()
    # transaction on DB is now bad
except:
    pass

# transaction on db is still bad
code_that_executes_working_query() # raises transaction error

Non c'è nulla di sbagliato nella seconda query, ma poiché è stato rilevato l'errore reale, la seconda query è quella che genera l'errore (molto meno informativo).

modifica: questo accade solo se la exceptclausola cattura IntegrityError(o qualsiasi altra eccezione di database di basso livello), se si rileva qualcosa di simile a DoesNotExistquesto errore non si verifica, perché DoesNotExistnon corrompe la transazione.

La lezione qui è non provare / tranne / passare.


16

Penso che lo schema menzionato da priestc sia più probabile che sia la solita causa di questo problema quando si utilizza PostgreSQL.

Comunque penso che ci siano usi validi per il modello e non penso che questo problema dovrebbe essere un motivo per evitarlo sempre. Per esempio:

try:
    profile = user.get_profile()
except ObjectDoesNotExist:
    profile = make_default_profile_for_user(user)

do_something_with_profile(profile)

Se ti senti bene con questo modello, ma vuoi evitare il codice di gestione delle transazioni esplicito in tutto il luogo, allora potresti voler attivare la modalità autocommit (PostgreSQL 8.2+): https://docs.djangoproject.com/en/ dev / ref / database / # autocommit-mode

DATABASES['default'] = {
    #.. you usual options...
    'OPTIONS': {
        'autocommit': True,
    }
}

Non sono sicuro se vi siano importanti considerazioni sulle prestazioni (o di qualsiasi altro tipo).


6

Se lo ottieni mentre sei nella shell interattiva e hai bisogno di una soluzione rapida, fai come segue:

from django.db import connection
connection._rollback()

originariamente visto in questa risposta


6

Ho riscontrato un comportamento simile durante l'esecuzione di una transazione non funzionante sul postgresterminale. Nulla è passato dopo questo, come databaseè in uno stato di error. Tuttavia, proprio come una soluzione rapida, se puoi permetterti di evitare rollback transaction. Di seguito è stato il trucco per me:

COMMIT;


Ero in sostituzione, questa è esattamente la risposta che stavo cercando.
sarink,

5

Ho il problema con il silimar. La soluzione era migrare db ( manage.py syncdbo manage.py schemamigration --auto <table name>se usi sud).


5

basta usare il rollback

Codice di esempio

try:
    cur.execute("CREATE TABLE IF NOT EXISTS test2 (id serial, qa text);")
except:
    cur.execute("rollback")
    cur.execute("CREATE TABLE IF NOT EXISTS test2 (id serial, qa text);")

1

Ho avuto anche questo errore, ma stava mascherando un altro messaggio di errore più rilevante in cui il codice stava cercando di memorizzare una stringa di 125 caratteri in una colonna di 100 caratteri:

DatabaseError: value too long for type character varying(100)

Ho dovuto eseguire il debug attraverso il codice per visualizzare il messaggio sopra, altrimenti viene visualizzato

DatabaseError: current transaction is aborted

1

In risposta a @priestc e @Sebastian, cosa succede se fai qualcosa del genere?

try:
    conn.commit()
except:
    pass

cursor.execute( sql )
try: 
    return cursor.fetchall()
except: 
    conn.commit()
    return None

Ho appena provato questo codice e sembra funzionare, fallendo silenziosamente senza doversi preoccupare di eventuali errori e lavorando quando la query è buona.


1

Credo che la risposta di AnujGupta sia corretta. Tuttavia, il rollback può sollevare un'eccezione che è necessario rilevare e gestire:

from django.db import transaction, DatabaseError
try:
    a.save()
except DatabaseError:
    try:
        transaction.rollback()
    except transaction.TransactionManagementError:
        # Log or handle otherwise

Se scopri che stai riscrivendo questo codice in varie save()posizioni, puoi estrarre il metodo:

import traceback
def try_rolling_back():
    try:
        transaction.rollback()
        log.warning('rolled back')  # example handling
    except transaction.TransactionManagementError:
        log.exception(traceback.format_exc())  # example handling

Infine, puoi prettificarlo utilizzando un decoratore che protegge i metodi che utilizzano save():

from functools import wraps
def try_rolling_back_on_exception(fn):
    @wraps(fn)
    def wrapped(*args, **kwargs):
        try:
            return fn(*args, **kwargs)
        except:
            traceback.print_exc()
            try_rolling_back()
    return wrapped

@try_rolling_back_on_exception
def some_saving_method():
    # ...
    model.save()
    # ...

Anche se si implementa il decoratore sopra, è comunque conveniente tenerlo try_rolling_back()come metodo estratto nel caso in cui sia necessario utilizzarlo manualmente per i casi in cui è richiesta una gestione specifica e la gestione generica del decoratore non è sufficiente.


1

Questo è un comportamento molto strano per me. Sono sorpreso che nessuno abbia pensato ai punti di salvataggio. Nel mio codice la query non riuscita prevedeva un comportamento:

from django.db import transaction
@transaction.commit_on_success
def update():
    skipped = 0
    for old_model in OldModel.objects.all():
        try:
            Model.objects.create(
                group_id=old_model.group_uuid,
                file_id=old_model.file_uuid,
            )
        except IntegrityError:
            skipped += 1
    return skipped

Ho modificato il codice in questo modo per utilizzare i punti di salvataggio:

from django.db import transaction
@transaction.commit_on_success
def update():
    skipped = 0
    sid = transaction.savepoint()
    for old_model in OldModel.objects.all():
        try:
            Model.objects.create(
                group_id=old_model.group_uuid,
                file_id=old_model.file_uuid,
            )
        except IntegrityError:
            skipped += 1
            transaction.savepoint_rollback(sid)
        else:
            transaction.savepoint_commit(sid)
    return skipped

1

In Flask shell, tutto quello che dovevo fare era session.rollback()superare questo.


1

Ho riscontrato questo problema, l'errore viene visualizzato poiché le transazioni di errore non sono state terminate correttamente, ho trovato il postgresql_transactionscomando Controllo transazioni qui

Controllo delle transazioni

I seguenti comandi vengono utilizzati per controllare le transazioni

BEGIN TRANSACTION  To start a transaction.

COMMIT  To save the changes, alternatively you can use END TRANSACTION command.

ROLLBACK  To rollback the changes.

quindi uso il END TRANSACTIONper terminare l'errore TRANSAZIONE, codice come questo:

    for key_of_attribute, command in sql_command.items():
        cursor = connection.cursor()
        g_logger.info("execute command :%s" % (command))
        try:
            cursor.execute(command)
            rows = cursor.fetchall()
            g_logger.info("the command:%s result is :%s" % (command, rows))
            result_list[key_of_attribute] = rows
            g_logger.info("result_list is :%s" % (result_list))
        except Exception as e:
            cursor.execute('END TRANSACTION;')
            g_logger.info("error command :%s and error is :%s" % (command, e))
    return result_list

-6

potresti disabilitare la transazione tramite "set_isolation_level (0)"

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.