web-dev-qa-db-fra.com

DatabaseError: la transaction en cours est abandonnée, les commandes sont ignorées jusqu'à la fin du bloc de transaction

J'ai eu beaucoup d'erreurs avec le message:

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

après avoir changé de python-psycopg à python-psycopg2 en tant que moteur de base de données du projet Django.

Le code reste le même, il suffit de ne pas savoir d'où proviennent ces erreurs.

216
jack

C'est ce que fait PostgreSQL lorsqu'une requête génère une erreur et que vous essayez d'exécuter une autre requête sans restaurer préalablement la transaction. (Vous pouvez penser à cela comme une fonctionnalité de sécurité, pour vous empêcher de corrompre vos données.)

Pour résoudre ce problème, vous devez déterminer où la requête incorrecte est exécutée dans le code. Il peut être utile d’utiliser les options log_statement et log_min_error_statement sur votre serveur postgresql.

151
ʇsәɹoɈ

Pour supprimer l'erreur, annulez la dernière transaction (erronée) après avoir corrigé votre code:

from Django.db import transaction
transaction.rollback()

Vous pouvez utiliser try-except pour éviter que l'erreur ne se produise:

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

Reportez-vous à: Documentation Django

127
Anuj Gupta

Donc, j'ai rencontré le même problème. Le problème que je rencontrais ici était que ma base de données n'était pas correctement synchronisée. Les problèmes simples semblent toujours causer le plus d'angoisse ...

Pour synchroniser votre base de données Django, à partir du répertoire de votre application et du terminal, tapez:

$ python manage.py syncdb

Edit: Notez que si vous utilisez Django-south, l’exécution de la commande '$ python manage.py migrate' peut également résoudre ce problème.

Bonne codage!

50
Michael Merchant

D'après mon expérience, ces erreurs se produisent de la manière suivante:

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

Il n'y a rien de mal à la deuxième requête, mais puisque l'erreur réelle a été interceptée, la deuxième requête est celle qui génère l'erreur (beaucoup moins informative).

edit: cela ne se produit que si la clause except intercepte IntegrityError (ou toute autre exception de base de données de bas niveau), si vous attrapez quelque chose comme DoesNotExist, cette erreur ne se produira pas car DoesNotExist ne corrompra pas la transaction.

La leçon ici est de ne pas essayer/sauf/passer.

30
priestc

Je pense que le motif mentionné parich est plus susceptible d’être la cause habituelle de ce problème lors de l’utilisation de PostgreSQL.

Cependant, j'estime qu'il existe des utilisations valables pour le motif et je ne pense pas que ce problème soit une raison pour toujours l'éviter. Par exemple:

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

do_something_with_profile(profile)

Si vous vous sentez bien avec ce modèle, mais que vous voulez éviter le code de traitement de transaction explicite, vous voudrez peut-être examiner l'activation du mode de validation automatique (PostgreSQL 8.2+): https://docs.djangoproject.com/ fr/dev/ref/databases/# autocommit-mode

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

Je ne suis pas sûr qu'il y ait des considérations de performances importantes (ou d'un autre type).

16
Sebastian

Dans Flask, il vous suffit d'écrire:

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

P.S. La documentation va ici https://www.postgresql.org/docs/9.4/static/sql-rollback.html

13

Si vous rencontrez ce problème dans un shell interactif et que vous avez besoin d'une solution rapide, procédez comme suit:

from Django.db import connection
connection._rollback()

vu à l'origine dans cette réponse

6
tutuDajuju

J'ai rencontré un comportement similaire lors de l'exécution d'une transaction défaillante sur le terminal postgres. Rien ne s'est passé après cela, car la database est dans un état de error. Cependant, juste comme solution rapide, si vous pouvez vous permettre d'éviter rollback transaction. Suivre le truc pour moi:

COMMIT;

6
faizanjehangir

J'ai le problème de silimar. La solution consistait à migrer la base de données (manage.py syncdb ou manage.py schemamigration --auto <table name> si vous utilisez south).

4
Daniil Ryzhkov

En réponse à @priestc et @Sebastian, que se passe-t-il si vous faites quelque chose comme ça?

try:
    conn.commit()
except:
    pass

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

Je viens d'essayer ce code et il semble fonctionner, échouant silencieusement sans se soucier d'éventuelles erreurs et fonctionnant lorsque la requête est bonne.

1
Nate

J'ai juste eu cette erreur aussi mais cela masquait un autre message d'erreur plus pertinent dans lequel le code essayait de stocker une chaîne de 125 caractères dans une colonne de 100 caractères:

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

J'ai dû déboguer à travers le code pour que le message ci-dessus apparaisse, sinon il affiche

DatabaseError: current transaction is aborted
1
Thierry Lam

juste utiliser le retour en arrière

Exemple de code

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
Umer

Je crois que la réponse de @ AnujGupta est correcte. Toutefois, la restauration peut elle-même générer une exception que vous devez capturer et gérer:

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

Si vous constatez que vous réécrivez ce code dans divers emplacements save(), vous pouvez extraire la méthode suivante:

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

Enfin, vous pouvez l’embellir à l’aide d’un décorateur protégeant les méthodes qui utilisent 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()
    # ...

Même si vous implémentez le décorateur ci-dessus, il est toujours pratique de conserver try_rolling_back() en tant que méthode extraite au cas où vous auriez besoin de l'utiliser manuellement pour les cas où une manipulation spécifique est requise et où la manipulation du décorateur générique ne suffit pas.

1
Jonathan

C'est un comportement très étrange pour moi. Je suis surpris que personne n'ait pensé aux points de sauvegarde. Dans mon code, la requête ayant échoué était le comportement attendu:

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

J'ai changé le code de cette manière pour utiliser les points de sauvegarde:

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
homm

J'ai rencontré ce problème, l'erreur se produit car les transactions d'erreur n'ont pas été terminées correctement, j'ai trouvé le postgresql_transactions de la commande de transaction ici

Contrôle des transactions

Les commandes suivantes sont utilisées pour contrôler les transactions

BEGIN TRANSACTION − To start a transaction.

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

ROLLBACK − To rollback the changes.

donc j'utilise le END TRANSACTION pour mettre fin à l'erreur TRANSACTION, un code comme celui-ci:

    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
0
Dean Fang

Dans Flask Shell, tout ce que je devais faire était un session.rollback() pour résoudre ce problème.

0
watsonic