Case
Stomp를 이용한 채팅 구현시 Spring Data Jpa의 save를 통해 데이터 저장후
SimpMessageSendingOperations convertAndSend 메소드를 통한 메세지 발행시 저장된 데이타를 Front에서 조회시 간헐적으로 못가져오는 이슈
FLOW
1. [Front] 채팅서버에 Join 메세지 전송
2. [채팅서버] Join 정보 저장 및 접속 유저에 Join 메세지 발행
3. [Front] Join 정보 구독 발생에 따른 join 정보 채팅창에 메세지 처리
4. [Front] Join 정보 Api 호출 을 통한 접속자 부가 정보 화면에 노출
소스 코드
@Transactional
fun joinByRoom(@Payload message: ChatMessageDto) {
val chatRoom:ChatRoom = chatRoomRepository.findByIdOrNull(message.roomId!!)?: return
if (chatRoom.isMe(message.userIdx!!)) {
chatRoom.updateRegUserHeartBeat()
}
message.registerDate = LocalDateTime.now(ZoneId.of("Asia/Seoul"))
message.message = "${message.nickName} 님이 참여 하였습니다"
message.nickName = CHAT_MANAGER
roomChatMessageRepository.save(chatMessageMapper.toEntityRoomChat(message))
val chatRoomTimeStamp:ChatRoomTimeStamp? = chatRoomTimeStampRepository.findByRoomIdxAndUserIdx(message.roomId!!,message.userIdx!!)
if (chatRoomTimeStamp == null) {
chatRoomTimeStampRepository.save(ChatRoomTimeStamp(
userIdx = message.userIdx
,roomIdx = message.roomId!!))
} else {
chatRoomTimeStamp.join()
}
messagingTemplate.convertAndSend("/subscribe/chat/room/${message.roomId}" ,message)
}
원인 (추측)
joinByRoom 메소드 소스 흐름상 chatRoomTimeStampRepository.save 나 chatRoomTimeStamp.join 보다
messagngTemplate.convertAndSend가 뒤에 있으나 JPA 흐름상 메소드가 실행후 끝나는 시점의 @Transaction commit이
발동하여 연속성 컨텍스트의 변경감지 수행 하여 DB에 반영하기 때문에 흐름상 프론트에서 먼저 구독이 발생하여 api를 호출해도
변경되기전 정보를 불러오는 경우가 간헐적으로 발생 하였다.
해결
위코드에서 flush를 먼저 호출하여 디비에 반영하도록 처리 하여 DB Transaction 원칙중 Isolation(고립성)을 부여하였다.
JPA 선점장금 기능 이용
변경된 소스코드
@Transactional
fun joinByRoom(@Payload message: ChatMessageDto) {
val chatRoom:ChatRoom = chatRoomRepository.findByIdOrNull(message.roomId!!)?: return
if (chatRoom.isMe(message.userIdx!!)) {
chatRoom.updateRegUserHeartBeat()
}
message.registerDate = LocalDateTime.now(ZoneId.of("Asia/Seoul"))
message.message = "${message.nickName} 님이 참여 하였습니다"
message.nickName = CHAT_MANAGER
roomChatMessageRepository.save(chatMessageMapper.toEntityRoomChat(message))
val chatRoomTimeStamp:ChatRoomTimeStamp? = chatRoomTimeStampRepository.findByRoomIdxAndUserIdx(message.roomId!!,message.userIdx!!)
if (chatRoomTimeStamp == null) {
chatRoomTimeStampRepository.save(ChatRoomTimeStamp(
userIdx = message.userIdx
,roomIdx = message.roomId!!))
} else {
chatRoomTimeStamp.join()
}
//#################추가된 코드 ###################
chatRoomTimeStampRepository.flush()
//#############################################
messagingTemplate.convertAndSend("/subscribe/chat/room/${message.roomId}" ,message)
}
참고 : Transaction 원칙
https://ko.wikipedia.org/wiki/ACID
ACID - 위키백과, 우리 모두의 백과사전
다른 뜻에 대해서는 애시드 문서를 참고하십시오. ACID(원자성, 일관성, 고립성, 지속성)는 데이터베이스 트랜잭션이 안전하게 수행된다는 것을 보장하기 위한 성질을 가리키는 약어이다. 짐 그
ko.wikipedia.org
참고 : isolation 예
고립성이란 하나의 트랜잭션이 실행하는 도중에 변경한 데이터는 이 트랜잭션이 완료될 때까지 다른 트랜잭션이 참조하지 못하게 하는 특성입니다. 하나의 트랜잭션이 A라는 계좌에서 작업을 하고 있다면, 다른 트랜잭션이 A계좌에 대해 참조하거나 관여 할 수 없고 작업이 끝날 때까지 대기하거나 해야합니다.
'BackEnd > spring' 카테고리의 다른 글
표현 영역 , 응용역역의 값 검증 논의 (0) | 2022.04.10 |
---|---|
도메인 구현과 DIP (0) | 2022.04.08 |
Reactive Kafka in Spring Kafka (0) | 2022.03.16 |
[springbatch]-[Error] pxc_strict_mode = ENFORCING (0) | 2021.11.30 |
@JsonSetter (0) | 2021.07.10 |