티스토리 뷰

지난 글에서 우리가 CDC 파이프라인을 JDBC source connector로 구축한 얘기를 했다. 동기화하면서 비정규화 변환까지 함께 처리할 수 있어서 우리 요구사항에 잘 맞는 도구였다.

근데 솔직히 그 글에서 못 한 얘기가 있다. 이 도구의 가장 큰 약점은 스키마 변경에 약하다는 거다. 운영하면서 이거 때문에 진짜 자주 깨졌다. 오늘은 그 얘기를 좀 해보려고 한다.

평일 오후 3시, 슬랙에 빨간 알림

상황은 대충 이렇다. 평화롭게 동기화 잘 되던 평일 오후, 슬랙에 알림이 뜬다.

🚨 connector user_activity_mart FAILED. config 백업 후 재시작 시도.

뭐 알아서 재시작되겠지 싶다. 10분 주기로 FAILED 감지하면 자동 재시작하는 로직을 돌리고 있으니까.

10분 뒤. 다시 FAILED.

또 10분 뒤. 다시 FAILED.

이쯤 되면 안다. 자동 재시작으로 못 푸는 케이스라는 걸.

JDBC source connector 동작을 다시 보자

뭐가 문제인지 보려면 이 커넥터가 어떻게 도는지부터 봐야 한다. 어렵진 않다.

설정된 SQL 쿼리 → 주기적으로 운영 DB에 실행 → 결과를 Kafka 토픽으로 publish

여기서 핵심은 토픽의 스키마다. 처음 커넥터를 등록할 때, 쿼리 결과 컬럼을 기반으로 토픽 스키마가 정해진다. user_id(INT), country_code(STRING), message_count(INT) 같은 식.

이 토픽을 컨슘하는 측(우리 환경에선 Kafka Connect의 sink 측, 분석 DB로 적재하는 곳)도 같은 스키마를 기대한다.

여기서 운영 DB의 컬럼이 바뀌면?

우리가 부딪힌 케이스들

케이스 1: 신규 컬럼 추가. 비즈니스 백엔드에서 users 테이블에 subscription_tier라는 컬럼이 추가됐다. 이 컬럼이 JDBC source connector 쿼리에 포함되도록 SQL이 업데이트됐다. 이제 토픽엔 신규 컬럼이 있는데 sink 쪽 테이블엔 없다. 컨슈머 측에서 "어 이 컬럼 어디 넣어?" 하다가 FAILED.

케이스 2: 컬럼 타입 변경. amount 컬럼이 INT에서 BIGINT로 바뀌었다. 토픽 스키마가 깨진다. 컨슈머가 받은 데이터를 기존 INT 컬럼에 넣으려다 오버플로우.

케이스 3: 컬럼 삭제 또는 이름 변경. SQL 쿼리 자체가 깨진다. SELECT ... FROM users 했는데 name 컬럼이 사라졌다. 쿼리 실패. 커넥터 FAILED.

케이스 4: 제일 짜증나는 케이스. 한쪽만 바뀌는 경우다. 운영 DB는 바뀌었는데 sink 쪽 테이블은 그대로일 때다. 양쪽이 어긋난 채로 며칠 가다가, 누군가 분석 쿼리 돌리면서 "어 이 컬럼 왜 다 NULL이지?" 하고 발견한다.

우리의 대응 방법: 수동 DDL

부끄럽지만 우리 방식은 단순하다. 사람이 직접 간다.

  • 슬랙 알림 받음. 자동 재시작이 무한 실패하는 거 확인.
  • 운영 DB 스키마 변경 사항 확인 (보통 PR 히스토리 뒤짐).
  • sink 측 테이블에 같은 변경을 DDL로 적용.
  • JDBC source connector의 SQL을 업데이트 (필요하면).
  • 커넥터 재시작.
  • 데이터 정합성 검증.

사이드 이펙트 없이 끝나면 평균 30분에서 1시간 정도 걸린다.

왜 자동화 안 하냐고? 자동 복구는 "상태 회복"만 가능해서다. config가 꼬여서 죽은 거면 백업 → 재기동으로 풀리지만, 스키마 불일치는 회복할 상태가 없다. 양쪽 스키마 중 어느 쪽이 맞는지를 사람이 정해줘야 한다.

그래서 결국 우리한테 필요한 건 사전 차단

운영해보면 안다. 사고 난 다음에 빨리 복구하는 것보다 사고 자체를 막는 게 훨씬 싸게 먹힌다는 것. 30분 복구도 그 자체로 시간이지만, 그 사이 분석 데이터가 텅 비어버리는 게 더 큰 문제다.

지금 우리가 하는 사전 조치:

  • 운영 DB 스키마 변경 PR에 [data-pipeline] 라벨 달기 → 데이터 인프라 담당자가 자동 알림 받음
  • sink 측에 영향 가는 변경은 PR 머지 전에 동기화 작업 우선 진행
  • 신규 컬럼이면 sink 측에 먼저 추가 → 운영 DB에 추가 (역순)

근데 이게 100% 되진 않는다. 사람이 까먹기도 하고, 긴급 핫픽스로 라벨 달 시간 없을 때도 있다. 그래서 결국 사고는 난다. 그래도 이걸 도입하고 나서 빈도는 확실히 줄었다.

Debezium의 schema registry 접근

하나 짚고 가고 싶은 게 있다. Debezium은 이 문제를 좀 다르게 푼다.

Debezium은 보통 Schema Registry라는 별도 컴포넌트와 함께 쓴다. Avro나 Protobuf 같은 스키마 정의 시스템을 두고, 모든 메시지는 스키마 ID와 함께 publish된다. Producer는 스키마 등록 → Consumer는 스키마 ID로 조회 → 둘이 호환되는지 확인.

호환이 안 맞으면? 설정에 따라 publish 자체가 막히거나 명확한 에러로 떨어진다. "몰래 깨지는" 일이 없다.

솔직히 이게 제일 부럽다. 우리 환경에선 스키마가 바뀌었는지를 사고 나고 나서야 안다. Debezium + Schema Registry 환경이면 바뀌는 시점에 바로 알 수 있다.

다만 Schema Registry를 운영하려면 별도 인프라가 필요한데, 우리 회사 상황에선 그것까지 도입할 인력 여유가 없었다. 그래서 일단은 사람이 라벨 달고 사전 조치하는 방식으로 버티고 있다.

한번 정리해보면 이렇다.

  • Schema Registry는 진짜 도입하고 싶다. 일단 스키마 호환성 체크가 자동화되면 휴먼 에러가 한 단계 차단된다.
  • PR 라벨 같은 휴먼 시스템은 임시방편이라는 걸 인정해야 한다. 안 까먹을 수 있는 사람은 없다. 사람이 까먹는 걸 전제로 시스템을 설계해야 한다.
  • Sink 측 스키마 자동 마이그레이션도 고민할 만하다. Source 스키마가 바뀌면 sink 쪽에 컬럼 추가까지 자동으로 해주는 식. 컬럼 삭제까진 위험해서 못 건드리겠지만.

스키마 변경 때문에 부서지는 건 JDBC source connector를 쓰는 한 계속될 거 같다. 도구가 가진 한계니까. 다만 그 한계를 알고 운영하는 거랑 모르고 깨지는 건 다르다. 사고 시간을 1시간에서 30분으로 줄이고, 빈도를 주 1회에서 월 1회로 줄이는 정도가 지금 우리가 할 수 있는 일이다.

다음 글에선 RDB에서 S3 + Athena로 전환했을 때 비용을 어떻게 계산했고 어디서 얼마나 아꼈는지 한번 풀어볼까 한다.

반응형
댓글