이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- Backfill을 실행하기 전에 대상 range, idempotency, 속도 제한을 어떻게 정하는가?
- 재처리 checkpoint와 dry run 결과는 어떤 형태로 남겨야 하는가?
- 운영 DB와 downstream target에 부하를 주지 않으려면 어떤 throttle과 stop condition이 필요한가?
- Backfill 완료를 source-target count, sum, freshness, quality rule로 어떻게 검증하는가?
개요
Backfill은 과거 데이터를 다시 계산해 target을 고치는 운영 작업이다.
단순히 job을 다시 실행하는 것과 다르다.
Backfill에는 대상 범위, 처리 단위, 속도 제한, 중단 기준, 중복 안전성, 완료 검증이 명시되어야 한다.
실행 전 결정할 것
Backfill 요청을 받으면 먼저 다음을 적는다.
- 왜 backfill이 필요한가?
- 대상 source와 target은 무엇인가?
- range는 시간 기준인가, id 기준인가, partition 기준인가?
- 기존 target row를 지우고 다시 쓰는가, upsert하는가?
- backfill 중 사용자에게 보이는 데이터는 어떻게 처리하는가?
- 운영 DB, Kafka, warehouse에 줄 부하는 어느 정도인가?
- 실패 시 어디부터 이어서 할 수 있는가?
이 질문에 답하지 못하면 backfill command를 만들면 안 된다.
Backfill 계획 예시
request_id: bf-2026-06-order-refund
reason: refund reason mapping bug
source: orders, order_refunds
target: mart_order_daily
range: 2026-05-01 ~ 2026-06-30
unit: 1 day
write_mode: delete target partition then insert
throttle: sleep 10 seconds per partition
dry_run: required
owner: data-platform + order-service
validation: source refund sum = mart refund sum계획에는 “무엇을 고치는지”와 “어디까지 고치는지”가 드러나야 한다.
Dry Run
Dry run은 실제 write 없이 범위와 예상량을 확인한다.
SELECT date(paid_at) AS target_date,
count(*) AS source_rows,
sum(paid_amount) AS source_amount
FROM orders
WHERE paid_at >= date '2026-05-01'
AND paid_at < date '2026-07-01'
GROUP BY date(paid_at)
ORDER BY target_date;Dry run에서 확인할 것:
- 예상 row 수가 비정상적으로 크지 않은가?
- 오래된 partition에 source 데이터가 남아 있는가?
- 개인정보 보관 기간을 넘지 않는가?
- target overwrite가 허용되는 partition인가?
- 실행 시간이 운영 peak와 겹치지 않는가?
Checkpoint Table
Backfill은 중간에 멈출 수 있어야 한다.
CREATE TABLE backfill_runs (
run_id text PRIMARY KEY,
job_name text NOT NULL,
target_from timestamptz NOT NULL,
target_to timestamptz NOT NULL,
status text NOT NULL,
current_cursor text,
processed_partitions int NOT NULL DEFAULT 0,
started_at timestamptz NOT NULL,
finished_at timestamptz,
requested_by text NOT NULL
);checkpoint가 없으면 실패 후 처음부터 다시 시작하게 되고, 운영 부하와 중복 위험이 커진다.
실행 Command
./run-backfill order-daily-mart \
--from 2026-05-01 \
--to 2026-07-01 \
--unit day \
--mode overwrite-partition \
--sleep-ms 10000 \
--run-id bf-2026-06-order-refund좋은 command는 재현 가능하다.
나중에 audit log만 봐도 어떤 범위를 어떤 mode로 처리했는지 알 수 있어야 한다.
속도 제한과 중단 기준
Backfill은 정상 트래픽과 경쟁한다.
다음 조건에서는 멈춘다.
- 운영 DB CPU 또는 replication lag가 기준을 넘는다.
- Kafka consumer lag가 급격히 증가한다.
- target DB lock wait가 길어진다.
- quality rule 실패가 연속으로 발생한다.
- 예상 row 수와 실제 처리 row 수 차이가 크다.
속도 제한은 성능 최적화가 아니라 운영 안전장치다.
Idempotency
Backfill은 같은 range를 다시 실행해도 결과가 같아야 한다.
권장 패턴:
- partition delete 후 insert.
- deterministic transform.
- unique key 기반 upsert.
- run_id와 source range 기록.
- 외부 side effect 금지.
주의할 패턴:
- append-only target에 중복 insert.
- now() 같은 실행 시각을 business value로 저장.
- 랜덤 샘플링 결과를 그대로 target에 저장.
- source가 변경되는 동안 snapshot 기준 없이 읽기.
완료 검증
SELECT 'source' AS side,
count(*) AS rows,
sum(refund_amount) AS refund_amount
FROM order_refunds
WHERE refunded_at >= date '2026-05-01'
AND refunded_at < date '2026-07-01'
UNION ALL
SELECT 'mart' AS side,
count(*) AS rows,
sum(refund_amount) AS refund_amount
FROM mart_order_daily
WHERE order_date >= date '2026-05-01'
AND order_date < date '2026-07-01';SELECT rule_name, status, details
FROM quality_rule_runs
WHERE dataset_name = 'mart_order_daily'
AND target_from = date '2026-05-01'
AND target_to = date '2026-07-01';완료는 “command 종료”가 아니라 source와 target이 다시 맞는 상태다.
운영 지표
backfill_rows_per_minute: 재처리 처리량.backfill_checkpoint_age: 마지막 checkpoint 이후 경과 시간.backfill_stop_count: 중단 조건으로 멈춘 횟수.target_duplicate_count: backfill 후 target 중복 수.validation_diff_ratio: source-target 검증 차이.
이 지표는 backfill이 운영 작업으로 통제되고 있는지 보여준다.
위험 신호!
- “최근 3개월 다시 돌림”처럼 range가 모호하다.
- dry run 없이 바로 write mode로 실행한다.
- sleep, batch size, stop condition이 없다.
- target writer가 append인데 중복 검증이 없다.
- 완료 검증 없이 dashboard 값만 눈으로 확인한다.
확인 질문
확인 질문
- Backfill에서 range와 unit을 명시해야 하는 이유는 무엇인가?
- dry run이 운영 부하와 개인정보 보관 기간 확인에 필요한 이유는 무엇인가?
- 같은 backfill을 두 번 실행해도 결과가 같으려면 writer는 어떤 속성이 있어야 하는가?
- command 종료와 품질 검증 완료는 왜 다른가?
참고 문서
- Spring Batch Reference
- Apache Airflow Documentation
- PostgreSQL Documentation
- Martin Kleppmann, Designing Data-Intensive Applications