DEV Community

박준현
박준현

Posted on

gold 숫자가 이상할 때 source_hash, quality, lineage로 원인 좁히기

gold 숫자가 이상할 때 source_hash, quality, lineage로 원인 좁히기

데이터 플랫폼에서 중요한 질문은 "gold 숫자를 만들었는가"에서 끝나지 않는다.

운영 중에는 오히려 이런 질문이 더 자주 나온다.

이 defect_rate가 왜 이렇게 높지?
이 gold row는 어느 source에서 왔지?
row가 처리 중 사라진 건가, 정상 필터링인가?
schema가 바뀐 상태로 계산된 건가?
quality check는 통과했나?
Enter fullscreen mode Exit fullscreen mode

manufacturing-data-platform-mini의 B4 slice는 이 질문을 작게 다룬다. 새 Spark/Iceberg 엔진을 붙이지 않고, 이미 남겨둔 JSON catalog/lineage/quality evidence를 read-only operator report로 읽는다.

이 report는 이상치를 자동 탐지하지 않는다. 숫자를 해석할 provenance/quality 맥락을 제공해서 사람이 원인 후보를 좁히게 하는 도구다.

Scenario

분석가가 business_date=2026-06-29defect_rate가 이상하다고 말한다.

운영자는 raw CSV를 바로 열기 전에, 먼저 metadata와 evidence로 원인을 좁히고 싶다.

확인 순서는 이렇다.

1. gold row grain 확인
2. 해당 business_date의 successful run 확인
3. run_id / source_hash / schema_hash 확인
4. quality checks에서 fail/warn 확인
5. lineage path로 gold -> silver -> bronze -> source 역추적
6. row count / conservation / schema drift를 보고 원인 후보 좁히기
Enter fullscreen mode Exit fullscreen mode

Decision Pressure

단순 CSV 변환 스크립트는 보통 output만 남긴다.

input.csv -> gold.csv
Enter fullscreen mode Exit fullscreen mode

하지만 운영 질문은 output만으로 답하기 어렵다.

이 gold.csv는 어떤 입력에서 왔나?
동일 source를 재실행한 건가, 다른 source로 새로 처리한 건가?
source row 5개가 silver 3개가 된 이유는 정상 필터링/중복 제거인가?
집계가 units/defects를 보존했나?
schema drift가 있었나?
Enter fullscreen mode Exit fullscreen mode

그래서 이 프로젝트는 transform output과 함께 run evidence를 남긴다.

source_hash
schema_hash
quality checks
stats
layer parent links
catalog state
Enter fullscreen mode Exit fullscreen mode

이 문제는 data lineage 도구들이 다루는 전형적인 운영 질문의 축소판이다. 예를 들어 OpenLineage는 dataset/job/run metadata를 추적해 문제의 원인과 변경 영향 이해를 돕는 표준을 제공하고, Databricks Unity Catalog lineage는 downstream 결과가 이상할 때 upstream source를 추적하는 root-cause investigation을 lineage의 사용 사례로 설명한다.

이 프로젝트는 그런 시스템을 구현한 것이 아니라, 같은 문제를 path-level JSON evidence로 작게 연습한다.

Options

Option 장점 문제 판단
raw CSV 직접 열기 가장 직접적 매번 수동, run/source identity를 놓치기 쉬움 보조 수단
gold 파일만 보기 빠름 원인 추적 불가 부족
full lineage system 강력함 v0에 과함, OpenLineage backend 미구현 backlog
read-only operator report 작고 검증 가능, 기존 evidence 재사용 path-level까지만 가능 선택

Decision

이 slice에서는 read-only operator report를 선택했다.

명령은 작다.

PYTHONPATH=src python -m manufacturing_data_platform.pipeline.operator_report \
  --output-dir /tmp/manufacturing-mini-operator-report-cli \
  --business-date 2026-06-29
Enter fullscreen mode Exit fullscreen mode

이 report는 JSON catalog state를 읽어서 아래를 보여준다.

gold grain
run_id
source_hash
schema_hash
quality summary
row counts
lineage trace: gold -> silver -> bronze -> source
claim boundary
Enter fullscreen mode Exit fullscreen mode

또한 report 자체가 자기 한계를 같이 출력한다. 블로그에서만 정직한 것이 아니라, 산출물도 claim boundary를 포함한다.

Evidence

구현 evidence:

src/manufacturing_data_platform/pipeline/operator_report.py
tests/test_operator_report.py
Enter fullscreen mode Exit fullscreen mode

검증 로그:

2026-07-10 — Operator evidence report slice
pytest: 35 passed
lakehouse JSON CLI: passed
operator evidence report CLI: passed
Enter fullscreen mode Exit fullscreen mode

실제 출력 일부:

{
  "gold_grain": {
    "dataset_id": "manufacturing_daily_metrics",
    "row_grain": [
      "business_date",
      "plant_id",
      "line_id",
      "product_code"
    ],
    "metrics": [
      "units_produced",
      "defect_count",
      "defect_rate",
      "avg_cycle_time_ms",
      "closing_status"
    ]
  },
  "run": {
    "run_id": "2026-06-29-20260710T033849Z-73005763",
    "source_hash": "b3ffc4acdd909db1b5a87db7155f76589dfdd4cedb4b00cedf18506f12948604",
    "schema_hash": "4414a80b70b7b386a13ee705a33aa9c99bd29cb3a717bba9d2c5f0bc892d3126",
    "quality_passed": true,
    "reuse_count": 0
  },
  "quality_summary": {
    "total_checks": 8,
    "pass_count": 8,
    "warn_count": 0,
    "fail_count": 0,
    "failed_checks": [],
    "warning_checks": [],
    "rca_focus_checks": [
      {"name": "row_count_source_to_silver", "status": "pass"},
      {"name": "unit_conservation_silver_to_gold", "status": "pass"},
      {"name": "freshness_business_date", "status": "pass"},
      {"name": "schema_drift", "status": "pass"}
    ]
  },
  "lineage_trace": [
    {"name": "gold", "parents": [".../silver/manufacturing_events.csv"]},
    {"name": "silver", "parents": [".../bronze/manufacturing_events.csv"]},
    {"name": "bronze", "parents": ["data/raw/manufacturing_events.csv"]},
    {
      "name": "source",
      "path": "data/raw/manufacturing_events.csv",
      "hash": "b3ffc4acdd909db1b5a87db7155f76589dfdd4cedb4b00cedf18506f12948604",
      "row_count": 5
    }
  ],
  "claim_boundary": {
    "supports": [
      "table/path-level lineage",
      "operator-inspectable run evidence",
      "source/schema identity trace",
      "quality check summary"
    ],
    "does_not_support": [
      "column-level lineage",
      "OpenLineage backend integration",
      "interactive lineage UI",
      "production incident workflow"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

이 출력을 시나리오에 대입하면:

  • quality_summary.fail_count=0, warn_count=0 -> 유실, 보존 불일치, schema drift warning이 없었다. 즉 이상한 defect_rate는 파이프라인 버그보다 실제 입력 데이터에서 온 값일 가능성이 크다.
  • lineage_tracesource.hashrow_count=5 -> 어느 파일의 5개 row에서 왔는지 raw를 열기 전에 고정할 수 있다.
  • run.reuse_count=0 -> 이전 successful run을 재사용한 skip이 아니라 새로 처리된 run이다.

report 하나로 "숫자가 이상하다"를 "파이프라인 check는 정상이고, 출처는 이 source_hash의 5-row file"까지 좁힌다.

Limitations

이건 production lineage system이 아니다.

명확한 한계:

이상치 자동 탐지 아님
자동 RCA 아님
latest successful run evidence 조회용 — failure-state forensics는 backlog
column-level lineage 아님
OpenLineage backend 통합 아님
interactive lineage UI 아님
production incident workflow 아님
real Mongo runtime 검증 아님
Airflow runtime trigger 검증 아님
Spark/Iceberg 구현 아님
Enter fullscreen mode Exit fullscreen mode

이 slice의 목적은 작다.

기존 run/catalog/quality/lineage evidence를 operator가 읽을 수 있는 형태로 묶는다.
Enter fullscreen mode Exit fullscreen mode

정리

output만 남기면 "왜 이 숫자지?"에 답하기 어렵다. output과 함께 source_hash, quality result, lineage evidence를 남기면 raw 파일을 열기 전에 "어디서 왔고, 파이프라인 check는 정상이었는지"까지 원인 후보를 좁힐 수 있다.

코드: github.com/junhyun-dev/manufacturing-data-platform-mini

Interview Line

면접에서는 이렇게 30초로 줄일 수 있다.

I added a read-only operator report for a synthetic manufacturing data platform.
Given a business_date, it reads the JSON catalog state and shows the gold grain,
run_id, source_hash, schema_hash, quality summary, row counts, and a path-level
lineage trace from gold to silver to bronze to source. The goal is to help an
operator narrow a suspicious number to its run/source/quality context. I kept
the claim boundary explicit: this is operator-inspectable path-level lineage
evidence, not anomaly detection, column-level lineage, or an OpenLineage backend.
Enter fullscreen mode Exit fullscreen mode

Top comments (0)