Błąd bazy danych: bieżąca transakcja została przerwana, polecenia ignorowane do końca bloku transakcji?

252

Otrzymałem wiele błędów związanych z komunikatem:

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

po zmianie z python-psycopg na python-psycopg2 jako silnik bazy danych projektu Django.

Kod pozostaje ten sam, po prostu nie wiem, skąd pochodzą te błędy.

Jacek
źródło
2
Jestem ciekawy, jakie było Twoje ostateczne rozwiązanie tego problemu? Mam ten sam problem, ale ponieważ mój dostawca hostingu nie rejestruje błędów zapytań, do tej pory nie mogłem dowiedzieć się, co się dzieje.
gerdemb
2
W końcu wyśledziłem swój problem aż do błędu, gdy używałem tabeli bazy danych jako zaplecza pamięci podręcznej. Błąd Django: code.djangoproject.com/ticket/11569 Dyskusja StackOverflow: stackoverflow.com/questions/1189541/...
gerdemb
7
Do Twojej wiadomości Jeśli używasz tylko psycopg2 bez django, conn.rollback()(gdzie conn jest twoim obiektem połączenia) usunie błąd, abyś mógł uruchamiać inne zapytania
Użytkownik

Odpowiedzi:

177

To właśnie robi postgres, gdy zapytanie powoduje błąd i próbujesz uruchomić inne zapytanie bez uprzedniego wycofania transakcji. (Możesz to potraktować jako funkcję bezpieczeństwa, która ochroni Cię przed uszkodzeniem danych).

Aby to naprawić, musisz dowiedzieć się, gdzie w kodzie jest wykonywane złe zapytanie. Pomocne może być użycie opcji log_statement i log_min_error_statement na serwerze postgresql.

ɈsәɹoɈ
źródło
Problem polega na tym, że kiedy używałem Python-psycopg, nie wystąpiły takie błędy. czy psycopg2 zaimplementował inny mechanizm rozmawiający z postgres?
jack
4
Metoda rozmowy z serwerem prawdopodobnie nie ma znaczenia, ale możliwe, że poprzednio użyta wersja domyślnie działała w trybie automatycznego zatwierdzania, podczas gdy nowa wersja nie. Błąd mógł nadal występować, ale łatwiej go przeoczyć. Możliwe jest również, że konwersja typu danych lub coś innego zmieniło się od starej wersji. Niezależnie od tego najlepszym rozwiązaniem jest wyśledzenie złego zapytania, aby zobaczyć, co jest z nim nie tak.
ʇsәɹoɈ
133

Aby pozbyć się błędu, wycofaj ostatnią (błędną) transakcję po naprawieniu kodu:

from django.db import transaction
transaction.rollback()

Możesz użyć try-wyjątkiem, aby zapobiec wystąpieniu błędu:

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

Patrz: dokumentacja Django

Anuj Gupta
źródło
3
Rozwiązuje to podstawowy problem i pozwala odzyskać po wyciągu, który spowodował przerwanie transakcji.
RichVel,
to w połączeniu z try / wyjątkiem.
tomwolber 24.04.13
3
Dlaczego warto używać, IntegrityErrora nie klasy podstawowej DatabaseError?
Jonathan
Z jakiegoś powodu musiałem przenieść wycofywanie poza sekcję „oprócz”. Używałem .bulk_create (), a nie .save ()
nu everest
Pracowałem z django 1.4.16 po tym, jak postępowałem zgodnie z tym stackoverflow.com/a/15753000/573034
Paolo,
50

Natknąłem się na ten sam problem. Problem, który tutaj miałem, polegał na tym, że moja baza danych nie była odpowiednio zsynchronizowana. Proste problemy zawsze wydają się powodować największy niepokój ...

Aby zsynchronizować db django, w katalogu aplikacji, w terminalu wpisz:

$ python manage.py syncdb

Edycja: Zauważ, że jeśli używasz django-south, uruchomienie polecenia „$ python manage.py migrate” może również rozwiązać ten problem.

Miłego kodowania!

Michael Merchant
źródło
3
Głosowano za stwierdzenie oczywistości. Nie dałbym jednak więcej niż jednego głosu, ponieważ prawdopodobnie nie była to oczekiwana odpowiedź.
Jameson Quinn
5
Naprawiłem to w podobny sposób przez python manage.py migrate <app>... dla wszystkich moich aplikacji.
Clayton
3
@Clayton - nie mówisz, ale zakładam, że używasz django-south - migratepolecenie nie jest wbudowane w django.
Greg Ball
@ GregBall - To prawda ... Używam django-south. Przepraszamy za brak określenia.
Clayton
Otrzymuję ten błąd podczas synchronizacji - myślę, że ma to związek z kolejnością, w której django przechodzi przez tabele.
Stuart Axon
35

W Flask musisz tylko napisać:

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

Dokumentacja PS znajduje się tutaj https://www.postgresql.org/docs/9.4/static/sql-rollback.html

Dmytro Lopushanskyy
źródło
To rozwiązanie jest również bardzo pomocne, gdy błąd występuje w notatniku Jupyter.
Skippy le Grand Gourou
Miły. Pomogło mi to w Jupyter
igorkf
34

Z mojego doświadczenia wynika, że ​​te błędy występują w następujący sposób:

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

W drugim zapytaniu nie ma nic złego, ale ponieważ wychwycono prawdziwy błąd, drugie zapytanie jest tym, które powoduje błąd (znacznie mniej informacyjny).

edycja: dzieje się tak tylko wtedy, gdy exceptklauzula zostanie złapana IntegrityError(lub inny wyjątek bazy danych niskiego poziomu), jeśli złapiesz coś takiego, DoesNotExistten błąd nie pojawi się, ponieważ DoesNotExistnie spowoduje to uszkodzenia transakcji.

Lekcja tutaj to nie próbuj / z wyjątkiem / pass.

kapłan
źródło
16

Myślę, że wzorzec wspomniany przez priestc jest bardziej prawdopodobną przyczyną tego problemu podczas używania PostgreSQL.

Wydaje mi się jednak, że wzorzec jest prawidłowy i nie sądzę, że ten problem powinien być powodem do tego, aby go zawsze unikać. Na przykład:

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

do_something_with_profile(profile)

Jeśli czujesz się dobrze z tym wzorcem, ale chcesz uniknąć jawnego kodu obsługi transakcji w dowolnym miejscu, możesz rozważyć włączenie trybu automatycznego zatwierdzania (PostgreSQL 8.2+): https://docs.djangoproject.com/en/ dev / ref / databases / # autocommit-mode

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

Nie jestem pewien, czy istnieją ważne względy dotyczące wydajności (lub jakiegokolwiek innego rodzaju).

Sebastian
źródło
6

Jeśli otrzymasz to w interaktywnej powłoce i potrzebujesz szybkiej poprawki, wykonaj następujące czynności:

from django.db import connection
connection._rollback()

pierwotnie widoczne w tej odpowiedzi

tutuDajuju
źródło
6

Podobne zachowanie spotkałem podczas nieprawidłowo działającej transakcji na postgresterminalu. Po tym nic nie przeszło, ponieważ databasejest w stanie error. Jednak tak szybko, jak można tego uniknąć rollback transaction. Następujący zrobił dla mnie lewę:

COMMIT;

faizanjehangir
źródło
Byłem w replice, to jest dokładnie odpowiedź, której szukałem.
sarink
5

Mam problem z silimarem. Rozwiązaniem była migracja bazy danych ( manage.py syncdblub manage.py schemamigration --auto <table name>jeśli korzystasz z południa).

Daniil Ryżkow
źródło
5

po prostu użyj wycofania

Przykładowy kod

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
źródło
1

Właśnie miałem ten błąd, ale maskował on inny, bardziej odpowiedni komunikat o błędzie, w którym kod próbował zapisać ciąg znaków o długości 125 znaków w kolumnie o długości 100 znaków:

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

Musiałem debugować przez kod, aby powyższa wiadomość się pojawiła, w przeciwnym razie zostanie wyświetlona

DatabaseError: current transaction is aborted
Thierry Lam
źródło
1

W odpowiedzi na @priestc i @Sebastian, co jeśli zrobisz coś takiego?

try:
    conn.commit()
except:
    pass

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

Właśnie wypróbowałem ten kod i wydaje się, że działa, po cichu zawiesza się bez konieczności dbania o ewentualne błędy i działa, gdy zapytanie jest poprawne.

Nate
źródło
1

Uważam, że odpowiedź @ AnujGupta jest poprawna. Jednak wycofanie może samo w sobie wywołać wyjątek, który należy złapać i obsługiwać:

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

Jeśli okaże się, że przepisujesz ten kod w różnych save()lokalizacjach, możesz wyodrębnić metodę:

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

Na koniec możesz go upiększyć za pomocą dekoratora, który chroni metody wykorzystujące 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()
    # ...

Nawet jeśli implementujesz dekorator powyżej, nadal wygodnie jest zachować go try_rolling_back()jako wyodrębnioną metodę na wypadek, gdybyś musiał użyć go ręcznie w przypadkach, w których wymagana jest specyficzna obsługa, a ogólna obsługa dekoratora nie jest wystarczająca.

Jonathan
źródło
1

To jest dla mnie bardzo dziwne zachowanie. Dziwi mnie, że nikt nie pomyślał o punktach zapisu. W moim kodzie nie można było oczekiwać zapytania:

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

Zmieniłem kod w ten sposób, aby używać punktów zapisu:

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
źródło
1

W muszli Flask wszystko, co musiałem zrobić, to session.rollback()ominąć to.

watsonic
źródło
1

Rozwiązałem ten problem, błąd pojawia się, ponieważ transakcje błędów nie zostały poprawnie zakończone, znalazłem tutajpostgresql_transactions polecenie kontroli transakcji

Kontrola transakcji

Poniższe polecenia służą do kontrolowania transakcji

BEGIN TRANSACTION  To start a transaction.

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

ROLLBACK  To rollback the changes.

więc używam END TRANSACTIONdo zakończenia błędu TRANSAKCJA, kod taki jak ten:

    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
źródło
-6

możesz wyłączyć transakcję poprzez „set_isolation_level (0)”

springrider
źródło