지각 전송은 90일, 미래 시각은 5분: 에이전트 시계 오차를 비대칭으로 처리한 이유

프로세스 수집 서버가 에이전트 시각의 과거·미래 오차를 모두 90일까지 허용해, 미래 자료가 집계를 오염시키고 최대 270일 남을 수 있었다. 지각 전송은 보존하면서 미래 시각만 5분으로 제한한 근거와 검증 결과를 정리한다.

개요

프로세스 수집 서비스의 process.py는 abs(server_ts - agent_ts) <= 90일이면 에이전트가 보낸 시각을 사용했다. 과거와 미래를 같은 범위로 허용했지만 두 방향은 성격이 다르다. 과거 자료에는 정상적인 지각 전송이 섞여 있고, 미래 자료에는 정상 사례가 없다.

과거 허용 범위는 90일로 유지하고 미래 방향만 5분으로 줄였다. 구현 커밋은 38da2a0이며 다음 패키지 회차에 포함된다.

프로세스 자료만 에이전트 시각을 믿는다

수집 경로hour를 정하는 시각
detect.py서버 수신 시각 _stamp
system.py서버의 현재 시각(5분 버킷·슬롯)
process.py에이전트 타임스탬프

프로세스 자료는 지각 도착이 정상이다. PC가 꺼진 동안 보내지 못한 자료가 며칠 뒤 전송될 수 있으며 문서로 확인한 최대 지연은 317시간이다. 해당 자료는 전송 시각이 아니라 측정 시점 버킷에 들어가야 한다. 서버 시각을 사용하면 밀린 자료가 모두 오늘 자료로 기록된다.

에이전트 시각을 사용하는 것은 필요하다. 문제는 그 신뢰 범위를 과거와 미래에 똑같이 적용한 점이었다.

과거와 미래가 다른 이유

구분시각이 과거인 이유서버가 구분할 수 있는가
지각 도착PC가 꺼진 동안 전송하지 못했다. 측정 시각은 맞다구분 불가
시계 오류PC 시계가 틀렸다. 측정 시각도 틀렸다구분 불가

서버에는 두 경우 모두 과거 시각이 찍힌 문서로 들어온다. 지각 자료를 받으려면 과거 방향의 시계 오류도 함께 허용할 수밖에 없다.

미래에는 이런 딜레마가 없다. 정상적인 자료가 미래 시각으로 측정될 이유가 없으므로 허용 범위를 줄여도 정상 지각 자료를 잃지 않는다.

미래 시각이 만드는 문제

  1. 해당 시각이 올 때까지 집계되지 않는다. 집계 대상 hour는 서버의 현재 시각을 기준으로 계산한다.
  2. 해당 시각이 오면 과거에 측정한 값이 현재 측정한 값처럼 집계에 섞인다.
  3. TTL이 hour + 보존기간으로 만료 시점을 계산하므로, 보존기간이 180일이면 미래 자료는 최대 270일 남는다.

초안에서는 첫 항목을 영구 누락으로 봤지만 모형으로 확인한 결과 누락이 아니라 지연이었다. 시계가 해당 시각을 따라잡으면 hourly cron이 그 hour를 정상 집계한다. 실제 문제는 집계 자료 오염이다.

실제 수신 시각과 에이전트 시각의 차이

검증 환경의 최근 12시간 자료 24,978건에서 서버 수신 시각과 에이전트 타임스탬프를 비교했다.

항목값
표본24,978건
최소0초
중앙값175초(2.9분)
최대91,571초(25.4시간)
미래 문서0건(0.00%)

5초 안에 도착한 문서가 0.72%였고 18대 중 4대는 최소 지연이 5초 미만이었다. 에이전트 시계가 5초만 앞서도 측정할 수 있는 자료였지만 미래 문서는 없었다.

측정과 수신 사이에는 항상 시간이 흐르므로 지연 값이 과거 방향으로 늘어지는 것은 자연스럽다. 현재 자료에서 새 기준에 걸리는 문서는 0건이었다. 최대 25.4시간 지연은 과거 허용 범위 90일 안이다.

구현

허용 범위를 두 상수로 분리했다.

PAST_SKEW_TOLERANCE_SECONDS = 90 * 24 * 3600
FUTURE_SKEW_TOLERANCE_SECONDS = 5 * 60

skew = server_ts - agent_ts  # 양수면 에이전트가 과거
if -FUTURE_SKEW_TOLERANCE_SECONDS <= skew <= PAST_SKEW_TOLERANCE_SECONDS:
    hour = agent_ts
else:
    hour = server_ts

허용 범위를 벗어난 문서를 버리지 않는다. 기존 fallback 경로에 따라 서버 시각으로 저장하며 hour만 바뀐다.

미래 허용 범위를 5분으로 정한 근거

정상 지터와 시계 오류 사이에 넓은 빈 구간이 있어 1분부터 30분 사이의 값을 써도 잡는 오류와 놓치는 오류가 같았다.

구간예상 원인
0~수십 초정상 지터
약 2분몇 주 동안 동기화하지 않은 PC의 시계 표류(±50ppm이면 하루 약 4초)
2분~1시간관측되지 않은 구간
1시간서머타임 계산 오류
9시간시간대 설정 오류
며칠~몇 달메인보드 배터리 방전

상한은 1시간보다 확실히 작아야 한다. 1시간을 허용하면 서머타임 오류가 그대로 통과한다. t만큼 앞선 시계는 매시간 마지막 t분의 자료를 다음 시간 칸으로 보낸다. 5분이면 8.3%, 30분이면 50%, 60분이면 100%다.

하한은 실제 수신 지터보다 커야 한다. 5초 안에 도착하는 문서가 있으므로 10초 정도로 줄이면 정상 PC도 fallback에 걸리고 관측 카운터의 의미가 흐려진다.

미래 시각 제한에 걸리는 자료는 방금 측정된 문서다. 지각 자료는 skew가 양수라 영향을 받지 않는다. 이 경우 NTP에 맞춘 서버 시각이 잘못된 PC 시각보다 정확하다.

기준을 잘못 잡았을 때의 차이

설정잘못 처리된 문서의 시각 오차
미래 허용 범위가 너무 좁음서버 시각 사용, 최대 1시간 차이
미래 허용 범위가 너무 넓음에이전트 시각 사용, 최대 90일 차이

두 경우의 최대 오차가 90배 다르다.

관측 정보

fallback 카운터를 과거와 미래로 분리했다. 과거 fallback은 지각 전송 또는 메인보드 배터리를 의심하고, 미래 fallback은 시계·시간대 설정을 확인해야 한다.

경고에는 방향, 노드 ID, skew를 기록한다. 단순 건수만으로는 어느 PC를 점검해야 하는지 알 수 없기 때문이다.

[ingest/sprocess] timestamp fallback(미래, agent 시계가 앞섬):
  id=node-1 agent_ts_raw=... server_ts=... skew=-32400s
  (누적 fallback:1 미래:1)

이 경로는 수집 처리 중 자주 실행되므로 기존 로그 제한인 5초 1회와 누계 방식을 유지했다. 로그 I/O가 paho 콜백을 오래 잡으면 QoS 0 메시지를 잃을 수 있다.

검증

tests/test_sprocess_hour_skew.py에 17건을 추가했고 service 전체 테스트 320건이 통과했다.

다음 변이 6종은 모두 테스트가 검출했다.

  • abs()를 사용해 과거·미래 대칭으로 되돌림
  • 미래 허용 범위 확대
  • 미래 카운터 제거
  • 과거 자료를 미래로 분류
  • 로그에서 방향 표시 제거
  • 로그 호출 제한 제거

허용 시간의 숫자 대신 과거 범위가 미래 범위보다 크다는 관계를 테스트했다. 나중에 값을 조정해도 의도한 비대칭은 유지된다.

검증 중 방향 표시를 지워도 통과하는 잘못된 테스트를 발견했다. 누계 문구의 미래:0이 단순 문자열 조건에 우연히 걸린 것이 원인이었다. 실제 방향을 나타내는 문구를 검사하도록 고쳤다.

범위 밖 항목

  • 이미 저장된 미래 hour 문서 정리: 두 검증 환경에서 0건이어서 정리 대상이 없었다. 다른 서버에서 발견되면 해당 시각이 오기 전에 처리해야 한다.
  • 보존기간을 90일보다 짧게 만드는 설정: 과거 허용 범위와 함께 조정해야 한다. 그렇지 않으면 61~90일 늦은 자료가 도착 직후 로그 없이 삭제될 수 있다.
  • filelist: hour 필드를 저장하지 않아 이 처리의 대상이 아니다.

Orange Platform 프로세스 텔레메트리의 타임스탬프 허용 범위 분석 리포트입니다.