검색마다 달라지는 Elasticsearch score (Bouncing Results)

문제

검색 서비스를 운영하면서 같은 쿼리를 날리더라도 매번 다른 정렬 순서로 결과가 반환되는 버그를 발견했습니다.

사용자의 메타데이터를 기반으로 추천순으로 결과를 정렬하는 서비스였는데, 같은 쿼리임에도 검색할 때마다 결과 순서가 미세하게 달라지는 문제가 있었습니다. 디버깅을 통해 각 document의 점수(score)가 매번 조금씩 달라져서 정렬이 어긋나는 것을 확인했습니다.

결국 문제는 검색엔진으로 사용 중인 Opensearch에서 쿼리를 실행하는 노드마다 document의 점수가 미세하게 다르게 계산된다는 것이었습니다. 이를 통해 정렬이 달라지는 현상이 발생하고 있음을 알게 되었고, 원인을 더 깊이 파악했습니다.

원인

이 문제는 “Bouncing Results"라고 불리는 현상으로 밝혀졌습니다. 이에 대한 해결책으로는 preference 옵션에 동일한 문자열을 제공하는 방법이 권장되었지만, 여전히 같은 버그가 발생했습니다. 결국, 검색 노드를 고정하는 방법을 택해 문제를 해결하기로 했습니다.

하지만, 모든 요청을 하나의 노드에서 처리하면 샤딩을 통한 성능 향상의 장점이 사라지기 때문에, 이 방법은 제외했습니다. 대신 동일한 사용자는 동일한 검색 결과를 보게 하도록 구현하기로 했습니다.

해결방법

preference 옵션에서 _prefer_nodes 값을 설정하는 방법을 사용했습니다. 검색 서비스는 로그인한 사용자만 이용할 수 있었기 때문에, 동일한 ID 값을 해시하여 특정 노드에서만 검색이 이루어지도록 했습니다.

노드의 개수나 설정은 상황에 따라 변동될 수 있으므로 _only_nodes 대신 _prefer_nodes를 사용했습니다. 또한, 인덱스 설정이 변경될 가능성도 고려하여 일정 시간마다 노드 ID 리스트를 조회하고, 노드 개수로 유저 ID를 모듈러 연산하여 노드를 할당하는 방법을 사용했습니다.

단순한 모듈러 연산을 사용했기 때문에 해시 함수의 결과가 적절하게 분배된다고 보장할 수는 없었지만, 모니터링 결과 특정 노드에 부하가 집중되거나 성능 문제가 발생하지 않아 그대로 유지했습니다.

마무리

2023년 겨울, 구직자에게 적합한 채용 공고를 추천하는 서비스를 운영하면서 겪었던 트러블슈팅 경험을 정리한 글입니다. 이 문제에 대한 보다 깊이 있는 분석을 다룬 포스팅이 있어 함께 확인하면 도움이 될 것 같아 링크를 첨부합니다.