๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

transaction

[NaBMart] ์ฃผ๋ฌธ ๊ฒฐ์ œ ์‹œ์Šคํ…œ ๊ฐœ์„ ๊ธฐ

๐Ÿš€  ๊ฐœ์š”

๋ฐ๋ธŒ์ฝ”์Šค 4๊ธฐ์—์„œ ๋ฐฐ๋‹ฌ์˜ ๋ฏผ์กฑ์˜ B๋งˆํŠธ ํด๋ก  ์ฝ”๋”ฉ ํ”„๋กœ์ ํŠธ์ธ NaBMart ๋ฅผ ๊ฐœ๋ฐœํ•˜์˜€๋‹ค. ํ”„๋กœ์ ํŠธ๋Š” ์ž‘๋…„ 9์›”์— ๋๋‚ฌ์ง€๋งŒ, ์ฃผ๋ฌธ๊ณผ ๊ฒฐ์ œ ๋ถ€๋ถ„์—์„œ ๊ฐœ์„ ํ•  ๋ถ€๋ถ„๋“ค์ด ๋ณด์—ฌ ๊ฐœ์ธ์ ์œผ๋กœ ๋ฆฌํŒฉํ† ๋ง์„ ์ง„ํ–‰ํ•˜์˜€๋‹ค. ๋ฆฌํŒฉํ† ๋งํ•œ ๋‚ด์šฉ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. ์ฃผ๋ฌธ ์Šค์ผ€์ค„๋Ÿฌ ๊ฐœ์„ 
  2. ์ฃผ๋ฌธ ๊ฒฐ์ œ ๋™์‹œ์„ฑ ์ด์Šˆ ํ•ด๊ฒฐ

์ฃผ๋ฌธ ์Šค์ผ€์ค„๋Ÿฌ ๊ฐœ์„ 

 ์šฐ์„  NaBMart ์˜ ์ฃผ๋ฌธ ๊ฒฐ์ œ flow ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. (๊ฒฐ์ œ PG๋Š” ํ† ์ŠคํŽ˜์ด๋จผ์ธ ๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋‹ค.)

  1. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„์— ์ฃผ๋ฌธ ์š”์ฒญ์„ ํ•˜๊ณ , ์„œ๋ฒ„๋Š” ์ฃผ๋ฌธ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žฌ๊ณ ๋ฅผ ๊ฐ์†Œํ•œ๋‹ค.
  2. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„์— ๊ฒฐ์ œ ์š”์ฒญ์„ ํ•˜๊ณ , ์„œ๋ฒ„๋Š” ์ฃผ๋ฌธ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•œ ํ›„ PG์‚ฌ์™€ ํ†ต์‹ ํ•  ๋•Œ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์‘๋‹ตํ•œ๋‹ค.
  3. ํด๋ผ์ด์–ธํŠธ๋Š” PG์‚ฌ์— ๊ฒฐ์ œ ์š”์ฒญ์„ ํ•˜๊ณ , ์‚ฌ์šฉ์ž๋Š” ๊ฒฐ์ œ ๋ฐฉ๋ฒ•์„ ์„ ํƒํ•˜์—ฌ ๊ฒฐ์ œ๋ฅผ ์ง„ํ–‰ํ•œ๋‹ค.
  4. ๊ฒฐ์ œ๊ฐ€ ์„ฑ๊ณต ๋˜๋Š” ์‹คํŒจ๋ฅผ ํ•˜๋ฉด ์ฝœ๋ฐฑ url๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋œ๋‹ค.
  5. ์„œ๋ฒ„๋Š” 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)์ด๋ผ๋ฉด ๊ฒฐ์ œ ์ •์ƒ ์ฒ˜๋ฆฌ

์œ„ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์ด ์ฃผ๋ฌธ ์Šค์ผ€์ค„๋Ÿฌ์— ์˜ํ•ด ์ฃผ๋ฌธ์ด ์ทจ์†Œ๋˜๊ณ  ์žฌ๊ณ ๋ฅผ ๋ณต๊ตฌํ•˜๋Š” ๋™์‹œ์— ๊ฒฐ์ œ๊ฐ€ ์™„๋ฃŒ๋  ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ฃผ๋ฌธ์ด ์ทจ์†Œ๋˜์–ด ์žฌ๊ณ ๊ฐ€ ๋ณต๊ตฌ๋˜์—ˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ๊ฒฐ์ œ๋Š” ์™„๋ฃŒ๋˜๋Š” ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ•ด๋‹น ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋‚™๊ด€์ ๋ฝ๊ณผ ๋น„๊ด€์ ๋ฝ์„ ๊ณ ๋ คํ•˜์˜€๊ณ , ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ด์œ ๋กœ ๋‚™๊ด€์ ๋ฝ์„ ์„ ํƒํ•˜์˜€๋‹ค.

  1. ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฐ์ œ๋Š” 30๋ถ„ ๋‚ด๋กœ ์™„๋ฃŒ๋จ
  2. ํŠน์ • ์ฃผ๋ฌธ์— ๋Œ€ํ•œ ๊ฒฐ์ œ ์ฒ˜๋ฆฌ์™€ ์ฃผ๋ฌธ ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ๋™์‹œ์— ์š”์ฒญ์ด ๋ฐœ์ƒํ•  ๊ฒฝ์šฐ๊ฐ€ ์ ์Œ
  3. ๋น„๊ด€์ ๋ฝ์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, ์ฃผ๋ฌธ ์Šค์ผ€์ค„๋Ÿฌ์—์˜ํ•ด ๊ธฐ์ค€ ์‹œ๊ฐ„ ์ดํ•˜์˜ ๋ชจ๋“  ์ฃผ๋ฌธ์— ๋ฐฐํƒ€๋ฝ์ด ๊ฑธ๋ ค ์ฃผ๋ฌธ ๊ด€๋ จ ์š”์ฒญ(๋ผ์ด๋” ๋ฐฐ์ฐจ ๋“ฑ)๋“ค์€ ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ๋ฝ์„ ๋ฐ˜๋‚ฉํ•  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐํ•ด์•ผํ•จ
  4. ๋น„๊ด€์ ๋ฝ์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, ์Šค์ผ€์ค„๋Ÿฌ ๋Œ€์ƒ ์ค‘ ํ•˜๋‚˜์˜ ์ฃผ๋ฌธ์ด๋ผ๋„ ๊ฒฐ์ œ ์„œ๋น„์Šค์— ์˜ํ•ด ๋ฝ์ด ๊ฑธ๋ ธ๋‹ค๋ฉด, ์Šค์ผ€์ค„๋Ÿฌ๋Š” ๋Œ€๊ธฐํ•ด์•ผํ•จ