Spring ๐ŸŒฑ

๋™์‹œ์„ฑ ์ด์Šˆ ํ•ด๊ฒฐํ•˜๊ธฐ : ๋น„๊ด€์  ๋ฝ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์— @Transactional์„ ์‚ฌ์šฉํ•˜๋ฉด Lock wait timeout exceeded๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ด์œ 

z.zzz 2023. 12. 16. 15:41

๊ฒฐ๋ก ๋ถ€ํ„ฐ ๋งํ•˜์ž๋ฉด @Transactional์˜ ๋™์ž‘ ๋ฐฉ์‹ ๋•Œ๋ฌธ์ด๋‹ค.

 

๋ฌธ์ œ ์ƒํ™ฉ


์žฌ๊ณ ๋ฅผ ํ•˜๋‚˜ ๊ฐ์†Œ์‹œํ‚ค๋Š” ์š”์ฒญ์ด ๋™์‹œ์— 100๊ฐœ๊ฐ€ ๋“ค์–ด์˜จ ์ƒํ™ฉ์ด๋‹ค.

๋น„๊ด€์  ๋ฝ์„ ์‚ฌ์šฉํ•ด์„œ Race condition์„ ํ”ผํ•˜๋ คํ•œ๋‹ค.

์ด๋•Œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์— @Transactional์„ ๋ถ™์ด๋ฉด Lock wait timeout exceeded๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉฐ ํ…Œ์ŠคํŠธ์— ์‹คํŒจํ•œ๋‹ค.

@SpringBootTest
@Transactional
class StockServiceTest {

    @Test
    public void ๋™์‹œ์—_100๊ฐœ์˜_์š”์ฒญ() throws InterruptedException {
        int threadCount = 100;
        ExecutorService executorService = Executors.newFixedThreadPool(32);
        CountDownLatch latch = new CountDownLatch(threadCount);

        for (int i = 0; i < threadCount; i++) {
            executorService.submit(() -> {
                try {
                    stockService.decrease(1L, 1L);
                } finally {
                    latch.countDown();
                }
            });
        }

        latch.await();

        Stock stock = stockRepository.findById(1L).orElseThrow();
        // 100 - (1 * 100) = 0
        assertEquals(0, stock.getQuantity());
    }
}
@Service
public class PessimisticLockStockService {

    @Transactional
    public void decrease(Long id, Long quantity) {
        Stock stock = stockRepository.findByIdWithPessimisticLock(id);

        stock.decrease(quantity);

        stockRepository.save(stock);
    }
}

 

์›์ธ


@Transactional์„ ๋ถ™์ด๋ฉด ํ”„๋ก์‹œ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

@Transactoinal์— ์˜ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ”„๋ก์‹œ ํด๋ž˜์Šค๊ฐ€ ๋งŒ๋“ค์–ด์ง€๊ณ  ๋™์ž‘ํ•œ๋‹ค.

  1. ์Šค๋ ˆ๋“œ A๊ฐ€ ๋ฝ์„ ์ทจ๋“ํ•œ๋‹ค.
  2. ์Šค๋ ˆ๋“œ B๋Š” ๋ฝ์ด ์ข…๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐํ•œ๋‹ค.
  3. ๋ฝ์„ ํ•ด์ œํ•˜๋ ค๋ฉด ๋ฉ”์†Œ๋“œ๊ฐ€ ์ข…๋ฃŒ๋˜์–ด์•ผ ํ•˜๋Š”๋ฐ ์Šค๋ ˆ๋“œ B๊ฐ€ ๋Œ€๊ธฐ ์ค‘์ด๋ผ ๋ฉ”์†Œ๋“œ๊ฐ€ ์ข…๋ฃŒ๋˜์ง€ ์•Š๋Š”๋‹ค. (์ด๋•Œ ์ข…๋ฃŒ๋˜์ง€ ์•Š๋Š” ๋ฉ”์†Œ๋“œ๋Š” ์›๋ณธ ClubService์˜ join ๋ฉ”์†Œ๋“œ๊ฐ€ ์•„๋‹Œ ํ”„๋ก์‹œ ํด๋ž˜์Šค์˜ join ๋ฉ”์†Œ๋“œ๋‹ค.)
  4. ๊ณ„์† ๋Œ€๊ธฐํ•˜๋‹ค๊ฐ€ Lock timeout์ด ๋ฐœ์ƒํ•˜์—ฌ ํ…Œ์ŠคํŠธ์— ์‹คํŒจํ•œ๋‹ค. 
class ClubServiceProxy (clubService: ClubService, transaction: TransactionManager) {
    func join(sessionUser : Long, key: Long) {
    	transaction.start()
        try {
            clubService.join(id, quantity)
            transaction.commit()
        } catch (e: Exeption)
            transaction.rollback()
        }
    }
}

 

 

ํ•ด๊ฒฐ๋ฐฉ๋ฒ•


@Transactional์„ ์ œ๊ฑฐํ•˜๊ณ  @AfterEach๋กœ ๋ฐ์ดํ„ฐ๋ฅผ Rollbackํ–ˆ๋‹ค.

๋˜๋Š” ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋งค๋ฒˆ ์ดˆ๊ธฐํ™”ํ•ด์„œ ์‚ฌ์šฉํ•˜๋„๋ก application.yml์˜ ์†์„ฑ์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค์ •ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

jpa:
  hibernate:
    ddl-auto: create

 

 

+ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์— @Transactional์„ ์‚ฌ์šฉํ•œ ์ด์œ 

๋ฐ์ดํ„ฐ๋ฅผ ๋กค๋ฐฑํ•˜๋ ค๋Š” ๋ชฉ์ ์œผ๋กœ ์‚ฌ์šฉํ–ˆ๋‹ค. 

ํ”„๋ก์‹œ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด ๋™์ž‘ํ•œ๋‹ค๋Š” ๊ฑธ ์œ ์˜ํ•ด๋‘ฌ์•ผ๊ฒ ๋‹ค

 

+ ๋น„๊ด€์  ๋ฝ์„ ์‚ฌ์šฉํ•œ ์ด์œ 

์ถฉ๋Œ์ด ๋นˆ๋ฒˆํ•˜๊ฒŒ ์ผ์–ด๋‚  ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ์ด๋ผ๊ณ  ํŒ๋‹จํ•ด ๋น„๊ด€์  ๋ฝ์„ ์‚ฌ์šฉํ–ˆ๋‹ค.

๋‚™๊ด€์  ๋ฝ์€ ๋ณ„๋„์˜ lock์„ ์žก์ง€ ์•Š๊ณ  retry ๋กœ์ง์œผ๋กœ ๋™์‹œ์„ฑ์„ ์ œ์–ดํ•œ๋‹ค. ๋”ฐ๋ผ์„œ 100๊ฐœ์˜ ์žฌ๊ณ  ๊ฐ์†Œ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋งŽ์€ ์š”์ฒญ์„ ๋ณด๋‚ด๊ฒŒ ๋œ๋‹ค. ๋ฐ˜๋ฉด ๋น„๊ด€์  ๋ฝ์€ lock์„ ๊ฑธ๊ณ  ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ฉฐ, ์ดํ›„ ์š”์ฒญ๋“ค์€ ๋ฝ์ด ํ•ด์ œ๋˜๊ธธ ๊ธฐ๋‹ค๋ฆฌ๋ฏ€๋กœ ๋งŽ์€ ์š”์ฒญ์„ ํ•˜์ง€ ์•Š๋Š”๋‹ค.

 

+ ๋น„๊ด€์  ๋ฝ์„ ์‚ฌ์šฉํ•  ๋•Œ, ๋ฝ์ด ํ•ด์ œ๋˜์—ˆ๋Š”์ง€ ์–ด๋–ป๊ฒŒ ์•Œ๊นŒ?

๋น„๊ด€์  ๋ฝ์€ ๋ฝ์ด ์ด์šฉ๊ฐ€๋Šฅํ•œ ์ƒํƒœ์ธ์ง€๋ฅผ ๊ณ„์†ํ•ด์„œ ํ™•์ธํ•˜๋Š” ๋กœ์ง์ด ์—†๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๋ฝ์ด ์ด์šฉ๊ฐ€๋Šฅํ•œ์ง€๋ฅผ ์–ด๋–ป๊ฒŒ ๊ฐ์ง€ํ•˜๋Š”๊ฑธ๊นŒ.

๋ฝ์ด ์ด์šฉ๊ฐ€๋Šฅํ•œ์ง€๋Š” ํŠธ๋žœ์žญ์…˜์ด ์ข…๋ฃŒ๋˜๋ฉด ์•Œ ์ˆ˜ ์žˆ๋‹ค! ํŠธ๋žœ์žญ์…˜์€ commit ๋˜๋Š” rollback์— ์˜ํ•ด ์ข…๋ฃŒ๋œ๋‹ค. ์ด๋•Œ commit, rollback์€ ๋ชจ๋‘ ๋ฝ์„ ํ•ด์ œํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ๋น„๊ด€์  ๋ฝ์€ ํŠธ๋žœ์žญ์…˜์˜ ์ข…๋ฃŒ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ๋ฝ ์‚ฌ์šฉ ๊ฐ€๋Šฅ ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.