๐ ๊ฐ์
๋ฐ๋ธ์ฝ์ค 4๊ธฐ์์ ๋ฐฐ๋ฌ์ ๋ฏผ์กฑ์ B๋งํธ ํด๋ก ์ฝ๋ฉ ํ๋ก์ ํธ์ธ NaBMart ๋ฅผ ๊ฐ๋ฐํ์๋ค. ํ๋ก์ ํธ๋ ์๋ 9์์ ๋๋ฌ์ง๋ง, ์ฃผ๋ฌธ๊ณผ ๊ฒฐ์ ๋ถ๋ถ์์ ๊ฐ์ ํ ๋ถ๋ถ๋ค์ด ๋ณด์ฌ ๊ฐ์ธ์ ์ผ๋ก ๋ฆฌํฉํ ๋ง์ ์งํํ์๋ค. ๋ฆฌํฉํ ๋งํ ๋ด์ฉ์ ๋ค์๊ณผ ๊ฐ๋ค.
- ์ฃผ๋ฌธ ์ค์ผ์ค๋ฌ ๊ฐ์
- ์ฃผ๋ฌธ ๊ฒฐ์ ๋์์ฑ ์ด์ ํด๊ฒฐ
์ฃผ๋ฌธ ์ค์ผ์ค๋ฌ ๊ฐ์
์ฐ์ NaBMart ์ ์ฃผ๋ฌธ ๊ฒฐ์ flow ๋ ๋ค์๊ณผ ๊ฐ๋ค. (๊ฒฐ์ PG๋ ํ ์คํ์ด๋จผ์ธ ๋ฅผ ์ฌ์ฉํ์๋ค.)

- ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ์ ์ฃผ๋ฌธ ์์ฒญ์ ํ๊ณ , ์๋ฒ๋ ์ฃผ๋ฌธ์ ์ฒ๋ฆฌํ๊ณ ์ฌ๊ณ ๋ฅผ ๊ฐ์ํ๋ค.
- ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ์ ๊ฒฐ์ ์์ฒญ์ ํ๊ณ , ์๋ฒ๋ ์ฃผ๋ฌธ์ ์ ํจ์ฑ์ ๊ฒ์ฌํ ํ PG์ฌ์ ํต์ ํ ๋ ํ์ํ ๋ฐ์ดํฐ๋ฅผ ์๋ตํ๋ค.
- ํด๋ผ์ด์ธํธ๋ PG์ฌ์ ๊ฒฐ์ ์์ฒญ์ ํ๊ณ , ์ฌ์ฉ์๋ ๊ฒฐ์ ๋ฐฉ๋ฒ์ ์ ํํ์ฌ ๊ฒฐ์ ๋ฅผ ์งํํ๋ค.
- ๊ฒฐ์ ๊ฐ ์ฑ๊ณต ๋๋ ์คํจ๋ฅผ ํ๋ฉด ์ฝ๋ฐฑ url๋ก ๋ฆฌ๋ค์ด๋ ํธ๋๋ค.
- ์๋ฒ๋ PG์ฌ์ ์์ฒญํ์ฌ ๊ฒฐ์ ํ์ธ ๋ฐ ์น์ธ ์์ฒญ์ ํ๋ค.
์ฐ๋ฆฌ ํ๋ก์ ํธ์์๋ ์ฃผ๋ฌธ ์์ฒญ์ ํ์ ๋ ์ฌ๊ณ ๋ฅผ ๊ฐ์ํ๋ค. ์ด ๋, ์ฃผ๋ฌธ ํ ๊ฒฐ์ ๊ฐ ์ ์์ ์ผ๋ก ์ด๋ฃจ์ด์ง์ง ์์ ๊ฒฝ์ฐ ์ค์ ์ํ์ ์ฌ๊ณ ๊ฐ ์์์๋ ๋ถ๊ตฌํ๊ณ ์ฃผ๋ฌธ์ ํ ์ ์๋ ์ํฉ์ด ๋ฐ์ํ ์ ์๋ค. ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด ์ฐ๋ฆฌ๋ ์ค์ผ์ค๋ฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ฃผ๊ธฐ์ ์ผ๋ก 30๋ถ ๋์ ์๋ฃ๋์ง ์์ ์ฃผ๋ฌธ์ ์ทจ์์ํค๊ณ ์ฌ๊ณ ๋ฅผ ๋ณต๊ตฌํ๋ ์์ ์ ํ์๋ค. ์คํ๋ง ์ค์ผ์ค๋ฌ๋ฅผ ์ฌ์ฉํ์์ผ๋ฉฐ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
// OrderSchedulterService
@Scheduled(cron = "0 */5 * * * *")
public void updateOrderStatus() {
orderService.updateOrdetatus();
}
// OrderService
@Transactional
public void updateOrderStatus() {
LocalDateTime expiredTime = LocalDateTime.now().minusMinutes(30);
List<OrderStatus> statusList = List.of(OrderStatus.PENDING, OrderStatus.PAYING);
List<Order> expiredOrders = orderRepository.findByStatusInBeforeExpiredTime(expiredTime, statusList);
expiredOrders.forEach(this::cancelOrder);
}
@Transactional
public void cancelOrder(Order order) {
order.updateOrderStatus(OrderStatus.CANCELED);
order.getOrderItems().forEach(
orderItem -> itemRepository.increaseQuantity(orderItem.getItem().getItemId(),
orderItem.getQuantity())
);
}
ํ ์คํธ๋ฅผ ํ๊ธฐ ์ํด 5๊ฐ์ ๋ง๊ธฐ๋ ์ฃผ๋ฌธ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ํ์๋ค.

์ด์ ์ค์ผ์ค๋ฌ๋ฅผ ์คํ์ํค๊ณ ๋ก๊ทธ๋ฅผ ์ฐ์ด๋ณด๋ฉด ์ทจ์ํ ์ฃผ๋ฌธ ๋ชจ๋ ํ๋์ ํธ๋์ญ์ ์์ ์ํ๋๋ ๊ฒ์ ์ ์ ์๋ค. (๋ก๊น ๋ ๋ฒจ : debug)

JpaTransactionManager ์ doCommit ๋ฉ์๋์ ๋๋ฒ๊ทธ ์ง์ ์ ์ฐ๊ณ , ๋ฝ ์กฐํ ๋ช ๋ น์ด๋ฅผ ํตํด ์ปค๋ฐ์ด ์๋ฃ๋๊ธฐ ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ด๋ค ๋ฝ์ด ์กด์ฌํ๋์ง ํ์ธํด๋ณด์. (๋ฐ์ดํฐ๋ฒ ์ด์ค๋ MySQL ์ ์ฌ์ฉํ์๋ค.)
SELECT * FROM performance_schema.data_locks;

์ทจ์ํด์ผํ๋ ๋ชจ๋ ์ฃผ๋ฌธ์ ์ํ์ ์ฌ๊ณ ๊ฐฑ์ ์ ์ํด ๋ฐฐํ๋ฝ์ด ๊ฑธ๋ฆฌ๊ฒ ๋๋ค. ๋ฐ๋ผ์ ์ฃผ๋ฌธ ์ค์ผ์ค๋ฌ ๋์์ด ๋๋ ๋๊น์ง ํด๋น ์ํ์ ์ฃผ๋ฌธํ ์ ์๋ค.
์ํ์ ๊ฑธ๋ฆฌ๋ ๋ฝ ์๊ฐ์ ์ต์ํํ๊ณ ์ ํธ๋์ญ์ ์ ํ์์ฑ์ธ REQUIRES_NEW ๋ฅผ ์ฌ์ฉํ์ฌ ์ฃผ๋ฌธ๋ค์ด ๊ฐ๊ฐ์ ๋ ๋ฆฝ๋ ํธ๋์ญ์ ์์ ์ํ๋๋๋ก ์์ ํ์๋ค.
// OrderCancelService
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void cancelOrder(final Order order) {
order.updateOrderStatus(OrderStatus.CANCELED);
order.getOrderItems().forEach(
orderItem -> itemRepository.increaseQuantity(orderItem.getItem().getItemId(),
orderItem.getQuantity())
);
}
๋ค์ ์ค์ผ์ค๋ฌ๋ฅผ ์คํ์์ผ ์คํ ๋ก๊ทธ๋ฅผ ์ฐ์ด๋ณด๋ฉด ๊ฐ๊ฐ์ ์ฃผ๋ฌธ์ด ํ๋์ ๋ถ๋ฆฌ๋ ํธ๋์ญ์ ์์ ๋์ํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.

์ด์ ์ฃผ๋ฌธ์ ์ทจ์ํ ๋, ํด๋น ์ฃผ๋ฌธ์ ์กด์ฌํ๋ ์ํ์๋ง ๋ฝ์ด ๊ฑธ๋ฆฌ๊ฒ ๋๋ค.

์ฃผ๋ฌธ ๊ฒฐ์ ๋์์ฑ ์ด์ ํด๊ฒฐ
์ฌ๊ณ ๋ณต๊ตฌ๋ฅผ ์ํ ์ฃผ๋ฌธ ์ค์ผ์ค๋ฌ๋ฅผ ๋์ ํ๋ฉด์ ๊ฒฐ์ ์๋น์ค์ ์ฃผ๋ฌธ ์ค์ผ์ค๋ฌ์์ ๋์ผํ ์ฃผ๋ฌธ์ ์ ๊ทผํ ์ ์๊ธฐ ๋๋ฌธ์ ๋์์ฑ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์๋ค.
// OrderService
@Transactional
public void updateOrderStatus() {
LocalDateTime expiredTime = LocalDateTime.now().minusMinutes(30);
List<OrderStatus> statusList = List.of(OrderStatus.PENDING, OrderStatus.PAYING);
List<Order> expiredOrders = orderRepository.findByStatusInBeforeExpiredTime(expiredTime, statusList);
expiredOrders.forEach(this::cancelOrder);
}
// PaymentService
@Transactional
public PaymentResponse processSuccessPayment(Long userId, String uuid, String paymentKey,Integer amount) {
...
validateOrderStatusWithPaying(order);
}
- ์ฃผ๋ฌธ ์ค์ผ์ค๋ฌ : ์ฃผ๋ฌธ์ ์ํ๊ฐ 30๋ถ ์ด์ ์ฃผ๋ฌธ ๋๊ธฐ(PENDING) ๋๋ ๊ฒฐ์ ์ค(PAYING) ๋ผ๋ฉด ํด๋น ์ฃผ๋ฌธ๋ฅผ ์ทจ์ํ๊ณ ์ฌ๊ณ ๋ฅผ ๋ณต๊ตฌ
- ๊ฒฐ์ ์ฑ๊ณต ์ฒ๋ฆฌ : ๊ฒฐ์ ๋์์ด ๋๋ ์ฃผ๋ฌธ์ ์ํ๊ฐ ๊ฒฐ์ ์ค(PAYING)์ด๋ผ๋ฉด ๊ฒฐ์ ์ ์ ์ฒ๋ฆฌ

์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ์ฃผ๋ฌธ ์ค์ผ์ค๋ฌ์ ์ํด ์ฃผ๋ฌธ์ด ์ทจ์๋๊ณ ์ฌ๊ณ ๋ฅผ ๋ณต๊ตฌํ๋ ๋์์ ๊ฒฐ์ ๊ฐ ์๋ฃ๋ ์ ์๋ค. ๋ฐ๋ผ์ ์ฃผ๋ฌธ์ด ์ทจ์๋์ด ์ฌ๊ณ ๊ฐ ๋ณต๊ตฌ๋์์์๋ ๋ถ๊ตฌํ๊ณ ๊ฒฐ์ ๋ ์๋ฃ๋๋ ์ํฉ์ด ๋ฐ์ํ ์ ์๋ค.
ํด๋น ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋๊ด์ ๋ฝ๊ณผ ๋น๊ด์ ๋ฝ์ ๊ณ ๋ คํ์๊ณ , ๋ค์๊ณผ ๊ฐ์ ์ด์ ๋ก ๋๊ด์ ๋ฝ์ ์ ํํ์๋ค.
- ๋๋ถ๋ถ์ ๊ฒฐ์ ๋ 30๋ถ ๋ด๋ก ์๋ฃ๋จ
- ํน์ ์ฃผ๋ฌธ์ ๋ํ ๊ฒฐ์ ์ฒ๋ฆฌ์ ์ฃผ๋ฌธ ์ค์ผ์ค๋ฌ๊ฐ ๋์์ ์์ฒญ์ด ๋ฐ์ํ ๊ฒฝ์ฐ๊ฐ ์ ์
- ๋น๊ด์ ๋ฝ์ ์ฌ์ฉํ ๊ฒฝ์ฐ, ์ฃผ๋ฌธ ์ค์ผ์ค๋ฌ์์ํด ๊ธฐ์ค ์๊ฐ ์ดํ์ ๋ชจ๋ ์ฃผ๋ฌธ์ ๋ฐฐํ๋ฝ์ด ๊ฑธ๋ ค ์ฃผ๋ฌธ ๊ด๋ จ ์์ฒญ(๋ผ์ด๋ ๋ฐฐ์ฐจ ๋ฑ)๋ค์ ์ค์ผ์ค๋ฌ๊ฐ ๋ฝ์ ๋ฐ๋ฉํ ๋๊น์ง ๋๊ธฐํด์ผํจ
- ๋น๊ด์ ๋ฝ์ ์ฌ์ฉํ ๊ฒฝ์ฐ, ์ค์ผ์ค๋ฌ ๋์ ์ค ํ๋์ ์ฃผ๋ฌธ์ด๋ผ๋ ๊ฒฐ์ ์๋น์ค์ ์ํด ๋ฝ์ด ๊ฑธ๋ ธ๋ค๋ฉด, ์ค์ผ์ค๋ฌ๋ ๋๊ธฐํด์ผํจ