인프런 커뮤니티 질문&답변

별천랑님의 프로필 이미지
별천랑

작성한 질문수

재고시스템으로 알아보는 동시성이슈 해결방법

Redisson 을 활용하여 재고로직 작성하기

RedissonLockStockFacade 트랜잭션시 실패 케이스

해결된 질문

작성

·

380

0

@Component
class RedissonLockStockFacade(
    private val redissonClient: RedissonClient,
    private val stockService: StockService
) {
    @Transactional
    fun decrease(key: Long, quantity: Long) {
        val lock = redissonClient.getLock(key.toString())
        try {
            val available = lock.tryLock(10, 1, TimeUnit.SECONDS)
            if (!available) {
                println("lock 획득 실패")
                return
            }
            stockService.decrease(key, quantity)
        } catch (e: Exception) {
            throw RuntimeException(e)
        } finally {
            lock.unlock()
        }
    }
}

RedissonLcokStockFacade 클래스의 decrease 메서드에 트랜잭션을 걸면 동일한 테스트 케이스가 실패하는데 이유를 알 수 있을까요?

답변 1

0

최상용님의 프로필 이미지
최상용
지식공유자

별천랑님 안녕하세요.

@Transactional 어노테이션을 사용하게 된다면 아래와 같은 매커니즘으로 동작하게 됩니다.

트랜잭션을 시작하고 로직이 정상적으로 종료된다면 커밋, 실행중 오류가 발생한다면 롤백을 하게 됩니다.

@Component
class RedissonLockStockFacadeProxy(
    private val redissonLockStockFacade: RedissonLockStockFacade
    private val transaction: TransactionManager,
) {
    fun decrease(key: Long, quantity: Long) {
        traction.start()
        try {
            redissonLockStockFacade.decrease(key, quantity)
            traction.commit()
        } catch(e: Exception) {
            traction.rollback()
        }
    }
}

그렇기때문에 RedissonLockStockFacade.decrease 안에서 락을 걸고 해제를 한후에 트랜잭션을 커밋하게 됩니다.

그래서 락을 해제하고 트랜잭션을 커밋하기전까지 다른 스레드가 갱신되지 않은 데이터에 접근가능하게 되므로 테스트케이스가 실패하는 것입니다.

별천랑님의 프로필 이미지
별천랑

작성한 질문수

질문하기