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.

Jack
fuente
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.

ʇsәɹoɈ
fuente
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

Anuj Gupta
fuente
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!

Michael Merchant
fuente
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
36

En Flask solo necesitas escribir:

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

La documentación de PS va aquí https://www.postgresql.org/docs/9.4/static/sql-rollback.html

Dmytro Lopushanskyy
fuente
Esta solución también es de gran ayuda cuando se produce el error en una computadora portátil Jupyter.
Skippy le Grand Gourou
Agradable. Me ayudó en Jupyter
igorkf
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.

sacerdote
fuente
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).

Sebastian
fuente
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

tutuDajuju
fuente
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;

faizanjehangir
fuente
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).

Daniil Ryzhkov
fuente
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);")
Umer
fuente
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
Thierry Lam
fuente
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.

Nate
fuente
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.

Jonathan
fuente
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
homm
fuente
1

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

Watsonic
fuente
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
Dean Fang
fuente
-6

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

Springrider
fuente