초록색은 타임스탬프입니다
요약
하드코딩된 날짜와 잘못된 반환 코드 합산 방식이 어떻게 시스템의 상태를 오판하게 만드는지 두 가지 사례를 통해 설명합니다. 테스트와 모니터링 결과가 시간이 흐름에 따라 어떻게 신뢰를 잃을 수 있는지 경고합니다.
핵심 포인트
- 테스트 픽스처에 날짜를 하드코딩하면 시간이 흐름에 따라 테스트가 실패할 수 있음
- 상태 확인 시 반환 코드를 잘못 합산하면 타임아웃을 성공으로 오인할 수 있음
- 초록색 체크(통과)는 영구적인 약속이 아닌 특정 시점의 영수증임
- 코드 변경 없이도 시간의 흐름만으로 시스템 경보가 무력화될 수 있음
아무도 코드를 건드리지 않은 날, 나의 Medicare (메디케어) 환급 엔진에서 테스트가 실패했습니다. 커밋도, 배포도, 새로운 데이터도 없었습니다. 무엇이 문제를 일으켰는지 찾기 위해 diff (차이점)를 두 번이나 읽었지만, 읽을 diff 자체가 없었습니다. 변한 것은 날짜였습니다. 테스트에 하드코딩된 (hardcoded) 날짜가 있었고, 달력이 마침내 그 날짜를 지나쳐 버린 것이었습니다. 테스트는 작성된 날에는 틀리지 않았습니다. 하지만 지금은 틀렸습니다. 그 차이는 올해 내가 배운 것 중 가장 유용한 것이 되었습니다.
만료된 테스트
이 엔진은 Medicare (메디케어)를 위한 원격 치료 모니터링 환급을 처리합니다. 일부 기록은 소급 적용이 가능하지만, 오직 72시간 이내의 창 (window) 안에서만 가능합니다. 그리고 한 테스트가 누군가 수동으로 입력한 fixture (픽스처) 날짜를 사용하여 그 로직을 확인하고 있었습니다. 작성된 날에는 그 날짜가 안전하게 창 안에 있었고, 테스트는 올바른 이유로 통과했습니다. 그 후 매일 날짜가 지나며 그 날짜를 창의 경계선으로 몰아넣었습니다. 코드의 어떤 것도 변하지 않았습니다. 픽스처는 우유가 유통기한을 지나듯 조용히, 그리고 예정된 대로 창을 넘어 노화되었고, 어느 날 아침 테스트는 빨간색(red)이 되었습니다. 잘못된 경보였습니다. 시스템은 올바른데, 체크는 실패한 것이죠.
당황스러운 점은 픽스처를 확인하기 전에 얼마나 오랫동안 코드를 뚫어지게 쳐다보았느냐 하는 것입니다. 테스트가 실패하면, 당신은 코드가 변했다고 가정합니다. 코드는 그대로 머물러 있는데 테스트가 변했을 수도 있다는 다른 가능성을 받아들이기까지 시간이 좀 걸렸습니다.
초록색 체크는 영수증이지 약속이 아닙니다. 그리고 영수증은 시간이 지나면 효력이 다합니다.
거짓말을 한 Cron (크론)
또 다른 실패는 동일한 플릿 (fleet) 내의 광고 기술 (ad-tech) 측에서 발생했으며, 이는 정반대 방향으로 움직였습니다. 그곳에서는 개선 루프 (improvement loop)가 매일 밤 cron (크론) 작업으로 실행됩니다. 이 루프는 사이클을 수행하고 밤 사이 작업이 잘 진행되었는지 보고합니다. 판단을 위해, 루프는 각 단계의 return code (반환 코드)를 합산했습니다. 그런데 합산하는 방식 때문에, 타임아웃 (time-out)이 발생한 단계가 OK (성공)로 계산되었습니다. 그래서 루프가 타임아웃되기 시작했을 때도 보고서는 몇 주 동안이나 초록색(green)을 유지했습니다.
무엇이 고장 났는가: 몇 주 동안 야간 개선 크론 (cron) 작업이 타임아웃(timeout)되었음에도 OK (성공)로 보고되었습니다. 상태 확인 (status check) 과정에서 반환 코드 (return codes)를 합산하는 방식이 타임아웃을 성공으로 읽을 수 있게 만들었기 때문입니다. 매일 아침 보고서는 초록색이었습니다. 매일 아침 나는 그것을 믿었습니다.
두 가지 실패 사례를 나란히 놓아보겠습니다. 의료 분야에서는 시간이 올바른 테스트를 허위 경보 (false alarm)로 만들었습니다. 광고 기술 (ad tech) 분야에서는 시간이 실패하는 작업을 허위 상황 해제 (false all-clear)로 만들었습니다. 어느 시스템도 수정되지 않았습니다. 유일한 입력값은 시간이 흐른 것뿐이었습니다.
- 72시간: 하드코딩된 고정값인 소급 적용 (backdate) 윈도우가 경과한 시간
- 0개: Medicare 테스트가 빨간색으로 변한 날의 커밋 (commit) 수
- 0개: 개선 크론 (cron)이 타임아웃된 몇 주 동안 발생한 알람 (alarm) 수
- 2개: 시간만으로 판결이 뒤집힌 산업 분야
초록색이 실제로 의미하는 것
여기에 제가 저질러온 실수, 그리고 대부분의 사람들이 저지르는 실수가 있습니다. 저는 초록색을 하나의 상태 (state), 즉 '시스템이 작동함'으로 취급했습니다. 하지만 통과된 확인 (passing check)이 알려주는 것은 그것이 아닙니다. 통과된 확인은 특정 시점에, 특정 가설 (assumptions) 하에서, 하나의 구체적인 질문에 대해 당신이 기대했던 답변을 얻었다는 것을 알려줄 뿐입니다. 확인이 끝나는 즉시 그 순간은 지나가 버립니다. 당신이 코드를 수정하든 안 하든 가설들은 계속해서 표류합니다. 어떤 확인은 오늘이 며칠인지 가정합니다. 어떤 것은 실패한 단계가 이를 알려줄 것이라고 가정합니다. 어떤 것은 누군가가 보고서를 읽고 있다고 가정합니다. 이 모든 가설에는 반감기 (half-lives)가 있습니다.
핵심 통찰 (Key insight): 초록색은 상태 (state)가 아니라 타임스탬프 (timestamp)입니다. 확인 (check)은 만료되는 가설 하의 한 순간을 검증하는 것이므로, 확인 작업 역시 코드와 마찬가지로 만료 감사 (expiry audit)가 필요합니다.
우리가 코드를 감사 (audit)하는 이유는 그것이 부식 (rots)된다는 것을 알기 때문입니다. 우리는 검증 (verification)을 거의 감사하지 않는데, 통과된 확인이 시스템에서 확실히 괜찮은 유일한 부분처럼 보이기 때문입니다. 그것은 정확히 반대입니다. 통과된 확인이야말로 아무도 들여다보지 않는 부분입니다.
확인 작업의 만료일
이제 두 산업 분야의 플릿 (fleet)은 동일한 방식으로 작동합니다.
- 모든 초록색(green)을 날짜가 지난 것으로 간주하십시오. 문제는 체크 결과가 초록색인지가 아니라, 그 초록색이 마지막으로 의미를 가졌던 것이 언제인가 하는 점입니다.
- 이제 절대 하드코딩하지 마십시오. 벽시계 날짜(wall-clock date)가 포함된 피스처 (fixture)는 상수가 아닙니다. 그것은 카운트다운입니다.
- 침묵을 크게 만드십시오. 모든 크론 (cron)은 외부의 하트비트 (heartbeat)에 보고합니다. 따라서 작업이 조용해진다는 것은 성공을 가정한 것이 아니라 알람입니다.
- 정기적으로 감사인 (auditors)을 감사하십시오. 의존성 (dependencies)을 CVE(취약점)에 대해 검토하는 것처럼, 피스처 (fixture)도 벽시계 가정 (wall-clock assumptions)에 대해 검토되어야 합니다.
결과: 이제 플릿 (fleet) 내의 모든 크론 (cron)은 외부 하트비트 (heartbeat)를 전달하며, 모든 테스트 피스처 (test fixture)는 벽시계 가정 (wall-clock assumptions)에 대해 감사를 받습니다. 침묵하는 작업은 기본적으로 알람이며, 어떤 테스트도 시간이 지나면 유효하지 않게 될 날짜를 포함하여 배포되지 않습니다.
이 중 어떤 것도 영리한 것이 아닙니다. 그것이 핵심입니다. 내 시스템의 영리한 부분들은 결코 문제가 되지 않았습니다. 문제는 수동으로 입력된 날짜와 상태 스크립트 (status script)의 합계였습니다. 실패는 저렴했습니다. 침묵은 값비쌌습니다.
코드는 변경할 때 부패합니다. 체크 (checks)는 변경하지 않을 때 부패합니다. 스스로를 감시해야 하는 시스템을 운영하고 있다면, 가장 오래된 초록색 체크 (green check)를 찾아 그것이 최근에 무엇을 검증했는지 물어보십시오. 대답은 대개 날짜일 것입니다. 그 질문을 던지는 빌더 (builders)들은 서로를 찾아내야 합니다.
원문은 nabbilkhan.com에 게시되었습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기