DatabaseError: se cancela la transacción actual, ¿se ignoran los comandos hasta el final del bloque de transacción?


252

Recibí muchos errores con el mensaje:

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

luego cambió de python-psycopg a python-psycopg2 como motor de base de datos del proyecto Django.

El código sigue siendo el mismo, simplemente no sé de dónde son esos errores.


2
Tengo curiosidad por saber cuál fue su resolución final a este problema. Tengo este mismo problema, pero como mi proveedor de alojamiento no registra errores de consulta, hasta ahora ha sido imposible descubrir qué está pasando.
gerdemb

2
Finalmente rastreé mi problema hasta un error al usar una tabla de base de datos como el backend de caché. Error de Django: code.djangoproject.com/ticket/11569 Discusión de StackOverflow: stackoverflow.com/questions/1189541/…
gerdemb

77
FYI Si solo está usando psycopg2 sin django, conn.rollback()(donde conn es su objeto de conexión) borrará el error para que pueda ejecutar otras consultas
Usuario

Respuestas:


177

Esto es lo que hace postgres cuando una consulta produce un error e intenta ejecutar otra consulta sin antes revertir la transacción. (Puede considerarlo como una característica de seguridad, para evitar que corrompa sus datos).

Para solucionar esto, querrás averiguar en qué parte del código se está ejecutando la consulta incorrecta. Puede ser útil usar las opciones log_statement y log_min_error_statement en su servidor postgresql.


El problema es que cuando estaba usando python-psycopg, no surgieron tales errores. ¿psycopg2 implementó un mecanismo diferente para hablar con postgres?
Jack

44
El método de hablar con el servidor probablemente no importa, pero es posible que la versión que usó antes de alguna manera haya pasado al modo de confirmación automática, mientras que la nueva versión no. El error aún puede haber ocurrido, pero podría haberlo pasado más fácilmente. También es posible que la conversión del tipo de datos o algo más haya cambiado desde la versión anterior. De todos modos, la mejor solución es rastrear la consulta incorrecta para que pueda ver qué le pasa.
ɈsәɹoɈ

133

Para deshacerse del error, revierta la última transacción (errónea) después de haber arreglado su código:

from django.db import transaction
transaction.rollback()

Puede usar try-except para evitar que ocurra el error:

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

Consulte: documentación de Django


3
Esto soluciona el problema central y le permite recuperarse después de una declaración que causó la transacción cancelada.
RichVel

esto, combinado con try / except.
tomwolber

3
¿Por qué usar IntegrityErrory no la clase base DatabaseError?
Jonathan

Por alguna razón, tuve que mover la reversión fuera de la sección "excepto". Estaba usando .bulk_create () y no .save ()
nu everest

Trabajó con django 1.4.16 después de seguir este stackoverflow.com/a/15753000/573034
Paolo

50

Entonces, me encontré con este mismo problema. El problema que tenía aquí era que mi base de datos no estaba sincronizada correctamente. Los problemas simples siempre parecen causar la mayor angustia ...

Para sincronizar su django db, desde el directorio de su aplicación, dentro de la terminal, escriba:

$ python manage.py syncdb

Editar: tenga en cuenta que si está utilizando django-south, ejecutar el comando '$ python manage.py migrate' también puede resolver este problema.

¡Feliz codificación!


3
Votado por decir lo obvio. Sin embargo, no daría más de un voto a favor porque probablemente no era la respuesta buscada.
Jameson Quinn

55
Lo arreglé de manera similar python manage.py migrate <app>... para todas mis aplicaciones.
Clayton

3
@Clayton: no lo dices, pero supongo que lo estás usando django-south : el migratecomando no está integrado en django.
Greg Ball

@ GregBall- Eso es correcto ... Estoy usando django-south. Perdón por no especificar.
Clayton

Recibo este error cuando hago syncdb: creo que tiene que ver con el orden en que django pasa por las tablas.
Stuart Axon


34

En mi experiencia, estos errores ocurren de esta manera:

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

No hay nada malo con la segunda consulta, pero como se detectó el error real, la segunda consulta es la que genera el error (mucho menos informativo).

editar: esto solo sucede si la exceptcláusula detecta IntegrityError(o cualquier otra excepción de base de datos de bajo nivel), si detecta algo como DoesNotExisteste error no aparecerá, porque DoesNotExistno corrompe la transacción.

La lección aquí es no intentar / excepto / pasar.


16

Creo que el patrón que menciona el sacerdote es más probable que sea la causa habitual de este problema cuando se usa PostgreSQL.

Sin embargo, siento que hay usos válidos para el patrón y no creo que este problema deba ser una razón para evitarlo siempre. Por ejemplo:

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

do_something_with_profile(profile)

Si se siente bien con este patrón, pero desea evitar el código explícito de manejo de transacciones en todo el lugar, entonces puede considerar activar el modo de confirmación automática (PostgreSQL 8.2+): https://docs.djangoproject.com/en/ dev / ref / bases de datos / # autocommit-mode

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

No estoy seguro de si hay consideraciones importantes de rendimiento (o de cualquier otro tipo).


6

Si obtiene esto mientras está en el shell interactivo y necesita una solución rápida, haga esto:

from django.db import connection
connection._rollback()

visto originalmente en esta respuesta


6

Encontré un comportamiento similar al ejecutar una transacción defectuosa en el postgresterminal. Nada pasó después de esto, ya que databaseestá en un estado de error. Sin embargo, solo como una solución rápida, si puede permitirse el lujo de evitar rollback transaction. Lo siguiente hizo el truco para mí:

COMMIT;


Estaba en una respuesta, esta es exactamente la respuesta que estaba buscando.
sarink

5

Tengo el problema de silimar. La solución fue migrar db ( manage.py syncdbo manage.py schemamigration --auto <table name>si usa el sur).


5

solo usa rollback

Código de ejemplo

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

También tuve este error, pero estaba enmascarando otro mensaje de error más relevante en el que el código intentaba almacenar una cadena de 125 caracteres en una columna de 100 caracteres:

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

Tuve que depurar el código para que aparezca el mensaje anterior, de lo contrario, se muestra

DatabaseError: current transaction is aborted

1

En respuesta a @priestc y @Sebastian, ¿qué pasa si haces algo como esto?

try:
    conn.commit()
except:
    pass

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

Acabo de probar este código y parece funcionar, falla silenciosamente sin tener que preocuparse por posibles errores y funciona cuando la consulta es buena.


1

Creo que la respuesta de @ AnujGupta es correcta. Sin embargo, la reversión puede generar una excepción que debería atrapar y manejar:

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

Si encuentra que está reescribiendo este código en varias save()ubicaciones, puede extraer el 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

Finalmente, puede embellecerlo usando un decorador que protege los métodos que usan 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()
    # ...

Incluso si implementa el decorador anterior, es conveniente mantenerlo try_rolling_back()como un método extraído en caso de que necesite usarlo manualmente para casos en los que se requiera un manejo específico, y el manejo genérico del decorador no sea suficiente.


1

Este es un comportamiento muy extraño para mí. Me sorprende que nadie haya pensado en los puntos de rescate. En mi código, la consulta fallida era comportamiento 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

He cambiado el código de esta manera para usar puntos de guardado:

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

En Flask Shell, todo lo que necesitaba hacer era session.rollback()superar esto.


1

He encontrado este problema, el error aparece porque las transacciones de error no se han terminado correctamente, encontré el postgresql_transactionscomando de Control de transacciones aquí

Control de transacciones

Los siguientes comandos se utilizan para controlar las transacciones.

BEGIN TRANSACTION  To start a transaction.

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

ROLLBACK  To rollback the changes.

entonces uso el END TRANSACTIONpara finalizar el error 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

-6

puede deshabilitar la transacción a través de "set_isolation_level (0)"

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.