pprof 적용은
pprof로 GC 튜닝하기를 참고 해주세요
비즈니스 요구사항에 따른 서비스를 수행하는 서비스 서버를 "서비스 서버"라 칭합니다
서비스 서버에서는 매번 단 건의 HTTP Request로 서버내에서 발생한 이벤트를 발행하고 있었습니다
비동기로 HTTP 요청을 수행하고는 있었지만, HTTP 요청 수에 비례하여 Batch Loader 응답지연 시간이 리니어하게 증가하고 있었습니다.
Interval Duration
에 따라 buffer를 모아 요청하기에 큐잉으로 인한 레이턴시 증가는 해결됨.Batch Loader 서버와는 느슨한 결합을 가지도록 공통 라이브러리를 구축하는 의사 결정 완료
공통 라이브러리에서 이벤트 버퍼를 모아 요청을 하는 모듈을 "Batch Processor"라 칭합니다.
goroutine
생명주기를 제어 가능해야 함여러개의 worker들이 각각의 Interval Duration
동안 내부적인 버퍼를 채워 동기적으로 API로 요청하는 방안
Interval Duration
주기로 Worse Case Worker Pool 수 만큼 HTTP Request 발생Stop the World
로 인한 서비스 서버의 지연 가능성 고려두가지 비교군 중, 공통 라이브러리를 구성하는 것에 있어 사용성이 보장되어야 하기에, 2안이 적합한 방안이라고 생각되었습니다.
하지만 deep copy
로 인한 CPU / memory 상의 오버헤드가 어느정도이고, 영향도가 없을지 검증이 필요했습니다.
deepCopy()
로 인한 메모리 부하 확인deep copy 코드
func deepCopy[T any](src []T) []T { if src == nil { return nil } dst := make([]T, len(src)) copy(dst, src) return dst }
10, 100개의 Worker Pool + Sync IO 방안과 하나의 Worker + Async IO 방안의 테스트를 진행합니다.
각 비교군은 CPU Profile을 활성화하여 docker image를 빌드합니다.
curl {endpoint}/debug/pprof/heap\?seconds\=30 --output {output_path}
처리된 개수는 어떻게 세나요?
API send() 메서드 중 로깅을 통해 확인
func (q *BatchProcessor) send(payload []byte, traceIDs []string) { ... response, err := q.client.Do(request) ... q.logger.Info().Int("count", len(traceIDs)).Send() }
count를 파싱하여 stop 이전에 처리된 개수를 확인 합니다
[e.g.] logfile 예시
- log file ( sum = ... + 1020 + 1000 )
... 2024-10-14T05:11:06Z INF count=1020 2024-10-14T05:11:07Z INF count=1000 2024-10-14T05:11:07Z INF stopping BatchProcessor, waiting for remaining events to be sent func=BatchProcessor.Stop 2024-10-14T05:11:07Z INF count=288
selectgo : select 구문 중 여러 채널에서 데이터를 동시에 기다리면서 그 중 하나가 준비되면 해당 작업을 수행하는 기능
- 채널 1: queue 에서 Pop()하여 stack buffer에 적재, buffer maxlimit을 초과하는 경우 API 전송 및 buffer Flush
- 채널 2:
time.Ticker
일정 시간 마다 내부 버퍼 스택으로 API 전송 및 Flush- 채널 3: 종료 시그널을 받아, 남아있는 버퍼 스택을 API 전송 및 고루틴 종료
runWithSync()
분석
runWithSync()
분석
run()
분석
run()
/ runWithSync()
함수가 실행되는 동안 수행되는 DeepCopy에 초점을 맞추어 프로파일링
deepCopy() 로 인한 오버헤드 없음
총 12.21MB의 run 과정 중 내부 버퍼를 쓰는 작업인 Write를 제외한 오버헤드를 산정
분류 | 분당 처리량 | CPU 오버헤드 | 메모리 오버헤드 |
---|---|---|---|
10 worker | 83,663 | 66% | 0% |
100 worker | 84,042 | 85% | 0% |
1 worker + async | 119,720 | 50% | 1.22% |