기존 구조에서는 부분 색인 시 MongoDB를 캐시로 활용하고, 전체 색인 시 Kafka를 통해 S3 URL을 전달하는 비동기 흐름을 사용하고 있었습니다.
하지만 해당 구조는 시스템 복잡도를 증가시키는 반면, 실질적인 안정성 향상 효과는 제한적이었습니다.
이에 따라 부분 색인에서는 MongoDB 캐싱을 제거하고 Kafka 이벤트 수신 후 즉시 Elasticsearch에 반영하도록 단순화하였으며, 전체 색인은 배치 작업의 특성에 맞게 Kafka 의존성을 제거했습니다.
대신 Redis Read/Write Lock을 도입하여 전체 색인과 부분 색인 간의 동시성을 보다 정교하게 제어하도록 구조를 개선했습니다.
기존에는 부분 색인 과정에서 Elasticsearch에 문제가 생길 경우를 대비해 변경 데이터를 MongoDB에 한 번 저장한 뒤 색인을 수행하고 있었습니다.
색인 실패 시 재시도나 복구에 활용하려는 목적이었습니다.
하지만 실제로 구조를 다시 살펴보니, 이 과정이 안정성을 크게 높여주기보다는 오히려 전체 흐름을 복잡하게 만들고 있다는 생각이 들었습니다.
Elasticsearch에 장애가 발생했을 때 MongoDB를 중간 저장소로 사용하더라도, MongoDB 자체에 문제가 생기면 결국 비슷한 상황이 반복될 수 있었고, 장애 지점을 하나 더 늘리는 구조가 된다고 판단했습니다.
또한 이미 Kafka를 통해 이벤트가 비동기적으로 전달되고 있는 상황에서 MongoDB 캐싱까지 추가되면서 색인 처리 흐름이 중복되고, 데이터 관리와 재처리 로직이 복잡해지는 문제가 있었습니다.
이러한 이유로 부분 색인에서는 MongoDB 캐싱을 제거하고, Kafka 이벤트를 수신한 뒤 Elasticsearch에 바로 반영하는 방식으로 구조를 단순화했습니다.
장애 대응은 중간 캐시를 두기보다는, 실패 처리와 재시도 로직을 명확히 하는 방향이 더 적절하다고 판단했습니다.
기존 구조에서는 Event 서버가 CSV 파일을 S3에 업로드한 뒤, Kafka를 통해 해당 CSV 파일의 URL을 Search 서버로 전달하는 방식으로 전체 색인을 진행했습니다.
전체 색인 흐름을 하나의 파이프라인으로 연결하려는 의도였습니다.
하지만 구조를 다시 살펴보면서, 이 방식이 오히려 서비스 간 의존성을 높이는 설계라는 판단이 들었습니다.
전체 색인은 실시간 처리가 필요한 작업이 아니기 때문에, Kafka를 사용해 메시지를 전달하더라도 즉각적인 이점을 얻기 어려웠습니다.
또한 Kafka를 단순히 CSV URL을 전달하는 pub/sub 용도로만 사용하는 것은 적절하지 않다고 판단했습니다.
메시지 큐가 가지는 장점을 충분히 활용하지 못한 채, 구조만 복잡해지는 형태였기 때문입니다.
이에 따라 전체 색인 과정에서는 Kafka를 제거하고, Event 서버는 CSV 업로드 완료 후 S3에 _SUCCESS 파일을 생성하도록 변경했습니다.
Search 서버는 해당 파일의 존재를 확인한 뒤 CSV 파일을 다운로드하여 이후 색인 작업을 진행하는 방식으로 구조를 단순화했습니다.
이 변경을 통해 전체 색인 흐름은 더 명확해졌고, 불필요한 메시지 의존성을 제거하면서도 안정적인 배치 처리가 가능해졌습니다.
기존 색인 구조에서는 전체 색인 여부를 판단하기 위해 단일 Redis 락을 사용하고 있었습니다.
전체 색인은 락을 생성하여 단독으로 실행하고, 부분 색식은 락이 존재하는지만 확인한 뒤 실행 여부를 결정하는 방식이었습니다.
이 구조는 구현이 단순하다는 장점이 있었지만, 전체 색인과 부분 색인의 경계가 명확하지 않다는 한계가 있었습니다.
부분 색인은 락을 직접 획득하지 않고 상태만 확인했기 때문에, 락 조회 시점과 실제 색인 실행 시점 사이에 원자성이 보장되지 않아 전체 색인과 부분 색인이 동시에 실행될 가능성이 있었습니다.
이를 해결하기 위해 기존 단일 락 기반 구조를 Redis Read / Write Lock 구조로 전환하였습니다.
이렇게 변경함으로써:
결과적으로 색인 흐름이 단순해지고, 전체 색인과 부분 색인 간 충돌 가능성을 줄이면서 보다 안정적인 검색 색인 구조를 구성할 수 있었습니다.
기존 구조에서는 전체 색인 시 Redis 락을 Event 서버에서 획득하고, 색인 작업이 완료된 이후에는 Search 서버에서 락을 해제하는 방식으로 동작했습니다.
Redis 락은 락을 획득한 주체와 해제하는 주체가 다를 경우 오류가 발생할 수 있어, 구조적으로 안전하지 않은 방식이었습니다.
이러한 문제를 해결하기 위해 락의 획득과 해제를 동일한 서비스에서 관리하도록 구조를 변경했습니다.
이 변경을 통해 락 관리가 단순해지고, 강제 해제에 의존하지 않아도 되는 안정적인 구조로 개선할 수 있었습니다.
기존 구조에서는 전체 색인이 진행되는 동안 발생한 부분 색인 요청을 대기열에 적재한 뒤, 전체 색인이 완료되면 대기열에 쌓인 작업을 처리한 후 락을 해제하는 방식으로 동작했습니다.
하지만 이 방식은 전체 색인 중에도 부분 색인 요청이 계속 들어올 경우, 대기열이 비워지지 않아 전체 색인 락이 해제되지 않는 문제가 발생할 수 있었습니다.
결과적으로 전체 색인은 완료되었음에도 부분 색인은 계속 대기열을 통해 처리되면서, 시스템이 전체 색인 상태에 머무르는 구조가 되었습니다.
이를 해결하기 위해 전체 색인 시작 시 부분 색인용 Kafka Consumer를 pause하고, 전체 색인 완료 시 전체 색인 완료 이벤트를 발행하여 Kafka Consumer를 resume하는 방식으로 구조를 변경했습니다.
이로써 전체 색인 중 부분 색인 요청을 자연스럽게 제어하고, 전체 색인 완료 시점에 맞춰 부분 색인 처리를 재개할 수 있게 되었습니다.