DatabaseError: поточна транзакція перервана, команди ігноруються до кінця блоку транзакцій?


252

У мене виникло багато помилок із повідомленням:

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

після зміни з python-psycopg на python-psycopg2 як двигун бази даних проекту Django.

Код залишається тим самим, просто не знаю, звідки ці помилки.


2
Мені цікаво, яким було ваше остаточне вирішення цієї проблеми? У мене є та сама проблема, але оскільки мій провайдер хостингу не записує помилок запиту, поки що неможливо було зрозуміти, що йде не так.
Гердем

2
Нарешті я відстежив свою проблему до помилки під час використання таблиці бази даних як резервного кешу. Django помилка: code.djangoproject.com/ticket/11569 StackOverflow обговорення: stackoverflow.com/questions/1189541 / ...
gerdemb

7
FYI Якщо ви просто використовуєте psycopg2 без django, conn.rollback()(де conn - ваш об'єкт з'єднання), ви очистите помилку, щоб ви могли запускати інші запити
Користувач

Відповіді:


177

Це те, що робить postgres, коли запит видає помилку, і ви намагаєтеся запустити інший запит, не попередньо відкочуючи транзакцію. (Ви можете вважати це засобом безпеки, щоб не пошкодити ваші дані.)

Щоб виправити це, ви захочете з’ясувати, де в коді виконується поганий запит. Можливо, буде корисно використовувати параметри log_statement та log_min_error_statement на сервері postgresql.


проблема в тому, що коли я використовував python-psycopg, таких помилок не виникало. чи реалізував psycopg2 інший механізм розмови з постгресами?
джек

4
Метод розмови з сервером, мабуть, не має значення, але можливо, що версія, яку ви використовували раніше, якось перейшла до режиму автокомісії, тоді як нова версія цього не має. Помилка все-таки може статися, але ви могли легше її пропустити. Можливо також, що конверсія типу даних або щось інше змінилося з давньої версії. Незважаючи на те, найкращим виправленням є виявлення поганого запиту, щоб ви могли побачити, що в ньому погано.
ʇsәɹoɈ

133

Щоб позбутися помилки, скасуйте останню (помилкову) транзакцію після виправлення коду:

from django.db import transaction
transaction.rollback()

Ви можете використовувати спробу, крім випадків, щоб запобігти появі помилки:

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

Довідково: Документація Джанго


3
Це стосується основної проблеми і дозволяє відновити після заяви, яка спричинила перервану транзакцію.
RichVel

це в поєднанні з спробувати / за винятком.
tomwolber

3
Навіщо використовувати, IntegrityErrorа не базовий клас DatabaseError?
Джонатан

Мені чомусь довелося перенести відкат за межі розділу «крім». Я використовував .bulk_create (), а не .save ()
nu everest

Працював з django 1.4.16 після виконання цього stackoverflow.com/a/15753000/573034
Паоло

50

Отже, я зіткнувся з цим самим питанням. Проблема, яку я мав тут, полягала в тому, що моя база даних не була синхронізована належним чином. Прості проблеми завжди, як видається, викликають найбільше зусиль ...

Щоб синхронізувати django db з каталогу вашого додатка, в терміналі, введіть:

$ python manage.py syncdb

Редагувати. Зауважте, що якщо ви використовуєте django-south, запуск команди $ $ python Manag.py migrate також може вирішити цю проблему.

Щасливого кодування!


3
Запропонований за виклад очевидного. Я б не дав цього більш ніж одного підсумку, хоча це, мабуть, не було шуканої відповіді.
Jameson Quinn

5
Я виправив це аналогічним чином python manage.py migrate <app>… для всіх моїх додатків.
Клейтон

3
@Clayton - ти не кажеш, але я припускаю, що ти використовуєш django-south - migrateкоманда не вбудована у django.
Грег Бал

@ GregBall - Це правильно ... Я використовую джанго-південь. Вибачте, що не вказали.
Клейтон

Я отримую цю помилку під час синхронізації - я думаю, що це стосується того, як django проходить через таблиці.
Стюарт Ексон

35

На колбі просто потрібно написати:

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

Документація PS подається тут https://www.postgresql.org/docs/9.4/static/sql-rollback.html


Це рішення також дуже допомагає, коли помилка виникає в зошиті Юпітера.
Skippy le Grand Gourou

Приємно. Це допомогло мені в Юпітері
igorkf

34

На мій досвід, ці помилки трапляються таким чином:

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

У другому запиті нічого поганого, але оскільки була виявлена ​​реальна помилка, другий запит - це той, що викликає (набагато менш інформативну) помилку.

редагувати: це трапляється лише в тому випадку, якщо exceptпункт ловить IntegrityError(або будь-який інший виняток із бази даних низького рівня), якщо ви вловили щось подібне, DoesNotExistпомилка не з’явиться, оскількиDoesNotExist не пошкоджує транзакцію.

Урок тут не намагайся / окрім / пройти.


16

Я думаю, що шаблон, який священик згадує, швидше за все, є звичайною причиною цієї проблеми при використанні PostgreSQL.

Однак я вважаю, що для цього шаблону є дійсне використання, і я не думаю, що ця проблема повинна бути причиною завжди уникати цього. Наприклад:

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

do_something_with_profile(profile)

Якщо ви відчуваєте себе добре з цим шаблоном, але хочете уникати явного коду обробки транзакцій у всьому світі, тоді, можливо, ви захочете перейти в режим автоматичного введення в дію (PostgreSQL 8.2+): https://docs.djangoproject.com/en/ dev / ref / бази даних / # режим автокомісії

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

Я не впевнений, чи є важливі міркування щодо ефективності роботи (або будь-якого іншого типу).


6

Якщо ви отримуєте це в інтерактивній оболонці і вам потрібно швидко виправити, зробіть це:

from django.db import connection
connection._rollback()

спочатку бачили в цій відповіді


6

Я зіткнувся з подібною поведінкою під час запуску несправної операції на postgresтерміналі. Після цього нічого не пройшло, оскільки стан databaseзнаходиться в стані error. Однак, як швидке виправлення, якщо ви можете дозволити собі цього уникнути rollback transaction. Слідом зробив для мене трюк:

COMMIT;


Я був у відповіді, саме таку відповідь я шукав.
Саринк

5

У мене проблема з силімаром. Рішенням було міграція db ( manage.py syncdbабо manage.py schemamigration --auto <table name>якщо ви використовуєте південь).


5

просто використовуйте відкат

Приклад коду

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

У мене просто була ця помилка, але вона маскувала ще одне більш відповідне повідомлення про помилку, де код намагався зберігати рядок 125 символів у стовпчику 100 символів:

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

Мені довелося налагоджувати код, щоб з’явилося вищезгадане повідомлення, інакше воно відображається

DatabaseError: current transaction is aborted

1

У відповідь на @priestc і @Sebastian, що робити, якщо ви робите щось подібне?

try:
    conn.commit()
except:
    pass

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

Я просто спробував цей код, і він, здається, працює, не мовчавши, не піклуючись про можливі помилки, і працюючи, коли запит хороший.


1

Я вважаю, що відповідь @ AnujGupta правильна. Однак відкат може сам по собі створити виняток, який слід ловити та обробляти:

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

Якщо ви виявите, що ви переписуєте цей код у різних save()місцях, ви можете вилучити метод:

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

Нарешті, ви можете зробити це гарненько за допомогою декоратора, який захищає методи, які використовують 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()
    # ...

Навіть якщо ви реалізуєте декоратор вище, його все ще зручно зберігати try_rolling_back()як витягнутий метод, якщо вам потрібно використовувати його вручну для тих випадків, коли потрібна конкретна обробка, а загальної обробки декораторів недостатньо.


1

Це дуже дивна поведінка для мене. Я здивований, що ніхто не думав про заощадження. У моєму коді очікувана поведінка:

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

Я змінив код таким чином, щоб використовувати точки збереження:

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

Я зустрів цю проблему, помилка виходить , так як угоди , помилки не закінчилося правильно, я виявив , що postgresql_transactionsкоманди управління транзакцій тут

Контроль транзакцій

Наступні команди використовуються для контролю транзакцій

BEGIN TRANSACTION  To start a transaction.

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

ROLLBACK  To rollback the changes.

тому я використовую END TRANSACTIONдля закінчення помилки TRANSACTION, такий код:

    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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.