DatabaseError: Aktuelle Transaktion wird abgebrochen, Befehle bis zum Ende des Transaktionsblocks ignoriert?

252

Ich habe viele Fehler mit der Nachricht erhalten:

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

nach dem Wechsel von Python-Psycopg zu Python-Psycopg2 als Datenbank-Engine des Django-Projekts.

Der Code bleibt derselbe, ich weiß nur nicht, woher diese Fehler stammen.

Jack
quelle
2
Ich bin gespannt, wie Sie dieses Problem endgültig gelöst haben. Ich habe das gleiche Problem, aber da mein Hosting-Anbieter keine Abfragefehler protokolliert, war es bisher unmöglich herauszufinden, was falsch läuft.
Gerdemb
2
Ich habe mein Problem schließlich auf einen Fehler zurückgeführt, als ich eine Datenbanktabelle als Cache-Backend verwendet habe. Django-Fehler: code.djangoproject.com/ticket/11569 StackOverflow- Diskussion: stackoverflow.com/questions/1189541/…
gerdemb
7
Zu Ihrer Information Wenn Sie nur psycopg2 ohne Django verwenden conn.rollback()(wobei conn Ihr Verbindungsobjekt ist), wird der Fehler behoben, sodass Sie andere Abfragen ausführen können
Benutzer

Antworten:

177

Dies ist, was postgres tut, wenn eine Abfrage einen Fehler erzeugt und Sie versuchen, eine andere Abfrage auszuführen, ohne zuvor die Transaktion zurückzusetzen. (Sie können es sich als Sicherheitsfunktion vorstellen, um zu verhindern, dass Ihre Daten beschädigt werden.)

Um dies zu beheben, möchten Sie herausfinden, wo im Code diese fehlerhafte Abfrage ausgeführt wird. Es kann hilfreich sein, die Optionen log_statement und log_min_error_statement auf Ihrem Postgresql-Server zu verwenden.

ʇSәɹoɈ
quelle
Das Problem ist, wenn ich Python-Psycopg verwendet habe, keine solchen Fehler aufgetreten. Hat psycopg2 einen anderen Mechanismus implementiert, der mit Postgres spricht?
Jack
4
Die Methode, mit dem Server zu sprechen, spielt wahrscheinlich keine Rolle, aber es ist möglich, dass die zuvor verwendete Version standardmäßig den Autocommit-Modus verwendet, während die neue Version dies nicht tut. Der Fehler ist möglicherweise immer noch aufgetreten, aber Sie hätten ihn leichter übersehen können. Es ist auch möglich, dass sich die Datentypkonvertierung oder etwas anderes seit der alten Version geändert hat. Unabhängig davon besteht die beste Lösung darin, die fehlerhafte Abfrage aufzuspüren, damit Sie sehen können, was daran falsch ist.
5sәɹoɈ
133

Um den Fehler zu beheben, setzen Sie die letzte (fehlerhafte) Transaktion zurück, nachdem Sie Ihren Code repariert haben:

from django.db import transaction
transaction.rollback()

Sie können try-Except verwenden, um das Auftreten des Fehlers zu verhindern:

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

Siehe: Django-Dokumentation

Anuj Gupta
quelle
3
Dies behebt das Kernproblem und ermöglicht die Wiederherstellung nach einer Anweisung, die die abgebrochene Transaktion verursacht hat.
RichVel
dies kombiniert mit try / excl.
Tomwolber
3
Warum verwenden IntegrityErrorund nicht die Basisklasse DatabaseError?
Jonathan
Aus irgendeinem Grund musste ich den Rollback außerhalb des Abschnitts "außer" verschieben. Ich habe .bulk_create () und nicht .save () verwendet
nu everest
Arbeitete mit Django 1.4.16 nach dem Folgen dieses stackoverflow.com/a/15753000/573034
Paolo
50

Also bin ich auf dasselbe Problem gestoßen. Das Problem, das ich hier hatte, war, dass meine Datenbank nicht richtig synchronisiert wurde. Einfache Probleme scheinen immer die größte Angst zu verursachen ...

Geben Sie Folgendes ein, um Ihre Django-Datenbank in Ihrem App-Verzeichnis innerhalb des Terminals zu synchronisieren:

$ python manage.py syncdb

Bearbeiten: Wenn Sie django-south verwenden, kann dieses Problem auch durch Ausführen des Befehls '$ python manage.py migrate' behoben werden.

Viel Spaß beim Codieren!

Michael Merchant
quelle
3
Upvoted für die Angabe des Offensichtlichen. Ich würde dies jedoch nicht mehr als eine Gegenstimme geben, da dies wahrscheinlich nicht die gesuchte Antwort war.
Jameson Quinn
5
Ich habe es auf ähnliche Weise behoben, indem ich python manage.py migrate <app>... für alle meine Apps.
Clayton
3
@Clayton - Sie sagen nicht, aber ich nehme an, Sie verwenden django-south - der migrateBefehl ist nicht in Django integriert.
Greg Ball
@ GregBall- Das ist richtig ... Ich benutze Django-Süd. Entschuldigung für die Nichtangabe.
Clayton
Ich erhalte diesen Fehler, wenn ich syncdb mache - ich denke, das hängt mit der Reihenfolge zusammen, in der Django die Tabellen durchläuft.
Stuart Axon
35

In Flask müssen Sie nur schreiben:

curs = conn.cursor()
curs.execute("ROLLBACK")
conn.commit()

Die PS-Dokumentation finden Sie hier https://www.postgresql.org/docs/9.4/static/sql-rollback.html

Dmytro Lopushanskyy
quelle
Diese Lösung ist auch dann sehr hilfreich, wenn der Fehler in einem Jupyter-Notebook auftritt.
Skippy le Grand Gourou
Nett. Es hat mir in Jupyter geholfen
igorkf
34

Nach meiner Erfahrung treten diese Fehler folgendermaßen auf:

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

An der zweiten Abfrage ist nichts auszusetzen, aber da der eigentliche Fehler abgefangen wurde, ist die zweite Abfrage diejenige, die den (viel weniger informativen) Fehler auslöst.

Bearbeiten: Dies geschieht nur, wenn die exceptKlausel abfängt IntegrityError(oder eine andere Datenbankausnahme auf niedriger Ebene). Wenn Sie so etwas abfangen, wird DoesNotExistdieser Fehler nicht angezeigt, da DoesNotExistdie Transaktion nicht beschädigt wird.

Die Lektion hier ist, nicht zu versuchen / außer / zu bestehen.

Priester
quelle
16

Ich denke, dass die von Priestc erwähnten Muster eher die übliche Ursache für dieses Problem sind, wenn PostgreSQL verwendet wird.

Ich bin jedoch der Meinung, dass es gültige Verwendungen für das Muster gibt, und ich denke nicht, dass dieses Problem ein Grund sein sollte, es immer zu vermeiden. Beispielsweise:

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

do_something_with_profile(profile)

Wenn Sie mit diesem Muster einverstanden sind, aber überall expliziten Code für die Transaktionsverarbeitung vermeiden möchten, sollten Sie den Autocommit-Modus (PostgreSQL 8.2+) aktivieren: https://docs.djangoproject.com/en/ dev / ref / database / # autocommit-mode

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

Ich bin mir nicht sicher, ob es wichtige Leistungsaspekte gibt (oder von irgendeiner anderen Art).

Sebastian
quelle
6

Wenn Sie dies in einer interaktiven Shell erhalten und eine schnelle Lösung benötigen, gehen Sie folgendermaßen vor:

from django.db import connection
connection._rollback()

ursprünglich in dieser Antwort gesehen

tutuDajuju
quelle
6

Beim Ausführen einer fehlerhaften Transaktion auf dem postgresTerminal ist ein ähnliches Verhalten aufgetreten . Danach ging nichts mehr durch, da sich das databasein einem Zustand von befindet error. Allerdings nur als schnelle Lösung, wenn Sie es sich leisten können, zu vermeiden rollback transaction. Folgendes hat den Trick für mich getan:

COMMIT;

faizanjehangir
quelle
Ich war in einer Antwort, das ist genau die Antwort, die ich gesucht habe.
Sarink
5

Ich habe das Silimar-Problem. Die Lösung bestand darin, db zu migrieren ( manage.py syncdboder manage.py schemamigration --auto <table name>wenn Sie South verwenden).

Daniil Ryzhkov
quelle
5

Verwenden Sie einfach Rollback

Beispielcode

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);")
Umer
quelle
1

Ich hatte gerade auch diesen Fehler, aber er maskierte eine andere relevantere Fehlermeldung, bei der der Code versuchte, eine 125-Zeichen-Zeichenfolge in einer 100-Zeichen-Spalte zu speichern:

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

Ich musste den Code debuggen, damit die obige Nachricht angezeigt wurde, sonst wird sie angezeigt

DatabaseError: current transaction is aborted
Thierry Lam
quelle
1

Was ist, wenn Sie als Antwort auf @priestc und @Sebastian so etwas tun?

try:
    conn.commit()
except:
    pass

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

Ich habe gerade diesen Code ausprobiert und er scheint zu funktionieren, schlägt stillschweigend fehl, ohne sich um mögliche Fehler kümmern zu müssen, und funktioniert, wenn die Abfrage gut ist.

Nate
quelle
1

Ich glaube, dass die Antwort von @ AnujGupta richtig ist. Der Rollback kann jedoch selbst eine Ausnahme auslösen, die Sie abfangen und behandeln sollten:

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

Wenn Sie feststellen, dass Sie diesen Code an verschiedenen save()Stellen neu schreiben , können Sie die folgende Extraktionsmethode verwenden:

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

Schließlich können Sie es mit einem Dekorateur verschönern, der Methoden schützt, die Folgendes verwenden 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()
    # ...

Selbst wenn Sie den obigen Dekorator implementieren, ist es dennoch praktisch, ihn try_rolling_back()als extrahierte Methode beizubehalten, falls Sie ihn manuell verwenden müssen, wenn eine bestimmte Behandlung erforderlich ist und die generische Behandlung des Dekorators nicht ausreicht.

Jonathan
quelle
1

Das ist ein sehr seltsames Verhalten für mich. Ich bin überrascht, dass niemand an Sicherungspunkte gedacht hat. In meinem Code fehlgeschlagene Abfrage wurde Verhalten erwartet:

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

Ich habe den Code auf diese Weise geändert, um Sicherungspunkte zu verwenden:

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
homm
quelle
1

In Flask Shell musste ich nur daran session.rollback()vorbeikommen.

Watsonic
quelle
1

Ich habe dieses Problem festgestellt. Der Fehler tritt auf, da die Fehlertransaktionen nicht ordnungsgemäß beendet wurden. Ich habe hier den postgresql_transactionsBefehl Transaktionssteuerung gefunden

Transaktionskontrolle

Die folgenden Befehle werden zur Steuerung von Transaktionen verwendet

BEGIN TRANSACTION  To start a transaction.

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

ROLLBACK  To rollback the changes.

Also benutze ich den END TRANSACTION, um den Fehler TRANSACTION zu beenden, Code wie folgt:

    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
Dean Fang
quelle
-6

Sie können die Transaktion über "set_isolation_level (0)" deaktivieren.

springrider
quelle