Files
HC900-Crawler/docs/작업지시-history-디지털잔재정리.md
windpacer bb99729e9e chore(report): 디지털잔재정리 문서 폐기 + 리포트 .QV 7개 keep 재분류
- docs/작업지시-history-디지털잔재정리.md: SUPERSEDED 배너(실행 금지).
  2026-06-18 실측 — 삭제대상(비-archive ~2천만)은 디지털 잔재 0, 전부 아날로그
  루프/적산. 특히 .QV는 리포트(history_table)가 의존 → 삭제 시 과거 리포트 파손.
- scripts/sql/report_keep_qv_tags.sql: SteamFlow 적산 FIQ-*.QV 7개를
  archive_enabled=TRUE로 재분류(register-map 누락 보정). realtime/is_active=FALSE
  로 폴링 부작용 0. 멱등(map 재빌드 후 재적용). DB에는 이미 적용.

검증: 리포트 태그 삭제대상 0행, 전체 삭제대상 19.98M→17.62M(2.36M 보호).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 20:42:37 +09:00

6.0 KiB

작업지시: hc900.history_table 디지털 상태점 잔재 정리

SUPERSEDED — 실행 금지 (2026-06-18 검증으로 폐기). 이 문서의 삭제 절차(§2·§3)를 실행하지 말 것. 작성 시점(2026-06-09) 이후 P0/P2 셀프서비스 리포트가 추가되면서 전제가 무효가 되었다.

2026-06-18 실측 분석 결과archive_enabled=FALSE 삭제대상(약 2천만 행)의 구성:

속성 행수 숫자비율 정체
.QV 5.45M 95% 적산 totalizer — 리포트 production_total/mass_balance_closure가 history_table에서 직접 읽음
.HZ/.HZSET 8.88M 100% 펌프 주파수
.PV 2.62M 100% 공정 측정값
.SP/.OP/.LSET 3.03M 100% 설정·출력·리미트
  • 삭제대상에 디지털 상태점(state 라벨/비숫자) 행은 0개 — 원래 목표였던 디지털 잔재는 이미 정리 완료 상태다(history_table에 남아있지 않음).
  • 남은 비-archive는 잔재가 아니라 루프 내부 파라미터 + 적산값이며, 특히 .QV는 리포트 기본 소스(history_table)에서 메트릭이 의존한다. 삭제 시 과거 일/월/연 리포트가 깨진다.
  • 후속 조치(완료): 리포트 의존 .QV 7개(FIQ-6115/6215/8115/9115/9215/10115/10215.QV)가 register-map 누락으로 keep에서 빠져 있어 archive_enabled=TRUE로 재분류 → scripts/sql/report_keep_qv_tags.sql (멱등, map 재빌드 후 재적용). 디지털 잔재 정리 자체는 완료로 종결.

아래 원문은 이력 보존용으로만 남긴다.


목적 (원문 — 폐기됨)

hc900.history_table에는 디지털 상태점(예: AG-3202, C3601_RUN, CH-5601{0 | L-STOP | } 형태, state 라벨 보유)의 과거 이력 약 15만 행이 잘못 쌓여 있다. 이들은 상태변경 이벤트라 event_history_table로 가야 하며 history_table(연속 측정값 60초 스냅샷)에는 있으면 안 된다.

이미 처리된 것(배경):

  • register-map 재작성으로 디지털 상태점(StatusPoint PV)은 map_master에서 archive_enabled = FALSE로 설정됨.
  • 실행 중 SnapshotToHistoryAsyncarchive_enabled + 디지털 제외(state 라벨)로 신규 디지털 행을 더는 적재하지 않음(검증됨: 재시작 후 신규 유입 0).
  • 따라서 남은 과거 잔재만 삭제하면 된다. (비가역 → 반드시 백업 먼저)

판정 기준

history_table에서 archive_enabled가 아닌 (controller_id, tagname) 행을 삭제한다. 이는 디지털 상태점 + 루프 내부 파라미터 등 비-archive 태그를 모두 정리한다. 유지: archive_enabled=TRUE (루프 PV/SP/OP/MD/QV + 아날로그 신호태그).

주의: 행수는 시점에 따라 변하므로 실행 시점에 live로 재계산할 것. 아래는 절차.

실행 절차 (복붙 가능)

1) 규모 확인

docker exec iiot-timescaledb psql -U postgres -d iiot_platform -At -c "
WITH arch AS (SELECT controller_id, tagname FROM hc900.hc900_map_master WHERE archive_enabled)
SELECT 'keep_rows',   count(*) FROM hc900.history_table h
  WHERE EXISTS (SELECT 1 FROM arch a WHERE a.tagname=h.tagname AND (a.controller_id=h.controller_id OR h.controller_id IS NULL))
UNION ALL
SELECT 'delete_rows', count(*) FROM hc900.history_table h
  WHERE NOT EXISTS (SELECT 1 FROM arch a WHERE a.tagname=h.tagname AND (a.controller_id=h.controller_id OR h.controller_id IS NULL));
"

2) 백업 (gzip CSV) — 삭제 대상 행

BK=/home/windpacer/db_backups; mkdir -p "$BK"
TS=$(date +%Y%m%d)
docker exec iiot-timescaledb psql -U postgres -d iiot_platform -c "
COPY (
  SELECT h.* FROM hc900.history_table h
  WHERE NOT EXISTS (
    SELECT 1 FROM hc900.hc900_map_master a
    WHERE a.archive_enabled AND a.tagname=h.tagname
      AND (a.controller_id=h.controller_id OR h.controller_id IS NULL))
) TO STDOUT WITH CSV HEADER" | gzip > "$BK/history_nonarchive_digital_$TS.csv.gz"
echo "backup rows: $(( $(zcat "$BK/history_nonarchive_digital_$TS.csv.gz" | wc -l) - 1 ))"
ls -lh "$BK/history_nonarchive_digital_$TS.csv.gz"

→ 백업 행수가 1단계 delete_rows정확히 일치하는지 확인. 불일치면 중단.

3) 삭제 (트랜잭션)

docker exec iiot-timescaledb psql -U postgres -d iiot_platform -v ON_ERROR_STOP=1 -c "
BEGIN;
DELETE FROM hc900.history_table h
WHERE NOT EXISTS (
  SELECT 1 FROM hc900.hc900_map_master a
  WHERE a.archive_enabled AND a.tagname=h.tagname
    AND (a.controller_id=h.controller_id OR h.controller_id IS NULL));
COMMIT;
"

4) 검증

docker exec iiot-timescaledb psql -U postgres -d iiot_platform -At -c "
WITH dig AS (SELECT DISTINCT base_tag FROM hc900.tag_metadata WHERE attribute LIKE 'state%' AND value<>'')
SELECT 'history_rows', count(*)::text FROM hc900.history_table
UNION ALL SELECT 'history_distinct_tags', count(DISTINCT tagname)::text FROM hc900.history_table
UNION ALL SELECT 'digital_tags_left (0이어야)', count(*)::text FROM (
  SELECT DISTINCT tagname FROM hc900.history_table WHERE tagname IN (SELECT base_tag FROM dig)) x;
"

digital_tags_left = 0, 남은 태그는 archive_enabled 집합과 일치해야 한다.

복구 (필요 시)

zcat /home/windpacer/db_backups/history_nonarchive_digital_<TS>.csv.gz | \
  docker exec -i iiot-timescaledb psql -U postgres -d iiot_platform \
    -c "COPY hc900.history_table FROM STDIN WITH CSV HEADER"

참고

  • DB: PostgreSQL/TimescaleDB 컨테이너 iiot-timescaledb, DB iiot_platform, 스키마 hc900.
  • history_table은 일반 테이블(하이퍼테이블 아님). 대량 삭제 후 디스크 회수는 autovacuum이 점진 처리(즉시 필요 시 VACUUM — 라이브 테이블이라 VACUUM FULL 락 주의).
  • 관련: 디지털 검출 기준은 tag_metadata의 state 라벨 보유(과거 i=7594 폐기). 값 N → state{N} 라벨, realtime는 {N | label | } 포맷(Hc900RealtimeService.FormatValue).