← 전체 글
히스토리언/약 15분 읽기/ 조회

지연 데이터가 보고서를 바꾸기 전에 히스토리언 Backfill 정책을 정하는 법

Store-and-forward 버퍼는 회선이 살아나는 순간 몇 시간 지난 값을 한꺼번에 밀어 넣습니다. 어떤 타임스탬프를 기준으로 할지, 늦게 온 값이 무엇을 덮어써도 되는지, 어떻게 표시할지, 이미 배포한 숫자를 바꿔도 되는지를 정하는 이야기.

히스토리언SCADA트렌드문제 해결프로젝트 노트

화요일 생산 보고서에는 41,200개로 찍혀 있었습니다. 목요일에 같은 보고서를 다시 열었더니 43,700개였습니다. 보고서 엔진에는 문제가 없었습니다. 원격 라인의 edge gateway가 화요일 오후에 망에서 떨어졌다가 수요일 23시 40분에 store-and-forward 큐를 비웠고, 다음 조회 때 보고서가 조용히 재계산된 것뿐입니다. 진짜 결함은 그 재계산을 허용할지 아무도 정해 두지 않았다는 점입니다. 히스토리언은 설정된 대로 정확히 동작했습니다.

Backfill은 원래 수집 시각이 지난 뒤에 샘플을 써 넣는 동작입니다. 예전 값을 예전 타임스탬프로 저장하는 것 자체는 쉽고, 어떤 제품이든 합니다. 다투게 되는 지점은 따로 있습니다. 어떤 시간을 기준으로 삼을지, 늦게 온 값이 무엇을 덮어써도 되는지, 나중에 그 값이 늦게 들어온 값이라는 걸 알아볼 수 있는지, 그리고 이미 누군가의 메일함에 들어간 숫자는 어떻게 할지입니다.

시간은 두 개지만 공정은 하나다

모든 샘플에는 측정 시각(장치가 값을 측정했다고 보는 시간)과 도착 시각(히스토리언이 실제로 저장한 시간)이 있습니다. 공정 분석에는 측정 시각이 필요합니다. 펌프가 10시 4분에 트립됐다면 트렌드도 10시 4분에 꺾여야 합니다. 반대로 통신 문제를 볼 때는 도착 시각이 필요합니다. 두 시간의 간격 자체가 조사 결과이기 때문입니다.

프로토콜이 뭘 주는지는 생각보다 편차가 큽니다.

  • OPC UA는 DataValue마다 SourceTimestampServerTimestamp를 함께 실어 보냅니다(Part 4). 다만 그대로 믿지 말고 확인해야 합니다. 하위 장치가 시간을 주지 않을 때 서버 시간을 SourceTimestamp에 그대로 복사해 넣는 서버가 흔하고, 그러면 구분은 이름만 남습니다.
  • **DNP3(IEEE 1815)**는 이벤트에 outstation이 시간을 붙입니다. 절대 시간(g2v2)을 쓰거나 CTO(Common Time of Occurrence, g51) 뒤에 상대 시간 변형(g2v3)을 붙이는 방식입니다. 무선이 복구된 뒤 버퍼에 쌓였던 이벤트가 제 시간에 꽂히는 이유가 이것입니다. 대신 IIN2.3은 따로 봐야 합니다. 이벤트 버퍼 오버플로는 지연이 아니라 유실이고, 어떤 backfill 정책으로도 되돌릴 수 없습니다.
  • Modbus에는 타임스탬프 필드 자체가 없습니다. 히스토리언이 폴링 시점에 시간을 찍습니다. 게이트웨이 뒤에 있는 Modbus 장치가 장애 동안 값을 버퍼링했다면, 게이트웨이가 직접 시간을 붙이지 않는 한 그 값들은 전부 재접속 시점에 일어난 것처럼 보입니다.

Collector 종류별로 타임스탬프를 어디서 만들고 히스토리언이 그걸 받아들이도록 설정돼 있는지 적어 두십시오. 시운전 때 10분이면 되고, 2년 뒤에 아무도 결론 못 내는 논쟁 하나를 미리 끝내 줍니다.

지연 데이터는 겉모습만 같은 네 가지 문제다

  • 셀룰러나 무선 장애 동안 원격 설비가 값을 버퍼링한 경우. 예상 가능한 상황이고, 장치의 버퍼 깊이가 한계입니다. 그 숫자를 알아 두십시오. 이벤트 2만 개를 보관하는 RTU가 초당 5개씩 쌓이면 한 시간쯤에 가득 차고, 그 뒤는 사라집니다.
  • Edge gateway 큐가 복구 후 밀려 나가는 경우. Sparkplug B를 쓴다면 버퍼된 메트릭에 is_historical = true가 붙어야 구독자가 두 시간 전 값을 현재 상태로 오해하지 않습니다. 이걸 빼먹는 게이트웨이가 장애 자체보다 더 큰 혼란을 만듭니다.
  • OPC UA 클라이언트가 재접속한 경우. 큐에 쌓인 값은 monitored item의 QueueSize가 1보다 크고 subscription이 끊김을 버텨 냈을 때만 재전송됩니다. 세션 자체가 죽었다면 누군가 HistoryRead를 하지 않는 한 아무것도 돌아오지 않습니다.
  • 패치 중에 멈춘 collector 서비스. 이게 제일 아픕니다. 그리고 이건 지연 데이터가 아니라 누가 알아챌 때까지 그냥 없는 데이터입니다. Backfill 규칙이 아니라 heartbeat 알람이 필요합니다.

실험실 분석값과 수동 품질 입력값은 별개 범주입니다. 몇 시간에서 며칠까지 늦고, 사람이 타임스탬프를 직접 타이핑하는 유일한 경우입니다.

아무도 안 적어 두는 항목: overwrite 규칙

OPC UA Part 11이 이 결정을 깔끔하게 정리해 두었고, UA가 아닌 시스템에서도 그대로 빌려 쓸 만합니다. HistoryUpdate는 PerformUpdateType으로 Insert, Replace, Update(없으면 넣고 있으면 바꿈), Remove를 구분합니다. 이미 데이터가 있는 시각에 Insert하면 Bad_EntryExists, 비어 있는 자리에 Replace하면 Bad_NoEntryExists가 돌아옵니다.

Collector는 Insert로 설정하십시오. 그래야 중복이 조용히 good 샘플을 덮어쓰는 대신 눈에 보이는 오류로 올라옵니다. 재시작 경계에서 중복은 드문 일이 아닙니다. Update와 Replace는 감사 로그가 붙은 수동 보정 경로에만, 그것도 지정된 담당자에게만 열어 두는 편이 낫습니다.

나머지 정책은 네 줄이면 됩니다.

  • 자동 허용 창. 기본을 24~48시간으로 잡고, 공정이 반박하면 조정합니다. 유틸리티 검침은 실제로 며칠이 필요합니다. 고속 포장 라인이라면 몇 분이면 되고, 거기서 사흘 늦게 들어온 값은 데이터가 아니라 고장 신고입니다.
  • 창 밖의 데이터. 승인과 감사 로그를 요구합니다. 아예 막지는 마십시오. 막으면 새벽 2시에 누군가 데이터베이스를 직접 건드립니다.
  • bad 품질은 good을 절대 덮어쓰지 않는다. 이 규칙 하나로 "밤사이 트렌드가 나빠졌다"류 문의 대부분이 사라집니다.
  • 원래 값을 남긴다. 값이 교체되면 이전 값은 어딘가에 살아 있어야 합니다. 아니면 사람들이 반드시 묻는 그 질문에 답할 수 없습니다.

Backfill된 값이 실시간 값과 똑같아 보이면 안 된다

두 시간짜리 통신 장애 구간에서 트렌드 선이 아무 표시 없이 이어져 있다면, 내부 품질 컬럼이 뭐라고 저장돼 있든 표시는 실패한 겁니다. 확인 방법도 그대로입니다. 운전자가 실제로 쓰는 트렌드 화면에서 장애 구간을 열어 보고, 그들이 구분할 수 있는지 보십시오.

OPC UA Part 11은 StatusCode 안에 historian 정보 비트를 정의합니다. raw인지 calculated인지 interpolated인지, 그리고 Partial 비트까지 있습니다. 이걸 트렌드에 제대로 노출하는 제품도 있고 아닌 제품도 많습니다. 플랫폼이 backfill된 good 값과 실시간 good 값을 구분하지 못한다면, 그냥 넘어가지 말고 annotation이나 companion 상태 태그를 쓰십시오. 구분할 가치가 있는 상태는 실시간 good, backfill된 good, 장애 중 bad/unavailable, 추정 또는 수동 입력, 검증 후 교체 — 다섯 가지면 거의 다 덮입니다.

Backfill을 켜기 전에 보고서 문제를 먼저 끝낸다

질문은 하나입니다. 이미 배포한 숫자가 바뀌어도 되는가.

  • 승인 시점에 고정하고, 늦게 온 데이터는 엔지니어링 트렌드에서만 보이게 한다. 공장 밖으로 나가는 문서에 대한 제 기본값입니다.
  • 정해진 창 안에서만 자동 재계산하고 그 뒤에는 고정한다. 내부 대시보드에는 합리적이고, 창의 길이를 반드시 문서에 적어야 합니다.
  • supervisor 검토 후에만 재계산한다. 사람이 서명하는 기록이라면 이쪽이 맞습니다.

나쁜 경우는 두 번째를 창도 표시도 없이 쓰는 것이고, 앞의 41,200 / 43,700 보고서가 정확히 그 사례입니다. 바뀔 수 있는 보고서라면 "생성 시각"과 "데이터 반영 기준 시각"을 문서 안에 찍어야 합니다. GxP 영역에서는 취향 문제도 아닙니다. 21 CFR Part 11은 기록 변경에 대해 누가, 왜 바꿨는지를 포함한 감사 추적을 요구합니다.

시운전 때 일부러 끊어 본다

버퍼가 어떻게 동작하는지를 첫 실제 장애 때 배우면 늦습니다.

태그 네 개를 고릅니다. 빠른 주기의 아날로그 하나, 디지털 하나, 적산값 하나, 알람 관련 상태 하나. 아날로그는 완만한 램프로 움직이게 두십시오. 램프는 결측, 중복, 시간 밀림이 눈으로 바로 보이는 모양입니다. 정상 수집을 확인한 뒤 소스 값이 계속 변하는 상태에서 상위 링크를 끊거나 forwarding 서비스를 30분쯤 멈춥니다. 복구하고 큐가 비워지는 걸 지켜본 다음 확인합니다.

  1. 타임스탬프가 재접속 시점에 몰리지 않고 장애 구간 안에 들어가는가.
  2. 재시작 경계에 중복 샘플이 없는가.
  3. 적산값이 한 번만 더해지는가. 이건 따로 시험해야 합니다. 히스토리언이 타임스탬프 순서가 아니라 도착 순서로 증분을 계산하면 replay된 카운터 값이 두 번 더해지고, 그 오차는 실제 생산 증가처럼 보입니다.
  4. 트렌드와 품질 플래그 양쪽에 장애가 드러나는가.
  5. 보고서가 정책대로 동작하는가.
  6. Replay된 값이 실시간 알람을 만들지 않는가.

마지막 항목은 따로 볼 값어치가 있습니다. 히스토리언이 과거 알람 관련 값을 저장하는 건 정상 동작입니다. 문제는 실시간 알람 시스템이 두 시간 전에 끝난 이벤트를 다시 띄우는 경우이고, 하필 가장 정신없는 시점에 알람 폭주가 됩니다. 일반적인 IEC 62682 / ISA-18.2 알람 철학에서 그걸 요구하는 조항은 없으니, 그렇게 동작한다면 설정 사고로 보면 됩니다.

다음 장애가 오기 전에, 원격 버퍼가 실제로 몇 시간치를 담는지 그리고 실제 회선에서 그걸 다 밀어내는 데 얼마나 걸리는지 확인해 두십시오. 여덟 시간치를 담는 게이트웨이가 되밀어 보내는 데 여섯 시간 걸리는 회선에 붙어 있다면 아직은 히스토리언 문제가 아니지만, 결국은 문제가 되고, 하필 긴 장애 때 알게 됩니다.