문제 개요

DRF 서버에서 Django 앱 서버로 웹훅 POST 요청을 보내고,
Django에서는 이를 받아 Post 모델 인스턴스를 생성하는 과정에서 다음과 같은 문제가 발생했다.

  1. 웹훅 응답 시간 초과
  2. Post 객체 생성 이후, ManyToMany 필드인 categories, tags 처리를 위해 시간이 소요됨
  3. 이로 인해 webhook 응답이 지연되고 DRF는 이를 실패로 인식함

  4. 데이터 정합성 미확보

  5. 후속 처리에서 Celery 태스크를 바로 호출하는 경우, tags.add(...), categories.add(...) 작업이 완료되지 않은 시점에 호출됨
  6. 이로 인해 Celery에서는 불완전한 데이터를 처리하게 되는 문제가 발생함

  7. on_commit() 이후에도 Celery에서 빈 데이터 수신

  8. on_commit()을 사용해 데이터의 정합성을 확보한 뒤, Celery 태스크를 예약했지만, 태스크 내부에서 tags.all() 등의 조회 결과가 여전히 빈 리스트로 나타남
  9. 이는 Celery가 읽기 작업을 레플리카 DB로 보내고 있었고, master → replica 동기화에는 지연이 존재했기 때문

해결 전략

  1. Post 생성 직후 바로 응답 반환
  2. Post 인스턴스만 생성하고 DRF에는 즉시 202 Accepted 응답을 반환함으로써 timeout 문제를 회피

  3. 후속 작업을 별도 스레드에서 처리

  4. threading.Thread()를 활용하여 관계 처리 및 Celery 호출을 메인 흐름과 분리
  5. 웹훅 응답이 느려지지 않도록 설계

  6. post_process 내부에서 트랜잭션 보장

  7. transaction.atomic()으로 ManyToMany 필드 처리 전체를 트랜잭션으로 묶음
  8. 작업 완료 이후 transaction.on_commit()으로 Celery 태스크 예약
  9. 이로 인해 관계 처리 완료 후 Celery가 실행되도록 보장함

  10. Celery 내부에서는 master DB를 직접 지정하여 조회

  11. 레플리카의 지연으로 인한 정합성 문제를 방지하기 위해, Celery 태스크 내에서 using('default')를 명시적으로 사용
post = Post.objects.using('default').get(id=post_id)
tags = post.tags.using('default').all()
categories = post.categories.using('default').all()

또는 DB Router를 활용해 Celery 요청은 항상 master DB를 조회하도록 분기 가능


최종 구조 요약

  1. DRF 서버 → Django 웹훅 요청
  2. Django:
    • Post 객체 생성 후 즉시 응답 반환
    • 후속 작업은 별도 스레드에서 실행
  3. 쓰레드 내부:
    • atomic()으로 관계 정리
    • on_commit()으로 Celery 태스크 예약
  4. Celery:
    • master DB(default)에서 데이터 조회

결론

모든 구성요소가 개별적으로는 정상이지만,
분산된 아키텍처 환경에서 발생하는 레이턴시와 데이터 정합성 문제를 해결하기 위해
코드 구조뿐 아니라 데이터 흐름과 타이밍, DB 레플리카 지연까지 고려한 시스템 설계가 필요했다.