Opchain 변환기 플러그인

Opchain 변환기 플러그인 (Opchain Converter Plugin)

다중 스테이지 엔진 확장성을 위한 커스텀 OpChain 변환기를 구현하는 방법을 다루는 문서예요.

출처: 문서

본문

OpChainConverter SPI(서비스 제공자 인터페이스)는 플러그인 개발자가 다중 스테이지 쿼리 엔진에서 논리적 쿼리 계획을 실행 가능한 OpChain 객체로 변환하는 대체 구현을 제공하게 해줘요. 이는 대체 실행 백엔드(예: 시계열 엔진 같은 전문 실행 엔진 통합)용 확장성과 Pinot의 다중 스테이지 실행(MSE) 통합을 가능하게 해요.

왜 OpChain 변환기 SPI를 사용하나요? (Why Use OpChain Converter SPI?)

OpChainConverter SPI는 다음이 필요할 때 유용해요:

  • 특정 실행 시나리오에서 대체 plan-to-OpChain 변환 로직 구현
  • 커스텀 실행 백엔드를 다중 스테이지 엔진과 통합
  • 전문 워크로드·하드웨어에 쿼리 실행 최적화
  • 대체 쿼리 실행 전략 지원

OpChain 변환기 구현 (Implementing an OpChain Converter)

커스텀 OpChain 변환기를 만들려면 OpChainConverter 인터페이스를 구현해요:

package org.apache.pinot.query.runtime.operator;

public interface OpChainConverter {
  /**
   * Converts a logical plan node into an OpChain for execution.
   *
   * @param context the plan request context containing query metadata
   * @param planNode the logical query plan node to convert
   * @return an OpChain ready for execution, or null if this converter cannot handle this plan
   */
  OpChain convert(PlanRequestContext context, PlanNode planNode);

  /**
   * Returns the priority of this converter. Higher priority converters are selected first.
   * When multiple converters have the same priority, they are ordered by converter ID.
   *
   * @return the priority value (higher = higher priority)
   */
  int priority();

  /**
   * Returns a unique identifier for this converter.
   *
   * @return the converter ID
   */
  String getId();
}

OpChain 변환기 등록 (Registering Your OpChain Converter)

OpChainConverter 인터페이스를 구현했다면 Java의 ServiceLoader 메커니즘으로 등록해요:

  1. Google의 com.google.auto:auto-service 라이브러리에서 @AutoService 어노테이션을 추가해요:
import com.google.auto.service.AutoService;
import org.apache.pinot.query.runtime.operator.OpChainConverter;

@AutoService(OpChainConverter.class)
public class CustomOpChainConverter implements OpChainConverter {

  @Override
  public OpChain convert(PlanRequestContext context, PlanNode planNode) {
    // Implement your conversion logic here
    // Return null if this converter cannot handle the plan
    if (!canHandle(planNode)) {
      return null;
    }
    return convertToOpChain(context, planNode);
  }

  @Override
  public int priority() {
    return 100; // Set your converter's priority
  }

  @Override
  public String getId() {
    return "custom-converter";
  }

  private boolean canHandle(PlanNode planNode) {
    // Implement logic to determine if this converter can handle the plan
    return true;
  }

  private OpChain convertToOpChain(PlanRequestContext context, PlanNode planNode) {
    // Implement your conversion logic
    return null;
  }
}
  1. JAR에 META-INF/services/org.apache.pinot.query.runtime.operator.OpChainConverter에 ServiceLoader 구성 파일을 만들어요:
com.example.CustomOpChainConverter

우선순위와 선택 (Priority and Selection)

OpChainConverterDispatcher는 등록된 모든 변환기를 관리하고 다음을 기준으로 활성 변환기를 선택해요:

  1. 우선순위 (Priority): 더 높은 우선순위 값이 먼저 평가됨
  2. 동점 처리 (Tie-breaking): 여러 변환기의 우선순위가 같으면 변환기 ID로 정렬(알파벳순)
  3. 명시적 오버라이드 (Explicit override): OpChainConverterDispatcher.setActiveConverterIdOverride(String converterId)로 활성 변환기를 명시적으로 설정 가능

예시: 커스텀 변환기 구현 (Example: Custom Converter Implementation)

특정 플랜 노드 유형을 처리하는 커스텀 OpChain 변환기의 완전한 예시예요:

import com.google.auto.service.AutoService;
import org.apache.pinot.query.planner.PlanNode;
import org.apache.pinot.query.runtime.operator.OpChain;
import org.apache.pinot.query.runtime.operator.OpChainConverter;
import org.apache.pinot.query.runtime.plan.PlanRequestContext;

@AutoService(OpChainConverter.class)
public class TimeSeriesOpChainConverter implements OpChainConverter {
  private static final String CONVERTER_ID = "timeseries-converter";
  private static final int PRIORITY = 50;

  @Override
  public OpChain convert(PlanRequestContext context, PlanNode planNode) {
    // Check if this is a time-series query that we can optimize
    if (!isTimeSeriesQuery(planNode)) {
      return null;
    }

    // Perform custom conversion optimized for time-series execution
    return optimizeTimeSeriesExecution(context, planNode);
  }

  @Override
  public int priority() {
    return PRIORITY;
  }

  @Override
  public String getId() {
    return CONVERTER_ID;
  }

  private boolean isTimeSeriesQuery(PlanNode planNode) {
    // Implement logic to detect time-series queries
    // For example, check for time-based aggregations, windowing, etc.
    return false;
  }

  private OpChain optimizeTimeSeriesExecution(PlanRequestContext context, PlanNode planNode) {
    // Implement time-series-specific optimization logic
    return null;
  }
}

기본 구현 (Default Implementation)

Pinot는 기존 PlanNodeToOpChain 변환기에 위임하는 DefaultOpChainConverter를 포함해요. 이 변환기는 가장 낮은 우선순위를 가지며, 다른 변환기가 쿼리 계획을 처리할 수 없을 때 폴백 역할을 해요.

OpChain 변환기 테스트 (Testing Your OpChain Converter)

OpChain 변환기를 테스트할 때:

  1. OpChainConverter 인터페이스를 제대로 구현했는지 확인
  2. 설계된 쿼리 계획 유형을 올바르게 처리하는지 테스트
  3. null 반환이 다음 변환기로 우아하게 위임되는지 확인
  4. 여러 변환기가 있을 때 우선순위 기반 선택을 테스트
  5. 명시적 오버라이드 기능이 올바르게 동작하는지 확인

더 알아보기 (Learn more)