주문 번호
커피 전문점에서 주문 번호는 중요한 역할을 합니다. 주문 번호는 주문을 구별하는데 사용하고 주문한 고객을 부르는데 사용합니다. 또한 사장님에게는 당일 몇 번의 주문이 들어왔는지 직관적으로 알려주는 역할도 합니다.
요구 사항
- 주문 번호는 매일 초기화 됩니다.(참고)
- 주문 번호는 가게별로 다릅니다.
- 같은 날 같은 같은 가게에는 동일한 주문 번호가 없습니다.
- 주문 번호는 1부터 시작해서 1씩 증가합니다.
최초 시도
synchronized
를 통해서 주문 번호를 생성하는 메서드를 동기화하였습니다.
그러면 각 주문 번호를 순차적으로 생성할 수 있을 것이라고 생각했습니다.
문제 발생
5s가 차이나도 동일한 주문 번호가 생성되는 문제가 발생했습니다.
문제 원인
메서드를 호출하는 매서드에서 @Transactional
을 사용하고 있습니다. 그래서 @Transactional
이 하위 전파되어 주문 번호 생성 매서드에 @Transactional
과 synchronized
을 동시에 적용됐습니다. 그 결과 race condition
이 발생했습니다.
이는 MySQL의 격리 수준이 REPEATABLE READ
이기 때문에 발생한 문제입니다.
MySQL 재현
T(트랜잭션) 시작
-> count
-> count + 1을 insert
-> commit
을 하는 코드를 2개 준비합니다.
spring boot
환경과 동일하게 T1 시작
-> T2 시작
-> ~~ -> T1 commit
-> T2 commit
순서로 실행하면서 어떤 결과가 나오는지 확인해보겠습니다.
T1
, T2
을 시작한 후 T1
의 count
결과 입니다.
T1
에 count + 1
을 insert
후 commit
까지 해줍니다.
T2
의 count
결과 입니다.
하지만 T2
의 count
결과는 여전히 2입니다.
T2
에 count + 1
을 insert
후 commit
까지 해줍니다.
최종 결과를 보면 동일한 order_number
가 생성되는 것을 확인할 수 있습니다.
해결 방법
order_number_sequence
를 생성하고 가게마다 주문 번호를 관리했습니다. 그리고 주문 번호를 생성할 때는 비관적 락
을 사용하여 race condition
을 방지했습니다.
이제 매일 주문 번호를 0
으로 초기화 해야하는데 이는 lambda
를 사용하여 해결할 수 있습니다. AWS Lambda로 SQL 배치 작업 하기
결과
5s가 차이나도 같은 주문 번호가 나오던 것이 1.4s의 차이가 생겨도 다른 주문 번호가 나오는 것을 확인할 수 있습니다.
고찰
개인적으로 DB를 활용해서 해결하는 것을 좋아하지 않습니다. 따라서 DB를 활용하지 않고 해결하는 방법을 현재 시도중에 있습니다. 2편을 기대해주세요.