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-29의 defect_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_trace의source.hash와row_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, row counts, lineage evidence를 남기면 raw 파일을 열기 전에 “어디서 왔고, 파이프라인 check는 정상이었는지”까지 원인 후보를 좁힐 수 있다.
이 report는 anomaly detection이나 자동 RCA가 아니다. 의심스러운 지표를 run, source, quality check, path-level lineage 맥락으로 좁히는 read-only evidence view다.
답글 남기기