서버 시작 상태 점검기
서버 시작 상태 점검기 (Server Startup Status Checkers)
개요 (Overview)
프로덕션 환경에서 Pinot을 운영할 때, 서버가 시작됐다고 해서 바로 쿼리를 서빙할 수 있는 게 항상 이상적이진 않아요. 특히 실시간 서버는 "따라잡기(caught up)" 전에 일정량의 데이터를 다시 소비해야 할 수 있어요. Pinot은 서버가 언제 뜨고 건강하며 쿼리를 서빙할 준비가 됐는지 판단하는 몇 가지 전략을 제공해요.
출처: 문서
본문
헬스 체크 (Health Checks)
Pinot 서버는 서버의 건강 상태를 판단하는 여러 엔드포인트를 제공해요.
기본적으로 admin API 포트는 8097이며, 예를 들어 <host>:8097/health 형태예요.
GET /health/liveness는 "이 서버가 떠 있는가"를 답해요. 서버가 시작됐고 연결할 수 있는지만 확인해요.
GET /health/readiness는 "이 서버가 떠서 데이터를 서빙할 준비가 됐는가"를 답해요. 아래 점검기들이 "readiness" 측면이 OK를 반환할지 결정해요.
GET /health는 readiness 엔드포인트와 같은 점검을 수행해요.
소비 상태 점검 없음 (기본 동작)
다음 구성을 비활성화하면 점검기 없이 Pinot을 운영할 수 있지만 권장되진 않아요. 대신 기본값은 다음과 같아요:
# 기본값, 10분.
pinot.server.startup.timeoutMs=600000
# 기본값. 명시할 필요 없음.
pinot.server.startup.enableServiceStatusCheck=true
Pinot은 모든 서버 시작 작업이 끝날 때까지 최대 10분을 기다려요. 서버의 Ideal State가 External State와 일치해야 서버를 건강하다고 표시해요. 이는 세그먼트 다운로드, 인덱스 빌드, 소비 스트림 생성까지 포함할 수 있어요. 기본 시간으로 시작해 필요에 따라 늘리는 것을 권장해요.
Ideal State가 External State와 일치할 때까지 기다리는 것은 구성할 수 없어요. enableServiceStatusCheck=true이면 이것은 항상 점검 중 하나가 돼요.
시작 점검기 타이밍 모니터링
Pinot은 서버 수준 시작 게이지(startup gauge)를 노출해서 어떤 점검이 여전히 readiness를 막고 있는지, 각각이 처음으로 healthy를 보고하는 데 얼마나 걸렸는지 볼 수 있게 해요:
STARTUP_STATUS_CHECK_IN_PROGRESS— 시작 서비스 상태 폴링이 실행 중이면1, 끝나면0.STARTUP_CURRENT_STATE_MATCH_TIME_MS— ideal-state/current-state 점검이 처음으로GOOD을 보고하는 데 걸린 시간.STARTUP_EXTERNAL_VIEW_MATCH_TIME_MS— ideal-state/external-view 점검이 처음으로GOOD을 보고하는 데 걸린 시간.STARTUP_REALTIME_CONSUMPTION_CATCHUP_TIME_MS— 오프셋 기반이나 신선도 기반 점검기를 쓸 때 realtime catchup 점검이 처음으로GOOD을 보고하는 데 걸린 시간.
Pinot은 정적 대기(static wait) 모드에서는 STARTUP_REALTIME_CONSUMPTION_CATCHUP_TIME_MS를 내보내지 않아요. 그 경로는 관찰된 따라잡기 진행이 아니라 경과된 벽시계 시간으로만 healthy가 되기 때문이에요.
정적 소비 대기 (Static Consumption Wait)
가장 기본적인 시작 점검은 정적(static) 점검이에요. 다음과 같이 구성돼요:
# 기본값, 10분.
pinot.server.startup.timeoutMs=600000
# 기본값. 명시할 필요 없음.
pinot.server.startup.enableServiceStatusCheck=true
# 기본값 0, 서버는 기다리지 않음
pinot.server.starter.realtimeConsumptionCatchupWaitMs=60000
위 예시에서 Pinot 서버는 모든 consuming 세그먼트에 대해 60초를 기다린 뒤 healthy가 되어 쿼리를 서빙해요. 이렇게 하면 서버가 healthy로 표시되기 전에 데이터를 제한 없이(un-throttled) 소비할 1분을 줘요. 전반적으로 서버는 모든 시작 작업을 완료하는 데 여전히 10분만 기다려요. 따라서 realtimeConsumptionCatchupWaitMs < timeoutMs인지 확인하세요.
오프셋 기반 세그먼트 점검기 (Offset Based Segment Checker)
더 새로운 실시간 데이터를 판단하는 첫 번째 옵션은 오프셋 기반 상태 점검기예요. 이 점검기는 Pinot 시작 시점에 각 consuming 세그먼트의 끝 오프셋을 결정해요. 그런 다음 세그먼트를 healthy로 표시하기 전에 그 오프셋까지 소비해요. 모든 세그먼트가 healthy가 되면 이 점검기는 healthy를 반환해요.
# 기본값, 10분.
pinot.server.startup.timeoutMs=600000
# 기본값. 명시할 필요 없음.
pinot.server.startup.enableServiceStatusCheck=true
# 기본값 0, 서버는 기다리지 않음
pinot.server.starter.realtimeConsumptionCatchupWaitMs=60000
# 기본적으로 비활성화.
pinot.server.starter.enableRealtimeOffsetBasedConsumptionStatusChecker=true
여기서 주의할 점이 몇 가지 있어요:
realtimeConsumptionCatchupWaitMs는 여전히 설정해야 해요. 이 점검기는realtimeConsumptionCatchupWaitMs값만큼만 기다려요.- 이 점검기는 시작 후 끝 오프셋을 다시 계산하지 않아요. 실시간 볼륨이 높으면 여전히 뒤처지게 돼요. 서버가 시작하는 데 8분이 걸려 이 점검기가 healthy가 됐다면, 8분 뒤처진 상태에서 서버가 쿼리를 서빙하기 시작하면서 데이터를 빠르게 소비하고 있을 거예요.
신선도 기반 세그먼트 점검기 (Freshness Based Segment Checker)
Pinot이 제공하는 가장 엄격한 점검기는 신선도(freshness) 기반 점검기예요. 오프셋 점검기와 비슷하지만 추가 조건이 있어요. 스트림의 실제 이벤트가 서버를 healthy로 표시하기 전에 최소 신선도 조건을 충족해야 해요. 이 점검기는 실시간 데이터에 대한 최상의 신선도 보장을 제공하지만 시작 시간이 더 길어져요.
# 기본값, 10분.
pinot.server.startup.timeoutMs=600000
# 기본값. 명시할 필요 없음.
pinot.server.startup.enableServiceStatusCheck=true
# 기본값 0, 서버는 기다리지 않음
pinot.server.starter.realtimeConsumptionCatchupWaitMs=60000
# 기본적으로 비활성화.
pinot.server.starter.enableRealtimeFreshnessBasedConsumptionStatusChecker=true
# 기본값. 서버는 이벤트가 10초보다 오래되지 않기를 원함.
pinot.server.starter.realtimeMinFreshnessMs=10000
# 기본값. 서버는 진행이 없더라도 세그먼트가 따라잡을 때까지 계속 기다림.
pinot.server.starter.realtimeFreshnessIdleTimeoutMs=0
# 서버는 따라잡지 못해도 여전히 시작해 쿼리를 서빙함
pinot.server.starter.exitServerOnStartupStatusFailure=false
위 예시에서 Pinot 서버는 모든 consuming 스트림의 데이터가 현재 시스템 시간의 10초 이내일 때까지 최대 1분을 기다려요. 이는 점검기가 지나갈 때마다 다시 평가되므로, 서버가 시작되기 전에 신선한 데이터를 갖는다는 최고의 보장을 줘요. 이 점검기는 세그먼트가 현재 위치한 오프셋을 스트림의 최대 오프셋과 비교해서 둘이 같으면 세그먼트를 healthy로 표시하기도 해요. realtimeConsumptionCatchupWaitMs보다 더 신선한 데이터가 절대 없을 수 있는 저볼륨 스트림에서 유용해요.
여기서도 적용되는 주의점이 있어요:
realtimeConsumptionCatchupWaitMs는 여전히 설정해야 해요. 이 점검기는realtimeConsumptionCatchupWaitMs값만큼만 기다려요.- 이벤트가 이벤트 타임스탬프를 올바르게 전달하려면
getMetadataAtIndex를 구현해야 해요. 현재 kafka, kinesis, pulsar 구현은 이벤트 적재 시간(ingestion time)으로 이미 이를 수행해요. 하지만 데이터가 여러 홉을 거치면 마지막 홉의 신선도만 계산돼요.
권장 구성 (Recommended Configurations)
QA
pinot.server.startup.enableServiceStatusCheck=true
pinot.server.starter.enableRealtimeFreshnessBasedConsumptionStatusChecker=true
# 일반적으로 따라잡는 데 걸리는 시간을 기준으로 환경에 맞게 설정
pinot.server.startup.timeoutMs=<your_timeout_ms>
pinot.server.starter.realtimeConsumptionCatchupWaitMs=<your_timeout_ms>
pinot.server.starter.realtimeMinFreshnessMs=<your_desired_freshness>
pinot.server.starter.realtimeFreshnessIdleTimeoutMs=1000
pinot.server.startup.exitOnServiceStatusCheckFailure=false
QA의 권장 구성은 유효한 점검 수행과 빠르고 성공적인 시작 사이의 균형을 노려요. 시작 상태가 실패해도 서버를 종료하지 않아 crashloop를 피하지만, 이벤트가 소비되지 않을 때 따라잡기 위해 무한정 기다리지도 않아요. 여기서 멈춘 파티션은 적재 래그(ingestion lag)로 이어질 거예요.
프로덕션 (Production)
pinot.server.startup.enableServiceStatusCheck=true
pinot.server.starter.enableRealtimeFreshnessBasedConsumptionStatusChecker=true
# 일반적으로 따라잡는 데 걸리는 시간을 기준으로 환경에 맞게 설정
pinot.server.startup.timeoutMs=<your_timeout_ms>
pinot.server.starter.realtimeConsumptionCatchupWaitMs=<your_timeout_ms>
pinot.server.starter.realtimeMinFreshnessMs=<your_desired_freshness>
pinot.server.starter.realtimeFreshnessIdleTimeoutMs=0
pinot.server.startup.exitOnServiceStatusCheckFailure=true
프로덕션의 권장 구성은 최고 가용성, 정확성, 가장 낮은 적재 래그를 목표로 해요. 세그먼트 신선도가 최소 기준과 일치할 때까지 무한정 기다리고, timeout까지 상태 점검이 충족되지 않으면 서버를 중지해요.
timeout 구성을 정확히 하는 것이 중요해요. 그렇지 않으면 서버가 할당된 시간 안에 신선도 기준을 충족하지 못해 무한정 중지할 수 있어요.