기록하며 성장한다 - 개발, 회고

개발 회고록

[ 개발 회고록 ] 비동기 메서드 동시성 문제 발견 및 수정

전대홍 2023. 12. 4. 09:44

해당 포스팅은 JPA native Query와 Spring Boot를 이용한 CRUD 개발 5편이다.

F-lab 멘토링 과제를 위한 개발을 진행중이며,

멘토님께 피드백을 받으며 개발을 할 때 신경써야 하는 부분을 찾아가고 있다.

 

이전 글 <<< 클릭하기

 

 


멘토님의 피드백을 받고 수정한 부분

1. 동시성 문제 : JMeter로 성능을 테스트 해 본 결과 문제점을 발견하였다. 100개의 스레드까지는 가능하지만, 그 이상 1,000개 10,000개의 스레드를 한 번에 보낼 시 데이터가 누락되는 문제가 생긴 것이다. ( 조회수 ) 그 부분을 해결해보고 다시 테스트 해보라고 하셨다.

 

 


개발 Main 내용

1. 동시성 문제 해결

의외로 간단한 코드 문제였다.

/**
 * Redis 에 조회수 추가 후 반환 ( 비동기로 처리 하는 메서드 )
 */
@Async
@Transactional
@Override
public void incrementRedisBoardView(Long boardId) {
    String redisKey = RedisStringCode.BOARD_KEY_CODE;
    String redisHashKey = String.valueOf(boardId);

    HashOperations<String, Object, Integer> hashOperations = redisTemplate.opsForHash();
    Integer redisView = hashOperations.get(redisKey, redisHashKey);
    if ( redisView != null ) {
        hashOperations.increment(redisKey, redisHashKey, 1);
    } else {
        hashOperations.put(redisKey, redisHashKey, 1);
    }
}

조회수를 증가시키는 로직인데, 처음에는 현재 조회수를 get으로 가져온 뒤에 증가를 시키는 로직이라 거기서 동시성 문제가 생기는 줄 알았다.

왜냐하면 예를들어 현재 조회수가 10인 상태에서 100명이 동시에 접근했을 경우 그 100명한테 현재 조회수가 10인 상태로 넘어가고, 그게 증가되어 100명이 조회했지만 11에서 멈출 수도 있겠다는 생각이 들었다. 그러나 Redis는 싱글스레드인 특성상 get으로 가져와도 문제가 없을 것이라 판단했다.

그럼 어디가 문제였을까?

바로 if else에서 else에 있는 put << 이 부분이었다. 현재 조회수가 들어있는 게시글 key가 없을 경우 put을 통해 만들어주는데, 최초에 들어오는 스레드들이 곂치면서 조회수가 1부터 시작인 스레드들이 많았던 것이다.

이 부분을 해결하기 위해 redis-cli를 참조하였고, increment 라는 코드가.. key가 없을 경우 자동으로 put까지 해주는 것을 알게되었다.

그렇게 수정한 것이 아래 코드이다.

    /**
     * Redis 에 조회수 추가 후 반환 ( 비동기로 처리 하는 메서드 )
     */
    @Async
    @Transactional
    @Override
    public void incrementRedisBoardView(Long boardId) {
        String redisKey = RedisStringCode.BOARD_KEY_CODE;
        String redisHashKey = String.valueOf(boardId);

        HashOperations<String, Object, Integer> hashOperations = redisTemplate.opsForHash();

        hashOperations.increment(redisKey, redisHashKey, 1);
    }

코드가 심플하게 바뀌었다 ㅎㅎㅎ

 

테스트 결과

 

 

80 이라는 Board ID에 1,000이라는 조회수가 정확히 들어갔다. ( 기존에는 100, 200씩 누락됐었다. )

 

 

스레드를 2,000개 날려도 2,000개가 정확하게 잘 들어간다.

 

 

스레드 10,000개를 하였을 때에는 필자 서버가 부하를 못견뎌서 중간에 터지는 일이 발생하였다. 그래서 스레드는 1,000으로 하고 각 스레드별로 10번을 반복 시켜 10,000 으로 만들었다.

 

결과는 성공이었다.