기존에는 Mongo의 Atlas search를 활용하여 Full text(전문 검색) 서비스를 제공하고 있었습니다. 하지만 동의어와 오탈자를 처리하기 위해서 에 nori
한글 형태소 분석기로
nori
와 사용하지 않는 한글 (죵프수트 등)과 같은 데이터가 들어있는 Synonyms Source Collection
을 동의어 매핑하는 경우, 인덱싱이 오류가 납니다. 더군다나 애플리케이션에서 해당 인덱스로 검색을 하던 것도 에러가 떨어집니다. 몽고 팀에게 문의를 해봤을 때도 명확한 해답을 얻지 못 했습니다. 그래서 검색 엔진으로도 유명했던 elasticsearch로 옮기기로 결정했습니다. 이는 로그 시스템에서도 사용하고 있던 터라 오히려 제품 비용 절감이 되는 효과도 있어서 쉬운 결정이었습니다.
Data sync를 위한 Double Write 로직 제거: PG를 main DB로 사용하고, Mongo를 read용으로 사용하고 있었습니다. 이 때, 두 저정소의 sync를 맞추기 위해서 application에서 double write로 삽입, 갱신, 삭제를 진행했었습니다. 이는 코드를 복잡하게 만들고, 버그를 낳았습니다. 그러던 중, RDB에서 CDC 패턴으로 파이프라인을 만드는 방식에 대해 알게 됐습니다. 이를 활용하면 위와 같은 요구사항을 만족할 수 있을 것으로 생각했습니다. 추가적으로, 최종 일관성만 맞으면 되는 비즈니스였기에 CDC로 최종 결정할 수 있었습니다.
전체적인 구조는 아래와 같이 설계되어 있습니다. 사실 비용 부담으로 인해 sink connector가 많은 역할을 하고 있지만 여기 설명에서는 관계가 없기 때문에 생략하여 표시했습니다. Kafka-connect Debezium을 사용했고, sink connector는 go로 구현했습니다.
초기 Sink Connector는 이벤트에 대해서 처리할 목록들이 처리되면 커밋 후에 다음 이벤트를 읽는 동기 Consumer로 구현을 했었습니다. 처리 목록 중 Webhook으로 부터 받는 응답이 딜레이가 되면서 lag이 커져 전체 이벤트의 처리가 9시간이 delay되어 서비스 장애가 생겼습니다. 이를 개선하기 위해, group.id(컨슈머 그룹)/job(작업)/worker count(워커 수)를 Config에서 설정해서 추가할 수 있도록 설계했습니다.
2번의 async로 100개의 worker로 작업을 하다보니, elasticsearch의 memory limit으로 circuit break걸리는 현상이 발생하여 순간 모든 쿼리가 안 되어, 조회 뿐만 아니라 동기화도 안 되는 현상이 발생했습니다. 이는 한가지의 원인으로만 발생하는 것은 아니기에 많은 분석이 필요한 상황입니다. 일단 데이터 생성 및 색인시 bulk api를 통해서 메모리 자원을 효율적으로 사용할 수 있다고 설명되어있어, event를 batch로 받아 bulk command 하도록 변경했습니다.
추가적으로 진행 중인 부분은 스코어링 쿼리 개선과, 재시도 처리입니다.