한 번 설정하면 끝나는 자동 동기화 시스템을 구축한 방법

제품 가격은 여러 곳에서 변경됩니다. 관리자 패널, 대량 가져오기(bulk imports), 또는 API 웹훅을 통해 변경됩니다.

이러한 변경 사항을 외부 마켓플레이스에 동기화하려면 문제에 직면하게 됩니다. 모든 코드 경로에 동기화 호출을 추가하는 것은 실수입니다. 하나를 빠뜨리거나, 하나를 망가뜨리게 될 것입니다. 유지보수는 악몽이 됩니다.

Django signals가 이 문제를 해결합니다. 모델의 save 이벤트를 후킹(hook)하면 됩니다. 이렇게 하면 모든 변경 사항을 한 곳에서 포착할 수 있습니다.

하지만 signals에는 결함이 있습니다. 한 번에 100개의 가격을 업데이트하면, signal이 100번 실행됩니다. 이는 100번의 API 호출을 유발하며, rate limit(호출 제한)에 걸리거나 리소스를 낭비하게 됩니다.

저는 이를 해결하기 위해 세 가지 단계로 구성된 패턴을 사용합니다:

• 즉시 실행하는 대신 ID를 수집하는 signal handler. • 중복을 제거하기 위한 thread별 set. • 모든 것을 한 번에 처리하기 위해 transaction.on_commit을 사용하는 flush callback.

작동 방식은 다음과 같습니다.

  1. threading.local() 사용하기 전역 변수를 사용하지 마세요. 전역 변수는 요청(request) 간에 상태를 공유하므로 데이터 유출(data leakage)로 이어질 수 있습니다. threading.local()은 데이터를 단일 스레드에 격리하여 유지합니다.

  2. 실행 대신 기록하기 signal handler는 단순히 제품 ID를 set에 추가합니다. 그런 다음 데이터베이스 트랜잭션이 성공한 후에만 flush 함수를 실행하도록 Django에 지시합니다. 이를 통해 저장에 실패한 데이터를 동기화하는 일을 방지할 수 있습니다.

  3. 작업 일괄 처리(Batching) 트랜잭션이 커밋되면 flush 함수가 실행됩니다. 이 함수는 set을 복사한 뒤 비웁니다. 그런 다음 ID 전체 목록을 서비스 레이어(service layer)로 보냅니다.

서비스 레이어는 단 한 번의 벌크 쿼리(bulk query)를 수행하여 모든 제품을 가져옵니다. 제품들을 상점(store)별로 그룹화한 다음, 마지막으로 상점당 하나의 Celery 태스크를 보냅니다.

이점은 명확합니다:

시스템을 한 번 구축해 두면, 나중에 추가하는 모든 새로운 기능이 자동으로 동기화 시스템과 연동됩니다.

여러분은 Django에서 외부 API 동기화를 어떻게 처리하시나요? signals를 사용하시나요, 아니면 다른 패턴을 사용하시나요?

Source: https://dev.to/acel/how-i-built-a-set-it-and-forget-it-sync-system-with-django-signals-2ld7