본문으로 건너뛰기

[Loki] Partition Ring Shuffle Sharding에 LRU 캐시 도입

PR 링크: grafana/loki#20270 상태: Merged | 변경: +192 / -44

들어가며

Grafana Loki의 partition ring shuffle sharding은 테넌트별로 서브링을 생성하고 캐싱합니다. 기존에는 무제한 map 기반 캐시를 사용하여 테넌트 수가 늘어나면 메모리 사용량이 무한히 증가하는 문제가 있었습니다. 이 PR은 dskit 의존성을 업데이트하여 LRU 기반의 바운디드 캐시 옵션을 도입합니다.

핵심 코드 분석

dskit의 새로운 PartitionRingOptions

type PartitionRingOptions struct {
    // ShuffleShardCacheSize is the size of the cache used for shuffle sharding.
    // If zero or negative, an unbounded map-based cache is used.
    // If positive, an LRU cache with the specified size is used.
    ShuffleShardCacheSize int
}

func DefaultPartitionRingOptions() PartitionRingOptions {
    return PartitionRingOptions{
        ShuffleShardCacheSize: 0,
    }
}

API 변경: NewPartitionRing이 error를 반환

// Before
partitionRing := ring.NewPartitionRing(ring.PartitionRingDesc{...})

// After
partitionRing, err := ring.NewPartitionRing(ring.PartitionRingDesc{...})
require.NoError(t, err)

기존에는 단순 생성자였지만, LRU 캐시 초기화 시 에러가 발생할 수 있으므로 error 반환이 추가되었습니다.

테스트 코드 업데이트

// 기존: panic 없이 바로 사용
partitionRing := ring.NewPartitionRing(partitionRing)

// 변경: error 체크 필수
r, err := ring.NewPartitionRing(partitionRing)
if err != nil {
    panic(err)
}
return r

왜 이게 좋은가

  1. 메모리 바운딩: LRU 캐시를 사용하면 캐시 크기가 설정된 한도를 초과하지 않습니다. 수만 개의 테넌트가 있어도 메모리 사용량이 예측 가능합니다.
  2. DataObj Consumer 지원: 새로운 DataObj consumer가 효율적으로 shard 캐싱을 관리하는 데 필요한 기능입니다.
  3. 하위 호환성: ShuffleShardCacheSize를 0으로 설정하면 기존의 무제한 map 캐시를 그대로 사용하므로, 기존 동작에 영향이 없습니다.

의존성 업데이트이지만, 대규모 멀티테넌트 환경에서 distributor의 메모리 안정성을 크게 개선하는 중요한 변경입니다.

참고 자료

댓글

관련 포스트

PR Analysis 의 다른글