Skip to content

bug(observability): request_outlived_lifetime 경보가 세션 나이와 턴 타임아웃을 비교해 정상 장시간 작업을 leak으로 오탐 #1291

Description

@seoseo-ai

증상

노숙에서 request_outlived_lifetime 경보가 30분마다, 정확히 1800초씩 증가하며 3시간 넘게 반복 발생했습니다.

01:45  Oldest in-flight request is  3653s old, beyond its 3600s lifetime — the request lifecycle leaked (#307 class).
02:15                               5453s
02:45                               7253s
03:15                               9054s
03:45                              10854s

실제로는 누수가 아니었습니다. 해당 요청은 살아 있는 대화형 세션(PID 984716, --resume=82683451-…, 00:44:21 시작)이었고, 그 프로세스가 종료되자 경보도 함께 멈췄습니다(이후 90분간 무발생). 유령 레지스트리 항목이 아니었습니다.

원인 — 단위가 다른 두 시계를 비교합니다

bridge/utils/health_alerts.pyevaluate_alerts()가 비교하는 두 값의 출처가 다릅니다.

출처 실제 의미
oldest_request_age_seconds project_chat.workload_snapshot()_agent_session_registry.metrics().oldest_started_at 에이전트 세션의 나이
request_lifetime_seconds project_chat._process_timeout_seconds 턴 하나에 걸리는 wait_for 한계

_process_timeout_secondsproject_chat_process.py:1019asyncio.wait_for(..., timeout=...)턴 단위에 강제됩니다. 반면 workload_snapshot()은 docstring이 밝히듯 "self-update가 요청 중 재시작을 피하려고" 세션 단위 busyness를 노출하는 함수입니다.

지속 스트리밍 세션(--input-format stream-json)은 프로세스 하나가 여러 턴을 처리합니다. 따라서 세션 나이가 턴 타임아웃을 넘는 것은 정상 동작이며, 넘었다는 사실 자체로는 아무것도 새지 않았음을 뜻합니다. 타임아웃이 실패한 게 아니라 애초에 적용 대상이 아닙니다.

workload_snapshot()background_workload_snapshot(run-in-background Bash 작업)도 합산하므로, 장시간 백그라운드 작업 역시 같은 방식으로 나이를 밀어올립니다.

#307 / #860과의 관계 — 재발이 아닙니다

경보 문구가 #307 class라고 단정하지만 성격이 다릅니다.

두 이슈 모두 CLOSED이며, 본 건을 그 재발로 묶으면 오분류가 됩니다.

왜 고쳐야 하는가

경보 피로가 실질적 위험입니다. 장시간 작업(대형 리팩터·하네스 실행·리뷰 스윕)마다 "leaked"가 반복 발화하면, 오퍼레이터는 이 경보를 무시하도록 학습됩니다. #307이 두 번 재발한 영역인 만큼 다음 진짜 누수를 놓치게 만드는 대가가 큽니다.

제안

판정식을 같은 단위끼리 비교하도록 바꾸는 방향을 제안합니다. 택일 또는 조합:

  1. 턴 나이로 비교 — 세션 시작이 아니라 현재 턴 시작 시각을 노출해 _process_timeout_seconds와 맞춥니다. 의미상 가장 정확합니다.
  2. 세션 전용 임계값 분리 — 세션 나이에는 별도의(훨씬 큰) 상한을 두고, 문구에서 #307 class와 "leaked" 단정을 제거합니다.
  3. 생존 확인 추가 — 해당 세션의 자식 프로세스가 실제로 살아 있고 진행 중이면 경보하지 않습니다. 진짜 누수(고아 항목)만 남습니다.

3번이 진짜 누수와 정상 장시간 작업을 가장 확실히 가르지만, 1번이 가장 작은 변경으로 오탐을 없앱니다.

재현

process_timeout_seconds보다 오래 지속되는 대화형 세션을 유지하면 30분 주기로 재발합니다. 별도 조작이 필요 없습니다.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions