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

252

Я получил много ошибок с сообщением:

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

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

Код остается прежним, просто не знаю, откуда эти ошибки.

разъем
источник
2
Мне интересно, каково было ваше окончательное решение этой проблемы? У меня такая же проблема, но поскольку мой хостинг-провайдер не регистрирует ошибки запросов, до сих пор было невозможно выяснить, что происходит не так.
gerdemb
2
Я, наконец, отследил свою проблему до ошибки при использовании таблицы базы данных в качестве бэкэнда кеша. Ошибка Django: code.djangoproject.com/ticket/11569 Обсуждение StackOverflow: stackoverflow.com/questions/1189541/…
gerdemb
7
К вашему сведению, если вы просто используете psycopg2 без django, conn.rollback()(где conn - ваш объект соединения), то ошибка будет сброшена, и вы сможете запускать другие запросы
пользователь

Ответы:

177

Это то, что делает postgres, когда запрос выдает ошибку, и вы пытаетесь выполнить другой запрос без предварительного отката транзакции. (Вы можете думать об этом как о функции безопасности, чтобы не повредить ваши данные.)

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

ʇsәɹoɈ
источник
проблема в том, что когда я использовал python-psycopg, таких ошибок не возникало. реализован ли в psycopg2 другой механизм общения с postgres?
Джек,
4
Способ обращения к серверу, вероятно, не имеет значения, но возможно, что версия, которую вы использовали ранее, каким-то образом по умолчанию перешла в режим автоматической фиксации, а новая версия - нет. Ошибка, возможно, все еще произошла, но вы могли бы легче ее пропустить. Также возможно, что преобразование типов данных или что-то еще изменилось со старой версии. В любом случае, лучшее решение - отследить неверный запрос, чтобы вы могли увидеть, что с ним не так.
ɈsәɹoɈ
133

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

from django.db import transaction
transaction.rollback()

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

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

См. Документацию Django

Анудж Гупта
источник
3
Это решает основную проблему и позволяет восстановить после оператора, вызвавшего прерванную транзакцию.
RichVel
это в сочетании с попыткой / исключением.
Томволбер
3
Почему использовать, IntegrityErrorа не базовый класс DatabaseError?
Джонатан
По какой-то причине мне пришлось перенести откат за пределы раздела «кроме». Я использовал .bulk_create (), а не .save ()
nu everest
Работал с django 1.4.16 после этого stackoverflow.com/a/15753000/573034
Paolo
50

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

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

$ python manage.py syncdb

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

Удачного кодирования!

Майкл Мерчант
источник
3
Проголосовал за констатацию очевидного. Я не дал бы это больше чем одно возражение, хотя, потому что это, вероятно, не был запрошенный ответ.
Джеймсон Куинн
5
Я исправил это аналогичным образом python manage.py migrate <app>... для всех моих приложений.
Клейтон
3
@Clayton - вы не говорите, но я предполагаю, что вы используете django-south - migrateкоманда не встроена в Django.
Грег Болл
@ GregBall- Это правильно ... Я использую django-south. Извините, что не уточнил.
Клейтон
Я получаю эту ошибку при выполнении syncdb - я думаю, что это связано с тем, как django проходит через таблицы.
Стюарт Аксон
36

В Flask вам просто нужно написать:

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

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

Дмитрий Лопушанский
источник
Это решение также очень помогает при возникновении ошибки в ноутбуке Jupyter.
Скиппи ле Гран Гуру
Ницца. Это помогло мне в Юпитере
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что не повредит транзакцию

Урок здесь - не пытайтесь / кроме / проходите.

priestc
источник
16

Я думаю, что шаблон, который упоминает priestc, скорее всего, является обычной причиной этой проблемы при использовании 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 / исх / базы данных / # автокоммит-режим

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

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

Себастьян
источник
6

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

from django.db import connection
connection._rollback()

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

tutuDajuju
источник
6

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

COMMIT;

faizanjehangir
источник
Я был в ответе, это именно тот ответ, который я искал.
Саринк
5

У меня проблема с силимаром. Решение было перенести дБ ( 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);")
Umer
источник
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

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

Nate
источник
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
HoMM
источник
1

В оболочке Flask все, что мне нужно было сделать, это session.rollback()пройти через это.

watsonic
источник
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для завершения ошибки транзакции, код, подобный этому:

    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

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

springrider
источник