[Elasticsearch] Circuit breaker 원인과 해결 방법
1. Circuit breaker 란
ElasticSearch 에서 OutOfMemory 발생을 방지하기 위해 사용하는 기능
좀 더 상세히 설명하면 elasticsearch 에서는 jvm의 OutOfMemory 가 발생하여
node 가 다운되어 서비스가 아예 안되는 상황을 막기 위해 memory 사용량이
특정 임계치이상 올라가면 request 를 아예 안받아 버리는(prevent) 안전장치
기능이 존재 합니다.
2. Circuit breaker 의 종류
2-1. parent circuit breaker
서로 다른 유형의 circuit breaker에서 사용되는 모든 메모리의 합계로 인해 발생
2-2. request circuit breaker
검색을 통해 요청별 데이터 구조(예: 요청 중 집계 계산에 사용되는 메모리)가 특정 메모리 양을 초과하는 것을 방지할 수 있습니다
2-3. in flight requests circuit breaker
검색을 통해 전송 또는 HTTP 수준에서 현재 활성 상태인 모든 수신 요청의 메모리 사용량이 노드의 특정 메모리 양을 초과하지
않도록 제한할 수 있습니다. 메모리 사용량은 요청 자체의 콘텐츠 길이를 기반으로 합니다.
2-4. accounting requests circuit breaker
검색을 통해 요청이 완료될 때 해제되지 않는 메모리에 저장된 항목의 메모리 사용량을 제한할 수 있습니다.
여기에는 루씬 세그먼트 메모리와 같은 것들이 포함됩니다
2-5. Script compilation circuit breaker
이전의 메모리 기반 circuit breaker 와 약간 다르게 일정 기간 내 인라인 스크립트 컴파일 수를 제한합니다
2-6. Regex circuit breaker
정규식을 잘못 작성하면 클러스터 안정성과 성능이 저하될 수 있습니다. 정규식 circuit breaker 는 스크립트에서
정규식의 사용과 복잡성을 제한합니다.
각 항목들에 대한 상세 설명은
https://www.elastic.co/guide/en/elasticsearch/reference/8.6/circuit-breaker.html
을 참고 하면 됩니다.
3. Circuit breaker 발생 시 로그
elasticsearch 의 node 안의 로그에서
Caused by: org.elasticsearch.common.breaker.CircuitBreakingException: [parent] Data too large, data for [<transport_request>] would be [num/numGB], which is larger than the limit of [num/numGB], usages [request=0/0b, fielddata=num/numKB, in_flight_requests=num/numGB, accounting=num/numGB]
와 같이 오류 메시지가 발생합니다.
해당 에러 메시지 내용은
https://www.elastic.co/guide/en/elasticsearch/reference/8.6/circuit-breaker-errors.html#diagnose-circuit-breaker-errors
을 참고 하시면 됩니다.
4. Circuit breaker 발생 시 영향도
Elasticsearch 에서는 문제가 되는 요청을 계속 호출할 것이므로 data node, client node(cordinating node) 등에서
- 계속 Circuit breaker 가 발생하여 hang 과 같은 현상이 발생 할수 있다.
- 위의 현상에 따른 영향으로 kibana process 가 kill 될 수 있다.
5. 조치 방법
elastic 의 공식 guide 에선
- jvm 에 대한 압력을 약화시켜라
https://www.elastic.co/guide/en/elasticsearch/reference/8.6/high-jvm-memory-pressure.html 참고
- fielddata 에 대해 text 를 피해라
- fieldata cache 를 삭제 해라
등의 하나마나한 얘기를 하고 있지만 가장 현실적인건 역시 jvm 메모리 증설입니다. (jvm 에 대한 압력을 약화시켜라 부분의 마지막 부분에 있습니다.)
오류 부분을 보면
5-1. ~CircuitBreakingException: [parent] Data too large ~~ 를 잘 보면 parent 라는 단어처럼
어떤 Circuit breaker 가 발생 했는지 확인 할수 있습니다.
5-2. kibana 의 dev tools 에서
GET /_cluster/settings?include_defaults=true
를 호출 해서 default 설정 값들도 다 조회 해 봅니다.
GET _nodes/stats/breaker 를 실행 해서 에러 메시지와 대조를 해 봅니다.
결과 예시)
"breakers" : {
"request" : {
"limit_size_in_bytes" : 20574004838,
"limit_size" : "19.1gb",
"estimated_size_in_bytes" : 0,
"estimated_size" : "0b",
"overhead" : 1.0,
"tripped" : 0
},
"fielddata" : {
"limit_size_in_bytes" : 13716003225,
"limit_size" : "12.7gb",
"estimated_size_in_bytes" : 0,
"estimated_size" : "0b",
"overhead" : 1.03,
"tripped" : 0
},
"in_flight_requests" : {
"limit_size_in_bytes" : 34290008064,
"limit_size" : "31.9gb",
"estimated_size_in_bytes" : 6254164,
"estimated_size" : "5.9mb",
"overhead" : 2.0,
"tripped" : 0
},
"accounting" : {
"limit_size_in_bytes" : 34290008064,
"limit_size" : "31.9gb",
"estimated_size_in_bytes" : 282771278,
"estimated_size" : "269.6mb",
"overhead" : 1.0,
"tripped" : 0
},
"parent" : {
"limit_size_in_bytes" : 32575507660,
"limit_size" : "30.3gb",
"estimated_size_in_bytes" : 13431618584,
"estimated_size" : "12.5gb",
"overhead" : 1.0,
"tripped" : 0
}
}
예를 들어 아까 메시지가 [parent] 부분이 있었기 때문에 parent 의 limit_size 와 node 의 jvm에 설정된
Xmx 값을 비교해보면 됩니다.
위의 GET /_cluster/settings?include_defaults=true 명령에서 indices.breaker.total.limit 라는
값으로 jvm 의 Xmx 대비 몇%다라는 걸 확인 할 수 있으므로 어느 노드에서 발생하는지 확인이 가능합니다.
5-3. (선택 1) kibana 의 dev tools 를 사용하여 임계치 관련 항목의 설정값을 수정합니다.
PUT /_cluster/settings
{
"transient" : {
"indices.breaker.total.limit" : "80%"
}
}
또는
PUT /_cluster/settings
{
"persistent" : {
"indices.breaker.total.limit" : "80%"
}
}
* transient 는 재기동 하면 설정값 삭제됨, persistent 는 재기동 후에도 해당 설정 적용 및 존재
5-4. (선택 2) 5-2 에서 확인 된 문제의 node 에 대한 JVM 의 Xmx 값 변경(증설)
당연하게도 가장 강력하고 빠른 해결책은 5-4 (JVM heap memory 증설) 이며 5-2에서 문제의 원인이 되는 elasticsearh node 를 찾는게 가장 중요합니다.