DatabaseError: การทำธุรกรรมปัจจุบันถูกยกเลิกคำสั่งจะถูกละเว้นจนกว่าจะสิ้นสุดการบล็อกธุรกรรม?


252

ฉันพบข้อผิดพลาดมากมายกับข้อความ:

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

หลังจากเปลี่ยนจาก python-psycopg เป็น python-psycopg2 เป็นเอ็นจิ้นฐานข้อมูลของโครงการ Django

รหัสยังคงเหมือนเดิมเพียงแค่ไม่รู้ว่าข้อผิดพลาดเหล่านั้นมาจากที่ใด


2
ฉันอยากรู้ว่าการแก้ปัญหาครั้งสุดท้ายของคุณคืออะไร ฉันมีปัญหาเดียวกันนี้ แต่เนื่องจากผู้ให้บริการโฮสต์ของฉันไม่บันทึกข้อผิดพลาดในการค้นหาจึงเป็นไปไม่ได้ที่จะเข้าใจว่าเกิดอะไรขึ้น
gerdemb

2
ในที่สุดฉันก็ติดตามปัญหาของฉันลงเป็นข้อผิดพลาดเมื่อใช้ตารางฐานข้อมูลเป็นแบ็กเอนด์แคช ข้อผิดพลาด Django: code.djangoproject.com/ticket/11569การอภิปราย StackOverflow: stackoverflow.com/questions/1189541//
gerdemb

7
FYI หากคุณเพียงแค่ใช้ psycopg2 โดยไม่ต้อง django conn.rollback()(ที่ conn เป็นวัตถุเชื่อมต่อของคุณ) จะล้างข้อผิดพลาดเพื่อให้คุณสามารถเรียกใช้แบบสอบถามอื่น ๆ
ผู้ใช้

คำตอบ:


177

นี่คือสิ่งที่ postgres ทำเมื่อแบบสอบถามสร้างข้อผิดพลาดและคุณพยายามเรียกใช้แบบสอบถามอื่นโดยไม่ย้อนกลับธุรกรรมก่อน (คุณอาจคิดว่ามันเป็นคุณสมบัติด้านความปลอดภัยเพื่อป้องกันไม่ให้ข้อมูลของคุณเสียหาย)

ในการแก้ไขปัญหานี้คุณจะต้องทราบว่าในโค้ดใดที่มีการเรียกใช้งานคิวรี่ที่ไม่ดี การใช้ตัวเลือกlog_statementและlog_min_error_statementในเซิร์ฟเวอร์ postgresql ของคุณอาจมีประโยชน์


ปัญหาคือเมื่อฉันใช้ python-psycopg ไม่มีข้อผิดพลาดเกิดขึ้น psycopg2 ใช้งานกลไกที่ต่างไปจากที่พูดกับ postgres หรือไม่?
แจ็ค

4
วิธีการพูดคุยกับเซิร์ฟเวอร์อาจไม่สำคัญ แต่เป็นไปได้ว่ารุ่นที่คุณใช้ก่อนที่จะตั้งค่าเริ่มต้นเป็นโหมดบันทึกอัตโนมัติในขณะที่เวอร์ชันใหม่ไม่ได้ ข้อผิดพลาดอาจยังคงเกิดขึ้น แต่คุณอาจพลาดได้ง่ายขึ้น อาจเป็นไปได้ว่าการแปลงประเภทข้อมูลหรือสิ่งอื่นมีการเปลี่ยนแปลงตั้งแต่รุ่นเก่า การแก้ไขที่ดีที่สุดคือการติดตามข้อความค้นหาที่ไม่ดีเพื่อให้คุณเห็นว่ามีอะไรผิดปกติ
ʇsәɹoɈ

133

หากต้องการกำจัดข้อผิดพลาดให้ย้อนกลับธุรกรรมสุดท้าย (ผิดพลาด)หลังจากที่คุณได้แก้ไขรหัส:

from django.db import transaction
transaction.rollback()

คุณสามารถใช้ลองยกเว้นเพื่อป้องกันข้อผิดพลาดเกิดขึ้น:

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

อ้างอิง: เอกสารของ Django


3
นี่คือปัญหาหลักและช่วยให้คุณสามารถกู้คืนหลังจากคำสั่งที่ทำให้เกิดการทำธุรกรรมที่ถูกยกเลิก
RichVel

สิ่งนี้รวมกับลอง / ยกเว้น
tomwolber

3
ทำไมต้องใช้IntegrityErrorและไม่ใช่คลาสพื้นฐานDatabaseError?
Jonathan

ด้วยเหตุผลบางอย่างฉันต้องย้ายการย้อนกลับด้านนอกของส่วน "ยกเว้น" ฉันใช้ .bulk_create () และไม่ใช่ .save ()
nu everest

ทำงานกับ django 1.4.16 หลังจากติดตามstackoverflow.com/a/15753000/573034
Paolo

50

ดังนั้นฉันจึงพบปัญหาเดียวกันนี้ ปัญหาที่ฉันมีที่นี่คือฐานข้อมูลของฉันไม่ได้ซิงค์อย่างถูกต้อง ปัญหาง่าย ๆ ดูเหมือนจะทำให้เกิดความกังวลมากที่สุด ...

หากต้องการซิงค์ django db ของคุณจากภายในไดเรกทอรีแอปของคุณภายในเทอร์มินัลให้พิมพ์:

$ python manage.py syncdb

แก้ไข: โปรดทราบว่าหากคุณใช้ django-south การรันคำสั่ง '$ python Manage.py migrate' ก็อาจแก้ไขปัญหานี้ได้เช่นกัน

การเข้ารหัสที่มีความสุข!


3
โหวตขึ้นเพื่อแจ้งความชัดเจน ฉันจะไม่ให้ upvote นี้มากกว่าหนึ่งเพราะมันอาจจะไม่ใช่คำตอบที่ต้องการ
Jameson Quinn

5
ฉันแก้ไขมันด้วยวิธีที่คล้ายกันโดยpython manage.py migrate <app>... สำหรับแอปทั้งหมดของฉัน
Clayton

3
@Clayton - คุณไม่ได้พูด แต่ฉันคิดว่าคุณกำลังใช้django-south - migrateคำสั่งไม่ได้อยู่ใน django
เกร็กบอล

@ GregBall- ถูกต้อง ... ฉันใช้ django-south ขออภัยที่ไม่ได้ระบุ
Clayton

ฉันได้รับข้อผิดพลาดนี้เมื่อทำ syncdb - ฉันคิดว่ามันจะทำอย่างไรกับการสั่งซื้อ django ผ่านตาราง
Stuart Axon

36

ในกระติกน้ำคุณเพียงแค่ต้องเขียน:

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

เอกสาร PS ไปที่นี่https://www.postgresql.org/docs/9.4/static/sql-rollback.html


วิธีนี้ยังช่วยได้ดีเมื่อเกิดข้อผิดพลาดในสมุดบันทึก Jupyter
Skippy le Grand Gourou

ดี มันช่วยฉันใน Jupyter
igorkf

34

จากประสบการณ์ของฉันข้อผิดพลาดเหล่านี้เกิดขึ้นในลักษณะนี้:

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

ไม่มีข้อผิดพลาดใด ๆ กับแบบสอบถามที่สอง แต่เนื่องจากมีการตรวจพบข้อผิดพลาดจริงแบบสอบถามที่สองคือแบบสอบถามที่ทำให้เกิดข้อผิดพลาด (ให้ข้อมูลน้อยกว่า)

แก้ไข: สิ่งนี้จะเกิดขึ้นเฉพาะในกรณีที่exceptข้อจับIntegrityError(หรือข้อยกเว้นฐานข้อมูลระดับต่ำอื่น ๆ ), ถ้าคุณจับบางสิ่งบางอย่างเช่นDoesNotExistข้อผิดพลาดนี้จะไม่เกิดขึ้นเพราะDoesNotExistไม่ทำลายการทำธุรกรรม

บทเรียนที่นี่ไม่ต้องลอง / ยกเว้น / ผ่าน


16

ฉันคิดว่ารูปแบบที่นักบวชกล่าวถึงมักจะเป็นสาเหตุของปัญหานี้เมื่อใช้ PostgreSQL

อย่างไรก็ตามฉันรู้สึกว่ามีการใช้งานที่ถูกต้องสำหรับรูปแบบและฉันไม่คิดว่าปัญหานี้ควรเป็นเหตุผลที่ควรหลีกเลี่ยงมันเสมอ ตัวอย่างเช่น:

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

do_something_with_profile(profile)

หากคุณรู้สึกว่าตกลงกับรูปแบบนี้ แต่ต้องการหลีกเลี่ยงการทำธุรกรรมการจัดการรหัสอย่างชัดเจนทั่วสถานที่คุณอาจต้องเปิดโหมด autocommit (PostgreSQL 8.2+): https://docs.djangoproject.com/en/ dev / โทษฐานข้อมูล / / # autocommit โหมด

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

ฉันไม่แน่ใจว่ามีข้อควรพิจารณาด้านประสิทธิภาพที่สำคัญ (หรือประเภทอื่นใด)


6

หากคุณได้รับสิ่งนี้ขณะอยู่ในเชลล์แบบโต้ตอบและต้องการการแก้ไขด่วนให้ทำดังนี้:

from django.db import connection
connection._rollback()

แต่เดิมเห็นในคำตอบนี้


6

ฉันพบพฤติกรรมที่คล้ายกันในขณะที่ทำธุรกรรมที่ผิดปกติบนpostgresเครื่อง ไม่มีอะไรที่ผ่านไปหลังจากนี้ขณะที่อยู่ในสถานะของdatabase แต่เช่นเดียวกับการแก้ไขอย่างรวดเร็วถ้าคุณสามารถที่จะหลีกเลี่ยงerror rollback transactionเคล็ดลับต่อไปนี้ทำให้ฉัน:

COMMIT;


ฉันอยู่ในสถานการณ์จำลองนี่เป็นคำตอบที่ฉันต้องการอย่างแน่นอน
sarink

5

ฉันมีปัญหาเกี่ยวกับ silimar ทางออกคือการโยกย้าย db ( manage.py syncdbหรือmanage.py schemamigration --auto <table name>ถ้าคุณใช้ใต้)


5

เพียงใช้การย้อนกลับ

รหัสตัวอย่าง

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

ฉันเพิ่งมีข้อผิดพลาดนี้เช่นกัน แต่เป็นการปกปิดข้อความแสดงข้อผิดพลาดที่เกี่ยวข้องมากขึ้นซึ่งรหัสพยายามเก็บสตริง 125 ตัวอักษรในคอลัมน์ 100 ตัวอักษร:

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

ฉันต้องแก้จุดบกพร่องผ่านรหัสเพื่อให้ข้อความด้านบนปรากฏขึ้นมิฉะนั้นจะปรากฏขึ้น

DatabaseError: current transaction is aborted

1

ในการตอบสนองต่อ @priestc และ @Sebastian ถ้าคุณทำอะไรแบบนี้

try:
    conn.commit()
except:
    pass

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

ฉันลองใช้รหัสนี้และดูเหมือนว่าจะทำงานล้มเหลวอย่างเงียบ ๆ โดยไม่ต้องสนใจข้อผิดพลาดที่เป็นไปได้และทำงานเมื่อการสืบค้นดี


1

ฉันเชื่อว่าคำตอบของ @ AnujGupta นั้นถูกต้อง อย่างไรก็ตามการย้อนกลับสามารถเพิ่มข้อยกเว้นที่คุณควรจับและจัดการ:

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

หากคุณพบว่าคุณเขียนรหัสนี้ใหม่ในหลาย ๆsave()ตำแหน่งคุณสามารถแยกวิธีการได้:

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

สุดท้ายคุณสามารถ prettify โดยใช้มัณฑนากรที่ปกป้องวิธีการที่ใช้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()
    # ...

แม้ว่าคุณจะใช้งานมัณฑนากรด้านบนมันก็ยังสะดวกที่จะเก็บไว้try_rolling_back()เป็นวิธีการแยกในกรณีที่คุณจำเป็นต้องใช้มันด้วยตนเองสำหรับกรณีที่จำเป็นต้องมีการจัดการที่เฉพาะเจาะจงและการจัดการมัณฑนากรทั่วไปไม่เพียงพอ


1

นี่เป็นพฤติกรรมที่แปลกมากสำหรับฉัน ฉันประหลาดใจที่ไม่มีใครคิดถึงจุดพัก ในแบบสอบถามรหัสของฉันล้มเหลวเป็นพฤติกรรมที่คาดหวัง:

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

ฉันได้เปลี่ยนรหัสด้วยวิธีนี้เพื่อใช้ 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


1

ฉันพบปัญหานี้แล้วข้อผิดพลาดเกิดขึ้นเนื่องจากการทำธุรกรรมข้อผิดพลาดยังไม่สิ้นสุดอย่างถูกต้องฉันพบpostgresql_transactionsคำสั่งการควบคุมธุรกรรมที่นี่

การควบคุมการทำธุรกรรม

คำสั่งต่อไปนี้ใช้เพื่อควบคุมการทำธุรกรรม

BEGIN TRANSACTION  To start a transaction.

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

ROLLBACK  To rollback the changes.

ดังนั้นฉันใช้END TRANSACTIONเพื่อยุติข้อผิดพลาดการทำธุรกรรมรหัสเช่นนี้:

    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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.