2024.01.22 - [항해99/[항해99] WIL] - [항해99] WIL(7) - Spring Project
[항해99] WIL(7) - Spring Project
이번 주에 한 일 Redis의 redisson을 이용한 락 구현 redisson의 락은 기본적으로 분산락이다. 처음에 락을 구현했을 때는 락을 구분하는 키인 LockKey의 값을 모두 같은 값으로 주고 만들었는데, 서비스
mingtian-chan.tistory.com
오늘은 저번 WIL 에서 하고자 했던 내용을 구현해보았으니, 저번 WIL이 궁금하다면 위 링크로!
오늘 한 일
Lock의 구현을 좀 더 다듬었다.
지난 주까지는 "같은 엔티티 끼리" 같은 락을 소지했는데, 그렇게 된다면, A보드에 있는 컬럼을 수정하는 중에 A보드가 삭제된다면 문제가 생길 것 같다는 생각에 계층적으로 락을 구현하면 어떨까 ? 라는 생각을 했다.
그래서 최대한 많은 상황에서 동시성의 문제가 생길 경우를 생각해보며 만들어야 할 것을 짜봤는데,

엄청 많았다.. 팀원들에게 피드백을 받았는데, 보드의 경우는 작성자만 수정 , 삭제를 할 수있으므로 따로 락으로 관리하지 않아도 괜찮다고 했다.
마찬가지로 댓글도 작성자만 수정, 삭제를 할 수 있으므로 따로 락으로 관리하지 않아도 괜찮다고 해서 두 엔티티는 락을 뺐다.
또한, 생성에서는 Id를 생성하는 과정이므로, Id가 같은 엔티티가 없을 것이니, 자기자신의 Id는 락으로 걸 필요가 없다.

기존 updateCol
public ColResponseDto updateCol(Long boardId, Long columnId, ColRequestDto requestDto) {
String lockKey = "ColLock";
RLock lock = redissonClient.getLock(lockKey);
try {
checkTryLock(lock);
Board board = boardService.findBoard(boardId);
Col col = findCol(columnId);
if (!col.getBoard().getId().equals(boardId)) {
throw new IllegalArgumentException(ErrorMessage.ID_MISMATCH_ERROR_MESSAGE.getErrorMessage());
}
col.setColName(requestDto.getColName());
return new ColResponseDto(colRepository.save(col));
} finally {
lock.unlock();
}
}
private void checkTryLock(RLock lock) {
if (!lock.tryLock()) {
throw new RuntimeException(ErrorMessage.LOCK_NOT_ACQUIRED_ERROR_MESSAGE.getErrorMessage());
}
}
수정한 updateCol
public ColResponseDto updateCol(Long boardId, Long columnId, ColRequestDto requestDto) {
RLock boardLock = boardService.createBoardLock(boardId);
RLock colLock = createColLock(columnId);
try {
Board board = boardService.findBoard(boardId);
Col col = findCol(columnId);
if (!col.getBoard().getId().equals(boardId)) {
throw new IllegalArgumentException(ErrorMessage.ID_MISMATCH_ERROR_MESSAGE.getErrorMessage());
}
col.setColName(requestDto.getColName());
return new ColResponseDto(colRepository.save(col));
} finally {
colLock.unlock();
boardLock.unlock();
}
}
public RLock createColLock(Long columnId) {
String lockKey = "ColLock"+ columnId.toString();
RLock lock = redissonClient.getLock(lockKey);
if (!lock.tryLock()) {
throw new RuntimeException(ErrorMessage.LOCK_NOT_ACQUIRED_ERROR_MESSAGE.getErrorMessage());
}
return lock;
}
락을 만든 다음, 락을 체크하는 과정을 밖으로 뺐는데, 락을 만드는 과정도 밖으로 뺄 수 있다고 생각해서 체크하고 만드는 과정까지 따로 뺴서 함수를 만들었다.
컬럼의 경우 컬럼의 수정, 삭제중에 소속된 보드가 삭제되면 안되므로, 보드서비스를 주입받은 보드서비스 객체로 보드 락을 만들어주었다.
락을 거는 순서는 큰 락부터 순서대로 걸어서 들어가고, 락을 푸는 과정은 락을 건 반대순서로 락을 해제해주었다.
그리고 락의 키를 각 id값을 받아서 boardId가 1인 boardLock이라면 boardLock1 이런 식으로 락을 만든다면 A보드와 B보드가 있을 때 서로 상관없으니까 과도하게 락을 넣어서 생기는 문제도 어느정도 막을 수 있을것이라고 생각했다.
깃허브 레포
https://github.com/hanghae99Trello/hanghae99Trello
회고
매니저님께 락 관련해서 이렇게 하는건 어떨까요? 하고 질문을 꺼냈는데, 이정도로 깊게 생각하는 것은 오버엔지니어링이다. 다른 것에 신경쓸 일도 있을텐데 이걸로만 너무 시간을 많이 쓰는게 아니냐 라는 충고를 듣고, 반성했다.
힘내자 명찬.
'항해99 > [항해99] Spring' 카테고리의 다른 글
[Spring] 24.01.25 - Spring Project 완료! (0) | 2024.01.27 |
---|---|
[Spring] 24.01.18 TIL (0) | 2024.01.19 |
[Spring] 24.01.15 - TIL (0) | 2024.01.16 |
[Spring] 게시판 만들기 (0) | 2024.01.04 |