나에게 47번 경고를 보내던 워크플로우: n8n 실패 알림 중복 처리하기
이 글은 프로덕션 환경에서 n8n으로 LLM 워크플로우를 실행하는 과정에 대한 저의 '빌드-인-퍼블릭(build-in-public)' 시리즈의 일부입니다. 여기에 있는 모든 내용은 제가 판매하고 지원하는 실제 템플릿에서 가져온 것입니다. 실수는 제 것이고, 수정된 부분은 테스트를 거쳤습니다.
47페이지, 하나의 실패
어느 날 밤, 서비스 제공업체(provider)가 다운되었고, 제 휴대폰에는 47번의 알림이 도착했습니다. 47번의 실패가 아닙니다. 단 한 번의 실패가 아침에 서비스 제공업체가 다시 올라올 때까지 재시도되고 재알림되는 과정에서 발생한 것이었습니다.
각 재시도는 새로운 실행(execution)을 만들었고, 각 실행은 동일하게 다운된 서비스 제공업체에 도달했으며, 각각 자체적인 알림을 발송했습니다. 12페이지쯤 되자 저는 읽는 것을 그만두었습니다. 30페이지가 되었을 때는 채널을 음소거했습니다. 제가 실제로 조치를 취해야 할 진정한 실패는 자신과 똑같은 복사본 46개 밑에 파묻혀 있었고, 아침에 도착한 첫 번째 진짜 새로운 실패 알림은 이미 무시하는 법을 배운 채널로 들어왔습니다.
알림 시스템은 제가 요청한 대로 정확하게 작동했습니다. 즉, 실행이 실패할 때 저에게 알려주는 것이었습니다. 하지만 제가 요청하지 않았고, 그것도 해주지 않은 것은 '무엇인가 새로운 것'이 실패했을 때 알려주는 것이었습니다.
알림은 로그 라인이 아니다
실수는 알림을 실행(execution)에 연결한 것이었습니다. 모든 실행은 고유하기 때문에, 모든 실패가 새롭게 보이는 것입니다. 알림은 '실패 자체'에 연결되어야 하며, 실패는 어떤 실행이 우연히 도달했는지 여부가 아니라 무엇이 망가졌는지(what broke)를 통해 식별되어야 합니다.
저는 알림이 발송되기 전에 모든 알림을 지문 인식(fingerprint)합니다. 워크플로우 ID, 실패한 노드(node), 그리고 오류 클래스—서비스 제공업체 타임아웃, 429, 4xx 유효성 검사, 크래시-알 수 없음 등입니다. 실행 ID나 타임스탬프, 배치 사이즈가 아닙니다. 이들은 재시도할 때마다 변경되기 때문에 지문 인식을 무력화할 것입니다. 동일한 지문을 가진 두 개의 알림은 달리 증명되지 않는 한 같은 실패로 간주됩니다.
이 지문은 작은 표에 기록되며, 첫 번째 알림 시간과 함께 카운트가 표시됩니다. 규칙은 간단합니다:
- 지문 최초 감지 시: 전체 컨텍스트와 함께 즉시 알림 전송.
- 대기 시간 내 동일한 지문 재감지 시: 다시 알림을 보내지 않음. 카운트를 증가시키고, 가장 최근 발생 시점을 기록함.
- 창이 종료되었는데도 지문이 계속 발생하는 경우: 47번의 페이지가 아닌, "여전히 실패 중, 02:14부터 47회 발생, 마지막 감지 03:02"와 같은 요약본(digest)을 한 번 전송함.
- 새로운 지문 감지 시: 다른 지문의 대기 시간 내에도 즉시 알림 전송. 새로운 실패는 절대 이전 실패 뒤에 기다려서는 안 됨.
대기 시간(cooldown window)은 추측이 아닌 실제 숫자가 필요한 부분입니다. 제공업체 오류의 경우 30분이 적절한데, 그보다 짧은 기간의 제공업체 장애는 보통 스스로 해결되고, 아무리 사람이 2분 만에 알림을 보내도 제공업체 장애를 고칠 수는 없기 때문입니다. 제가 작성한 코드 자체의 오류(잘못된 쿼리, 누락된 필드 등)의 경우 대기 시간은 5분인데, 이는 자가 치유되지 않으며 두 번째 페이지는 첫 번째 수정이 작동하지 않았음을 의미하기 때문입니다. 이 대기 시간은 전역 상수(global constant)가 아니라 오류 클래스(error class)의 속성입니다.
세 가지 메시지, 결코 하나로 혼합되어서는 안 됨
첫 알림, 요약본(digest), 그리고 에스컬레이션은 단어 선택을 공유해서는 안 됩니다. 왜냐하면 이들은 서로 다른 조치를 요구하기 때문입니다:
- 첫 알림: "Workflow X failed at node Y: provider timeout." 조치: 확인하고, 이것이 당신이 고쳐야 할 문제인지 결정함.
- 요약본(Digest): "Still failing: 47 occurrences, first seen 02:14, last seen 03:02." 조치: 새로운 정보가 없다. 단지 카운트가 예상보다 빠르게 증가하는 경우를 제외하고는 — 이는 비상 상황이 아니라 정보임.
- 에스컬레이션(Escalation): "Still failing after three windows." 조치: 이것은 더 이상 일시적인 장애(transient outage)가 아님. 재할당하거나, 계획을 변경해야 함 — 경로를 비활성화하거나, 제공업체를 전환하거나, 고객에게 알림을 보내야 함.
상향 알림(escalation)은 대부분의 경고 설정이 건너뛰는 메시지이며, 바로 이것이 가장 중요합니다. 상향 알림이 없다면, 밤새 지속된 장애와 단 10분 동안 지속된 장애가 똑같이 보입니다. 즉, 한 번의 페이지 발생 후 침묵하는 것입니다. 페이지 발생 이후의 침묵은 '해결됨'으로 간주됩니다. 계속 실패하고 있는 집계 보고서(digest) 뒤에 상향 알림이 오면, 그것이 무엇인지—즉, 해결되지 않았고 시간이 지남에 따라 심각도가 높아지고 있다는 것—로 인식됩니다.
또 다른 함정이 있습니다. 서비스 중단 기간 중에 실행이 한 번 성공했다고 해서 카운트를 초기화해서는 안 됩니다. 빨간색 경고 사이에 나타나는 단 하나의 녹색 실행은 보통 복구가 아니라 불안정한 제공자(flaky provider)의 징후일 뿐입니다. 저는 실제로 발생한 기록이 없는 완전한 침묵 기간 이후에만 식별자(fingerprint)를 초기화합니다. 첫 성공 시점에 이를 초기화하면, 불안정한 제공자가 플랩(flap)할 때마다 경고가 울리는 상황을 초래하며, 이는 본래 문제의 더 예쁜 이름일 뿐입니다.
테스트는 죽은 제공자가 아닌, 불안정한 제공자여야 한다
단순히 다운된 제공자로 이를 테스트하는 것은 거의 아무것도 증명하지 못합니다. 왜냐하면 죽은 제공자는 어느 쪽이든 하나의 식별자와 한 번의 페이지를 발생시키기 때문입니다. 중요한 테스트는 플랩(flapping)을 하는 경우입니다. 즉, 동일한 식별자를 가지고 두 개의 냉각 기간에 걸쳐 실패-성공-실패-성공을 반복하는 것입니다. 세 가지 사항을 확인해야 합니다. 첫째, 첫 번째 장애에 대해 정확히 한 번의 페이지가 발생해야 하며, 플랩할 때마다 하나씩 발생하는 것이 아니어야 합니다. 둘째, 해당 기간이 끝날 때 진정한 발생 횟수(true occurrence count)를 가진 하나의 집계 보고서(digest)가 나와야 합니다—n8n에서의 실행 횟수가 아니라, 발생했을 페이지 수를 계산해야 합니다. 셋째, 냉각 기간 동안 실제로 다른 식별자가 감지되면 즉시 페이지가 발생해야 합니다. 왜냐하면 이전 장애의 창 뒤에 새로운 실패를 억제하는 것은 그것을 대체한 경고 폭풍보다 더 나쁜 상황이기 때문입니다.
이러한 테스트는 또한 적절한 시간 간격(window)을 설정하는 방법이 됩니다. 만약 이 기간이 실제 서비스 중단 시간보다 길다면, 고객이 기다리는 동안 집계 보고서에 머무르게 될 것입니다. 반면, 제공자의 플랩 주기보다 짧다면, 모든 플랩마다 경고가 울리게 됩니다. 저는 사전에 임의로 설정하기보다는 실제 장애 사례를 기준으로 삼아, 제공자 오류에는 30분, 제 코드 자체에는 5분을 적용하는 지점에 도달했습니다.
원칙
알림(페이지)은 인간의 주의를 요구하는 것이며, 주의는 시스템에서 가장 희소한 자원입니다. 모든 중복 알림(페이지)은 이 주의를 소모하며, 중복으로 가득 찬 채널은 인간에게 그 채널을 무시하도록 학습시키고—결과적으로 다음 실제 장애가 놓치게 만듭니다. 장애를 지문 인식(fingerprint)하여 한 번만 페이지로 만들고, 나머지는 카운트하며, 창이 끝날 무렵에 정리하고, 그 시간이 창을 초과할 때 에스컬레이션(escalate)해야 합니다. 목표는 알림의 수를 줄이는 것이 아닙니다. 도착하는 모든 알림이 인간이 이미 본 적 없는 것일 때가 목표입니다.
저는 AI Automation Lab을 구축하고 있습니다—운영 환경에서 사용 가능한(production-ready) n8n 워크플로우 템플릿입니다. 무료 AI Hook Generator는 제가 실제 워크플로우를 어떻게 구성하는지 보여주며, 저의 오픈 소스 템플릿은 github.com/zhp910318/n8n-ai-automation-lab에 있습니다.