728x90
개요
프로젝트는 종료되었지만, 해당 포스팅에서 다룬 좋아요 동시성 문제를 다시 테스트하려고 합니다.
이전 시나리오에서는 100명의 사용자가 동시에 좋아요 요청을 보냈지만, 이번에는 더 높은 트래픽 상황을 가정하고 테스트를 진행하려고 합니다.
테스트 코드 수정
@SpringBootTest(classes = PostApplication.class)
@Slf4j
public class PostServiceTest {
@Autowired
private PostService postService;
@Autowired
private PostRepository postRepository;
private UUID postId;
@BeforeEach
void setUp() {
AuthUserInfo testUser = AuthUserInfoImpl.builder()
.id(1L)
.build();
Post post = Post.createPostBuilder()
.postRequestDto(new PostRequestDto("LIKE Race condition Test Title", "Test Content"))
.authUserInfo(testUser)
.build();
Post savedPost = postRepository.save(post);
postRepository.flush();
this.postId = savedPost.getPostId();
}
@Test
void loadTest() throws InterruptedException {
int numberOfThreads = 100; // 쓰레드 수
int loopCount = 10; // 루프 카운트
int rampUpTimeSeconds = 1; // Ramp-up 시간(초)
ExecutorService executorService = Executors.newFixedThreadPool(numberOfThreads);
CountDownLatch latch = new CountDownLatch(numberOfThreads * loopCount);
// 성능 측정을 위한 지표들
AtomicInteger successCount = new AtomicInteger(0);
List<Long> responseTimes = Collections.synchronizedList(new ArrayList<>());
long startTime = System.currentTimeMillis();
// Ramp-up을 위한 지연 계산
long delayPerThread = (rampUpTimeSeconds * 1000L) / numberOfThreads;
// 각 쓰레드별 작업 실행
for (int threadNum = 0; threadNum < numberOfThreads; threadNum++) {
final int threadIndex = threadNum;
// Ramp-up을 위한 지연 추가
Thread.sleep(delayPerThread);
executorService.execute(() -> {
for (int i = 0; i < loopCount; i++) {
long requestStartTime = System.nanoTime();
try {
postService.updatePostLikeCount(postId, true);
successCount.incrementAndGet();
// 응답 시간 기록 (밀리초 단위)
responseTimes.add((System.nanoTime() - requestStartTime) / 1_000_000);
} catch (Exception e) {
log.error("Error in thread {}, iteration {}: {}", threadIndex, i, e.getMessage());
} finally {
latch.countDown();
}
}
});
}
// 모든 작업 완료 대기
latch.await();
long endTime = System.currentTimeMillis();
executorService.shutdown();
long totalTime = endTime - startTime;
double avgResponseTime = responseTimes.stream()
.mapToLong(Long::valueOf)
.average()
.orElse(0.0);
double throughput = (successCount.get() * 1000.0) / totalTime;
double successRate = (successCount.get() * 100.0) / (numberOfThreads * loopCount);
// 테스트 결과 출력
log.info("부하 테스트 결과:");
log.info("총 요청 수: {}", numberOfThreads * loopCount);
log.info("성공 요청 수: {}", successCount.get());
log.info("평균 응답 시간: {}ms", String.format("%.2f", avgResponseTime));
log.info("처리량: {} requests/second", String.format("%.2f", throughput));
log.info("성공률: {}%", String.format("%.2f", successRate));
// 최종 좋아요 수 검증
Post post = postService.getPostById(postId);
assertThat(post.getLikes()).isEqualTo(successCount.get());
}
}
이전 코드와 비교하여 이번 코드는 더 높은 트래픽 상황을 시뮬레이션하도록 개선되었습니다. 주요 차이점은 다음과 같습니다.
1. 요청 수 증가 (Loop 사용)
- 이전 코드
- 각 스레드가 단 한 번의 좋아요 요청을 수행했습니다.
- 개선된 코드
- loopCount 변수를 도입하여 각 스레드가 반복적으로 좋아요 요청을 수행하도록 했습니다.
- 각 스레드는 loopCount 값만큼 반복하므로, 총 요청 수가 증가하고 트래픽이 더 높은 상황을 테스트할 수 있습니다.
2. Ramp-up 설정
- 이전 코드
- 모든 스레드가 동시에 시작하며, 단기간 내에 모든 요청이 발생했습니다.
- 개선된 코드
- 스레드별로 시작 간격을 두기 위해 Ramp-up 시간을 설정하고, 각 스레드 시작 전에 delayPerThread만큼 Thread.sleep()으로 지연을 추가했습니다.
- 이는 시스템이 점진적으로 부하를 경험하도록 하여, 실제 높은 트래픽 상황을 더욱 현실적으로 시뮬레이션합니다.
3. 성공 여부 및 성능 측정
- 이전 코드
- 좋아요 수만 검증했으며,
성공률이나응답 시간,처리량을 기록하지 않았습니다.
- 좋아요 수만 검증했으며,
- 개선된 코드
- 성공적인 요청 수를 추적하는 successCount, 요청별 응답 시간을 기록하는 responseTimes 리스트 등을 추가하여 성능 측정을 세부적으로 관리했습니다.
- 결과로 평균 응답 시간(avgResponseTime), 처리량(throughput), 성공률(successRate)과 같은 지표를 계산하고 로그로 출력했습니다.
4. 총 실행 시간 측정 및 부하 테스트 결과 기록
- 이전 코드
총 테스트 시간이 별도로 기록되지 않았습니다.
- 개선된 코드
- startTime과 endTime을 측정하여 총 실행 시간을 계산하고 이를 기준으로 부하 테스트 결과를 기록했습니다.
- 이를 통해 시스템의 응답성과 처리 능력을 구체적으로 분석할 수 있습니다.
2024-10-22 11:00:34.241 [Test worker] INFO com.tk.gg.post.PostServiceTest - 부하 테스트 결과:
2024-10-22 11:00:34.241 [Test worker] INFO com.tk.gg.post.PostServiceTest - 총 요청 수: 1000
2024-10-22 11:00:34.242 [Test worker] INFO com.tk.gg.post.PostServiceTest - 성공 요청 수: 1000
2024-10-22 11:00:34.242 [Test worker] INFO com.tk.gg.post.PostServiceTest - 평균 응답 시간: 59.88ms
2024-10-22 11:00:34.242 [Test worker] INFO com.tk.gg.post.PostServiceTest - 처리량: 478.47 requests/second
2024-10-22 11:00:34.242 [Test worker] INFO com.tk.gg.post.PostServiceTest - 성공률: 100.00%
로그와 데이터베이스를 확인한 결과, 예상한 1000개의 좋아요 요청이 정상적으로 반영되었으며, 성공률 100%와 안정적인 처리량을 기록했습니다.
평균 응답 시간 역시 60ms 미만으로 준수한 성능을 보여주었습니다. 이를 통해 시스템이 고트래픽 상황에서도 안정적으로 동작함을 확인할 수 있었습니다.
'TIL,일일 회고' 카테고리의 다른 글
[TIL, 일일 회고] 2024.11.05 - 왜 N+1 문제에서 Fetch Join을 주로 사용할까❓ (0) | 2024.11.05 |
---|---|
[TIL, 일일 회고] 2024.11.04 - 코딩 관련 기초 지식 (등차수열) (0) | 2024.11.04 |
[TIL, 일일 회고] 2024.11.03 - GlowGrow 프로젝트: 쿠폰 유형에 따른 맞춤 정산 로직 도입기 (2) | 2024.11.03 |
[TIL, 일일 회고] 2024.11.02 - JWT 인증을 위한 Swagger 설정방법 (0) | 2024.11.02 |
[TIL, 일일 회고] 2024.11.01 - springdoc-openapi-ui (0) | 2024.11.01 |