작성
·
181
·
수정됨
0
현재 강의 내용을 바탕으로 OptimisticLock 을 구현중인데,
version이 증가하지 않는 이유를 알고 싶습니다.
도메인은 이렇습니다.
@Entity
@Table(name = "appointment")
public class Appointment extends BaseEntity {
...
@OneToMany(mappedBy = "appointment")
private List<AppointmentUser> appointmentUsers = new ArrayList<AppointmentUser>();
@Version
private Long version;
그리고, 레포지토리는 이렇습니다.
public interface AppointmentRepository extends JpaRepository<Appointment, Long> {
...
@Lock(LockModeType.OPTIMISTIC)
@Query("SELECT au FROM Appointment a " +
"JOIN a.appointmentUsers au " +
"WHERE au.id = :appointmentUserId ")
AppointmentUser findByIdWithOptimisticLock(Long appointmentUserId);
}
그리고, 서비스 계층 메서드는 이렇습니다.
@Transactional(readOnly = true)
@Service
public class AppointmentUserService {
...
@Transactional
public void updateAuthority(Long appointmentId, Long loginMemberId, Long targetMemberId) {
Member loginMember = memberRepository.getById(loginMemberId);
Member targetMember = memberRepository.getById(targetMemberId);
Appointment appointment = appointmentRepository.getById(appointmentId);
AppointmentUser loginAppointmentUser = appointmentUserRepository.getByMemberAndAppointment(loginMember, appointment);
AppointmentUser targetAppointmentUser = appointmentUserRepository.getByMemberAndAppointment(targetMember, appointment);
validateIsAdminMember(loginAppointmentUser.getId());
MemberAuthority targetAuthority = targetAppointmentUser.getMemberAuthority();
targetAppointmentUser.updateAuthority(MemberAuthority.getAnotherAuthority(targetAuthority));
appointmentUserRepository.save(targetAppointmentUser);
}
private void validateIsAdminMember(Long loginAppointmentUserId) {
if (appointmentRepository.findByIdWithOptimisticLock(loginAppointmentUserId).getMemberAuthority() != MemberAuthority.ADMIN) {
throw new NotAdminMemberException();
}
}
}
위 updateAuthority 메서드 내에서 validateIsAdminMember를 호출하여, validateIsAdminMember 안에 있는 findByIdWithOptimisticLock을 통하여 Version을 올려주어서 낙관적 락을 성공적으로 구현할 수 있을 줄 알았는데 테스트를 해보니 아래와 같이 실패합니다..
@Test
void 동시성_테스트() throws InterruptedException {
Logger logger = Logger.getLogger(MultiThreadTest.class.getName());
ExecutorService executorService = Executors.newFixedThreadPool(2);
CountDownLatch latch = new CountDownLatch(2);
AtomicReference<Boolean> flag = new AtomicReference<>(false);
executorService.execute(() -> {
try {
logger.log(Level.INFO, "첫 번째 요청 시작: member1 -> member2");
logger.log(Level.INFO, "버전1전" + appointmentRepository.getById(1L).getVersion());
appointmentUserService.updateAuthority(1L, 1L, 2L);
logger.log(Level.INFO, "버전1완" + appointmentRepository.getById(1L).getVersion());
logger.log(Level.INFO, "첫 번째 요청 완료: member1 -> member2");
} catch (Exception e) {
logger.log(Level.SEVERE, "첫 번째 요청 중 예외 발생", e);
flag.set(true);
} finally {
latch.countDown();
}
});
executorService.execute(() -> {
try {
logger.log(Level.INFO, "두 번째 요청 시작: member2 -> member1");
logger.log(Level.INFO, "버전2전" + appointmentRepository.getById(1L).getVersion());
appointmentUserService.updateAuthority(1L, 2L, 1L);
logger.log(Level.INFO, "버전2완" + appointmentRepository.getById(1L).getVersion());
logger.log(Level.INFO, "두 번째 요청 완료: member2 -> member1");
} catch (Exception e) {
logger.log(Level.SEVERE, "두 번째 요청 중 예외 발생", e);
flag.set(true);
} finally {
latch.countDown();
}
});
latch.await();
};
위와 같이, 두 개의 스레드에서 진행을 하였고, 각 스레드 내에서 updateAuthority()를 실행 전후에 version을 찍어보았으나, 찍힌 4개의 version 모두 0이 나왔고 또한, 동시성 제어도 안되는 상황입니다.
왜 0이 나오는걸까요.. 궁금합니다!
(컴파일 에러는 없습니다)
답변 2
1
0
안녕하세요, 인프런 AI 인턴입니다. 한수현님의 질문과 유사한 내용을 다룬 게시글이 있어, 도움이 될 만한 자료를 공유드립니다.
한수현님께서 낙관적 락(Optimistic Lock) 구현 중에 @Version
이 증가하지 않는 문제에 부딪혔군요. 낙관적 락을 올바르게 사용하기 위해선 몇 가지 확인해야 할 사항들이 있습니다. 가장 중요한 것은, 실제 데이터베이스 엔티티에 변경사항이 발생하고, 그 변경사항이 데이터베이스에 반영될 때 @Version
필드가 올바르게 증가하는지 여부입니다.
이 게시물에서는 낙관적 락이 무한 루프에 돌게 되는 상황과 그 해결법에 대해서 설명하고 있습니다. 비록 정확히 @Version
컬럼이 증가하지 않는 문제에 대한 직접적인 해결법을 제시하지 않지만, 낙관적 락과 관련된 동작 원리와 주의사항들을 이해하는 데에 큰 도움이 될 것입니다.
@Version
필드는 엔티티가 데이터베이스에 성공적으로 반영될 때 자동으로 증가해야 합니다. 이 과정에서 트랜잭션 관리가 올바르게 이루어져야 합니다. 예를 들어, 동일한 트랜잭션 내에서 반복적인 조회와 변경 작업이 이루어질 경우 예상치 못한 문제가 발생할 수 있습니다.저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
감사합니다!
혹시나 문제가 발생한다면 질문 남겨주세요!