이제 간단한 토이프로젝트에 transaction을 적용해볼 것이다.
토이프로젝트의 로직과 구조를 설명하고 trasaction을 적용시킨 코드를 공유해본다.
이 토이프로젝트는 project와 audio 테이블로 구성되어 있다.
문단을 받아서, 문장 단위로 구분하고 각각을 audio에 저장한 후, tts서비스를 통해 mp3 파일을 만들어준다.
project는 audio와 1:N 관계이다.
각각의 audio는 해당 project의 몇번째 문장인지 index 컬럼을 가지고 있다.
project의 중간에 audio를 추가 할 수 있고, 이 때 project의 audio들의 index값을 재조정해준다.
코드로 보면 아래와 같다.
def _insert_audio_and_update_indexs(
self, project_id: int, index: int, text: str, speed: int, user_id: int
) -> dict:
"""
오디오를 지정한 위치에 생성하고, 뒤로 밀려나게 되는 audio의 index를 업데이트 합니다.
생성된 오디오 정보를 dict로 return
"""
# project의 전체 audio index를 재조정합니다.
self._update_audios_index(project_id, index, is_increase=True)
create_params = {
"project": project_id,
"index": index,
"text": text,
"speed": speed,
"user": user_id,
}
# 새로 생성할 오디오를 만듭니다
new_audio = audio_repo.create(create_params)
return new_audio
아래 코드에서, 전체 audio의 index들을 bulkupdate 한 후에, audio 생성을 실패한다면 어떻게 될까?
전체 audio의 index값은 수정되었으나, audio가 새로 생성되지 않았으니, index의 데이터 무결성이 지켜지지 않게 된다.
따라서, audio index의 재조정과 audio 생성을 논리적으로 하나의 연산으로 보고 transaction으로 묶어줄 필요가 있다.
def _insert_audio_and_update_indexs(
self, project_id: int, index: int, text: str, speed: int, user_id: int
) -> dict:
"""
오디오를 지정한 위치에 생성하고, 뒤로 밀려나게 되는 audio의 index를 업데이트 합니다.
생성된 오디오 정보를 dict로 return
"""
self._update_audios_index(project_id, index, is_increase=True)
create_params = {
"project": project_id,
"index": index,
"text": text,
"speed": speed,
"user": user_id,
}
# 여기서 오류가 발생한다면!!!
new_audio = audio_repo.create(create_params)
return new_audio
django transaction 적용은 매우 간단하다.
transaction은 decorator와 context manager 두 가지 형태로 제공된다.
아래 코드는 context manager를 사용한 방법이다.
context 내부에서 error가 발생할 경우, context 내부에서 실행된 쿼리들은 rollback 될 것이다.
from django.db import transaction
...
def _insert_audio_and_update_indexs(
self, project_id: int, index: int, text: str, speed: int, user_id: int
) -> dict:
"""
오디오를 지정한 위치에 생성하고, 뒤로 밀려나게 되는 audio의 index를 업데이트 합니다.
생성된 오디오 정보를 dict로 return
"""
with transaction.atomic():
# project의 전체 audio index를 재조정합니다.
self._update_audios_index(project_id, index, is_increase=True)
create_params = {
"project": project_id,
"index": index,
"text": text,
"speed": speed,
"user": user_id,
}
# 새로 생성할 오디오를 만듭니다
new_audio = audio_repo.create(create_params)
return new_audio
과연 transaction을 사용한다고 해서, 무조건 데이터 무결성이 지켜질까?
반드시 그렇지는 않다.
3편에서는 transaction을 사용했으나, 잘못된 데이터가 DB에 저장되는 현상을 동시성 처리와 연관지어 설명할 것이다.