LabHub
배우기 러닝패스 코스

FDE総合演習:倉庫に同じ注文が3回届いた

ロールバック計画書の復旧コマンドは一度も実行されたことがなかった

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

WAL 모드 SQLite 를 쓰는 고객 서비스에서 백업 → 증거 → 마이그레이션 → 실패 주입 → 복원 → 백업 시점과 같음 증명을 실제로 돌리고, 그 절차를 migrate.sh·restore.sh·rehearse.sh 로 굳힌다.

왜 중요한가

롤백 계획서의 복구 명령은 대개 실행해 본 적이 없다. 서비스가 떠 있는 채로 cp 한 백업은 integrity_check 가 ok 인데 커밋이 빠져 있고, 강제 종료 뒤 남은 -wal 은 복원한 파일 위에 다시 적용된다. 리허설로 확인하고 숫자로 남기지 않으면, 롤백이 필요한 날 처음 알게 된다.

재료: /opt/lab/p1a-rehearsal/ — liveapp.py(주문 서비스 흉내: start·status·stop·crash), migrations/002_add_currency.sql, migrations/003_customer_unique.sql. 작업 디렉터리는 /root/rehearsal 이다(서비스 DB 는 /root/rehearsal/live.db). 증거 파일은 /root/rehearsal/evidence 에 둔다. 예상 60분이고, 세션이 끝나면 /root/rehearsal 은 사라진다.

단계

  1. liveapp.py start 로 서비스를 띄우고, 서비스가 떠 있는 채로 live.db 본파일만 /root/rehearsal/copies/cp-only.db 로 cp 한 뒤, /root/rehearsal/evidence/cp.tsvlivecp-only 의 행 수·integrity_check 를 적는다.
  2. 서비스가 떠 있는 채로 .backup 으로 /root/rehearsal/backups/pre-v2.db, VACUUM INTO/root/rehearsal/backups/pre-v2-vacuum.db 를 만들고 evidence/backup.tsvfile<TAB>rows<TAB>integrity<TAB>journal_mode 로 적는다.
  3. 두 백업의 파일 sha256 과 .sha3sum --schema, 행 수·user_version·integrity·시각을 evidence/baseline.json 에 남긴다.
  4. migrate.sh DB NNN_이름.sql/root/rehearsal/migrate.sh 로 만들고 live.db 에 002 를 적용한다. user_version 이 NNN-1 일 때만 적용하고, 이미 NNN 이면 아무것도 하지 않고 0, 그 밖이면 거절한다.
  5. live.db 에 003 을 적용해 실패시키고 evidence/failure.json 에 종료 코드·user_version·내용 해시 전후·오류를 남긴다. migrate.sh 는 실패하면 흔적이 없어야 한다.
  6. restore.sh BACKUP TARGET/root/rehearsal/restore.sh 로 만든다. 백업이 온전하지 않으면 대상에 손대지 않고 거절하고, 남은 -wal 이 있어도 백업과 같은 내용으로 복원해 증명한다.
  7. liveapp.py crash 로 배포 중 강제 종료를 일으킨 뒤 live.db 를 pre-v2.db 로 복원하고 evidence/restore.json 을 남긴다.
  8. rehearse.sh DB WORKDIR UP.sql BROKEN.sql/root/rehearsal/rehearse.sh 로 만든다. 백업부터 복원 증명까지 한 번에 돌고 WORKDIR/report.json 을 남기며, 무엇 하나 증명하지 못하면 0 이 아닌 값으로 끝난다.

참고

떠 있는 서비스의 DB 를 cp 하면 무엇을 잃나

서비스를 띄우고 live.db 를 /root/rehearsal/copies/cp-only.db 로 cp 한 뒤 /root/rehearsal/evidence/cp.tsv 에 live·cp-only 의 행 수와 integrity_check 를 적는다.

ls -l live.db* 로 본파일 옆에 무엇이 있는지 먼저 보세요. 행 수는 서비스가 떠 있는 동안 live.db 에서 세고, 사본은 immutable URI 로 읽기만 하세요. integrity_check 결과와 행 수가 서로 다른 이야기를 하는지 보는 것이 이 단계의 요점입니다.

.backup 과 VACUUM INTO 로 온라인 백업

서비스가 떠 있는 채로 /root/rehearsal/backups/pre-v2.db(.backup)와 /root/rehearsal/backups/pre-v2-vacuum.db(VACUUM INTO)를 만들고 /root/rehearsal/evidence/backup.tsv 에 적는다.

VACUUM INTO 는 대상 파일이 이미 있으면 실패합니다. journal_mode 는 파일 머리의 바이트로 확인하면 파일을 열지 않고도 알 수 있습니다. 두 백업의 행 수가 1단계의 cp 사본과 어떻게 다른지 비교해 보세요.

복원 뒤 비교할 기준값 남기기

두 백업의 sha256·.sha3sum --schema, pre-v2.db 의 행 수·user_version·integrity·시각을 /root/rehearsal/evidence/baseline.json 에 남긴다.

파일 해시와 내용 해시가 각각 무엇을 증명하는지 생각해 보세요. 두 백업은 파일 해시가 다르고 내용 해시는 같아야 합니다. 시각은 파드 시각으로 %Y-%m-%dT%H:%M:%S 형식입니다. 이 뒤로 백업 파일은 열어서 쓰지 마세요.

판 번호를 지키는 migrate.sh

/root/rehearsal/migrate.sh 를 만들고 live.db 에 002_add_currency.sql 을 적용한다.

판 번호는 파일 이름 앞의 숫자에서 얻습니다. 마이그레이션 파일에는 BEGIN·COMMIT·user_version 이 없으니 스크립트가 감싸야 합니다. 채점기는 다른 번호와 다른 user_version 의 DB 로도 돌려 봅니다.

실패한 마이그레이션은 흔적이 없어야 한다

live.db 에 003_customer_unique.sql 을 적용해 실패시키고 /root/rehearsal/evidence/failure.json 을 남긴다. migrate.sh 는 중간 문장이 실패해도 아무것도 바꾸지 않아야 한다.

실패 전후로 user_version 과 .sha3sum --schema 를 재세요. sqlite3 CLI 가 오류 뒤에 무엇을 하는지, 제약 위반이 트랜잭션 전체를 되돌리는지 문서에서 확인해 보세요. 채점기는 실패 위치가 다른 마이그레이션으로 migrate.sh 를 시험합니다.

남은 -wal 까지 처리하는 restore.sh

/root/rehearsal/restore.sh BACKUP TARGET 을 만든다. 망가진 백업은 대상에 손대기 전에 거절하고, 복원 뒤 내용 해시가 백업과 같음을 확인한다.

대상 옆에 -wal 이 남아 있으면 다음에 열 때 무슨 일이 생기는지 WAL 문서를 다시 보세요. 백업 검사는 대상에 손대기 전에, 증명은 복원한 뒤에. 채점기는 강제 종료로 -wal 이 남은 DB 와 페이지가 망가진 백업을 줍니다.

배포 중 강제 종료된 운영 DB 되돌리기

liveapp.py crash 뒤 restore.sh 로 live.db 를 backups/pre-v2.db 로 복원하고 /root/rehearsal/evidence/restore.json 을 남긴다.

crash 가 끝나면 ls -l live.db* 로 무엇이 남았는지 보세요. 장애의 token 은 app/events.log 의 마지막 crashed 줄에 있습니다. 복원 뒤 hotfix- 로 시작하는 주문이 남았는지도 세어 보세요.

한 번에 도는 리허설과 보고서

/root/rehearsal/rehearse.sh DB WORKDIR UP.sql BROKEN.sql 을 만든다. WORKDIR/backup.db 와 WORKDIR/report.json 을 남기고, 모든 증명이 맞을 때만 0 으로 끝난다.

앞에서 만든 migrate.sh·restore.sh 를 스크립트 위치 기준으로 부르세요. 실패 주입이 실패하지 않은 경우, 실패 뒤 내용 해시가 바뀐 경우, 복원 뒤 백업과 다른 경우 모두 0 이 아니어야 합니다. 채점기는 -wal 에만 커밋이 남은 DB 와 무작위 판 번호로 돌립니다.