사용자가 게시판을 생성할 때, 개수의 제한을 5개로 제한하고 짧은 시간 내에 많은 수의 게시판을 생성하려고 시도할 때 DB에 어떻게 저장되는지 동시성 테스트를 해봤습니다.
public void createBoard(BoardDto boardDto) {
Long userId = jwtUtil.getUserId();
if(getBoardUserSize(userId) > 5) {
throw new IllegalArgumentException("보드 생성 제한 5개가 넘습니다.");
}
User user = findUser(userId);
Board board = new Board(boardDto, userId);
BoardUser boardUser = new BoardUser(board, user);
boardRepository.save(board);
boardUserRepository.save(boardUser);
}
jMeter로 1000개의 쓰레드를 보내 테스트 해봤습니다.
DB에 저장된 모습을 보면 5개가 아닌, 10개가 저장된 모습을 볼 수 있습니다.
짧은 시간 내에 많은 요청을 보내게 되어 유저가 게시판을 생성하기 전 게시판의 개수를 조회하고, 생성하여 DB에 commit을 하기 전에 다른 요청이 DB를 조회하여 개수가 5개 전이라고 판별하기 때문에 5개보다 많이 생성된 모습을 확인할 수 있습니다.
간단하게는 method에 syncronized를 붙일 수 있습니다.
public syncronized void createBoard(BoardDto boardDto) {
...
}
syncronized를 붙이게 되면 단일 스레드로 동작하기 때문에 여러 스레드에서 요청이 들어와도 하나의 스레드만 접근 가능하기 때문에 데이터의 유효성을 지킬 수 있습니다.
하지만 syncronized는 근본적인 해결책이 될 수 없습니다.
실제로는 서버가 1개가 아닌 2대 이상을 사용하게 되는데 데이터에 대한 접근을 막을 수가 없게 됩니다.
이 문제를 Lock을 사용하여 해결할 수 있습니다.