DatabaseError: текущая транзакция отменяется, команды игнорируются до конца блока транзакции

186

У меня появилось много ошибок с сообщением:

"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/…
Показать ещё 1 комментарий
Теги:
psycopg2
psycopg

13 ответов

132
Лучший ответ

Это то, что postgres делает, когда запрос вызывает ошибку, и вы пытаетесь запустить другой запрос без предварительного отката транзакции. Чтобы исправить это, вы захотите выяснить, где в коде выполняется неправильный запрос. Возможно, было бы полезно использовать параметры log_statement и log_min_error_statement в ваш сервер postgresql.

  • 0
    проблема в том, что когда я использовал python-psycopg, таких ошибок не возникало. реализован ли в psycopg2 другой механизм общения с postgres?
  • 4
    Способ обращения к серверу, вероятно, не имеет значения, но возможно, что версия, которую вы использовали ранее, каким-то образом по умолчанию перешла в режим автоматической фиксации, а новая версия - нет. Ошибка, возможно, все еще произошла, но вы могли бы легче ее пропустить. Также возможно, что преобразование типов данных или что-то еще изменилось со старой версии. В любом случае, лучшее решение - отследить неверный запрос, чтобы вы могли увидеть, что с ним не так.
119

Чтобы избавиться от ошибки, откат последней (ошибочной) транзакции после исправления кода:

from django.db import transaction
transaction.rollback()

Вы можете использовать try-except, чтобы предотвратить возникновение ошибки:

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

Обратитесь: Документация Django

  • 3
    Это решает основную проблему и позволяет восстановить после оператора, вызвавшего прерванную транзакцию.
  • 0
    это, в сочетании с попыткой / исключением.
Показать ещё 6 комментариев
49

Итак, я столкнулся с этой проблемой. Проблема, с которой я столкнулась, заключалась в том, что моя база данных не была правильно синхронизирована. Простые проблемы всегда, кажется, вызывают наибольшее беспокойство...

Чтобы синхронизировать django db из каталога приложений в терминале, введите:

$ python manage.py syncdb

Изменить: обратите внимание, что если вы используете django-south, запуск команды $python manage.py migrate также может решить эту проблему.

Счастливое кодирование!

  • 3
    Проголосовал за констатацию очевидного. Я не дал бы это больше чем одно возражение, хотя, потому что это, вероятно, не был запрошенный ответ.
  • 5
    Я исправил это аналогичным образом: python manage.py migrate <app> ... для всех моих приложений.
Показать ещё 7 комментариев
29

По моему опыту, эти ошибки происходят следующим образом:

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

Во втором запросе нет ничего плохого, но поскольку была обнаружена реальная ошибка, второй запрос - это тот, который вызывает (менее информативную) ошибку.

edit: это происходит только в том случае, если предложение except ловит IntegrityError (или любое другое исключение базы данных низкого уровня). Если вы поймаете что-то вроде DoesNotExist, эта ошибка не появится, потому что DoesNotExist не повреждает транзакции.

Урок здесь не делает try/except/pass.

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/#autocommit-mode

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

Я не уверен, есть ли важные соображения производительности (или любого другого типа).

5

Если вы получаете это в интерактивной оболочке и нуждаетесь в быстром исправлении, сделайте следующее:

from django.db import connection
connection._rollback()

изначально видел в этот ответ

3

У меня проблема с силимаром. Решением было мигрировать db (manage.py syncdb или manage.py schemamigration --auto <table name>, если вы используете юг).

2

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

COMMIT;

  • 0
    Я был в ответе, это именно тот ответ, который я искал.
0

Это очень странное поведение для меня. Я удивлен, что никто не думал о точках сохранения. В моем запросе с ошибкой кода ожидалось поведение:

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
0

Я считаю, что ответ @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() в качестве извлеченного метода, если вам нужно использовать его вручную для случаев, когда требуется определенная обработка, а общая обработка декораторов недостаточна.

0

В ответ на @priestc и @Sebastian, что делать, если вы делаете что-то вроде этого?

try:
    conn.commit()
except:
    pass

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

Я просто пробовал этот код, и он, похоже, работает, не работает без каких-либо ошибок и не работает, когда запрос хорош.

0

У меня тоже была эта ошибка, но она маскировала еще одно релевантное сообщение об ошибке, в котором код пытался сохранить строку из 125 символов в столбце из 100 символов:

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

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

DatabaseError: current transaction is aborted
  • 4
    Как ты это сделал, может быть полезно ...
-5

вы можете отключить транзакцию через "set_isolation_level (0)"

Ещё вопросы

Сообщество Overcoder
Наверх
Меню