본문으로 건너뛰기

[Grafana Loki] Allocator에 동시 접근 감지를 추가하여 메모리 안전성 확보

PR 링크: grafana/loki#20598 상태: Merged | 변경: +64 / -1

들어가며

Grafana Loki의 pkg/memory 패키지는 pkg/dataobj에서 사용하는 arena 스타일 메모리 Allocator를 제공합니다. 이 Allocator는 고루틴 안전(goroutine-safe)하지 않으며, Allocator를 사용하는 모든 것도 마찬가지입니다. 동시 접근이 발생하면 미묘한 메모리 손상 버그가 발생할 수 있어 디버깅이 매우 어렵습니다. 이 PR은 atomic 변수를 사용하여 동시 접근을 감지하고 즉시 panic을 발생시킵니다.

핵심 코드 분석

Before: 동시 접근에 대한 보호 없음

type Allocator struct {
    parent  *Allocator
    regions []*Region
    avail   bitmap
    used    bitmap
}

func (alloc *Allocator) Allocate(size int) *Region {
    // 동시 접근 시 데이터 레이스 - 미묘한 메모리 손상
    for i := range alloc.avail.IterValues(true) {
        // ...
    }
}

After: atomic CAS로 동시 접근 감지

type Allocator struct {
    locked atomic.Bool  // 동시 접근 감지용

    parent  *Allocator
    regions []*Region
    avail   bitmap
    used    bitmap
}

func (alloc *Allocator) lock() {
    if !alloc.locked.CompareAndSwap(false, true) {
        panic(errConcurrentUse)
    }
}

func (alloc *Allocator) unlock() {
    if !alloc.locked.CompareAndSwap(true, false) {
        panic(errConcurrentUse)
    }
}

func (alloc *Allocator) Allocate(size int) *Region {
    alloc.lock()
    defer alloc.unlock()
    // 안전하게 접근
    for i := range alloc.avail.IterValues(true) {
        // ...
    }
}

Allocate, Trim, Reclaim, AllocatedBytes, FreeBytes 등 모든 공개 메서드에 lock/unlock 가드가 적용되었습니다.

왜 이게 좋은가

  • 빠른 실패(Fail Fast): 동시 접근 시 미묘한 메모리 손상 대신 명확한 panic 메시지로 즉시 문제를 발견합니다.
  • 뮤텍스 대비 경량: sync.Mutex 대신 atomic.Bool의 CAS 연산을 사용하여 정상 경로에서의 오버헤드가 최소화됩니다.
  • 미래 확장 고려: 향후 동시 할당이 필요한 사용 사례가 나타나면 뮤텍스로 교체할 수 있다고 PR 설명에 명시했습니다.
  • 테스트 포함: 정상적인 lock/unlock과 이중 lock 시 panic 발생을 검증하는 테스트가 함께 추가되었습니다.

참고 자료

댓글

관련 포스트

PR Analysis 의 다른글