DatabaseError: a transação atual é abortada, os comandos são ignorados até o final do bloco da transação?

252

Eu recebi muitos erros com a mensagem:

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

depois mudou de python-psycopg para python-psycopg2 como o mecanismo de banco de dados do projeto Django.

O código permanece o mesmo, apenas não sei de onde são esses erros.

jack
fonte
2
Estou curioso para saber qual foi sua solução final para esse problema. Estou com o mesmo problema, mas como meu provedor de hospedagem não registra erros de consulta, até agora não foi possível descobrir o que está errado.
gerdemb
2
Finalmente, rastreei meu problema até um bug ao usar uma tabela de banco de dados como o back-end do cache. Bug do Django: code.djangoproject.com/ticket/11569 Discussão do StackOverflow: stackoverflow.com/questions/1189541/…
gerdemb 21/10
7
FYI Se você estiver usando apenas psycopg2 sem Django, conn.rollback()(onde conn é o seu objeto de conexão) irá limpar o erro para que você possa executar outras consultas
Utilizador

Respostas:

177

É isso que o postgres faz quando uma consulta produz um erro e você tenta executar outra consulta sem reverter a transação. (Você pode considerá-lo um recurso de segurança para impedir a corrupção de seus dados.)

Para corrigir isso, você deve descobrir em que parte do código essa consulta incorreta está sendo executada. Pode ser útil usar as opções log_statement e log_min_error_statement no seu servidor postgresql.

ʇsәɹoɈ
fonte
o problema é quando eu estava usando python-psycopg, nenhum desses erros foi gerado. O psycopg2 implementou um mecanismo diferente conversando com o postgres?
jack
4
O método de conversar com o servidor provavelmente não importa, mas é possível que a versão que você usou antes tenha assumido o modo de confirmação automática, enquanto a nova versão não. O erro ainda pode ter ocorrido, mas você pode ter perdido mais facilmente. Também é possível que a conversão do tipo de dados ou outra coisa tenha mudado desde a versão antiga. Independentemente disso, a melhor solução é rastrear a consulta incorreta para que você possa ver o que há de errado com ela.
ʇsәɹoɈ
133

Para se livrar do erro, reverta a última transação (incorreta) depois de corrigir seu código:

from django.db import transaction
transaction.rollback()

Você pode usar try-except para impedir que o erro ocorra:

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

Consulte: Documentação do Django

Anuj Gupta
fonte
3
Isso soluciona o problema principal e permite recuperar após uma instrução que causou a transação abortada.
RichVel
isso, combinado com try / except.
tomwolber
3
Por que usar IntegrityErrore não a classe base DatabaseError?
Jonathan
Por alguma razão, tive que mover a reversão para fora da seção "exceto". Eu estava usando .bulk_create () e não .Save ()
nu Everest
Trabalhou com o django 1.4.16 após seguir este stackoverflow.com/a/15753000/573034
Paolo
50

Então, eu tive esse mesmo problema. O problema que eu estava tendo aqui era que meu banco de dados não estava sincronizado corretamente. Problemas simples sempre parecem causar a maior angústia ...

Para sincronizar seu django db, no diretório do seu aplicativo, no terminal, digite:

$ python manage.py syncdb

Edit: Observe que se você estiver usando o django-south, a execução do comando '$ python manage.py migrate' também poderá resolver esse problema.

Feliz codificação!

Michael Merchant
fonte
3
Votado por afirmar o óbvio. Eu não daria isso a mais de um voto positivo, porque provavelmente não era a resposta procurada.
Jameson Quinn
5
Corrigi de maneira semelhante por python manage.py migrate <app>... para todos os meus aplicativos.
Clayton
3
@Clayton - você não diz, mas eu assumo que você está usando django-south - o migratecomando não está embutido no django.
Greg Bola
@ GregBall- Isso está correto ... Estou usando o django-south. Desculpe por não especificar.
Clayton
Estou recebendo esse erro ao fazer syncdb - acho que tem a ver com a ordem em que o django percorre as tabelas.
Stuart Axon
36

No Flask, você só precisa escrever:

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

A documentação do PS está aqui https://www.postgresql.org/docs/9.4/static/sql-rollback.html

Dmytro Lopushanskyy
fonte
Essa solução também é de grande ajuda quando o erro ocorre em um notebook Jupyter.
Skippy le Grand Gourou 06/04
Agradável. Isso me ajudou em Jupyter
igorkf 04/06
34

Na minha experiência, esses erros acontecem desta maneira:

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

Não há nada errado com a segunda consulta, mas desde que o erro real foi detectado, a segunda consulta é a que gera o erro (muito menos informativo).

edit: isso só acontece se a exceptcláusula IntegrityErrorcapturar (ou qualquer outra exceção de banco de dados de baixo nível), se você capturar algo comoDoesNotExist esse erro, não será exibido, porque DoesNotExistnão corrompe a transação.

A lição aqui é não tente / exceto / passe.

padre
fonte
16

Eu acho que o padrão que o sacerdote menciona é mais provável que seja a causa usual desse problema ao usar o PostgreSQL.

No entanto, sinto que há usos válidos para o padrão e não acho que esse problema deva ser um motivo para sempre evitá-lo. Por exemplo:

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

do_something_with_profile(profile)

Se você se sente bem com esse padrão, mas deseja evitar um código explícito de manipulação de transações em todo o lugar, convém ativar o modo de confirmação automática (PostgreSQL 8.2+): https://docs.djangoproject.com/en/ dev / ref / database / # modo de confirmação automática

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

Não tenho certeza se existem considerações importantes sobre desempenho (ou de qualquer outro tipo).

Sebastian
fonte
6

Se você obtiver isso no shell interativo e precisar de uma solução rápida, faça o seguinte:

from django.db import connection
connection._rollback()

originalmente visto nesta resposta

tutuDajuju
fonte
6

Encontrei um comportamento semelhante ao executar uma transação com defeito no postgresterminal. Nada passou depois disso, pois o databaseestado está em error. No entanto, apenas como uma solução rápida, se você puder evitar rollback transaction. A seguir, fiz o truque para mim:

COMMIT;

faizanjehangir
fonte
Eu estava em um repl, esta é exatamente a resposta que eu estava procurando.
sarink
5

Eu tenho o problema de silimar. A solução foi migrar o banco de dados ( manage.py syncdbou manage.py schemamigration --auto <table name>se você usar o sul).

Daniil Ryzhkov
fonte
5

basta usar reversão

Código de exemplo

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
fonte
1

Eu também tive esse erro, mas ele estava mascarando outra mensagem de erro mais relevante em que o código estava tentando armazenar uma sequência de 125 caracteres em uma coluna de 100 caracteres:

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

Eu tive que depurar o código para a mensagem acima aparecer, caso contrário ela exibe

DatabaseError: current transaction is aborted
Thierry Lam
fonte
1

Em resposta a @priestc e @Sebastian, e se você fizer algo assim?

try:
    conn.commit()
except:
    pass

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

Eu apenas tentei esse código e ele parece funcionar, falhando silenciosamente sem precisar se preocupar com possíveis erros e funcionando quando a consulta é boa.

Nate
fonte
1

Creio que a resposta de @ AnujGupta está correta. No entanto, a reversão pode gerar uma exceção que você deve capturar e manipular:

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

Se você estiver reescrevendo esse código em vários save()locais, poderá extrair o método:

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

Por fim, você pode pré-modificá-lo usando um decorador que protege métodos que usam 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()
    # ...

Mesmo se você implementar o decorador acima, ainda é conveniente mantê-lo try_rolling_back()como um método extraído, caso você precise usá-lo manualmente nos casos em que é necessário um manuseio específico, e o manuseio do decorador genérico não é suficiente.

Jonathan
fonte
1

Este é um comportamento muito estranho para mim. Estou surpreso que ninguém pensou em pontos de salvaguarda. No meu código, a consulta com falha era o comportamento esperado:

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

Alterei o código dessa maneira para usar os pontos de salvamento:

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
fonte
1

No shell Flask, tudo que eu precisava fazer era session.rollback()superar isso.

watsonic
fonte
1

Eu encontrei esse problema, o erro aparece porque as transações de erro não foram encerradas corretamente, encontrei o postgresql_transactionscomando Transaction Control aqui

Controle de Transação

Os seguintes comandos são usados ​​para controlar transações

BEGIN TRANSACTION  To start a transaction.

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

ROLLBACK  To rollback the changes.

então eu uso o END TRANSACTIONpara terminar o erro TRANSACTION, código como este:

    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
fonte
-6

você pode desativar a transação via "set_isolation_level (0)"

cavaleiro
fonte