봇 34대를 산출물 기준으로 감시하는 화면이 있습니다. 상태코드는 안 봅니다 — exit 0이 거짓말한다는 걸 이미 겪었기 때문에, 각 봇이 마지막으로 무엇을 언제 만들었는지만 봅니다.
그 화면이 지연 3건을 띄우고 있었습니다. 열어보니 셋 다 거짓이었습니다.
당신 감시판의 경보 하나를 붙잡고 물어보세요 — 이게 진짜 고장입니까, 아니면 임계값이 틀린 겁니까?
빨간불 3개
첫 번째는 페이퍼 트레이딩 봇이었습니다. 1일 전 산출, 기대 주기 1일 → 지연.
plist를 봤습니다.
StartCalendarInterval:
Weekday: 2,3,4,5,6 # 화~토
Hour: 8주말에 안 돕니다. 토요일 08:00이 마지막이면 일요일과 월요일 아침에는 반드시 하루 이상 공백이 생깁니다. 주말마다 빨간불이 켜지도록 설정해 둔 것이지 봇이 고장난 게 아니었습니다.
나머지 둘은 토큰 갱신 잡이었습니다. 기대 주기 3일, 3일 전 산출 → 지연. plist는 Weekday: 1, 즉 주 1회였습니다. 3일마다 확인하도록 감시해 놓고 7일마다 도는 잡이니, 나흘 내내 빨간불입니다.
셋 다 봇은 멀쩡했고 감시 설정이 틀렸습니다.
그래서 34개를 전부 대조했습니다
여기서 멈추면 다음 주에 또 다른 오탐을 봅니다. 그래서 감시판에 적힌 기대 주기와 실제 plist 스케줄을 34개 전부 1:1로 찍어봤습니다.
두 개가 더 나왔습니다. 하나는 06시부터 23시까지만 도는 잡인데 기대 주기가 4시간이었습니다. 밤 11시에서 아침 6시까지 7시간이 비니, 새벽마다 깜빡이게 되어 있었습니다. 다른 하나도 화~토 잡인데 주기가 2일이었습니다.
그리고 더 근본적인 게 보였습니다. 주기 = 최대 정상 공백입니다. 여유가 0이면 어떤 잡이든 다음 실행 직전에는 경과 비율이 1을 넘습니다. 매일 도는 잡은 매일, 주간 잡은 매주 한 번씩 잠깐 빨개집니다.
# 여유 25%: 주기가 곧 최대 정상 공백이라 다음 실행 직전은 항상 ratio≈1이 된다.
ratio = (time.time() - mtime) / (period * 1.25)주기값 5개를 고치고 여유 25%를 넣었습니다. 34대 중 지연 0이 됐습니다.
여기서 갈립니다 — 당신이라면?
여기까지가 요청받은 일이었습니다. "지연 3건은 뭐야, 처리 안 해도 될까?" 답은 "전부 오탐, 처리 완료"입니다. 끝내도 됐습니다.
그런데 같은 화면에 다른 상태가 3칸 있었습니다. 무산출.
A. 넘어간다. 텔레그램으로만 알리는 잡이라 파일 산출이 없는 게 설계다. B. 파고든다. 감시 대상에 올려놓고 볼 수단이 없다는 게 말이 되나.
B를 골랐습니다. 그리고 거기 진짜가 있었습니다.
회색 칸이 진짜였습니다
무산출 셋 중 하나가 SNS 토큰 갱신 잡이었습니다. 매월 1일 08:00, 장기 토큰을 60일 연장합니다.
$ launchctl print gui/501/com.***.refresh | grep runs
runs = 0
$ ls logs/refresh.out.log
No such file or directory한 번도 안 돌았습니다. launchd는 프로세스를 띄우는 순간 stdout 파일을 만듭니다. 파일이 없다는 건 발화 자체가 없었다는 뜻입니다. 8월 1일 08:00에 맥이 깨어 있지 않았고, 그 세션은 그대로 사라졌습니다.
토큰 수명이 60일입니다. 다음 달 1일도 놓치면, 그때는 갱신 기회 없이 만료됩니다. 그 계정에 걸린 자동 게시가 전부 조용히 멈춥니다. 에러 없이, 알림 없이.
수동으로 돌려보니 정상 작동했습니다(약 60일 연장). 코드 문제가 아니라 한 번의 누락이 곧 만료가 되는 스케줄이 문제였습니다.
주 1회로 바꿨습니다. 갱신은 멱등이라 자주 돌아도 손해가 없고, 주 1회면 8번 연속 실패해야 위험해집니다. 월 1회는 1번 실패하면 끝입니다.
감시 대상이 되려면 흔적이 있어야 합니다
남은 무산출 하나는 매시간 텔레그램 요약을 쏘는 잡이었습니다. 파일을 안 남기니 화면에서 영원히 회색입니다. 죽어도 모릅니다.
한 줄 추가했습니다.
echo "$(date '+%Y-%m-%d %H:%M') heartbeat 발송 rc=$CODE ..." >> "$DIR/heartbeat.log"보낸 사실 자체를 남깁니다. 이제 감시판이 그 파일의 나이를 잽니다.
자가진단 3개
- 감시판의 기대 주기가 실제 스케줄과 일치합니까? 주중만 도는 잡, 야간에 쉬는 잡, 격주 잡. 한 번은 1:1로 대조해 보세요. 저는 34개 중 5개가 틀렸습니다.
- 경보 임계에 여유가 있습니까? 주기를 그대로 임계로 쓰면 멀쩡한 잡이 매 주기 끝자락마다 깜빡입니다. 그 깜빡임이 반복되면 사람은 경보를 무시하기 시작합니다.
- "산출물 없음"으로 표시된 칸이 있습니까? 그건 정상이 아니라 감시 불가입니다. 흔적을 남기게 하거나, 감시판에서 빼세요. 회색으로 남겨두면 볼 수 없는 걸 보고 있다고 착각하게 됩니다.
솔직한 부분
오탐 3개를 고친 건 사실 별 가치가 없습니다. 봇은 처음부터 멀쩡했고, 제가 화면을 잘못 만들어놨던 것뿐입니다.
가치가 있었던 건 오탐을 고치다가 회색 칸을 열어본 것입니다. 빨간불은 시끄러워서 언젠가는 봤을 겁니다. 회색 칸은 조용해서, 안 열어봤으면 9월 1일에 토큰이 만료되고 나서야 알았을 겁니다. 그때는 왜 게시가 멈췄는지 찾는 데 또 반나절을 썼겠죠.
경보의 진짜 실패 모드는 안 울리는 게 아닙니다. 거짓으로 자주 울려서 사람이 화면 자체를 안 믿게 되는 것입니다. 오탐을 방치하면 그 옆의 진짜 신호까지 같이 죽습니다.
지금 당신 감시판에서 "정상"도 "실패"도 아닌 애매한 상태로 오래 앉아 있는 항목을 하나 찾아보세요. 그게 제일 오래 방치된 문제일 가능성이 높습니다.