터칭 데이터
장고 (Django) 에러 방어하기 2 본문
만약에 복수의 서버에서 같은 요청을 동시에 실행한다면?
상황 하나를 가정해 보겠습니다.
현재 커피의 득표수는 99입니다.
그런데
서버1에서 A라는 사람이 커피에 한표를 투표하고
서버2에서 B라는 사람이 커피에 한표를 투표했습니다.
그러면 커피는 기존의 99에 2표를 더 얻어 커피의 득표수는 101이 되어야 합니다.
그런데 정말 우연히도 A와 B가 버튼을 동시에 클릭하고 서버가 이를 동시에 수행하는 극히 희박한 우연이 발생한다면 우리가 기존에 작성한 코드는 제대로 작동할까요??
우리가 뷰에서 작성했던 코드입니다.
우리가 위에서 가정한 상황에서
서버1은 기존의 커피 득표수 99를 가져와 1을 더합니다. 서버2 역시 99를 가져와 1을 더합니다.
서비1은 메모리에 커피 득표수 100을 가지고 있고 서버2 역시 마찬가지로 메모리에 커피 득표수 100을 가지고 있습니다.
이 상태로 서버1이 먼저 save()를 진행해 디비의 값을 변경하든 서버2가 먼저 save()를 진행해 디비의 값을 변경하든 커피는 득표수가 100이 되어버립니다. 서버1에서 커피의 득표수는 100이라고 DB의 값을 변경할테고 서버2에서도 마찬가지이기 때문입니다. 서버1과 서버2의 순서가 바뀌어도 똑같습니다. 분명 A와 B 두사람이 기존의 커피 득표수 99에 각각 한표씩 행사해 99+2 = 101표가 되어야 하는데 잘못 되었습니다.
여러대의 서버를 운영하는 것은 매우 흔한 일이고 짧은 시간 내에 많은 사람들이 몰리는 일 역시 매우 흔한 일입니다. 희박할 것 같지만 위와 같은 상황은 종종 발생하는 일입니다. 투표나 계좌 잔고 등의 결과가 잘못 계산되는 큰 일이 발생할 수 도 있습니다.
이를 어떻게 방지할 수 있을까요?
서버는 여러대지만 DB는 하나이기 때문에 여러 서버에서 동시에 실행한 명령이 디비에서 동시에 실행될리는 없습니다. 어떤 서버든 먼저 실행되고 그 다음 것이 실행됩니다. 그러므로 서버의 메모리 상에서 득표수를 올리고 디비에 반영하지 말고 애초에 디비에서 값을 변경하도록 하는 장고의 F() 객체를 활용합니다.
F() 객체를 사용하면 파이썬에서 해당 값을 메모리에 가지고 있다가 처리하는 것이 아니라 DB에서 바로 작업을 처리하기 때문에 경쟁 조건을 피할 수 있습니다. Django에서는 F() 객체를 만나면, Python 연산자를 오버라이딩하여 캡슐화된 SQL문을 생성합니다. 즉, 작업은 전적으로 데이터베이스에서 처리하게되어 투표수가 디비에 잘못 반영되는 경우를 피할 수 있습니다.
django.db.models 에서 F를 import해 코드를 수정합니다.
장고에서는 F를 이용해 경쟁 조건(race condition)을 방어합니다.
'장고 (Django)' 카테고리의 다른 글
장고(Django) 시리얼라이저(Serilaizer) (0) | 2023.11.04 |
---|---|
장고(Django) 결과(result) 조회 페이지 (0) | 2023.11.03 |
장고(Django) 에러 방어 (0) | 2023.11.03 |
장고(Django) 폼(Forms) (0) | 2023.11.03 |
장고(Django) 404 에러 처리하기 (0) | 2023.11.03 |