DatabaseError : 현재 트랜잭션이 중단되고 트랜잭션 블록이 끝날 때까지 명령이 무시됩니까?


252

메시지에 많은 오류가 있습니다.

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

Django 프로젝트의 데이터베이스 엔진으로 python-psycopg에서 python-psycopg2로 변경된 후.

코드는 동일하게 유지되며 오류의 출처를 모릅니다.


2
이 문제의 최종 해결 방법이 궁금합니다. 동일한 문제가 발생하지만 호스팅 제공 업체가 쿼리 오류를 기록하지 않기 때문에 지금까지 무엇이 잘못되었는지 파악하는 것은 불가능합니다.
gerdemb

2
마지막으로 데이터베이스 테이블을 캐시 백엔드로 사용할 때 문제를 버그로 추적했습니다. 장고 버그 : code.djangoproject.com/ticket/11569 StackOverflow 토론 : stackoverflow.com/questions/1189541/…
gerdemb

7
참고 django없이 psycopg2를 사용하는 경우 conn.rollback()(conn은 연결 객체 임) 오류를 지우므로 다른 쿼리를 실행할 수 있습니다.
User

답변:


177

이것은 쿼리에서 오류가 발생하고 트랜잭션을 먼저 롤백하지 않고 다른 쿼리를 실행하려고 할 때 postgres가 수행하는 작업입니다. 데이터를 손상시키지 않도록 안전 기능으로 생각할 수 있습니다.

이 문제를 해결하려면 코드에서 잘못된 쿼리가 실행되는 위치를 파악해야합니다. postgresql 서버에서 log_statementlog_min_error_statement 옵션 을 사용하는 것이 도움이 될 수 있습니다 .


문제는 python-psycopg를 사용할 때 그러한 오류가 발생하지 않는 것입니다. psycopg2는 postgres와 통신하는 다른 메커니즘을 구현합니까?
jack

4
서버와 대화하는 방법은 중요하지 않지만 새 버전은 중요하지 않지만 이전에 사용한 버전이 자동 커밋 모드로 기본 설정되었을 수 있습니다. 오류가 여전히 발생했을 수 있지만 더 쉽게 놓칠 수 있습니다. 이전 버전 이후로 데이터 유형 변환 또는 기타 사항이 변경되었을 수도 있습니다. 어쨌든, 가장 좋은 해결책은 잘못된 쿼리를 추적하여 문제가있는 것을 확인할 수 있습니다.
ʇsәɹoɈ

133

오류를 제거하려면 코드를 수정 한 후 마지막 (잘못된) 트랜잭션을 롤백 하십시오.

from django.db import transaction
transaction.rollback()

try-except를 사용하여 오류가 발생하지 않도록 할 수 있습니다.

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

참조 : Django documentation


3
이것은 핵심 문제를 해결하고 중단 된 트랜잭션을 일으킨 명령문 이후에 복구 할 수있게합니다.
RichVel

이를 try / except와 함께 사용합니다.
tomwolber

3
IntegrityError기본 클래스가 아닌 왜 사용 DatabaseError합니까?
조나단

어떤 이유로 롤백을 "제외"섹션 외부로 이동해야했습니다. 나는 .bulk_create ()와 .save하지 ()를 사용하여 한
뉴 에베레스트


50

그래서 나는이 같은 문제에 부딪쳤다. 내가 가진 문제는 데이터베이스가 올바르게 동기화되지 않았다는 것입니다. 간단한 문제는 항상 가장 성가신 것 같습니다 ...

django db를 동기화하려면 앱 디렉토리 내 터미널에서 다음을 입력하십시오.

$ python manage.py syncdb

편집 : django-south를 사용하는 경우 '$ python manage.py migrate'명령을 실행하면이 문제가 해결 될 수 있습니다.

행복한 코딩!


3
명백한 내용을 언급했습니다. 나는 그것이 답이 아니었기 때문에 이것을 하나 이상의 공짜로 제공하지 않을 것입니다.
Jameson Quinn

5
python manage.py migrate <app>모든 앱에 대해 비슷한 방식으로 수정했습니다 .
Clayton

3
@Clayton-말하지 않지만 사용하고 있다고 가정합니다 django-south . migrate명령은 django에 내장되어 있지 않습니다.
Greg Ball

@ GregBall- 맞습니다 ... django-south를 사용하고 있습니다. 지정하지 않아서 죄송합니다.
Clayton

syncdb를 수행 할 때이 오류가 발생합니다 .django가 테이블을 통과하는 순서와 관련이 있다고 생각합니다.
스튜어트 Axon


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절이 catch IntegrityError(또는 다른 저수준 데이터베이스 예외) 인 경우 에만 발생합니다. DoesNotExist이 오류 와 같은 것을 포착 DoesNotExist하면 트랜잭션이 손상되지 않기 때문에 발생 하지 않습니다.

여기서 교훈은 시도 / 제외 / 통과하지 않습니다.


16

PostgreSQL을 사용할 때 패턴 priric 언급 이이 문제의 일반적인 원인 일 가능성이 더 큽니다.

그러나 패턴에 대한 올바른 사용법이 있다고 생각 하며이 문제가 항상 피해야하는 이유는 아니라고 생각합니다. 예를 들면 다음과 같습니다.

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

do_something_with_profile(profile)

이 패턴으로 괜찮다고 느끼지만 어디서나 명시 적 트랜잭션 처리 코드를 피하려면 자동 커밋 모드 (PostgreSQL 8.2 이상)를 켜고 싶을 수 있습니다. https://docs.djangoproject.com/en/ dev / ref / databases / # autocommit-mode

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

중요한 성능 고려 사항 (또는 다른 유형)이 있는지 확실하지 않습니다.


6

대화식 쉘에있는 동안이 문제가 발생하고 빠른 수정이 필요한 경우 다음을 수행하십시오.

from django.db import connection
connection._rollback()

이 답변 에서 원래 본


6

postgres터미널 에서 오작동하는 트랜잭션을 실행하는 동안 비슷한 동작이 발생했습니다 . (가)로 아무것도,이 후 겪은 database의 상태에있다 error. 그러나 피할 수 있다면 빠른 수정처럼 rollback transaction. 다음은 나를 위해 트릭을 수행했습니다.

COMMIT;


나는 repl에 있었고, 이것은 내가 찾던 정답입니다.
sarink

5

실 리마 문제가 있습니다. 해결책은 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

방금이 오류가 있었지만 코드가 100 자 열에 125 자 문자열을 저장하려고하는 다른 관련 오류 메시지를 마스킹했습니다.

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

마지막으로 다음을 사용하는 메소드를 보호하는 데코레이터를 사용하여이를 확인할 수 있습니다 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

저장 점을 사용하도록이 방법으로 코드를 변경했습니다.

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

플라스크 껍질에서 내가해야 할 일은 session.rollback()이것을 지나는 것입니다.


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.