새소식

인기 검색어

개발일기

django bulk_create시 같은 트랜잭션에서 created objects 조회할 수 없는 현상

  • -

개요

view에서 request 단위로 transaction.atomic()을 걸고 bulk_create와 그 후 생성한 obj를 find했으나 조회할 수 없는 현상이 있었다.

 

@transaction.atomic()
some_view():
  svc1.create()
  svc2.bulk_create()
  svc3.retrieve()

 

이런 느낌의 view였는데, svc3에서 svc1에서 생성한 obj는 조회가 됐었으나, svc2에서 생성한 objs는 조회할 수 없었다.

 

본문

내가 기대했던 건, svc1이든 svc2든 같은 물리 트랜잭션 내에 존재하기 때문에 svc3에서 둘다 모두 조회되는 것이었다. 그럼에도 불구하고 svc1에서 생성된 것만 조회된다는 것은 무언가 문제가 있어 보였다.

 

여기서 조사했던 건 bulk_create가 어떻게 동작하는지였다. 따라서 django.db.models.query.py의 bulk_create() 소스를 보다보니 다음과 같은 것이 있었다.

- 참고: https://github.com/django/django/blob/main/django/db/models/query.py#L815

 

bulk_create시 내부적으로 context manager를 통해 트랜잭션을 하나 더 만든다. 이건 내 예상인데, spring처럼 트랜잭션 매니저가 물리 트랜잭션으로 묶는 식으로 동작하는 것이 아니라 진짜 별개로 트랜잭션이 하나 더 뜨는 것으로 보인다. (이것은 가설일 뿐이고 정확힌 공부 혹은 실험을 해봐야 안다) 트랜잭션을 하나 더 만든다고 표현한 것이다.

 

즉, READ COMMITED 이상이면 다른 트랜잭션이면 조회할 수 없는 것이 당연할 것이다. 그렇다면 우리의 트랜잭션 격리 수준은 무엇일까? InnoDB의 기본 전략이 REPEATABLE READ니까 너무 당연하게 REPEATABLE READ일까? 정답은 아니다.

 

django에서는 MySQL default인 REPEATABLE READ가 아니라 READ COMMITTED일때 best라고 표현한다. 그래서 default로 이걸로 두었다. 트랜잭션 순서에 따라 문제가 발생할 수도 있는 부분이라... 트랜잭션이 깊어지면 기억은 해두는 게 좋아 보인다

 

아무튼 이 문제를 어떻게 해결했을까? 간단하다. 컨텍스트 매니저로 트랜잭션 범위를 좁혀주었다

 

some_view():
  with transaction.atomic:
    svc1.create()
    svc2.bulk_create()
  svc3.retrieve()

 

이러면 한 svc3에서 commit된 것이 보장되기 때문에 올바르게 조회할 수 있다

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.