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 + 쓰레드를 함께 쓸 때,
정말 조심해야겠다는 걸 느꼈습니다.