주식 시황 피드의 종가 오류 해결기(데이터 일관성 개선)
항상 버그는 퇴근하고 발생하는 것 같은 기분이 듭니다.
주식 시장이 마감되고 코스피/코스닥에 상장된 종목들의 종가 및 종목 수익률을 계산하여 당일 시황의 전반적인 상황을 피드로 생성하여 제공하는 기능을 개발하여 개발 서버에 배포된 상태입니다.
그런데 당일 코스피/코스닥 종목들의 주가 변동률이 잘못 계산되어 피드가 생성되는 버그가 발생하였습니다.
문제의 원인을 파악하기 전 기존 피드를 생성하는 프로세스를 확인해 보겠습니다.
피드 생성 프로세스
현재 저희 서비스는 MSA 구조로 설계되어 있고 피드를 생성하는 데는 3개 서버의 의존성이 있습니다.
코스콤에서 코스피/코스닥에 상장된 종목들의 주가 정보를 호출하여 DB에 저장하는 서버
당일 시황 관련 정보를 수집하고 요약하여 생성하는 서버 (ML팀)
피드를 생성하는 서버
1. 15:40 분에 당일 코스피/코스닥에 상장된 종가 정보를 호출하기 시작합니다.
2. 16시에 ML팀에서 시황 정보를 생성하여 토픽에 전송합니다.
3. 시황 정보가 저장되는 토픽을 구독하고 있다, 메시지가 들어오면 당일 종가 정보를 DB에서 가져와 시황 정보와 함께 피드로 생성하는 로직을 실행합니다.
그런데 종목들의 주가 변동률이 잘못 계산된 거지?
당일 종가 정보와 이전 영업일의 종가 정보를 DB에서 참조하여 주가 변동률을 계산하는데 정상적으로 계산이 되지 않았습니다.
가장 먼저 의심해 볼 수 있었던 건 DB에 당일 종가 정보가 저장이 되지 않았는지 의심되어 확인해 보니 정상적으로 종가 정보가 저장되어 있었습니다.
두 번째로 의심했던 부분은 피드가 생성되는 시점에 종가 정보가 저장되지 못했으면 당일 종가 정보가 아닌 이전 영업일의 종가 정보를 참조하기 때문에 잘못 계산되었을 가능성이 있다고 생각하였습니다.
로그를 확인해 보니 피드가 생성되는 시점에 코스콤에서 호출한 종가 정보가 DB에 저장되지 않았습니다.
당일 종가 정보가 16:13:57초에 저장 완료
ML팀에서 생성한 시황 메시지는 15:52:13초에 생성
즉, 시황 메시지가 저장되는 토픽을 구독하고 있는 피드 생성 서버는 16시에 당일 시황 메시지가 생성되었기 때문에 피드를 생성하려고 DB에서 종가 정보를 참조하지만 아직 DB에는 당일 종가 정보가 저장이 되지 않아 이전 영업일의 종가를 참조하여 주가 변동률을 계산하고 있었습니다.
이러한 문제가 발생했던 이유를 생각해 보면 종가 정보를 저장하기 위해 코스콤에 15시 40분에 호출하는데 항상 5분 내외로 저장이 되었고, ML팀에서 생성하는 시황 정보는 16시부터 생성하기 때문에 항상 시황 정보가 생성되는 시점에는 당일의 종가 정보가 DB에 저장되어 있을 줄 알았지만, 언제나 예외는 발생할 수 있다는 걸 다시 느끼며 버그를 수정할 방안을 생각해 보았습니다.
데이터 정합성을 맞춰보자
우선 가장 빨리 버그를 픽스하기 위해서는 제가 컨트롤할 수 있는 범위인 피드를 생성하는 서버의 코드를 수정하여 버그를 수정해야만 합니다.
즉, 종가 정보를 저장하는 서버나, ML팀에서 생성하는 시황 정보 메시지를 생성하는 서버는 수정이 불가능한 상황입니다.
만일 종가 정보를 저장하는 서버 쪽을 수정할 수 있었다면, 이벤트 기반 트리거를 사용하여 피드 생성 프로세스를 동기화할 수 있을 것 같습니다.
이벤트 기반 트리거를 사용하여 피드 생성 프로세스를 동기화
1. ML팀에서 시황 정보를 생성해 줄 때 바로 DB에 당일 종가 정보를 찾아 피드를 생성하는 것이 아니라 우선 날짜 정보와 함께 시황 정보를 저장합니다.
2. 종가 정보가 DB에 저장이 완료되는 시점에 이벤트를 발생시켜 토픽에 메시지를 저장합니다.
3. 피드 생성 서버에서는 종가 정보가 저장되었다는 이벤트 리스너를 통해 종가 정보가 저장되었다는 메시지를 받으면 해당 날짜의 종가 정보를 DB에서 가져와 피드를 생성하여 프로세스를 동기화합니다.
종가 정보가 저장되는 시점에 이벤트를 발생시킴으로써 피드 생성 시 종가 정보가 없는 경우를 방지할 수 있다고 생각하였습니다.
시황 정보와 종가 데이터 동기화를 위한 스케줄러 기반 피드 생성 방법
하지만 현재 종가 정보를 저장하는 서버 쪽 코드를 수정할 수 없어, 피드를 생성하는 서버 쪽을 수정하여 문제를 해결해야만 했습니다.
현재 해결 방안은 아래와 같습니다.
1. 피드 생성 서비스에서 ML팀에서 생성해 준 시황 정보 메시지를 받았을 때, 당일 날짜의 종가 정보가 DB에 있는지 찔러봅니다.
2. 만일 당일 종가 정보가 생성되어 있지 않은 경우 피드를 생성하지 않고 시황 정보의 상태를 아직 피드가 생성되지 않은 시황 상태(e.g INIT..)와 함께 DB에 저장합니다.
3. 이후 스케쥴러를 통해 평일 16시부터 18시까지 10분마다 DB에 피드로 생성되지 않은 상태의 시황 데이터를 가져와 당일 종가 정보가 있는지 DB에 찔러봅니다.
4. 만일 DB에 종가 정보가 있으면 가져온 시황 정보와 종가 데이터와 함께 피드를 생성하고 해당 시황 정보의 상태를 생성된 시황 상태(e.g COMPLETED ..)로 변경하여 DB에 저장합니다. (DB에 종가 정보가 아직 없는 경우 피드를 생성하지 않고 그대로 종료)
종가 정보가 저장되었다는 이벤트를 발생시킬 수 없었기 때문에, 주기적으로 DB에 종가 정보가 있는지 확인할 수밖에 없었고 ML팀에서 생성하는 시황 정보의 생성 시점을 정확히 알 수 없기 때문에 리스너를 통해 받은 정보를 DB에 저장하여 상태를 관리하였습니다.