본문으로 건너뛰기

[Grafana Loki] 쿼리 옵티마이저를 bottom-up에서 top-down 방식으로 리팩터링하여 중복 작업 제거

PR 링크: grafana/loki#19581 상태: Merged | 변경: +691 / -722

들어가며

Grafana Loki의 물리 플랜 옵티마이저는 DAG(방향 비순환 그래프)를 순회하며 최적화 규칙을 적용합니다. 기존 구현은 bottom-up 방식으로 각 노드를 방문할 때마다 규칙의 apply 메서드를 호출했습니다. 그러나 규칙들은 내부적으로 top-down 전파를 수행하므로, 동일한 변환이 여러 번 중복 적용되거나 결과를 예측하기 어려웠습니다. 이 PR은 apply를 루트 노드에서 한 번만 호출하고, 각 규칙이 스스로 대상 노드를 수집하도록 리팩터링합니다.

핵심 코드 분석

Before: 각 노드에서 개별 규칙 적용

// apply는 DAG의 각 노드에서 호출됨
func (r *predicatePushdown) apply(node Node) bool {
    switch node := node.(type) {
    case *Filter:
        for i := 0; i < len(node.Predicates); i++ {
            if ok := r.applyPredicatePushdown(node, node.Predicates[i]); ok {
                // ...
            }
        }
    }
    return false
}

After: 루트에서 시작하여 대상 노드를 수집 후 적용

// apply는 루트 노드에서만 한 번 호출됨
func (r *predicatePushdown) apply(root Node) bool {
    // 대상 노드를 먼저 수집
    nodes := findMatchingNodes(r.plan, root, func(node Node) bool {
        _, ok := node.(*Filter)
        return ok
    })

    changed := false
    for _, n := range nodes {
        filter := n.(*Filter)
        for i := 0; i < len(filter.Predicates); i++ {
            if !canApplyPredicate(filter.Predicates[i]) {
                continue  // 적용 가능 여부를 먼저 체크
            }
            if ok := r.applyToTargets(filter, filter.Predicates[i]); ok {
                changed = true
                filter.Predicates = slices.Delete(filter.Predicates, i, i+1)
                i--
            }
        }
    }
    return changed
}

왜 이게 좋은가

  1. 중복 규칙 적용 제거: bottom-up 순회에서는 하위 노드에서 이미 적용된 규칙이 상위 노드에서 다시 적용될 수 있지만, top-down은 이를 방지한다.
  2. 추론 용이성: 규칙의 진입점이 루트 하나이므로, 규칙 적용 순서와 결과를 예측하기 쉬워진다.
  3. 관심사 분리: canApplyPredicate 체크를 applyToTargets 진입 전에 수행하여, 대상 탐색 로직과 적용 가능 여부 판단을 분리했다.
  4. 새 규칙 추가 용이: groupByPushdown, projectionPushdown 등 새 규칙도 동일한 findMatchingNodes + applyToTargets 패턴을 따르면 된다.

참고 자료

댓글

관련 포스트

PR Analysis 의 다른글