-
Notifications
You must be signed in to change notification settings - Fork 4
부하 테스트를 통한 메일수신 및 DB저장, FCM전송까지의 병목 지점을 찾고 성능 개선 #108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…치처리 및 캐시 업데이트 비동기처리 전환
anaconda77
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
성능 개선에 대한 지식이 적어 변경 사항 이해하는데 초점을 맞추다 보니 피드백 드릴 여지가 많이 부족하네요..배치 및 병렬 처리로 I/O 병목을 개선한 덕에 유의미한 성능 개선 지표가 나와 인상깊게 보았습니다!
| } catch (Exception e) { | ||
| log.error("Cache update ERROR for key '{}': {}", entry.getKey(), | ||
| e.getMessage()); | ||
| private void updateRedisCacheAsync(Map<String, String> toCache) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
캐시 업데이트를 비동기적으로 수행하도록 변경하였다고 하셨는데, 순차적으로 처리되는 다음 배치 작업 이전에는 캐시가 업데이트 되는건가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
100프로 보장하지는 않습니다..! 비동기의 한계가 아닌가 싶네요 😢
| private void processSingleNotificationFast(FcmNotification fcmNotification, | ||
| AtomicInteger successCount, AtomicInteger failCount, AtomicInteger invalidTokenCount) { | ||
|
|
||
| if (!fcmNotification.isValid()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
invalid한 토큰 값이 정확히 무엇인지에 대한 로그를 남길 필요는 없나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
로그를 정리하는 과정에서 실수로 삭제한 것 같네요. 다시 로그 추가하였습니다 👍
| long duration = System.currentTimeMillis() - startTime; | ||
| logBatchSummary(fcmNotifications.size(), result, duration); | ||
| CompletableFuture.allOf(futures) | ||
| .get(BATCH_TIMEOUT_SECONDS, TimeUnit.SECONDS); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
get 메서드의 역할은 모든 작업 결과를 기다리는 것인가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
네네 맞습니다. 비동기긴 하지만 일정 시간동안 대기하지 않고 트랜잭션이 끝나버린다면 fcm 전송 자체가 안될수도 있기 때문에 사전 설정한 시간만큼은 블로킹합니다!
| FcmSenderOrchestrator fcmSenderOrchestrator) { | ||
| super(redisTemplate, reactiveRedisTemplate, batchConfig, optionalObjectMapper); | ||
| this.fcmSenderOrchestrator = fcmSenderOrchestrator; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
실제 소비자가 FCM을 전송하는 로직은 순차적이라 1000개중 5~600개의 FCM 전송 이후 Redis 타임아웃이 빈번하게 발생하였다.
이 이유는 아티클 배치 처리부터 fcm 전송까지의 전체 흐름에서, fcm 순차 전송 과정의 병목 때문에 시간이 많이 소요되어 Redis와의 연결이 끊어졌다고 이해하면 될까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 정확합니다! 병목으로 인해 500ms로 설정된 타임아웃이 걸려 1000ms로 늘려도 걸리는 경우가 있더라구요
현재 방식으로는 타임아웃이 걸리지 않습니다 :)
…Refactor/issue-#107
…s stream 소비를 위한 로직 추가
변경사항article 저장 로직 배치단위 원자성 부여로 ack 처리 개선 및 멱등성 보장기존 saveAll 메서드, 배치 저장 프로세서의 try-catch문을 제거하여 배치사이즈의 저장 로직의 원자성을 부여, exception이 발생 시 ack를 보내지 않아 Pending Entity List에 남아있도록 한다 비동기 배치처리 재시작시 failover 처리비동기 배치처리를 재시작할 경우 Pending Entity List에 남아있는 redis Stream이 있는지 우선적으로 확인하고 있을경우 consumer가 소비하게 하여 failover처리가 가능하도록 구현 |
개요
부하 테스트를 통한 메일수신 및 DB저장, FCM전송까지의 병목 지점을 찾고 성능 개선
배경
일반적인 순차 동기 처리방식으로 메일 수신 및 DB저장, FCM전송의 전 과정을 수행함에 있어서 부하 테스트를 진행해보니 평균 3.2초, P95 6.6초, 실패율 2%의 성능을 보였다.
이를 개선하기 위해 비동기 배치처리를 도입하였고, 이에 대한 부하테스트를 진행하니 성공율 100%를 기록하였지만 FCM을 배치단위로 발행만 하고 실제 소비자가 FCM을 전송하는 로직은 순차적이라 1000개중 5~600개의 FCM 전송 이후 Redis 타임아웃이 빈번하게 발생하였다.
또한, Redis를 활용한 중복처리단과 캐시 업데이트단에서도 병목 현상이 일어나 성능을 악화시킴을 확인했다.
따라서, 피크 부하 상황에서 사용자 경험 저하 및 시스템 불안정성을 극복하기 위한 성능 개선이 필요했다.
변경된 점
완전 비동기 아키텍처 도입
Article 중복 처리 조회 Mutil Get 적용
FCM 병렬 처리 구현
동시성 안전 로깅 시스템
Redis 캐시 비동기 업데이트
참고자료
순차 동기처리 부하테스트 결과 (동시 1000회 요청)

기존 비동기 배치처리 부하테스트 결과 (동시 1000회 요청, Redis 타임아웃 발생)


최종 비동기 배치처리 부하테스트 결과 (동시 1000회 요청)

성능 개선 결과
k6 부하 테스트 비교
동기 처리 방식:
최종 비동기 배치 방식:
리소스 효율성 개선
CPU 사용률:
메모리 사용 최적화:
네트워크 최적화:
관련 이슈
close #107