[Grafana Loki] JSON 파서에서 bytes.Runes() 할당을 in-place UTF-8 디코딩으로 제거
PR 링크: grafana/loki#20602 상태: Merged | 변경: +130 / -32
들어가며
Grafana Loki의 쿼리 엔진 워커에서 JSON 로그 라인을 파싱할 때, appendSanitized 함수는 키 바이트 슬라이스를 정제하기 위해 bytes.Runes()를 호출합니다. 이 함수는 매번 새로운 []rune 슬라이스를 할당하는데, 프로파일링 결과 이것이 전체 할당의 상당 부분을 차지했습니다. 이 PR은 in-place UTF-8 디코딩으로 교체하고, 추가로 JSON 파서 재사용과 요청 키 프리필터링 최적화를 적용합니다.
핵심 코드 분석
Before: bytes.Runes()로 새 슬라이스 할당
func appendSanitized(to, key []byte) []byte {
// ...
for _, r := range bytes.Runes(key) { // 매번 새 []rune 슬라이스 할당
if (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') && r != '_' && (r < '0' || r > '9') {
to = append(to, jsonSpacer)
continue
}
to = append(to, key...)
}
return to
}
After: utf8.DecodeRune으로 in-place 디코딩
func appendSanitized(to, key []byte) []byte {
// ...
for i := 0; i < len(key); {
r, size := utf8.DecodeRune(key[i:]) // 할당 없이 in-place 디코딩
i += size
if (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') && r != '_' && (r < '0' || r > '9') {
to = append(to, jsonSpacer)
continue
}
to = append(to, key[i-size:i]...)
}
return to
}
추가 최적화: 불필요한 중첩 객체 탐색 프리필터링
func (j *jsonParser) shouldProcessNextKey(key []byte, requestedKeyLookup map[string]struct{}) bool {
j.prefixBuffer = append(j.prefixBuffer, key)
sanitized := j.buildSanitizedPrefixFromBuffer()
return shouldExtractPrefix(sanitized, requestedKeyLookup)
}
func shouldExtractPrefix(prefix []byte, requestedKeyLookup map[string]struct{}) bool {
if len(requestedKeyLookup) == 0 {
return true
}
for l := range requestedKeyLookup {
if strings.HasPrefix(l, string(prefix)) {
return true
}
}
return false
}
왜 이게 좋은가
- 할당 제거:
bytes.Runes()는 매번 새 슬라이스를 할당하지만,utf8.DecodeRune()은 기존 바이트 슬라이스 위에서 직접 동작하여 할당이 없다. - JSON 파서 재사용:
newJSONParser()를 라인마다 생성하지 않고 한 번 생성하여 재사용한다. - requestedKeys 룩업 최적화: 요청된 키 목록을
map[string]struct{}로 변환하는 작업을 라인마다 반복하지 않고 한 번만 수행한다. - 불필요한 중첩 탐색 차단:
shouldProcessNextKey로 요청된 키 접두사에 해당하지 않는 중첩 객체를 탐색하지 않아 CPU 시간을 절약한다.
참고 자료
관련 포스트
PR Analysis 의 다른글
- 이전글 [pytest] 캐시 디렉터리 생성 로직 단순화 — 원자적 생성 함수 추출
- 현재글 : [Grafana Loki] JSON 파서에서 bytes.Runes() 할당을 in-place UTF-8 디코딩으로 제거
- 다음글 [Open WebUI] SCIM 그룹 변환에서 N+1 쿼리를 배치 조회로 제거
댓글