처음에는 Redis getFairLock을 사용하여 처리 순서를 보장하려 했으나, 테스트 로그를 통해 순서가 보장되지 않는다고 잘못 판단했다. 이로 인해 FairLock은 성능만 저하시킨다고 생각해 사용을 배제했다.
그러나 서비스 로그를 통해 Redis Fair Lock이 요청 순서를 정확히 보장하고 있었음을 뒤늦게 확인했다.
초기 테스트 코드는 스레드 시작 및 완료 로그를 출력하여 처리 순서를 확인했다.
@Test
@DisplayName("Redis 락 동시성 제어 테스트")
void redisLockConcurrencyTest() throws InterruptedException {
for (int i = 0; i < threadCount; i++) {
int threadId = i;
executorService.submit(() -> {
try {
System.out.println("Thread 시작: " + Thread.currentThread().getId());
Member threadMember = new Member("Test User" + threadId, "test" + threadId + "@example.com",
"password", MemberRole.USER);
memberRepository.save(threadMember);
couponService.issueCoupon(coupon.getId(), new RequestDto(threadMember.getId()), threadMember);
} catch (GlobalException e) {
System.out.println("Thread 예외: " + Thread.currentThread().getId() + ", " + e.getMessage());
} finally {
latch.countDown();
System.out.println("Thread 완료: " + Thread.currentThread().getId());
}
});
}
}
테스트 로그 결과:
Thread 시작: 70
Thread 시작: 63
Thread 시작: 61
...
Thread 완료: 61
Thread 완료: 70
Thread 완료: 63
...
문제점: 이 로그를 기반으로 FairLock이 제대로 동작하지 않는다고 판단했다.
그러나 테스트 코드의 Thread 시작 로그는 실제 락 처리 순서를 반영하지 않았다.
서비스 코드에서 실제로 락을 대기하고 획득하는 부분에 로그를 추가해 검증했다.
@Transactional
public void issueCoupon(Long couponId, RequestDto request, Member member) {
String lockKey = "coupon:" + couponId;
RLock lock = redissonClient.getFairLock(lockKey);
try {
System.out.println("대기중: " + Thread.currentThread().getId());
if (!lock.tryLock(1, 10, TimeUnit.SECONDS)) {
System.out.println("Thread " + Thread.currentThread().getId() + "락 획득 실패.");
throw new IllegalStateException("동시에 너무 많은 요청이 발생했습니다. 잠시 후 다시 시도해주세요.");
}
System.out.println("Thread " + Thread.currentThread().getId() + "락 획득.");
// 비즈니스 로직 처리
Coupon coupon = couponRepository.findById(couponId)
.orElseThrow(() -> new GlobalException(COUPON_NOT_FOUND));
coupon.validateQuantity();
coupon.decreaseQuantity();
Issuance issuance = Issuance.builder()
.member(member)
.coupon(coupon)
.build();
issuanceRepository.save(issuance);
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
System.out.println("Thread " + Thread.currentThread().getId() + " released lock.");
}
}
}
대기 중 로그와 락 획득 및 해제 로그를 비교해 FairLock이 요청 순서를 정확히 보장하고 있음을 확인했다.

대기 중 순서:
63, 71, 65, 70, 62, 61, 66, 64, 68, 67, 103, 98, 139...
락 획득 순서:
70, 63, 61, 65, 64, 71, 62, 66, 68, 67, 103, 98, 139...
분석:
Redis Fair Lock은 요청 순서를 보장:
FairLock은 Redis 대기열을 기반으로 락 획득 순서를 보장한다는 점이 확인됐다.초기 순서 불일치는 자연스러운 현상:
FairLock 사용 유지 결정:
FairLock을 사용하면 성능이 약간 저하될 수 있지만, 요청 순서가 중요한 경우에는 반드시 사용해야 한다는 결론을 내렸다.