1. 문제 상황: on_commit()이 너무 빨리 실행된다?
최근 Django 프로젝트에서 transaction.on_commit()
을 사용하여
Celery 작업을 트랜잭션이 성공한 후에 실행하도록 구성했습니다.
그러나 로그를 보니 이상한 현상이 발생했습니다.
아직
post.categories.add(...)
같은 작업이 완료되지 않았는데도
on_commit()
안에 있던 Celery task가 실행되더라구요.
2. 코드를 살펴보니…
def view():
post = Post.objects.create(...)
def post_process():
with transaction.atomic():
post.categories.add(...)
post.tags.add(...)
def run_async():
translate_post.delay(post.id)
transaction.on_commit(run_async)
threading.Thread(target=post_process).start()
처음엔 문제 없어 보였는데…
바로 이 threading.Thread()
가 함정이었습니다.
3. 원인: 트랜잭션은 쓰레드 로컬이다
Django의 트랜잭션은 현재 쓰레드에 종속됩니다.
즉, transaction.atomic()
과 on_commit()
은 지금 이 쓰레드에서 열린 트랜잭션에만 적용됩니다.
새로운 쓰레드에서 열었던
transaction.atomic()
은
메인 트랜잭션과 아무 관련이 없습니다.
4. 결과적으로 벌어진 일
- Celery task는 "트랜잭션 커밋 이후" 실행되었지만
- 그 트랜잭션은 서브 쓰레드 안의 작은 트랜잭션이었을 뿐
post.categories.add(...)
등은 아직 실행 중인 상태
즉, 의도보다 너무 빨리 Celery가 실행되면서,
불완전한 데이터를 참조하는 문제가 생긴 겁니다.
5. 해결 방법
그냥 이렇게 고치면 됩니다:
def view():
post = Post.objects.create(...)
with transaction.atomic():
post.categories.add(...)
post.tags.add(...)
def run_async():
translate_post.delay(post.id)
transaction.on_commit(run_async)
🚫 threading.Thread(...)
✅ 그냥 메인 쓰레드에서 on_commit()
등록
6. 정리
- Django의 트랜잭션은 쓰레드 로컬
on_commit()
도 현재 쓰레드의 트랜잭션 커밋 이후에만 실행됨- 다른 쓰레드에서 등록하면, 그 쓰레드의 트랜잭션에만 반응
이 경험은 아주 강력한 교훈이 되었어요.
트랜잭션 + Celery + 쓰레드를 함께 쓸 때,
정말 조심해야겠다는 걸 느꼈습니다.
Add a New Comment