Skip to content

health: 사본이 호스트를 벗어나지 못했으면 백업 행은 ok 가 아니다 - #39

Merged
mossland merged 1 commit into
mainfrom
claude/backup-offhost-honest
Aug 26, 2026
Merged

health: 사본이 호스트를 벗어나지 못했으면 백업 행은 ok 가 아니다#39
mossland merged 1 commit into
mainfrom
claude/backup-offhost-honest

Conversation

@mikim

@mikim mikim commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

무엇

/api/healthdb_backup 행이 off-host 사본이 없는 상태를 ok 로 보고하던 것을 warn 으로 내린다.

지난주 들어온 일일 백업 cron 은 두 가지에 답한다 — cron 이 돌았는가(heartbeat), 이 파일이 실제로 복원되는가(PRAGMA integrity_check). 세 번째 질문에는 답하지 않는다: 이 호스트의 디스크가 아닌 곳에 사본이 있는가.

프로덕션 실측 (2026-08-26, 읽기 전용):

원본  /home/atrn/data/moss_land.sqlite              18M
사본  /home/atrn/backups/alpha/alpha-daily-*.sqlite  17~18M × 6 (8/21~8/25)
둘 다 /dev/mapper/ubuntu--vg-ubuntu--lv
grep -c BACKUP_REMOTE ~/alpha/.env.local  →  0

디스크 하나가 죽으면 원본과 백업이 같이 사라진다. 그 상태에 대해 화면은 ok 였다. scripts/backup-db.ts 의 헤더 주석이 스스로 적어둔 문장이 정확하다 — "그건 백업이 아니라 실행 취소다."

이건 trackable_calls 가 95일 무산출 동안 ok 를 보고하던 것과 같은 모양이고, applyContentStaleness 가 그때 쓴 방식(heartbeat 판정 위에 두 번째 의견을 얹는다)을 그대로 따른다.

어떻게

  • scripts/backup-db.ts 가 heartbeat note 맨 앞에 offhost=none|ok|fail 을 적는다.
  • lib/health.tsapplyOffHostGap 이 그 토큰만 읽고 none 이면 warn 으로 내린다.
  • 토큰은 lib/cron-heartbeat.ts 에 둔다 — heartbeat note 규약이 사는 곳이고, 쓰는 쪽과 읽는 쪽이 각자의 문자열 리터럴로 갈라지면 이 행은 조용히 ok 로 돌아간다. tests/offhost.test.ts 가 그 드리프트를 막는다.

warn 에서 멈추고 fail 로 가지 않는다. cron 자체는 건강하고, ?strict=1 이 설정 공백을 503 으로 바꾸면 안 된다. 다만 ok 는 과장이었다.

토큰이 없는 옛 heartbeat 는 건드리지 않는다. 토큰의 부재가 사본의 부재를 뜻하지는 않으므로 추측하지 않고, 다음 03:00 실행이 사실을 채운다.

검증

getSystemHealth() 를 임시 DB 로 직접 돌린 결과:

heartbeat note db_backup
offhost=none · … warn — "사본이 원본과 같은 호스트에 있습니다 — BACKUP_REMOTE 미설정"
offhost=ok · … ok
토큰 없음 (옛 기록) ok

pnpm typecheck 통과 · pnpm test 28/28 통과 (신규 4) · pnpm build exit 0.

범위 밖

오프호스트 목적지를 설정하는 것. Alpha 는 Signal 로 흡수될 예정이라, 무엇을 이관하고 무엇을 폐기할지 목록이 먼저 나와야 백업 대상이 DB 전체인지 테이블 두어 개인지 정해진다 — 통폐합 문서 §5.5 는 콜 원장을 '없앨 것' 첫 줄에 적어두었다. 이 PR 은 그 결정을 기다리는 동안 화면이 거짓말을 하지 않게만 한다.

참고로 rsync 경로(BACKUP_REMOTE)는 공짜가 아니다 — writer 는 Lightsail 로 ssh 가 안 되고(키 없음, 테일넷에도 없음) 키 배포부터 필요하다. 같은 박스의 signalmap 은 rclone 으로 S3 에 밀고 있고(~/bin/rclone, [sm-backup], 마지막 성공 2026-08-25T22:04Z) 자격증명이 이미 작동한다.

🤖 Generated with Claude Code

지난주 들어온 일일 백업 cron 은 스냅샷을 뜨고 PRAGMA integrity_check 로
"이 파일이 실제로 복원되는가"까지 확인한다. 그 두 질문에는 답하지만 세 번째
질문에는 답하지 않는다 — 이 호스트의 디스크가 아닌 곳에 사본이 있는가.

스냅샷은 ~/backups/alpha 에 떨어지고 그건 원본 DB 와 같은 볼륨이다
(프로덕션 실측: 원본 /home/atrn/data/moss_land.sqlite 와 사본 6개 모두
/dev/mapper/ubuntu--vg-ubuntu--lv). BACKUP_REMOTE 는 설정돼 있지 않다.
그런데 /api/health 의 db_backup 행은 ok 였다. 디스크 하나 죽으면 원본과
백업이 같이 사라지는 상태에 대해서.

이건 trackable_calls 가 95일 무산출 동안 ok 를 보고하던 것과 같은 모양이고,
그때와 같은 방식으로 고친다: heartbeat 판정 위에 두 번째 의견을 얹는다.

- scripts/backup-db.ts 가 heartbeat note 앞에 offhost=none|ok|fail 을 적는다.
- lib/health.ts 의 applyOffHostGap 이 그 토큰만 읽고 none 이면 warn 으로 내린다.
- 토큰은 lib/cron-heartbeat.ts 에 둔다. 쓰는 쪽과 읽는 쪽이 각자의 문자열
  리터럴로 갈라지면 이 행은 조용히 ok 로 돌아간다.

warn 에서 멈추고 fail 로 가지 않는다. cron 자체는 건강하고, ?strict=1 이
설정 공백을 503 으로 바꾸면 안 된다. 다만 ok 는 과장이었다.

토큰이 없는 옛 heartbeat 는 건드리지 않는다 — 토큰의 부재가 사본의 부재를
뜻하지는 않으므로 추측하지 않고, 다음 03:00 실행이 사실을 채운다.

오프호스트 목적지 자체는 이 커밋의 범위가 아니다. Alpha 는 Signal 로 흡수될
예정이라, 무엇을 이관하고 무엇을 폐기할지 목록이 먼저 나와야 백업 대상이
DB 전체인지 테이블 두어 개인지 정해진다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mossland
mossland merged commit f1ae8e2 into main Aug 26, 2026
1 check passed
@mossland
mossland deleted the claude/backup-offhost-honest branch August 26, 2026 01:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants