DatabaseError: transaksi saat ini dibatalkan, perintah diabaikan sampai akhir blok transaksi?

252

Saya mendapat banyak kesalahan dengan pesan:

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

setelah diubah dari python-psycopg ke python-psycopg2 sebagai mesin basis data proyek Django.

Kode tetap sama, hanya tidak tahu dari mana kesalahan itu berasal.

mendongkrak
sumber
2
Saya ingin tahu apa resolusi terakhir Anda untuk masalah ini? Saya mengalami masalah yang sama, tetapi karena penyedia hosting saya tidak mencatat kesalahan kueri, sejauh ini tidak mungkin untuk mencari tahu apa yang salah.
gerdemb
2
Saya akhirnya melacak masalah saya hingga bug ketika menggunakan tabel database sebagai backend cache. Django bug: code.djangoproject.com/ticket/11569 Diskusi StackOverflow: stackoverflow.com/questions/1189541/…
gerdemb
7
FYI Jika Anda hanya menggunakan psycopg2 tanpa Django, conn.rollback()(di mana samb adalah objek koneksi Anda) akan menghapus kesalahan sehingga Anda dapat menjalankan kueri lain
Pengguna

Jawaban:

177

Inilah yang dilakukan postgres ketika kueri menghasilkan kesalahan dan Anda mencoba menjalankan kueri lain tanpa terlebih dahulu mengembalikan transaksi. (Anda mungkin menganggapnya sebagai fitur keselamatan, agar Anda tidak merusak data Anda.)

Untuk memperbaikinya, Anda akan ingin mencari tahu di mana dalam kode yang kueri buruknya dijalankan. Mungkin bermanfaat untuk menggunakan opsi log_statement dan log_min_error_statement di server postgresql Anda.

ʇsәɹoɈ
sumber
masalahnya adalah ketika saya menggunakan python-psycopg, tidak ada kesalahan seperti itu. apakah psycopg2 menerapkan mekanisme berbeda berbicara dengan postgres?
jack
4
Metode berbicara ke server mungkin tidak masalah, tetapi ada kemungkinan bahwa versi yang Anda gunakan sebelumnya entah bagaimana default ke mode autocommit sementara versi baru tidak. Kesalahan mungkin masih terjadi, tetapi Anda bisa lebih mudah melewatkannya. Mungkin juga konversi tipe data atau hal lain telah berubah sejak versi lama. Apapun itu, perbaikan terbaik adalah melacak kueri buruk sehingga Anda dapat melihat apa yang salah dengannya.
ʇsәɹoɈ
133

Untuk menghilangkan kesalahan, gulung kembali transaksi terakhir (yang salah) setelah Anda memperbaiki kode Anda:

from django.db import transaction
transaction.rollback()

Anda dapat menggunakan coba-kecuali untuk mencegah kesalahan terjadi:

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

Lihat: dokumentasi Django

Anuj Gupta
sumber
3
Ini mengatasi masalah inti dan memungkinkan Anda memulihkan setelah pernyataan yang menyebabkan transaksi dibatalkan.
RichVel
ini, dikombinasikan dengan coba / kecuali.
tomwolber
3
Mengapa menggunakan IntegrityErrordan bukan kelas dasar DatabaseError?
Jonathan
Untuk beberapa alasan saya harus memindahkan rollback di luar bagian "kecuali". Saya menggunakan .bulk_create () dan bukan .save ()
nu everest
Bekerja dengan Django 1.4.16 setelah mengikuti stackoverflow.com/a/15753000/573034
Paolo
50

Jadi, saya mengalami masalah yang sama. Masalah yang saya alami di sini adalah bahwa database saya tidak disinkronkan dengan benar. Masalah sederhana sepertinya selalu menyebabkan ...

Untuk menyinkronkan django db Anda, dari dalam direktori aplikasi Anda, dalam terminal, ketik:

$ python manage.py syncdb

Sunting: Perhatikan bahwa jika Anda menggunakan Django-selatan, menjalankan perintah '$ python manage.py migrate' juga dapat menyelesaikan masalah ini.

Selamat coding!

Michael Merchant
sumber
3
Terpilih untuk menyatakan yang sudah jelas. Saya tidak akan memberikan ini lebih dari satu suara karena itu mungkin bukan jawaban yang dicari.
Jameson Quinn
5
Saya memperbaikinya dengan cara yang sama oleh python manage.py migrate <app>... untuk semua aplikasi saya.
Clayton
3
@Layton - Anda tidak mengatakan, tapi saya menganggap Anda menggunakan django-south - migrateperintah tidak dibangun ke Django.
Greg Ball
@ GregBall- Itu benar ... Saya menggunakan Django-selatan. Maaf karena tidak menentukan.
Clayton
Saya mendapatkan kesalahan ini ketika melakukan syncdb - Saya pikir ini berkaitan dengan urutan Django melewati tabel.
Stuart Axon
36

Di Flask Anda hanya perlu menulis:

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

Dokumentasi PS ada di sini https://www.postgresql.org/docs/9.4/static/sql-rollback.html

Dmytro Lopushanskyy
sumber
Solusi ini juga sangat membantu ketika kesalahan terjadi pada notebook Jupyter.
Skippy le Grand Gourou
Bagus. Itu membantu saya di Jupyter
igorkf
34

Dalam pengalaman saya, kesalahan ini terjadi seperti ini:

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

Tidak ada yang salah dengan permintaan kedua, tetapi karena kesalahan yang sebenarnya ditangkap, permintaan kedua adalah yang menimbulkan kesalahan (jauh lebih informatif).

sunting: ini hanya terjadi jika exceptklausa menangkap IntegrityError(atau pengecualian basis data tingkat rendah lainnya), Jika Anda menangkap sesuatu seperti DoesNotExistkesalahan ini tidak akan muncul, karena DoesNotExisttidak merusak transaksi.

Pelajaran di sini adalah jangan coba-coba / kecuali / lulus.

pendeta
sumber
16

Saya pikir pola priestc menyebutkan lebih mungkin menjadi penyebab umum masalah ini ketika menggunakan PostgreSQL.

Namun saya merasa ada kegunaan yang valid untuk pola dan saya tidak berpikir masalah ini harus menjadi alasan untuk selalu menghindarinya. Sebagai contoh:

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

do_something_with_profile(profile)

Jika Anda merasa OK dengan pola ini, tetapi ingin menghindari kode penanganan transaksi eksplisit di semua tempat maka Anda mungkin ingin melihat ke dalam mengaktifkan mode autocommit (PostgreSQL 8.2+): https://docs.djangoproject.com/en/ dev / ref / database / # autocommit-mode

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

Saya tidak yakin apakah ada pertimbangan kinerja yang penting (atau jenis lainnya).

Sebastian
sumber
6

Jika Anda mendapatkan ini saat berada di shell interaktif dan perlu perbaikan cepat, lakukan ini:

from django.db import connection
connection._rollback()

awalnya terlihat dalam jawaban ini

tutuDajuju
sumber
6

Saya mengalami perilaku serupa saat menjalankan transaksi yang tidak berfungsi di postgresterminal. Tidak ada yang melewati setelah ini, karena databaseberada dalam keadaan error. Namun, sama seperti perbaikan cepat, jika Anda mampu menghindarinya rollback transaction. Mengikuti melakukan trik untuk saya:

COMMIT;

faizanjehangir
sumber
Saya sedang dalam jawaban, inilah jawaban yang saya cari.
sarink
5

Saya punya masalah silimar. Solusinya adalah dengan bermigrasi db ( manage.py syncdbatau manage.py schemamigration --auto <table name>jika Anda menggunakan selatan).

Daniil Ryzhkov
sumber
5

cukup gunakan rollback

Kode contoh

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

Saya baru saja mengalami kesalahan ini juga, tetapi menutupi pesan kesalahan lain yang lebih relevan di mana kode mencoba menyimpan string 125 karakter dalam kolom 100 karakter:

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

Saya harus men-debug kode untuk menampilkan pesan di atas, jika tidak akan ditampilkan

DatabaseError: current transaction is aborted
Thierry Lam
sumber
1

Menanggapi @priestc dan @Sebastian, bagaimana jika Anda melakukan sesuatu seperti ini?

try:
    conn.commit()
except:
    pass

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

Saya baru saja mencoba kode ini dan tampaknya berhasil, gagal diam-diam tanpa harus peduli tentang kemungkinan kesalahan, dan berfungsi saat kueri baik.

Nate
sumber
1

Saya percaya jawaban @ AnujGupta benar. Namun rollback itu sendiri dapat menimbulkan pengecualian yang harus Anda tangkap dan tangani:

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

Jika Anda menemukan menulis ulang kode ini di berbagai save()lokasi, Anda dapat mengekstrak-metode:

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

Akhirnya, Anda dapat memproduksinya menggunakan dekorator yang melindungi metode yang menggunakan 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()
    # ...

Bahkan jika Anda menerapkan dekorator di atas, masih nyaman untuk disimpan try_rolling_back()sebagai metode yang diekstraksi jika Anda perlu menggunakannya secara manual untuk kasus-kasus di mana penanganan spesifik diperlukan, dan penanganan dekorator generik tidak cukup.

Jonathan
sumber
1

Ini perilaku yang sangat aneh bagi saya. Saya terkejut bahwa tidak ada yang memikirkan savepoint. Dalam kode saya, kueri yang gagal diharapkan perilaku:

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

Saya telah mengubah kode cara ini untuk menggunakan savepoints:

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

Dalam shell Flask, semua yang perlu saya lakukan adalah session.rollback()melewati ini.

watsonic
sumber
1

Saya telah bertemu masalah ini, kesalahan keluar karena transaksi kesalahan belum berakhir dengan benar, saya menemukan postgresql_transactionsperintah Kontrol Transaksi di sini

Kontrol Transaksi

Perintah berikut digunakan untuk mengendalikan transaksi

BEGIN TRANSACTION  To start a transaction.

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

ROLLBACK  To rollback the changes.

jadi saya gunakan END TRANSACTIONuntuk mengakhiri kesalahan TRANSACTION, kode seperti ini:

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

Anda dapat menonaktifkan transaksi melalui "set_isolation_level (0)"

Springrider
sumber