이번에 진행하게된 프로젝트에서 레디스 동시성 이슈에 대한 문제에 부딪혔다.
기존에는 어떤 방식을 사용했고, 어떻게 수정을 했는지에 대해 기록한다.
기존에는 아래와 같은 로직으로 되어 있었다.
아직까지는 큰 문제는 없었다고 하지만, 만약 아래와 같은 상황이 발생한다면 장애로 이어지게 된다.
찾아보니 레디스에서 setnx 명령어를 활용하는 방법이 현재 상황에 가장 알맞아 보였다.
보통은 spin lock 으로 구현을 하는데 (lock 점유를 루프로 시도), 현재 나의 경우는 점유가 되어 있으면 skip 을 하면 되기 때문에 레디스 부하없이 쉽게 해결이 가능했다.
코드는 아래와 같다.
override fun doHandleMessage(message: Message) {
val key = String(message.body)
if (lock(key)) {
try {
// 로직 수행
} finally {
unlock(key)
}
}
}
private fun lock(key: String): Boolean = redisTemplate.opsForValue().setIfAbsent(key, "lock") ?: false
private fun unlock(key: String) = redisTemplate.delete(key)참고로, 스핀락을 구현해야하는 상황이면 Lettuce 라이브러리보단 Redisson 라이브러리를 사용하는게 더 낫다..!