"[260904] 어제 남긴 우회 경로 허점과 오탐 버그 두 건을 마저 수선하다"
수동 라운드를 막던 하드코딩 문제를 근본적으로 고치고, 이틀째 재발한 테스트 오염과 야간 복구 로직의 오탐 원인도 함께 잡았습니다
어제 남겨둔 우회 경로, 근본 원인부터 다시 봤다
어제 밤새 못 돈 배치를 복구하던 중, 수동으로 거래 라운드를 트리거하면 전부 무발주로 실패하는 문제가 있었습니다.
당시엔 더 낮은 단계에서 직접 실행하는 우회 경로로 급한 불을 끄고, 정식 수선은 나중으로 미뤄뒀습니다.
오늘 그 원인을 제대로 들여다봤습니다. 실행 유닛 파일 안에 "이건 정기 자동 라운드다"라는 표식이 아예 고정값으로 박혀 있었고, 그래서 사람이 수동으로 실행해도 시스템은 무조건 정기 라운드로 착각했습니다.
AI에게 설계 자문을 구해, systemd가 타이머로 실행될 때만 주입하는 별도 환경변수를 판별 근거로 쓰기로 방향을 잡았습니다. 새 파일이나 마커를 추가하지 않고, 기존 판별 로직만 손보는 방식입니다.
작업은 세 단계로 나눠 진행했습니다. 먼저 새 판별 함수를 코드에 넣고 테스트로 검증했고, 실제 유닛을 건드리기 전에 일회용 타이머로 예행연습을 해서 재시도 상황에서도 판별이 흔들리지 않는지 확인했습니다.
마지막으로 실제 유닛 세 개에서 하드코딩된 표식을 지우고, 저장소에 보관된 유닛 사본도 맞춰 동기화했습니다.
이 과정에서 사본 관리 사각지대도 하나 찾았습니다. 최근에 새로 만든 유닛 하나가 애초에 저장소 사본에 등록조차 안 돼 있어서, 지금까지 동기화 검증에서 계속 빠져 있었습니다. 이번에 같이 채워 넣었습니다.
최종 확인은 다음 주 첫 정기 라운드가 돌 때 남습니다. 로그에 "타이머로 발화됐다"는 표식이 정상적으로 찍히는지 보고 나서야 이 작업을 완전히 마무리 지을 생각입니다.
이틀째 재발한 원장 오염, 이번엔 뿌리를 찾았다
어제 원인 불명으로 남겨뒀던 유령 거래 기록 문제가 오늘 또 나타났습니다. 다만 이번엔 재발 시점을 거슬러 올라가서 뿌리를 확실히 찾아냈습니다.
범인은 역시 회귀 테스트 하나였습니다. 어제 잡은 문제와 같은 계열이지만 다른 함수를 대상으로 한 테스트였는데, 이 테스트도 주문 체결을 기록하는 내부 함수의 실제 처리 경로를 가짜로 바꿔치기하지 않은 채 열어두고 있었습니다.
그래서 전체 테스트를 돌릴 때마다 실제 운영 데이터베이스에 가짜 체결 기록이 몇 건씩 섞여 들어가고 있었습니다. 오염된 기록을 백업하고 정확히 골라 지운 뒤, 실제 보유 내역과 다시 대조해 맞췄습니다.
테스트도 그 함수를 제대로 격리하도록 고쳤습니다. 그리고 이번엔 같은 계열 사고가 다시 나지 않도록, 그 데이터베이스 파일 자체를 테스트 격리 대상 목록에 정식으로 등록해 뒀습니다. 등록만 돼 있었어도 이틀 다 바로 잡혔을 문제였습니다.
같은 날 조사 중에 별개 사실도 하나 확인했습니다. 모의투자 계좌 하나가 시세조회 자체를 거부당해 라운드가 무거래로 끝난 경우가 있었는데, 이건 코드 버그가 아니라 증권사 쪽에서 모의계좌에는 해당 조회 기능 자체를 제공하지 않는 정책 제약이었습니다. 안전하게 아무 거래도 안 나간 채로 끝나서 실손실은 없었습니다.
야간 복구 로직이 정상 상황을 사고로 오판하고 있었다
이날 별도로, 매일 밤 진행되는 모델 교체 작업을 감시하다가 "멈춘 것 같다"고 판단하면 강제로 되돌리는 안전장치 하나를 들여다봤습니다.
확인해보니 이 장치가 최근 정상적으로 완주한 두 번의 밤 전부에서 오발동하고 있었습니다. 우연이 아니라 매번 똑같이 발동하는 구조적인 문제였습니다.
원인은 감시 주기와 실제 작업 소요 시간 사이의 타이밍이었습니다. 작업이 끝나기까지 걸리는 여유 시간보다 감시 주기가 짧아서, 아직 정상 진행 중인데도 감시 쪽이 먼저 "멈췄다"고 판정해버리는 구조였습니다.
오너 지시로 원인을 좀 더 거슬러 추적해봤더니, 애초에 이 오판정이 처음 나타난 날의 진짜 계기도 찾을 수 있었습니다. 그날 밤 누군가 별도 스크립트로 같은 작업을 수동 실행했는데, 그 직후 정규 프로세스가 재시작되면서 서로 다른 두 프로세스의 흔적이 뒤섞여 "먼저 시작한 게 멈췄다"는 오판으로 이어진 것이었습니다.
그래서 수선 방향도 "얼마나 오래 걸렸는지" 대신 "그 작업을 진행 중인 프로세스가 실제로 살아있는지"를 직접 확인하는 방식으로 바꿨습니다. 어느 프로세스가 시작했든 상관없이 살아있으면 그대로 두는 판정이라, 오늘 찾은 크로스 프로세스 사례도 함께 해결됩니다.
배포는 장중 재시작을 피해서, 정해진 시간대에 한 번만 조용히 재시작되도록 예약해뒀습니다. 실제로 오탐 없이 넘어가는지는 오늘 밤 결과를 봐야 확정됩니다.
하루 안에 겹친 세 가지 일 모두 패턴이 비슷했습니다. 어제는 급한 대로 우회했던 문제, 같은 계열이지만 다른 함수에서 재발한 문제, 그리고 원래 안전장치인데 정상 상황을 사고로 오판하던 문제. 셋 다 증상만 막지 않고 근본 원인까지 추적한 뒤에야 마무리했습니다.