python - postgres - DatabaseError: la transacción actual se cancela, los comandos se ignoran hasta el final del bloque de transacción
psycopg2 python 3 (16)
Creo que el patrón que menciona clcc es más probable que sea la causa habitual de este problema cuando se usa PostgreSQL.
Sin embargo, siento que existen 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 debería considerar activar el modo de confirmación automática (PostgreSQL 8.2+): https://docs.djangoproject.com/en/dev/ref/databases/#autocommit-mode
DATABASES[''default''] = {
#.. you usual options...
''OPTIONS'': {
''autocommit'': True,
}
}
No estoy seguro de si hay consideraciones de rendimiento importantes (o de cualquier otro tipo).
Recibí muchos errores con el mensaje:
"DatabaseError: current transaction is aborted, commands ignored until end of transaction block"
después de cambiar 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 provienen esos errores.
Creo que la respuesta de @ AnujGupta es correcta. Sin embargo, la reversión puede generar una excepción que debe capturar y manejar:
from django.db import transaction, DatabaseError
try:
a.save()
except DatabaseError:
try:
transaction.rollback()
except transaction.TransactionManagementError:
# Log or handle otherwise
Si descubre que está reescribiendo este código en varias ubicaciones de save()
, 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 pretenderlo utilizando 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, aún es conveniente mantener try_rolling_back()
como un método extraído en caso de que necesite usarlo manualmente para casos donde se requiera un manejo específico, y el manejo genérico del decorador no sea suficiente.
En Frask solo tienes que escribir:
curs = conn.cursor()
curs.execute("ROLLBACK")
conn.commit()
La documentación de PS se encuentra aquí https://www.postgresql.org/docs/9.4/static/sql-rollback.html
En el shell Flask, todo lo que necesitaba hacer era una session.rollback()
para superar esto.
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).
edición: esto solo sucede si la cláusula de except
detecta IntegrityError
(o cualquier otra excepción de base de datos de bajo nivel). Si DoesNotExist
algo como DoesNotExist
este error no aparecerá, porque DoesNotExist
no daña la transacción.
La lección aquí es no intente / excepto / pase.
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, fallando en silencio sin tener que preocuparme por los posibles errores y trabajando cuando la consulta es buena.
Encontré un comportamiento similar mientras ejecutaba una transacción defectuosa en el terminal de postgres
. Nada pasó después de esto, ya que la database
está en un estado de error
. Sin embargo, solo como una solución rápida, si puede permitirse evitar la rollback transaction
. A continuación hice el truco para mí:
COMMIT;
Este es un comportamiento muy extraño para mí. Me sorprende que nadie haya pensado en los puntos de salvamento. En mi código, la consulta era el 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 los puntos guardados:
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
Esto es lo que hace postgres cuando una consulta produce un error e intenta ejecutar otra consulta sin revertir primero la transacción. (Podría pensar que es una característica de seguridad para evitar que corrompa sus datos).
Para solucionar este problema, querrá 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.
Para deshacerse del error, deshaga la última transacción (errónea) una vez que haya corregido 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.
Por lo tanto, me encontré con este mismo problema. El problema que estaba teniendo 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 dentro del directorio de su aplicación, dentro de la terminal, escriba:
$ python manage.py syncdb
Edición: 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!
Si obtiene esto mientras está en el shell interactivo y necesita una solución rápida, haga lo siguiente:
from django.db import connection
connection._rollback()
visto originalmente en esta respuesta
También tuve este error, pero estaba ocultando otro mensaje de error más relevante cuando 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 a través del código para que aparezca el mensaje anterior, de lo contrario se muestra
DatabaseError: current transaction is aborted
Tengo el problema silimar. La solución fue migrar db ( manage.py syncdb
o manage.py schemamigration --auto <table name>
si usas el sur).
puede deshabilitar la transacción a través de "set_isolation_level (0)"
solo usa revertir
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);")