엔지니어링

실제 메모리 회로 차단기를 이용한 노드 복원력 개선

사용자는 사이트가 상당한 부하를 받고 있더라도 Elasticsearch가 검색 트래픽을 안정적으로 처리해줄 것으로 믿고 싶어합니다. Elasticsearch는 분산 시스템이기 때문에, 처음부터 개별 노드의 오류에 대해 복원력이 있도록 설계됩니다. 실제로 우리는 Elasticsearch 7.0.0에서 새롭고 엄청나게 개선된 클러스터 조정 알고리즘을 구현했습니다.

또한, Elasticsearch의 개별 노드는 복원력을 염두에 두고 구축됩니다. 노드에 요청을 너무 많이 보내거나 요청이 너무 크면, 노드는 푸시백을 하게 됩니다. 이것은 _회로 차단기_에 의해 이루어집니다. 이 회로 차단기는 네트워크 요청이 노드에 들어갈 때 또는 집계가 실행될 때 등과 같이 요청을 처리하는 경로의 특정 지점에 배치됩니다. 핵심 아이디어는 요청이 노드를 구성된 제한 이상으로 푸시하여 갑자기 중단되는 대신 요청을 거부할지 여부를 미리 예상함으로써 OutOfMemoryError를 피하는 것입니다. 모든 진행 중인 요청 같은 개별 측면을 위한 회로 차단기나 필드 데이터 회로 차단기뿐 아니라, Elasticsearch에는 모든 회로 차단기에 걸쳐 전체적으로 파악할 수 있는 “상위 회로 차단기”도 있습니다. 이를 통해 Elasticsearch는 모든 개별 회로 차단기의 예산 내에 있지만 모든 회로 차단기에 걸쳐 총 제한 이상으로 시스템을 푸시하는 요청을 거부할 수 있습니다.

모든 할당을 추적하는 것은 현실적으로 불가능하기 때문에 회로 차단기는 명시적으로 예약된 메모리만 추적할 수 있습니다. 그리고 정확한 메모리 사용을 미리 예상할 수 없는 때도 있습니다. 이것은 회로 차단기가 최상의 노력을 하는 메커니즘일 뿐이며, 노드가 오버로드되지 않도록 일정 정도 복원력을 제공하긴 하지만, OutOfMemoryError가 발생하여 노드가 중단될 가능성도 여전히 존재한다는 뜻입니다. 힙이 작을수록 특히 문제가 됩니다. 추적되지 않는 메모리의 상대적인 오버헤드가 더 크기 때문입니다.

더 나은 회로 차단기 구축 (및 테스팅)

회로 차단기에서 예약을 할 때 노드가 얼만큼의 메모리를 사용하고 있는지 정확히 알 수 있다면 어떨까요? 그렇다면 여러 회로 차단기에 걸쳐 현재 예약을 기준으로 예상을 하는 대신 그 시점의 실제 시스템 상태를 기준으로 요청을 거부할 수 있을 것입니다. Elasticsearch 7.0에서 새로운 실제 메모리 회로 차단기를 이용해 정확히 바로 그 작업을 했습니다. 이것은 상위 회로 차단기의 대체 구현으로서, 현재 추적된 메모리만 확인하는 대신 현재 메모리 사용을 측정하기 위해 JVM의 기능을 사용합니다. 숫자 몇 개를 합산하기만 하는 것보다는 비용이 더 들지만, 메모리 사용을 측정하는 것은 여전히 아주 저렴한 작업입니다. 마이크로벤치마크에서 우리는 400나노초에서 900나노초 사이의 오버헤드를 관찰했습니다. 다른 조건에서 실제 메모리 회로 차단기의 효과성을 테스트하기 위해 다양한 실험을 실시했습니다. 한 시나리오에서는, 256MB 밖에 안되는 힙으로 구성된 노드에 대해 전문 색인 벤치마크를 실시했습니다. Elasticsearch의 이전 버전은 이 작업 부하를 견딜 수 없으며 거의 즉시 메모리가 부족한 상태에 이르게 되지만, 실제 메모리 회로 차단기는 푸시백하여 Elasticsearch가 그 부하를 견뎌낼 수 있습니다. Elasticsearch는 그러한 경우 오류 응답을 반환하며 적절한 백오프와 재시도 메커니즘을 구현하는 것은 클라이언트에게 달려 있다는 점에 유의하세요. 물론, 이미 공식 언어 클라이언트 중 하나를 사용하고 계시다면 쉽게 이를 수행할 수 있습니다. .NET, Ruby, Python, Java 클라이언트는 이미 이러한 재시도 방침을 구현하고 있으며 대량 색인 작업을 처리하기 위한 확장도 제공합니다.

또 다른 실험에서는, 힙이 16GB인 노드에서 비현실적으로 높은 숫자의 버킷을 의도적으로 생성한 집계를 실행했습니다. 마찬가지로, Elasticsearch의 이전 버전은 메모리가 부족한 상태에 빠졌지만 집계는 오류가 발생할 때까지 거의 30분 동안 실행되었습니다. 실제 메모리 회로 차단기를 이용하면 노드는 대신에 우리가 일 분이 조금 지난 후에 아니면 대략 20분 후에 부분적인 결과를 허용했는지에 따라 응답을 제공했습니다. 여러 실험 결과, 우리는 새로운 회로 차단기를 위한 기본값을 전체적으로 이용 가능한 힙의 95%로 설정했습니다. 이것은 실제 메모리 회로 차단기가 트립을 실행할 때까지 Elasticsearch가 힙의 최대 95%까지 사용할 수 있다는 뜻입니다.

모든 다른 검사를 통과할 정도로 작지만 노드 제한을 초과하여 노드를 푸시하게 되기 때문에 실제 메모리 회로 차단기가 트립을 실행하게 되는 대량 요청을 전송하는 예제를 살펴봅시다. 이 노드는 힙이 128MB로 구성되어 실행됩니다. 이것은 상위 회로 차단기의 95% 제한이 117.5MB라는 뜻입니다. 이 요청이 전송되면, 노드는 다음과 같이 HTTP 429 코드로 응답하게 됩니다.

{
  'error': {
    'type': 'circuit_breaking_exception',
    'reason': '[parent] Data too large, data for [<http_request>] would be [123848638/118.1mb], which is larger than the limit of [123273216/117.5mb], real usage: [120182112/114.6mb], new bytes reserved: [3666526/3.4mb]',
    'bytes_wanted': 123848638,
    'bytes_limit': 123273216,
    'durability': 'TRANSIENT'
  },
  'status': 429
}

회로 차단기가 이것을 일시적 오류로 표시하는 것도 볼 수 있습니다. 클라이언트는 이것을 참고로 일정 시간 후에 해당 요청을 재시도할 수 있습니다. 회로 차단 예외가 영구적인지 일시적인 것인지 여부는 모든 회로 차단기에서 예약된 메모리를 기준으로 결정됩니다. 각 회로 차단기 유형마다 관련된 내구성이 있습니다. 예약된 메모리의 대다수가 일시적인 메모리 사용을 추적하는 회로 차단기에 의해 예약되어 있는 경우, 실제 메모리 회로 차단기는 이것을 일시적인 상태로 처리합니다. 그렇지 않다면 영구적인 상태로 처리합니다.

결론

특정 시나리오에서는 Elasticsearch 노드가 여전히 메모리가 부족한 상태에 빠질 수 있지만, Elasticsearch의 새로운 실제 메모리 회로 차단기는 회로 차단기가 추적한 메모리를 확인하기만 하는 대신 실제로 측정된 메모리 사용을 기준으로 역 압력을 행사함으로써 개별 노드의 복원력을 크게 개선합니다. 우리의 실험에서, Elasticsearch는 이전 버전에서는 한도를 크게 벗어나던 작업 부하를 이제 견뎌낼 수 있었으며, 이로써 사용자의 프로덕션 클러스터는 작업 부하의 피크까지 훨씬 더 개선된 복원력을 갖게 됩니다. 새로운 실제 메모리 회로 차단기를 사용해 보려면, 최신 7.0 베타 릴리즈를 다운로드하여 실행하시고, 피드백을 남겨주세요.

포스팅 맨 위의 이미지는 Kiran Raja Bahadur SRKCC BY-NC-ND 2.0 라이선스(원본 출처) 하에서 제공한 것입니다.