루씬 버그 모험: 손상된 인덱스 예외 수정

때로는 한 줄의 코드를 작성하는 데 며칠이 걸리기도 합니다. 여기에서는 잠재적인 Apache Lucene 인덱스 손상을 해결하기 위해 며칠에 걸쳐 디버깅을 진행했던 엔지니어의 고군분투를 엿볼 수 있습니다.

Elastic 자격증을 취득하고 싶으신가요? 다음 Elasticsearch Engineer 교육이 언제 진행되는지 알아보세요! 지금 무료 클라우드 체험을 시작하거나, 내 로컬 기기에서 Elastic을 사용해 볼 수 있습니다.

준비하세요:

이 특별한 블로그는 평소와 다릅니다. 새로운 기능에 대한 설명이나 튜토리얼이 아닙니다. 이것은 작성하는 데 3일이 걸린 한 줄의 코드에 관한 것입니다. 잠재적인 Apache Lucene 인덱스 손상을 수정할 예정입니다. 몇 가지 팁을 알려드리고자 합니다:

  • 충분한 시간과 올바른 도구만 있다면 모든 결함 테스트는 반복할 수 있습니다.
  • 견고한 시스템을 위해서는 여러 단계의 테스트가 중요합니다. 그러나 테스트 수준이 높아질수록 디버깅과 재현이 점점 더 어려워집니다.
  • 수면은 훌륭한 디버거입니다

Elasticsearch 테스트 방법

Elastic에서는 Elasticsearch 코드베이스에 대해 실행되는 수많은 테스트가 있습니다. 일부는 단순하고 집중적인 기능 테스트이고, 다른 일부는 단일 노드 "해피 경로" 통합 테스트이며, 또 다른 일부는 장애 시나리오에서 모든 것이 올바르게 작동하는지 확인하기 위해 클러스터를 중단하려고 시도합니다. 테스트가 계속 실패하면 엔지니어 또는 도구 자동화가 깃허브 이슈를 생성하고 특정 팀에서 조사할 수 있도록 플래그를 지정합니다. 이 특정 버그는 지난 테스트에서 발견되었습니다. 이러한 테스트는 까다로워서 여러 번 실행해야만 반복할 수 있는 경우도 있습니다.

이 테스트는 실제로 무엇을 테스트하나요?

이 특별한 테스트는 흥미로운 테스트입니다. 특정 매핑을 생성하여 기본 샤드에 적용합니다. 그런 다음 복제본을 만들려고 할 때. 주요 차이점은 복제본이 문서 구문 분석을 시도할 때 테스트가 예외를 삽입하여 예상치 못한(그러나 예상했던) 방식으로 복구가 실패한다는 것입니다.

하지만 모든 것이 예상대로 작동했지만 한 가지 중요한 문제가 있었습니다. 테스트 정리 과정에서 일관성을 검증하는 과정에서 이 테스트에 문제가 발생했습니다.


이 테스트는 예상대로 실패했습니다. 일관성 검사 중에 모든 복제된 파일과 기본 Lucene 세그먼트 파일이 일관성이 있는지 확인합니다. 즉, 손상되지 않고 완전히 복제된다는 의미입니다. 부분적인 데이터나 손상된 데이터는 완전히 실패하는 것보다 훨씬 더 나쁩니다. 다음은 실패에 대한 무섭고 축약된 스택 추적입니다.

강제 복제 실패로 인해 복제된 샤드가 결국 손상되었습니다! 오류의 핵심 부분을 쉬운 말로 설명해 드리겠습니다.

Lucene은 세그먼트 기반 아키텍처로, 각 세그먼트가 자체 읽기 전용 파일을 알고 관리합니다. 이 특정 세그먼트는 세그먼트코어리더를 통해 모든 것이 정상적으로 작동하는지 검증하고 있었습니다. 각 핵심 리더에는 특정 세그먼트에 대해 어떤 필드 유형과 파일이 존재하는지 나타내는 메타데이터가 저장되어 있습니다. 그러나 Lucene90PointsFormat의 유효성을 검사할 때 예상되는 특정 파일이 누락되었습니다. 세그먼트 _0.cfs 파일을 사용하면 kdi 이라는 포인트 형식 파일이 예상됩니다. cfs 는 "복합 파일 시스템" 의 약자로, Lucene은 때때로 모든 필드 유형과 모든 작은 파일을 하나의 큰 파일로 결합하여 보다 효율적인 복제 및 리소스 활용을 위해 사용합니다. 실제로 세 가지 포인트 파일 확장자( kdd, kdi, kdm 의 세 가지 확장자가 모두 누락되었습니다. 루씬 세그먼트가 포인트 파일을 찾을 것으로 예상되는 위치에 어떻게 들어가야 하는데 파일이 없습니다!!! 무서운 손상 버그인 것 같습니다!

모든 버그 수정의 첫 번째 단계, 복제하기

이 특정 버그의 오류를 재현하는 것은 매우 고통스러운 작업이었습니다. Elasticsearch에서는 무작위 값 테스트를 활용하지만, 모든 실패를 조사할 수 있도록 모든 실패에 대해 (희망적으로) 재현 가능한 무작위 시드를 제공해야 합니다. 이 방법은 경쟁 조건으로 인한 장애를 제외한 모든 장애에 대해 잘 작동합니다.

몇 번을 시도해도 특정 시드는 로컬에서 실패를 반복하지 않았습니다. 그러나 테스트를 실행하고 더 반복 가능한 실패를 향해 나아갈 수 있는 방법이 있습니다.

특정 테스트 스위트를 사용하면 -Dtests.iters 매개 변수를 통해 동일한 명령에서 지정된 테스트를 두 번 이상 실행할 수 있습니다. 하지만 이것만으로는 충분하지 않았고, 실행 스레드가 전환되어 이 경쟁 조건이 발생할 가능성이 높아지는지 확인해야 했습니다. 시스템의 또 다른 문제점은 테스트 실행 시간이 너무 오래 걸려 테스트 러너가 타임아웃을 일으킨다는 점입니다. 결국 다음과 같은 악몽 배쉬를 사용하여 테스트를 반복적으로 실행했습니다:

스트레스를 받습니다. 이렇게 하면 점심시간에 CPU 코어를 먹는 프로세스를 빠르게 시작할 수 있습니다. 실패한 테스트를 여러 번 반복하면서 무작위로 스트레스를 스팸으로 전송한 결과 마침내 실패를 재현할 수 있었습니다. 한 걸음 더 가까이. 시스템에 스트레스를 주려면 다른 터미널 창을 열고 실행하면 됩니다:

버그 공개



테스트 실패로 버그가 대부분 반복 가능하므로 이제 원인을 찾아야 할 때입니다. 이 특정 테스트를 이상하게 만드는 것은 루씬이 포인트 값을 기대하기 때문에 던지는 것이지만, 테스트에서 직접 추가하는 것은 없다는 것입니다. 텍스트 값만 입력할 수 있습니다. 이 때문에 저는 낙관적인 동시성 제어 필드에 대한 최근 변경 사항을 살펴보기로 했습니다: _seq_no_primary_term. 이 두 가지 모두 포인트로 색인되며 모든 Elasticsearch 문서에 존재합니다.

실제로 커밋으로 인해 _seq_no 매퍼가 변경되었습니다! 예! 이것이 원인일 것입니다! 하지만 흥분은 잠시뿐이었습니다. 이렇게 하면 문서에 필드가 추가되는 순서만 변경됩니다. 이 변경 전에는 _seq_no 필드가 문서의 마지막에 추가되었습니다. 그 후에 먼저 추가되었습니다. Lucene 문서에 필드를 추가하는 순서 때문에 이 오류가 발생할 리가 없습니다...

네, 필드를 추가한 순서를 변경한 것이 실패의 원인이었습니다. 놀랍게도 이것은 Lucene 자체의 버그로 밝혀졌습니다! 구문 분석되는 필드의 순서를 변경해도 문서 구문 분석의 동작은 변경되지 않습니다.

Lucene의 버그

실제로 Lucene의 버그는 다음 조건에 초점을 맞췄습니다:

  • 포인트 값 필드 색인화(예 _seq_no)
  • 분석 중 텍스트 필드 던지기를 색인화하려고 합니다.
  • 이 이상한 상태에서 텍스트 인덱스 분석 예외를 경험하는 작성자로부터 거의 실시간에 가까운 리더를 엽니다.

하지만 아무리 여러 가지 방법을 시도해도 완벽하게 복제할 수 없었습니다. 저는 Lucene 코드베이스 전체에 디버깅을 위한 일시 중지 지점을 직접 추가했습니다. 예외 경로 중에 무작위로 리더를 열려고 시도했습니다. 이 장애가 발생한 정확한 경로를 찾기 위해 몇 메가바이트의 로그를 출력하기도 했습니다. 도저히 할 수 없었습니다. 하루 종일 싸우고 지는 데 시간을 보냈습니다.

그리고 잠을 잤습니다.

다음 날 원본 스택 추적을 다시 읽다가 다음 줄을 발견했습니다:


모든 재창조를 시도할 때 보존 병합 정책을 구체적으로 설정한 적이 없습니다. SoftDeletesRetentionMergePolicy는 복제본에서 삭제를 정확하게 복제하고 문서가 실제로 제거될 때 모든 동시성 제어를 담당할 수 있도록 Elasticsearch에서 사용됩니다. 그렇지 않으면 Lucene이 모든 권한을 가지며 병합 시 해당 항목을 제거합니다.

이 정책을 추가하고 위에서 언급한 가장 기본적인 단계를 복제하자 장애가 즉시 복제되었습니다.


루씬에서 버그를 발견한 것이 이렇게 기뻤던 적이 없었습니다.


Elasticsearch에서는 경쟁 조건으로 표시되었지만, 모든 조건이 충족되면 Lucene에서는 반복적으로 실패하는 테스트를 작성하는 것이 간단했습니다.

결국 모든 좋은 버그가 그렇듯 단 한 줄의 코드로 해결되었습니다. 단 한 줄의 코드로 며칠 동안 작업할 수 있습니다.

하지만 그만한 가치가 있었습니다.

끝이 아닙니다.

저와 함께 이 거친 여정이 즐거우셨기를 바랍니다! 소프트웨어, 특히 Elasticsearch와 Apache Lucene처럼 널리 사용되고 복잡한 소프트웨어를 작성하는 것은 보람 있는 일입니다. 하지만 때로는 매우 실망스러울 때도 있습니다. 저는 소프트웨어를 좋아하기도 하고 싫어하기도 합니다. 버그 수정은 끝나지 않았습니다!

관련 콘텐츠

최첨단 검색 환경을 구축할 준비가 되셨나요?

충분히 고급화된 검색은 한 사람의 노력만으로는 달성할 수 없습니다. Elasticsearch는 여러분과 마찬가지로 검색에 대한 열정을 가진 데이터 과학자, ML 운영팀, 엔지니어 등 많은 사람들이 지원합니다. 서로 연결하고 협력하여 원하는 결과를 얻을 수 있는 마법 같은 검색 환경을 구축해 보세요.

직접 사용해 보세요