Cơ sở dữ liệuError: giao dịch hiện tại bị hủy bỏ, các lệnh bị bỏ qua cho đến khi kết thúc khối giao dịch?


252

Tôi đã nhận được rất nhiều lỗi với thông báo:

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

sau khi đổi từ python-psycopg thành python-psycopg2 làm công cụ cơ sở dữ liệu của dự án Django.

Mã vẫn giữ nguyên, chỉ cần không biết những lỗi đó đến từ đâu.


2
Tôi tò mò giải quyết cuối cùng của bạn về vấn đề này là gì? Tôi đang gặp vấn đề tương tự, nhưng vì nhà cung cấp dịch vụ lưu trữ của tôi không ghi lại các lỗi truy vấn nên cho đến nay không thể hiểu được chuyện gì đang xảy ra.
gerdemb

2
Cuối cùng tôi đã theo dõi vấn đề của mình xuống một lỗi khi sử dụng bảng cơ sở dữ liệu làm phụ trợ bộ đệm. Lỗi Django: code.djangoproject.com/ticket/11569 Thảo luận về StackOverflow: stackoverflow.com/questions/1189541/
Kẻ

7
FYI Nếu bạn chỉ sử dụng psycopg2 mà không có django, conn.rollback()(trong đó Conn là đối tượng kết nối của bạn) sẽ xóa lỗi để bạn có thể chạy các truy vấn khác
Người dùng

Câu trả lời:


177

Đây là những gì postgres thực hiện khi một truy vấn tạo ra lỗi và bạn cố gắng chạy một truy vấn khác mà không thực hiện lại giao dịch trước. (Bạn có thể nghĩ về nó như một tính năng an toàn, để tránh làm hỏng dữ liệu của bạn.)

Để khắc phục điều này, bạn sẽ muốn tìm ra nơi mà mã truy vấn xấu đang được thực thi. Nó có thể là hữu ích để sử dụng log_statementlog_min_error_statement tùy chọn trong máy chủ postgresql của bạn.


Vấn đề là khi tôi đang sử dụng python-psycopg, không có lỗi nào được nêu ra. psycopg2 có thực hiện một cơ chế khác để nói chuyện với postgres không?
jack

4
Phương pháp nói chuyện với máy chủ có thể không thành vấn đề, nhưng có thể phiên bản bạn đã sử dụng trước đây bằng cách nào đó được mặc định là chế độ tự động trong khi phiên bản mới thì không. Lỗi vẫn có thể xảy ra, nhưng bạn có thể dễ dàng bỏ qua nó hơn. Cũng có thể chuyển đổi loại dữ liệu hoặc một cái gì đó đã thay đổi kể từ phiên bản cũ. Bất kể, cách khắc phục tốt nhất là theo dõi truy vấn xấu để bạn có thể thấy những gì sai với nó.
ʇsәɹoɈ

133

Để thoát khỏi lỗi, hãy quay lại giao dịch (lỗi) cuối cùng sau khi bạn đã sửa mã của mình:

from django.db import transaction
transaction.rollback()

Bạn có thể sử dụng thử ngoại trừ để ngăn lỗi xảy ra:

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

Tham khảo: Tài liệu Django


3
Điều này giải quyết vấn đề cốt lõi và cho phép bạn phục hồi sau khi tuyên bố gây ra giao dịch bị hủy bỏ.
RichVel

này, kết hợp với thử / ngoại trừ.
tomwolber

3
Tại sao sử dụng IntegrityErrorvà không phải là lớp cơ sở DatabaseError?
Jonathan

Vì một số lý do, tôi đã phải di chuyển rollback bên ngoài phần "ngoại trừ". Tôi đã sử dụng .bulk_create () chứ không phải .save ()
nu everest

Đã làm việc với django 1.4.16 sau khi theo dõi stackoverflow.com/a/15753000/573034
Paolo

50

Vì vậy, tôi gặp vấn đề tương tự. Vấn đề tôi gặp phải ở đây là cơ sở dữ liệu của tôi không được đồng bộ hóa đúng cách. Những vấn đề đơn giản dường như luôn gây ra sự giận dữ nhất ...

Để đồng bộ hóa django db của bạn, từ trong thư mục ứng dụng của bạn, trong thiết bị đầu cuối, hãy nhập:

$ python manage.py syncdb

Chỉnh sửa: Lưu ý rằng nếu bạn đang sử dụng django-south, việc chạy lệnh '$ python Manage.py di chuyển' cũng có thể giải quyết vấn đề này.

Chúc mừng mã hóa!


3
Upvote cho nêu rõ ràng. Tôi sẽ không cung cấp nhiều hơn một upvote bởi vì nó có thể không phải là câu trả lời tìm kiếm.
Jameson Quinn

5
Tôi đã sửa nó theo cách tương tự bằng python manage.py migrate <app>... cho tất cả các ứng dụng của mình.
Clayton

3
@Clayton - bạn không nói, nhưng tôi cho rằng bạn đang sử dụng django-south - migratelệnh không được tích hợp vào django.
Greg Ball

@ GregBall- Đúng vậy ... Tôi đang sử dụng django-south. Xin lỗi vì không chỉ định.
Clayton

Tôi gặp lỗi này khi thực hiện syncdb - Tôi nghĩ rằng nó phải làm với thứ tự django đi qua các bảng.
Stuart Axon


34

Theo kinh nghiệm của tôi, những lỗi này xảy ra theo cách này:

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

Không có gì sai với truy vấn thứ hai, nhưng vì lỗi thực sự đã bị bắt, truy vấn thứ hai là truy vấn gây ra lỗi (ít thông tin hơn nhiều).

chỉnh sửa: điều này chỉ xảy ra nếu exceptmệnh đề bắt IntegrityError(hoặc bất kỳ ngoại lệ cơ sở dữ liệu cấp thấp nào khác), Nếu bạn bắt gặp DoesNotExistlỗi như thế này sẽ không xuất hiện, bởi vìDoesNotExist không làm hỏng giao dịch.

Bài học ở đây là đừng thử / ngoại trừ / vượt qua.


16

Tôi nghĩ rằng các linh mục mẫu đề cập nhiều khả năng là nguyên nhân thông thường của vấn đề này khi sử dụng PostgreQuery.

Tuy nhiên tôi cảm thấy có những cách sử dụng hợp lệ cho mẫu và tôi không nghĩ vấn đề này nên là một lý do để luôn luôn tránh nó. Ví dụ:

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

do_something_with_profile(profile)

Nếu bạn cảm thấy ổn với mẫu này, nhưng muốn tránh mã xử lý giao dịch rõ ràng ở mọi nơi thì bạn có thể muốn xem xét bật chế độ tự động (PostgreQuery 8.2+): https://docs.djangoproject.com/en/ dev / ref / cơ sở dữ liệu / # autocommit-mode

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

Tôi không chắc chắn nếu có những cân nhắc hiệu suất quan trọng (hoặc bất kỳ loại nào khác).


6

Nếu bạn nhận được điều này trong khi trong vỏ tương tác và cần sửa chữa nhanh chóng, hãy làm điều này:

from django.db import connection
connection._rollback()

ban đầu nhìn thấy trong câu trả lời này


6

Tôi đã gặp một hành vi tương tự trong khi chạy một giao dịch bị trục trặc trên postgresthiết bị đầu cuối. Không có gì đi qua sau này, như databaselà trong trạng thái error. Tuy nhiên, chỉ là một sửa chữa nhanh chóng, nếu bạn có thể đủ khả năng để tránh rollback transaction. Sau đây đã lừa tôi:

COMMIT;


Tôi đã ở trong một thay thế, đây chính xác là câu trả lời tôi đang tìm kiếm.
sarink

5

Tôi đã có vấn đề silimar. Giải pháp là di chuyển db ( manage.py syncdbhoặc manage.py schemamigration --auto <table name>nếu bạn sử dụng phía nam).


5

chỉ cần sử dụng rollback

Mã ví dụ

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

Tôi cũng gặp lỗi này nhưng nó đang che giấu một thông báo lỗi khác có liên quan hơn trong đó mã đang cố lưu trữ chuỗi 125 ký tự trong cột 100 ký tự:

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

Tôi đã phải gỡ lỗi thông qua mã cho thông báo trên để hiển thị, nếu không nó sẽ hiển thị

DatabaseError: current transaction is aborted

1

Đáp lại @priestc và @Sebastian, nếu bạn làm điều gì đó như thế này thì sao?

try:
    conn.commit()
except:
    pass

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

Tôi vừa thử mã này và nó dường như hoạt động, thất bại trong âm thầm mà không phải quan tâm đến bất kỳ lỗi nào có thể và làm việc khi truy vấn tốt.


1

Tôi tin rằng câu trả lời của @ AnujGupta là chính xác. Tuy nhiên, việc rollback có thể tự đưa ra một ngoại lệ mà bạn nên nắm bắt và xử lý:

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

Nếu bạn thấy bạn đang viết lại mã này ở nhiều save()vị trí khác nhau , bạn có thể trích xuất phương thức:

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

Cuối cùng, bạn có thể làm đẹp nó bằng cách sử dụng một công cụ trang trí bảo vệ các phương thức sử dụng 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()
    # ...

Ngay cả khi bạn triển khai trình trang trí ở trên, vẫn thuận tiện để giữ try_rolling_back()như một phương pháp trích xuất trong trường hợp bạn cần sử dụng thủ công cho các trường hợp cần xử lý cụ thể và xử lý trang trí chung là không đủ.


1

Đây là hành vi rất lạ đối với tôi. Tôi ngạc nhiên khi không ai nghĩ đến điểm lưu trữ. Trong mã của tôi không truy vấn được hành vi dự kiến:

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

Tôi đã thay đổi mã theo cách này để sử dụng các điểm lưu trữ:

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

Trong Flask shell, tất cả những gì tôi cần làm là session.rollback()vượt qua điều này.


1

Tôi đã gặp vấn đề này, lỗi xuất hiện do các giao dịch lỗi chưa được kết thúc đúng, tôi đã tìm thấy postgresql_transactionslệnh Kiểm soát giao dịch ở đây

Kiểm soát giao dịch

Các lệnh sau được sử dụng để kiểm soát các giao dịch

BEGIN TRANSACTION  To start a transaction.

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

ROLLBACK  To rollback the changes.

Vì vậy, tôi sử dụng END TRANSACTIONđể kết thúc lỗi GIAO DỊCH, mã như thế này:

    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

-6

bạn có thể vô hiệu hóa giao dịch qua "set_isolation_level (0)"

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.