데이터베이스 마이그레이션: 계획과 현실의 괴리

데이터베이스 마이그레이션: 계획과 현실의 괴리

데이터베이스 마이그레이션: 계획과 현실의 괴리

수요일 오전, 계획

“이번 마이그레이션은 여유 있게 잡았습니다.”

회의실에서 내가 말했다. 프로젝터에는 깔끔한 타임라인이 떠 있었다. 목요일 오후 2시 시작, 6시 완료. 4시간. 넉넉하다고 생각했다.

기획자가 물었다. “혹시 문제 생기면요?”

“백업 있고, 롤백 절차도 문서화했습니다. 최악의 경우 1시간 추가면 됩니다.”

팀장이 고개를 끄덕였다. “좋네요. 그럼 목요일 저녁 회식 예약할게요.”

나는 대답했다. “네, 7시면 충분합니다.”

거짓말이었다. 아니, 그땐 진심이었다.

지난 3번의 마이그레이션도 비슷했다. 계획은 4시간, 실제는 9시간. 계획은 6시간, 실제는 새벽 2시. 계획은 “간단한 작업”, 실제는 주말 출근.

그런데 이번엔 다르다고 믿었다. 체크리스트를 만들었다. 리허설도 했다. 개발 서버에서 세 번이나 연습했다.

펜을 돌리면서 생각했다. ‘이번엔 된다.‘

목요일 오후 1시 30분, 준비

점심은 거르기로 했다. 김치찌개집 사장님이 섭섭해하셨다.

“오늘 안 오세요?”

“죄송해요, 오늘 중요한 일 있어서요.”

“그럼 저녁에 보겠네요?”

“아마도요.”

커피를 세 번째 마셨다. 아이스. 손이 약간 떨렸는데 카페인 때문인지 긴장 때문인지 모르겠다.

슬랙에 공지를 올렸다.

“14:00부터 DB 마이그레이션 시작합니다. 서비스 일시 중단됩니다. 예상 완료 시간 18:00.”

CS팀에서 답장이 왔다. “고객 공지 올렸습니다. 파이팅!”

파이팅. 그래.

체크리스트를 다시 확인했다.

  • 백업 생성
  • 서비스 중단 공지
  • read-only 모드 전환
  • 스키마 변경 실행
  • 데이터 검증
  • 인덱스 재생성
  • 서비스 재개
  • 모니터링

8개 항목. 각각 30분씩 잡았다. 여유 시간까지 합치면 4시간 반.

후배 민수가 물었다. “형, 제가 도와드릴 거 있나요?”

“아니, 괜찮아. 그냥 대기하고 있어. 혹시 모르니까.”

“네, 슬랙 켜놓을게요.”

시계를 봤다. 1시 55분.

5분 남았다.

오후 2시, 시작

정각에 시작했다.

-- 백업 시작
mysqldump -u root -p production_db > backup_20240116_1400.sql

진행 표시줄이 천천히 움직였다. 10%… 20%… 30%…

예상 시간 15분. 실제로는 20분 걸렸다. 데이터가 생각보다 많았다. 괜찮다. 5분 차이.

2시 20분. read-only 모드로 전환했다.

SET GLOBAL read_only = ON;

슬랙에 알림이 왔다. CS팀. “고객들 문의 들어옵니다. 언제 복구되나요?”

“예정대로 6시입니다.”

“알겠습니다.”

스키마 변경을 시작했다.

ALTER TABLE users ADD COLUMN last_login_device VARCHAR(50);
ALTER TABLE orders ADD INDEX idx_created_at (created_at);
ALTER TABLE products MODIFY COLUMN description TEXT;

세 개 테이블. 간단하다고 생각했다.

첫 번째는 2분 만에 끝났다. users 테이블은 10만 건. 문제없다.

두 번째가 시작됐다. orders 테이블. 300만 건.

진행 표시줄이 1%에서 멈췄다.

기다렸다. 5분. 10분. 15분.

2%로 올라갔다.

계산했다. 2%에 15분이면 100%는… 750분. 12시간 반.

“뭐야.”

소리 내서 말했다. 옆자리 동료가 쳐다봤다.

오후 3시, 현실

민수한테 슬랙을 보냈다. “orders 인덱스 생성이 너무 오래 걸려.”

“개발 서버에선 괜찮았잖아요?”

“개발 서버 데이터는 10만 건이었어. 실서버는 300만 건이야.”

“아…”

아. 그래. 아.

리허설을 제대로 안 한 거다. 데이터 볼륨을 고려 안 했다.

팀장한테 보고했다. “예상보다 시간이 걸립니다.”

“얼마나요?”

“아직 모르겠습니다.”

“회식은요?”

“글쎄요.”

“일단 8시로 미뤄놓을게요.”

8시. 가능할까.

구글에서 검색했다. “mysql alter table slow large table”

스택오버플로우가 떴다. 누군가 5년 전에 똑같은 질문을 했다.

답변: “pt-online-schema-change 쓰세요.”

지금 설치할 순 없다. 이미 시작했다.

다른 답변: “그냥 기다리세요. 중단하지 마세요.”

기다린다. 할 수 있는 게 그것뿐이다.

모니터를 보면서 펜을 돌렸다. 3%… 4%… 5%…

한 시간에 3%. 33시간 걸린다는 계산이 나왔다.

“미쳤나.”

오후 5시, 결정

팀장이 자리로 왔다. “어때요?”

“안 좋습니다.”

“어느 정도?”

“지금 진행률 8%. 예상 완료 시간은… 내일 오전입니다.”

“내일요?”

“네.”

팀장이 한숨을 쉬었다. “롤백하고 다시 계획 세울까요?”

그 말에 화가 났다. 나한테가 아니라 나 자신한테.

“롤백하면 다시 백업 복구하는 데 1시간 걸립니다. 이미 3시간 썼고요. 지금 취소하면 3시간이 날아갑니다.”

“그럼?”

“계속합니다. 끝까지 합니다.”

“언제까지?”

“모르겠습니다. 하지만 멈추는 것보단 낫습니다.”

팀장이 고개를 끄덕였다. “알겠어요. 저녁은 제가 시킬게요. 뭐 먹을래요?”

“아무거나요.”

“치킨?”

“네.”

민수한테 말했다. “너는 퇴근해.”

“저도 남을게요.”

“왜?”

“혼자 두기 싫어서요.”

고맙긴 한데 미안했다. 내 삽질에 후배까지 붙잡는 거다.

오후 7시, 야근

치킨이 왔다. 먹었다. 맛은 모르겠다.

진행률은 15%. 앞으로 10시간 남았다는 계산.

아내한테 문자를 보냈다. “오늘 늦을 것 같아.”

“또?”

“응. 미안.”

“저녁은?”

“회사에서 먹어.”

“알았어. 조심히 와.”

조심히. 뭘 조심해. 키보드 두드리는 거를.

슬랙을 확인했다. CS팀에서 메시지. “고객들 화나셨어요. 6시에 복구된다고 했는데.”

“죄송합니다. 예상보다 오래 걸립니다. 자정까지는 완료됩니다.”

자정. 말하고 보니 진짜 자정까지 걸릴 것 같다.

회사는 조용했다. 다들 퇴근했다. 불 켜진 자리는 우리 팀뿐.

민수가 커피를 가져다줬다. “형, 아아요.”

“고마워.”

“제 잘못도 있는 것 같아요. 제가 테스트 데이터 더 넣어볼 걸 그랬어요.”

“아니야. 내가 확인 안 한 거야.”

“하지만…”

“됐어. 그냥 조용히 있어줘.”

뭐라고 말해야 할지 몰랐다. 위로도 싫고 변명도 싫었다.

오후 10시, 대기

25%. 6시간 남았다.

회의실 바닥에 누워서 천장을 봤다. 형광등이 깜빡였다.

‘다음엔 더 여유 있게 일정 잡자.’

항상 하는 다짐. 항상 지켜지지 않는 다짐.

지난번 마이그레이션도 그랬다. 4시간 계획이 10시간 걸렸다. 그때도 똑같이 다짐했다. ‘다음엔 이틀 잡자. 롤백 시간까지 포함하자.’

근데 이번엔 또 4시간을 잡았다. 왜? 기억하지 못해서? 아니면 기억하고 싶지 않아서?

아니면 그냥 희망회로 때문에. ‘이번엔 다를 거야.’

민수가 과자를 내밀었다. “형, 이거라도 드세요.”

“너 집 가.”

“형 혼자 두기 싫다니까요.”

“나 괜찮아.”

“안 괜찮아 보이는데요.”

솔직히 괜찮지 않았다. 짜증났다. 화났다. 나한테.

왜 매번 같은 실수를 반복할까. 왜 매번 ‘이번엔 다르다’고 믿을까.

7년차인데 아직도 이러고 있다. 선배들은 다 이렇게 사나.

자정, 완료

35%.

민수는 회의실 소파에서 잤다. 깨우지 않았다.

커피를 또 마셨다. 몇 번째인지 세지 않았다.

슬랙에 팀장 메시지. “아직도 있어요?”

“네.”

“고생하네요. 내일 오전 출근 안 해도 돼요.”

“알겠습니다.”

오전에 안 나가봤자 오후엔 나가야 한다. 똑같다.

새벽 2시. 50%.

창밖을 봤다. 건물 불은 다 꺼졌다. 우리 사무실만 불이 켜져 있었다.

새벽 4시. 75%.

눈이 따가웠다. 안약을 넣었다.

새벽 5시 30분. 95%.

거의 다 왔다.

새벽 6시 10분.

Query OK, 3000000 rows affected (14 hours 50 min 23 sec)

끝났다.

인덱스가 생성됐다.

이제 세 번째 테이블. products. 5만 건. 5분이면 된다.

실제로는 3분 걸렸다.

데이터 검증을 했다. 문제없다.

read-only 모드를 해제했다.

SET GLOBAL read_only = OFF;

서비스가 복구됐다.

모니터링 대시보드를 확인했다. 정상.

슬랙에 공지를 올렸다.

“마이그레이션 완료했습니다. 서비스 정상화됐습니다.”

CS팀에서 답장. “고생하셨습니다!”

답장을 안 했다.

민수를 깨웠다. “끝났어. 가자.”

“벌써요? 아, 벌써 아침이네.”

“응.”

그다음 날 오후, 회고

점심때 출근했다. 김치찌개집 사장님이 놀라셨다.

“어제 저녁엔 어디 갔어요?”

“야근했어요.”

“힘들겠네. 오늘은 서비스로 계란 하나 더 드릴게요.”

“감사합니다.”

계란을 먹으면서 생각했다. 이게 위로가 되나.

팀 회의가 있었다. 회고 시간.

팀장이 물었다. “이번에 뭐가 문제였죠?”

내가 말했다. “데이터 볼륨을 고려 안 했습니다. 리허설 환경이 실서버와 달랐습니다.”

“다음엔 어떻게 할 건가요?”

“실서버 스냅샷으로 테스트하겠습니다. 그리고 일정을 두 배로 잡겠습니다.”

“좋네요.”

좋긴 뭐가 좋아.

회의가 끝나고 민수가 물었다. “형, 진짜 다음엔 두 배로 잡을 거예요?”

“응.”

“그럼 8시간?”

“아니, 16시간.”

“하루 종일이네요.”

“주말에 하는 게 낫겠지.”

“그것도 그렇네요.”

거짓말이다. 다음에도 또 4시간을 잡을 것이다. 그리고 또 10시간 넘게 걸릴 것이다.

왜냐면 16시간을 일정에 잡으면 기획팀이 거부할 테니까. “왜 이렇게 오래 걸려요? 간단한 작업 아닌가요?”

그럼 나는 대답할 것이다. “아닙니다. 복잡합니다.”

근데 결국 일정은 4시간으로 줄어들 것이다. 회의 끝에. 협의 끝에. 타협 끝에.

그리고 나는 또 믿을 것이다. ‘이번엔 된다.‘

금요일 저녁, 퇴근

6시에 퇴근했다. 정시 퇴근이 이렇게 어색한지 몰랐다.

민수한테 말했다. “너도 가.”

“네, 형도 푹 쉬세요.”

“응.”

지하철에서 아내한테 문자를 보냈다. “집 가는 중.”

“진짜? 일찍 오네.”

“응. 저녁 뭐 먹을래?”

“치킨 말고 다른 거.”

“알았어.”

집에 도착했다. 현관문을 열었다. 아내가 맥주를 내밀었다.

“고생했어.”

“응.”

맥주를 마셨다. 소파에 앉았다. 넷플릭스를 켰다.

아내가 물었다. “이번엔 왜 늦었어?”

“데이터베이스 마이그레이션.”

“또?”

“응, 또.”

“계획은 몇 시간이었는데?”

“4시간.”

“실제로는?”

“16시간.”

아내가 웃었다. “매번 그러네.”

“그러게.”

“다음엔 더 여유 있게 잡아.”

“응, 그럴게.”

거짓말이다.

다음에도 똑같을 것이다. 계획은 낙관적으로, 현실은 비관적으로.

그게 개발자 인생이니까.


다음 마이그레이션은 다음 달이다. 벌써 일정이 잡혔다. 3시간. 웃긴다.