이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- CDC는 application query가 아니라 DB commit log를 읽는다는 말의 의미는 무엇인가?
- Debezium connector는 snapshot, streaming, offset, schema 변경을 어떻게 다루는가?
- outbox table과 Debezium을 연결할 때 어떤 설정과 운영 지표를 봐야 하는가?
- CDC를 붙여도 왜 consumer idempotency와 lag 관리가 필요한가?
개요
CDC는 Change Data Capture의 약자다.
DB에 commit된 변경을 감지해 다른 시스템으로 전달하는 방식이다.
Debezium은 PostgreSQL WAL, MySQL binlog 같은 commit log를 읽어 Kafka 이벤트로 변환하는 대표적인 CDC 도구다.
application이 table을 주기적으로 polling하는 방식과 다르게, Debezium은 DB log를 따라간다.
그래서 commit된 변경 순서를 비교적 자연스럽게 따라갈 수 있다.
하지만 Debezium도 만능은 아니다.
snapshot, connector offset, schema 변경, delete 이벤트, lag, 권한, 운영 장애를 모두 관리해야 한다.
기본 흐름
PostgreSQL
orders table
outbox_events table
WAL
Debezium PostgreSQL Connector
-> Kafka Connect
-> Kafka topic
-> consumer groupSpring application은 business transaction 안에서 outbox row를 insert한다.
Debezium은 commit log에서 그 insert를 읽는다.
Kafka Connect는 이를 Kafka topic으로 발행한다.
consumer는 Kafka topic을 읽어 downstream DB나 warehouse에 반영한다.
Snapshot과 Streaming
connector가 처음 시작하면 기존 table 내용을 snapshot으로 읽을 수 있다.
snapshot 이후에는 WAL/binlog를 따라 streaming한다.
initial snapshot
-> 기존 outbox_events row 읽기
streaming
-> 새 commit log 계속 읽기outbox table에 기존 published row가 많이 남아 있으면 initial snapshot이 예상보다 커질 수 있다.
운영에서는 snapshot mode를 명확히 정해야 한다.
새로운 connector를 붙일 때 과거 이벤트를 모두 내보낼지, 이후 변경만 볼지 결정해야 한다.
Connector Offset
Debezium connector도 offset을 가진다.
이 offset은 Kafka consumer offset과 다르다.
connector offset은 DB log에서 어디까지 읽었는지 나타낸다.
consumer offset은 Kafka topic에서 어디까지 처리했는지 나타낸다.
DB WAL position -> Debezium connector offset -> Kafka topic offset -> consumer group offset장애 분석에서는 이 네 위치를 분리해서 봐야 한다.
DB에는 commit됐지만 Kafka topic에 없다면 connector 문제일 수 있다.
Kafka topic에는 있는데 downstream DB에 없다면 consumer 문제일 수 있다.
Outbox Event Router
Debezium은 outbox table을 event topic으로 변환하는 SMT를 제공한다.
개념적으로는 다음 mapping을 한다.
outbox_events.aggregate_type + event_type -> route/topic
outbox_events.event_key -> Kafka message key
outbox_events.payload -> Kafka message value
outbox_events.event_id -> header or payload idoutbox table column 이름은 이 변환과 맞아야 한다.
payload만 있으면 routing, key, version, dedup 기준이 흐려진다.
event topic 설계는 application outbox schema와 Debezium route 설정을 함께 봐야 한다.
설정에서 보는 것
운영 설정에서는 connector class, DB 연결 정보, table.include.list, plugin.name, snapshot.mode, converter, schema history, replication slot을 확인한다.
중요한 것은 이 값들이 application outbox schema와 event routing 계약에 맞는지다.
Schema 변경
CDC는 DB schema 변경에 민감하다.
outbox payload 안의 JSON 구조가 바뀌면 consumer가 깨질 수 있다.
outbox table column이 바뀌면 connector나 SMT 설정이 깨질 수 있다.
따라서 event schema version을 payload에 포함한다.
consumer는 모르는 version을 무시하거나 DLQ로 보내야 한다.
DB migration과 consumer 배포 순서도 맞춰야 한다.
schema 변경은 단순 DDL 문제가 아니라 downstream 계약 변경이다.
Delete와 Tombstone
CDC는 insert, update, delete를 모두 event로 표현할 수 있다.
Kafka compacted topic에서는 delete가 tombstone message로 표현될 수 있다.
outbox event stream에서는 보통 outbox row delete를 business delete event로 해석하면 안 된다.
outbox row cleanup은 운영 정리이고, OrderCanceled 같은 business event와 다르다.
이 둘을 혼동하면 cleanup job이 downstream 삭제로 오해될 수 있다.
따라서 outbox table cleanup과 business delete event는 명확히 분리한다.
Lag 관리
Debezium lag는 DB commit과 Kafka 발행 사이의 지연이다.
consumer lag는 Kafka 발행과 downstream 처리 사이의 지연이다.
DB commit time: 10:00:00
Kafka event time: 10:00:05
consumer processed time: 10:00:20
CDC lag = 5s
consumer lag by time = 15s
end-to-end lag = 20s운영에서는 end-to-end 최신성을 봐야 한다.
CDC lag만 낮아도 consumer가 느리면 downstream 데이터는 늦다.
consumer lag만 낮아도 connector가 멈춰 있으면 새로운 DB 변경은 topic에 오지 않는다.
장애 모델
- replication slot이 오래 유지되어 WAL이 쌓인다.
- connector 권한이 부족해 table 변경을 읽지 못한다.
- snapshot이 예상보다 오래 걸려 초기 발행이 지연된다.
- schema 변경 후 converter 또는 consumer가 payload를 해석하지 못한다.
- Kafka Connect 재시작 후 connector offset 복구가 꼬인다.
- outbox cleanup이 너무 빨라 감사나 재처리 근거가 사라진다.
CDC 장애는 application log만 보면 놓치기 쉽다.
Kafka Connect status, connector task error, DB replication lag를 함께 봐야 한다.
운영 지표
- connector status: running, failed, paused 상태다.
- source lag: DB log 읽기 지연이다.
- connector error count: task 오류 수다.
- snapshot progress: 초기 snapshot 진행률이다.
- replication slot retained bytes: WAL 보관 부담이다.
- outbox insert to Kafka publish latency: outbox row 생성부터 topic 도착까지 시간이다.
- consumer end-to-end lag: DB commit부터 downstream 반영까지 시간이다.
개인 프로젝트 기준
개인 프로젝트에서 Debezium을 바로 붙이기 어렵다면 polling publisher로 outbox 개념을 먼저 검증한다.
Debezium을 붙인다면 최소한 다음을 문서화한다.
- connector가 읽는 table 목록
- snapshot mode
- Kafka topic routing 규칙
- message key column
- schema version 처리
- connector lag 확인 방법
단순히 “Debezium으로 CDC 적용”이라고 쓰면 운영 판단을 보여주기 어렵다.
위험 신호!
- CDC를 DB trigger나 application polling과 같은 것으로 설명한다.
- connector offset과 consumer offset을 구분하지 않는다.
- snapshot mode를 정하지 않고 운영 DB에 붙인다.
- outbox cleanup delete를 business delete event와 혼동한다.
- connector lag를 보지 않고 Kafka consumer lag만 본다.
확인 질문
확인 질문
- Debezium connector는 DB log의 어디까지 읽었고, consumer는 Kafka topic의 어디까지 처리했는가?
- connector offset과 consumer group offset을 분리해서 답해야 한다.
- 새 connector를 붙이면 과거 outbox row도 발행되는가?
- snapshot mode와 table 상태를 기준으로 결정된다.
- outbox table cleanup이 downstream delete로 해석되지 않는가?
- cleanup event와 business event를 topic routing과 payload에서 구분해야 한다.