퍼포스 서버 데이터베이스 오류 문제해결
개인 용도로 퍼포스를 사용하기 시작한 다음 1년 이상이 지난 다음 처음으로 인시던트라고 말할 만한 상황을 겪었습니다. 문제해결 과정과 앞으로 개선해야 할 점을 살펴봅니다.

제가 개인 파일 관리에 주로 규모가 큰 그룹의 형상관리에 사용되곤 하는 퍼포스를 사용한다는 것을 여러 차례 소개했습니다. 처음에는 다른 사람이 만든 도커 이미지를 사용해 퍼포스를 컨테이너 모양으로 운영하다가 자잘한 불편함이 있어 그 누군가가 만든 도커 이미지에 기반해 제 필요에 맞게 수정하며 도커 이미지를 만들어 봤고 이 이야기를 처음으로 도커 이미지를 만들어 보고 배운 것에 소개했습니다. 퍼포스는 AWS에서 사용하던 때부터 온프레미스로 전환을 거친 지금에 이르기까지 1년 이상 개인 파일 관리를 안정적으로 수행하고 있습니다. 하지만 종종 근본적으로 이런 행동이 불필요한 것이 아닌가 의심할 때도 있습니다. 사실 개인 수준에서 퍼포스 서버로부터 워크스페이스를 생성해 로컬에 다운로드 한 상태는 그냥 운영체제의 파일시스템과 기본 파일 관리 앱 - 윈도우 탐색기나 맥의 파인더 같은 - 으로 파일을 관리하는 상태와 똑같습니다. 여기에 드랍박스 같은 서비스를 사용하면 일정 기간에 걸쳐 각 파일의 이전 버전을 유지할 수도 있고 필요하다면 파일 이름을 수정해 수동으로 여러 버전을 유지할 수도 있습니다. 종종 퍼포스 서버에 뭔가 조치를 취할 일이 생길 때마다 이런 행동 자체가 그저 모든 관리 행동이 기록으로 남기를 바라는, 실은 불필요한 기록을 만들기 위한 근본적인 오버헤드가 아닐까 고민하곤 했습니다.
하지만 각 파일을 변경한 다음 서브밋 할 때마다 코멘트를 남기고 이전 버전과 비교하고 또 한 달 단위로만 기록을 내보낼 수 있는 서비스로부터 파일을 받아 간단히 이전 기록에 머지하며 이 과정 전체를 형상관리도구의 도움을 받는 경험은 오버헤드를 고려하더라도 그리 나쁘지 않았습니다. 또 드랍박스 같은 도구를 사용할 때와 비교해 명시적으로 제가 요구할 때만 새 버전을 만들 수 있다는 점도 마음에 들었고 또 개인 환경에서 여러 실험을 해보거나 사용해본 적 없는 기능을 사용해볼 수 있다는 점도 좋았습니다. 회사 퍼포스에서는 이런 행동을 함부로 하기 좀 어렵습니다. 사실 퍼포스는 근본적으로 권한이 제한된 사용자 입장에서 어떤 실수를 하더라도 서버가 멀쩡한 이상 그 실수를 되돌릴 수 있어 사실 안전합니다. 그러나 그 서버에 근거해 여러 사람들이 작업하고 또 빌드가 만들어지는 등 여러 작업이 일어나기 때문에 함부로 뭔가를 시도하지 않을 뿐입니다. 가령 퍼포스에 잘못된 파일을 서브밋 했다면 이를 퍼포스 기능으로 되돌릴 수 있습니다만, 여러 파일을 함께 올린 체인지리스트로부터 파일 하나를 되돌리는 행동은 절차를 처음 수행해 보는 사람에게 설명하기 좀 어렵고 또 파일 하나만 되돌리고 싶었을 뿐인데 체인지리스트 전체를 되돌리는 식으로 실수할 여지도 있습니다. 그래서 회사에서 누군가의 트러블슈팅을 도울 때는 같은 상황에서 그냥 되돌리기를 원하는 리비전을 로컬에 익스포트 한 다음 잘못 올린 파일을 체크아웃 해서 덮어씌워 새 리비전을 만들라고 안내하기도 합니다. 가능한 방법이지만 퍼포스의 명령어 체계 상 훌륭한 방법은 아닙니다. 다만 이해하기 쉽고 알려주기 쉬우며 훨씬 덜 위험한 방법일 뿐입니다.
제 개인 퍼포스 서버는 사용자가 적기 때문에 - 한 명은 아님 - 어지간한 실수를 해도 상관없습니다. 그래서 보통은 잘 사용하지 않는 기능들을 테스트해볼 수 있는데 가령 서로 다른 경로로 브랜치를 만들거나 이들을 다시 인티그레이션 하거나 퍼포스 기능에 기반해 복사하거나 이름을 수정하고 또 브랜치 매핑과 레이블을 만들어 이들에 근거해 버전을 오가는 등 여러 시도를 해볼 수 있고 이 과정에서 실수해도 상관없습니다. 퍼포스 상에서 한 작업이니 그냥 되돌리면 그만일 뿐 아니라 제 실수가 저 자신에게만 영향을 끼치기 때문에 미안해 하며 최대한 빨리 문제를 해결해야만 하는 압력이 전혀 없기 때문입니다. 다만 지금은 가장 간단한 로컬 디포를 사용하고 있기에 브랜치와 머지 과정을 반복하더라도 더 큰 조직에서 사용하는 스트림 디포에서 스트림 사이에 일어나는 브랜치와 머지를 경험하지는 않아 이건 장기적으로 개선할 점이라고 생각하고 있습니다. 개인적으로 사용하기에 스트림 디포가 필요하지는 않지만 적당한 시점에 로컬 디포를 스트림 디포로 이전해 스트림 사이에 브랜치 전환과 머지를 경험해볼 계획을 가지고만 있습니다. 이렇게 퍼포스는 이제 제 개인의 파일 관리를 넘어 일할 때 주로 사용하는 형상관리도구에 더 익숙해지고 또 더 자신있게 사용하면서도 제가 지금 정확히 어떤 결과를 가져올 행동을 하고 있는지 잘 알 수 있게 해 주었습니다. 그래서 가끔은 크래시플랜을 사용하지 마세요에 말한 대로 그냥 아주 간단한 모양을 사용하면 모든 인생이 훨씬 단순해지지 않을까 하는 생각을 하면서도 현재 체계를 유지하고 있습니다.
이 모든 나쁘지 않은 사용 경험을 지탱하기 위해서는 제가 퍼포스 서버를 구동하고 이를 잘 유지해야 합니다. 처음에는 다른 사람이 만든 도커 이미지에 의존해 시작했지만 어느 시점부터는 제가 퍼포스 서버를 운용하며 서버에서 필요했던 설정과 간단한 유틸리티를 포함해 이미지를 만들기 시작했습니다. 퍼포스 서버는 처음 개발된지 수 십 년이 흘러 여러 가지 문제가 해결되어 상당히 단순하고 또 안정적으로 동작하지만 퍼포스 서버 외적인 원인으로 문제를 일으킬 여지는 얼마든지 있습니다. 가령 퍼포스에 서브밋 한 모든 파일을 기록하는 스토리지에 퍼포스가 아닌 다른 주체가 접근해 문제를 일으킬 수도 있고 파일을 대량으로 관리하는 사람들의 신경을 긁곤 하는 Bit Rot 같은 문제도 있습니다. 일반 사용자용 파일시스템은 현대적인 장비들의 안정성을 신뢰해 이런 문제에 대응하도록 설계되지 않기 때문에 퍼포스는 이런 문제에 대응할 자신의 방법을 가지고 있습니다. 개인 백업 전략에 처음에는 퍼포스 서버 스토리지로 단일 디스크 스토리지를 사용하다가 가용성을 유지하기 위해 2베이 스토리지로 이전한 이야기를 했는데 이렇게 슽오리지를 이전하기 전과 후에 모든 파일이 정말로 안전하게 옮겨졌는지 확인하는 것은 퍼포스 서버를 운영하는 제가 해야 하는 일입니다. 퍼포스는 모든 파일의 상태가 처음 기록될 때의 상태와 같은지 확인하는 p4 verify 명령을 제공합니다. 이 명령은 아카이브에 파일 하나하나를 열어 해시를 계산한 다음 처음 기록할 때 만들어 둔 해시와 비교해 파일이 이전과 같은 상태인지 확인합니다. 그래서 스토리지를 이전하기 전후에, 또 그냥 몇 달에 한번 정도 점검해 문제가 없는지 확인하는데 사용합니다. 만약 서버 규모가 더 커져 ZFS 파일시스템을 사용한다면 이런 기능이 파일시스템에 내장되어 있어 신경 쓸 필요가 없지만 일반 사용자용 운영체제의 파일시스템 - 제 경우에는 APFS - 에는 이런 기능이 없어 퍼포스 서버 수준에서 신경을 써야 합니다.
그러던 어느 날 멀쩡히 퍼포스를 사용하다가 서브밋이 안 되는 상황을 겪었습니다. 네트워크 문제로 인한 일시적인 문제라고 생각했지만 좀 기다려도 상황이 개선되지 않습니다. 이번에는 그냥 퍼포스를 구동하는 도커 컨테이너 하나를 재시작하면 해결될 문제라고 생각해 컨테이너를 재시작했지만 문제는 해결되지 않았습니다. 그러는 사이에 문제는 서브밋이 안될 뿐 아니라 싱크(p4 sync)도 안 된다는 사실을 깨달았습니다. 뭔가 아주 단순하지는 않은 어떤 문제가 생긴 것이었습니다. 이번에는 그냥 기계를 재시작하면 해결되지 않을까 하는 생각을 했고 원격에서 기계를 재시작했습니다. 그런데 작은 문제가 생겨 기계를 재시작한 다음 원격에서 기계에 접근할 수 없어 더 이상 문제를 해결할 수가 없었고 그 날 집에 돌아갈 때까지 퍼포스 없이 아주 불편하고 불안한 생활을 해야만 했습니다. 저녁때 집에 돌아와 도커 엔진을 재시작 하고 퍼포스 서버 컨테이너 역시 멀쩡하게 실행된 상태를 확인했지만 여전히 서브밋이 불가능했을 뿐 아니라 퍼포스 클라이언트가 싱크를 시도하면 서버에서 CPU를 한없이 사용하며 싱크가 영원히 끝나지 않고 있었습니다. 원격에서는 그냥 싱크가 안된다고만 생각했는데 서버 쪽에서 보니 싱크가 안될 뿐 아니라 싱크 명령을 실행하면 명령 하나 당 CPU를 100%까지 사용하고 있었습니다. 이 기계는 코어가 12개 있어 도커 데스크탑에 최대 CPU 사용률이 1200%로 표시되는데 서로 다른 클라이언트에서 싱크를 시도할 때마다 CPU 사용률은 약 100%씩 상승했습니다. 이쯤 되면 예상보다 좀 더 심각한 문제가 있어 보입니다.
최대한 침착하게, 하지만 실은 침착하지는 못한 마음가짐으로 퍼포스 서버의 행동을 살펴봤습니다. 퍼포스 서버는 이따금씩 db.sendq
파일을 잠갔다가 풀기를 반복할 뿐 다른 어떤 데이터베이스 파일도 건드리지 않았습니다. 실은 데이터베이스 파일에 광범위하게 락을 걸고 오동작을 일으키고 있을 거라고 예상했지만 그렇지 않았습니다. 먼저 문제 범위를 좁혀야 했습니다. 퍼포스 서버는 근본적으로 세 부분으로 구분할 수 있습니다. 퍼포스 바이너리, 데이터베이스, 그리고 파일 아카이브입니다. 제가 파일을 서브밋 하면 먼저 데이터베이스에 메타데이터를 기록한 다음 파일을 아카이브에 기록합니다. 아카이브에 문제가 생기더라도 서버가 아예 응답이 없어지지는 않습니다. 아카이브로부터 필요한 파일을 찾지 못하면 퍼포스 바이너리의 ‘라이브라리안’이라는 모듈이 에러를 냅니다. 그럼 바로 아카이브에 문제가 있음을 알 수 있는데 이번에는 그렇지 않았습니다. 또 갑자기 파일 아카이브에 이런 광범위한 장애를 일으킬 문제가 그렇게 순식간에 발생할 것 같지도 않았습니다. 어차피 퍼포스 바이너리에 문제가 있다면 이는 제가 대응할 수 있는 장애가 아니기에 이건 바로 가능성 목록에서 제거하고 데이터베이스 문제일 거라고 예상했습니다. 하지만 이 시점에 당장 제가 할 수 있는 일이 없었습니다. 나중에 문제를 해결한 다음에서야 p4 dbverify를 수행해 문제가 있는 데이터베이스 테이블을 정확히 확인할 수 있다는 것을 알게 됐지만 문제를 겪고 있던 때는 몰랐습니다.
퍼포스 리커버리 매뉴얼을 살펴보면 뭔가 여러 가지 시나리오와 대응 방법을 안내하고 있을 것 같지만 결국 이 모든 방법의 핵심은 문제를 데이터베이스 또는 아카이브로 좁힌 다음 각각을 백업으로부터 복원하는 것이 전부입니다. 이 글을 쓰고 있는 시점의 저는 이제 문제가 생긴 퍼포스 데이터베이스의 특정 테이블을 덤프해 좀 더 본격적으로 문제를 해결할 방법이 있다는 것도 알게 됐지만 알고 있다고 해서 실제 문제가 생긴 상황에 이를 적용할 것 같지는 않습니다. 정확한 문제의 원인을 파악하는 것과 당장 동작하지 않는 서비스를 복원하는 것은 서로 상당히 다른 일입니다. 정확한 원인은 됐고 빨리 문제를 해결하고 그만 자고 싶었습니다. 일단 현 시점에 문제를 해결하는 방법은 데이터베이스 파일을 백업으로부터 복원하는 것입니다. 퍼포스는 체크포인트와 저널이라는 데이터베이스 백업 기능이 있습니다. 체크포인트는 일종의 데이터베이스 덤프 기능으로 특정 시점에 데이터베이스 파일(db.*
) 전체를 한 파일에 덤프합니다. 이 파일을 다른 곳에 옮겨 놓으면 데이터베이스 백업을 확보할 수 있습니다. 또 저널 파일도 있는데 이는 이전 체크포인트 생성 후 데이터베이스에 가해진 변경사항을 별도로 보관합니다. 새 체크포인트 파일이 생성되면 이전 체크포인트로부터 이번 체크포인트 사이에 일어난 변경사항을 별도 저널 파일로 만들어 주며 이 역시 다른 곳에 옮겨 놓으면 됩니다. 퍼포스의 데이터베이스 리커버리 방법은 체크포인트와 저널에 기반해 데이터베이스 파일을 재구축 하는 것입니다.
그러나 지난 개인 백업 전략에 언급했다시피 제가 백업을 사용해야 하는 상황에 처할 때 과연 이런 각 소프트웨어마다 서로 다른 복원 방법을 침착한 상태로 올바르게 수행할 수 있을지 믿을 수 없습니다. 도커 컨테이너에서 동작하는 여러 서비스가 의존하는 mysql 데이터베이스 역시 표준 복구 방법은 덤프로부터 데이터베이스 파일을 재구축 하는 것입니다. 퍼포스도 마찬가지입니다. 그런데 과연 제가 평소에 해볼 일이 없는 이 각각의 작업을 침착하게 정확히 수행할 수 있을 거라고 예상할 수 있을까요? 저는 미래의 저를 믿을 수 없습니다. 때문에 복구 과정은 최대한 단순하고 또 여러 서비스에 걸쳐 일관된 모양이기를 원했습니다. 크래시플랜을 사용하지 마세요라고 말한 이유 중 가장 큰 것이 크래시플랜이나 백블레이즈 백업 같은 서비스는 서로 다른 플랫폼에 대한 의존성을 최소화 해 소프트웨어 개발 비용을 낮추려는 목적으로 각 파일시스템이 제공하는 스냅샷 기능에 의존하지 않고 파일을 그냥 복사하기 때문이라고 말했습니다. 이런 동작은 특히 데이터베이스 파일처럼 여러 파일에 걸쳐 특정 시점의 상태가 중요한 경우 적용할 수가 없습니다. 겉으로는 모든 파일을 백업한 것처럼 보이지만 파일 각각은 서로 조금씩 다른 시점에 복사되어 일관성이 없어진 상태일 가능성이 있습니다. 그러면 파일을 복원했지만 서비스를 시작할 수 없어 복원이 아무 의미 없는 상태가 될 수 있습니다. 퍼포스 데이터베이스도 마찬가지입니다. 이 많은 데이터베이스 파일은 백업 될 때 어느 특정 순간의 상태를 한 번에 복사해야만 미래에 단순한 방법으로 복원할 수 있습니다.

지금 당장 데이터베이스 복원을 시도해야 할 지는 결정하기 어려웠습니다. 분명 퍼포스 서버가 높은 CPU 점유율을 보이며 이상하게 동작하고 있었지만 어쩌면 그냥 일시적인 작업으로 이 상태에서 퍼포스 서버가 지금 수행하고 있을지도 모르는 어떤 작업을 끝내면 다시 정상으로 돌아올지도 모른다고 생각했습니다. 괜히 제가 개입해 긁어부스럼 만드는 것 아닌가 싶은 걱정이 들었습니다. 어차피 서버 기계를 강력한 것으로 교체한 다음이어서 퍼포스 서버가 CPU를 좀 많이 사용한다고 해서 다른 서비스에 영향을 주지도 않았기에 일단 이 날 밤은 이 상태로 방치하고 몇 시간 기다려 보기로 합니다. 일단 자러 갔다가 다음 날 아침에 일어나 여전히 같은 상태이면 더 이상 생각할 것도 없이 데이터베이스를 복원하고 만약 문제가 해결되었다면 아무 것도 안 하고 그냥 놔 두면 됩니다. 문제를 말끔하게 해결하지 않은 상태에서 자러 가느라 좀 찜찜했지만 어차피 더 이상 할 수 있는 일도 없었기에 미련 없이 … 는 아니고 미련을 좀 남긴 채 자러 갔습니다. 다음 날 아침이 됐고 예상하셨겠지만 문제는 해결되지 않았습니다.
제가 사용할 수 있는 퍼포스 데이터베이스를 복원하는 방법은 두 가지입니다. 하나는 퍼포스 스스로가 권장하는 체크포인트와 저널 파일에 근거해 데이터베이스 파일 전체를 재구축 하는 것입니다. 실은 이건 한 번도 해본 적이 없어 시도하고 싶지 않았습니다. 이럴 줄 알았으면 평소에 한번 연습해 볼 걸 그랬습니다. 나중에 이야기 하겠지만 도커 환경이므로 다른 컨테이너를 생성해 시도해보기로 했습니다. 다른 한 가지 방법은 파일 백업으로부터 복원하는 것입니다. 개인 백업 전략에서 타임머신 기반의 로컬 백업 두 개와 오프사이트 백업 하나가 있었습니다. 로컬 백업으 모두 멀쩡하니 오프사이트 백업을 건드릴 필요는 없었습니다. 그런데 이 때 자칫 큰일 날 뻔 했다는 사실을 알게 됐는데 타임머신 백업은 단순하고 견고하게 동작하는 대신 설정할 수 있는 것이 거의 없습니다. 특히 백업 리텐션 설정은 지난 24시간 동안은 매 한 시간마다 생성된 백업을 유지하고 지난 7일 동안은 하루 마다 생성된 백업을 유지하며 그 다음부터는 한 주 단위의 백업을 유지하다가 스토리지가 가득 차면 가장 오래된 백업부터 삭제하는 식으로 동작합니다. 저는 이 문제를 전 날 오전 11시 경에 겪었습니다. 본격적으로 데이터베이스를 복원해야겠다고 마음 먹은 시점은 다음 날 오전 7시 경이었고 만약 네 시간 늦게 작업을 시작했다면 전 날 생성된 한 시간 단위의 백업이 바꿀 수 없는 리텐션 정책에 의해 사라질 위기에 처해 있었습니다. 다행히 제가 복원하기를 원하는 시점으로부터 20시간이 지난 시점이어서 백업이 남아 있었습니다만, 자칫 훨씬 더 큰 유실이 발생할뻔 했습니다.
제가 문제를 처음 인지한 것은 전날 오전 11시를 조금 넘긴 시점이었고 이 시각과 가장 가까운 타임머신 백업은 10시 30분에 일어났습니다. 타임머신은 APFS 파일시스템이 제공하는 스냅샷에 기반해 백업하므로 모든 파일이 정확히 같은 시점에 복사되었으리라 예상해도 됩니다. 타임머신 인터페이스는 아름답기는 하지만 실제로 사용해보면 느리고 굉장히 불편합니다. 또 GUI 애플리케이션과 CLI 환경 사이에 설정이 서로 달라 경로 길이가 길면 타임머신 인터페이스 상에서 복원이 불가능하기도 합니다. 참고로 CLI 환경에서는 같은 상황에 아무 문제 없이 복원할 수 있습니다. 일단 퍼포스 서버를 멈추고 데이터베이스 파일을 과감히 삭제한 다음 타임머신 백업으로부터 전날 오전 10시 30분 경에 생성된 백업으로부터 데이터베이스 파일을 꺼내 그들이 원래 있어야 할 자리에 복사했습니다. 전체 파일 용량은 약 8.5기가로 크지 않아 복사는 순식간에 끝났습니다. 이제 진실의 시간입니다. 퍼포스 서버를 재시작합니다. 여기까지는 똑같습니다. 데이터베이스를 복원하기 전에도 서버는 멀쩡하게 재시작되었고 p4 info
나 p4 fstat
같은 명령에 아무 문제 없이 응답했습니다. p4 sync
같은 쓰기 명령들이 오동작했을 뿐입니다. 퍼포스 클라이언트를 켜고 싱크를 시도해 봅니다. 황당할 정도로 아무 일도 없었다는 듯 순식간에 싱크를 마칩니다. 다른 기계의 클라이언트도 마찬가지입니다. 문제는 순식간에 해결되었습니다. 정확히 기억하지는 못하지만 대략 여섯 개 정도의 체인지리스트를 유실했습니다. 하지만 여기에 해당하는 파일은 여전히 퍼포스 워크스페이스를 구동하는 기계 각각의 로컬에 남아 있었기에 파일 자체를 유실하지는 않았습니다. 그럴 필요가 없음을 알고 있지만 마침 이렇게 된 김에 p4 verify
를 실행해 둡니다. 서버 기계를 교체하기 전에는 이 명령을 실행하면 퍼포스 서버를 거의 사용할 수 없을 정도로 느려졌지만 기계를 교체한 다음에는 이 명령이 실행되는 도중에도 아무 문제 없이 퍼포스 서버를 사용할 수 있습니다.
문제의 정확한 원인은 어떤 이유로 퍼포스 데이터베이스 파일(db.*
) 중 하나 이상이 깨졌고 이를 곧이곧대로 받아들인 퍼포스 서버가 오동작일 일으킨 것입니다. 파일이 깨진 원인은 아직 모르겠습니다. 어쩌면 컨테이너가 재시작 되는 과정에서 파일을 다 쓰지 못한 채 컨테이너가 종료되었을른지도 모르지만 이는 추측일 뿐입니다. 일단 문제가 다시 일어날 가능성을 줄이기 위해 도커 컨테이너가 종료될 때 이벤트를 받아 명령을 실행할 수 있다는 것을 알게 되었습니다. 지금 까지는 마우스 버튼을 딸깍거리며 컨테이너를 아무 준비 없이 끄고 켜곤 하면서도 문제를 겪지 않았지만 컨테이너가 종료될 때 명시적으로 서버 종료 절차를 거쳐야 할 것 같습니다. 아직 실행하지는 않았지만 도커 컨테이너 종료 신호가 오면 여기 맞춰 퍼포스 서버를 명시적으로 종료하도록 수정할 작정입니다. 그럼 최소한 이 과정에서 일어날 가능성이 있는 데이터베이스 파일 오류를 피할 수는 있을 겁니다.
모든 데이터베이스 파일이 깨지지는 않았을 것 같습니다. 하지만 저는 어느 파일이 깨졌는지 확인할 수 없으니 모든 파일을 복원했고 이 과정에서 체인지리스트 몇 개를 유실했습니다. 그런데 p4 verify와 비슷한 p4 dbverify 명령으로 정확히 어떤 데이터베이스 파일에 문제가 일어났는지 확인할 수 있다는 것을 나중에서야 알게 되었습니다. 이 명령을 미리 알았더라면 모든 데이터베이스 파일을 복원하는 대신 문제가 생긴 파일만 복원하는 결정을 내릴 여지도 있었을 겁니다. 물론 여느 데이터베이스 파일과 같이 퍼포스 데이터페이스 파일 역시 한 가지 동작을 위해 여러 테이블을 읽고 쓰기 때문에 문제가 생긴 파일 하나만 되돌리는 것은 별로 좋은 생각이 아닐 가능성이 있습니다. 하지만 장애 상황에서 db.sendq
테이블을 계속해서 열었다 닫기를 반복하는 동작을 봤는데 만약 이 테이블 하나만 깨져 있음을 확인할 수 있었다면 이 파일만 덮어썼을 겁니다. 이 파일은 여러 스레드를 사용해 동시에 파일을 주고 받는 상황에서 클라이언트 별, 스레드 별 파일 목록을 관리하는 테이블입니다. 그러니 이 테이블이 문제라면 나머지 테이블과 직접적으로 관련되지 않으니 마음 편히 이 테이블 하나만 복원해 훨씬 더 빨리, 그리고 정확한 방법으로 문제를 해결할 수 있었을른지도 모르겠습니다. 핵심은 데이터베이스가 깨졌다고 의심될 때 데이터베이스 무결성을 확인하는 명령이 있고 이를 통해 어느 데이터베이스 파일이 깨졌는지, 또 데이터베이스가 깨진 것이 맞긴 한지 확인할 수 있다는 것입니다.
복구 과정에서 두 가지 생각할 점이 있습니다. 먼저 타임머신 백업은 리텐션 설정을 변경할 수 없어 한 시간 단위 백업은 이전 24시간 동안만 유지된다는 점입니다. 다행히 한 시간 단위 백업이 남아 있어 문제를 인지한 시점과 가장 가까운 문제가 발생하지 않은 상태의 백업을 사용할 수 있었지만 몇 시간 늦게 복원을 시도하려 했다면 하루 단위의 백업만 남아 있어 더 많은 체인지리스트를 유실했을 가능성이 있습니다. 이는 근본적으로 타임머신으로 로컬 백업을 하는 이상 피할 방법이 없습니다. 그나마 실행 가능한 방법은 복원하기를 원하는 시점의 백업이 리텐션 정책에 의해 사라지기 전에 일단 백업을 중단하거나 두 드라이브를 사용해 번갈아가며 백업하고 있으므로 복원하기를 원하는 시점의 백업이 기록된 드라이브를 타임머신 백업으로부터 제외하는 것입니다. 두 드라이브 중 하나를 제거하더라도 여전히 나머지 한 드라이브로 백업이 계속되고 또 타임머신 백업에 할당되어 있지 않더라도 운영체제 수준에서 타임머신 백업에서 제외된 드라이브에 접근할 수 있으니 리텐션 정책이 적용되지 않는 상태에서 느긋하게 복원을 시도할 수 있습니다. 핵심은 타임머신 스스로 제가 필요한 백업을 파괴하지 않도록 빠른 조치가 필요하다는 점입니다.
다른 하나는 굳이 타임머신 백업까지 갈 필요도 없이 퍼포스가 직접 제공하는 체크포인트 파일과 저널 파일을 가지고 있었기에 이를 활용해 데이터베이스 파일을 재구축했더라면 데이터 유실 없이 데이터베이스 파일을 복구할 수 있었을 거라는 점입니다. 퍼포스 매뉴얼에는 대규모 서버의 경우 매 주 주말에 체크포인트를 생성하고 매일 저널 파일을 갱신할 것을 권장합니다. 체크포인트는 앞서 설명한 대로 일종의 데이터베이스 덤프인데 서버 규모가 크면 시간이 아주 오래 걸릴 수 있으니 주말에 수행하는 것이 좋습니다. 그리고 저널 파일은 체크포인트와 체크포인트 사이에 일어난 데이터베이스 변경사항을 기록하는데 저널 파일 역시 너무 커지면 파일이 손상될 가능성이 높아지므로 매일 새 저널 파일에 쓰기 시작하도록 해 손상 가능성을 줄이려는 전략입니다. 이 시나리오에서 체크포인트 파일로 데이터베이스를 재구축 하려면 지난 주말에 생성된 체크포인트 파일 하나와 그 후 기록되었으며 매일 다음 파일에 기록한 저널 파일 여러 개를 사용합니다. 제 경우에는 규모가 훨씬 작아 주말에 체크포인트를 생성할 필요가 없습니다. 매일 새벽 두 시에 체크포인트를 생성하는데 약 10분 정도 걸립니다. 매일 체크포인트를 생성하기에 저널 파일은 굳이 새로 생성하지 않고 체크포인트를 생성할 때 자동으로 저널이 새 파일로 시작하는 상태로 놔 두고 있습니다. 다만 저는 이들로부터 데이터베이스를 재구축하는 연습을 한 번도 해본 적 없다는 점이 문제입니다. 다행히 퍼포스 서버는 도커 컨테이너에서 돌고 있기에 그냥 아카이브에 연결되지 않는 퍼포스 서버 컨테이너를 새로 만든 다음 그 안에서 체크포인트 파일을 사용해 데이터베이스를 재구축 하는 연습을 해 봐야 합니다. 이 역시 앞서 언급한 컨테이너가 종료될 때 명시적으로 서버를 종료하도록 하는 것과 마찬가지로 아직 수행하지는 않았습니다. 하지만 조만간 시간을 낼 작정입니다.
퍼포스 서버는 저에게 이 강력한 파일 관리 환경을 무료로 제공합니다. 또 일할 때 사용하는 것과 비슷한 환경을 개인 수준에서 지속적으로 사용하며 익숙해지고 또 제가 하는 행동이 어떤 결과를 가져올지 좀 더 자신감을 가질 수 있게 해 주기도 합니다. 제가 사용한 모든 파일은 제가 의도한 시점에 버전이 만들어져 있고 아무리 오래 된 과거 버전이라도 순식간에 현재로 소환할 수 있게 해 줍니다. 드랍박스에 별도로 추가 요금을 지불하더라도 최대 1년 동안만 히스토리를 보존해 주지만 퍼포스 서버를 사용하는 한 그런 제한 따위는 없습니다. 아무리 오래 된 파일이라도 끄집어낼 수 있습니다. 또한 퍼포스는 파일의 매 버전을 별도 파일에 기록하므로 오프사이트 백업 서비스의 리텐션 정책으로부터 자유롭습니다. 파일을 삭제하더라도 삭제되지 않고 새 버전은 별도 파일을 생성하므로 파일의 삭제된 상태에 근거하는 리텐션 정책에 신경을 꺼도 아무 문제를 일으키지 않습니다. 그런데 이 모든 장점을 계속해서 누리기 위해서는 퍼포스 서버가 멀쩡히 동작해야만 하고 문제가 생길 때 이를 안전하게 복구할 수 있어야 합니다. 이번에는 파일시스템의 스냅샷에 근거해 생성된 백업을 사용한 덕분에 복원하기는 했지만 체인지리스트 몇 개를 잃어버렸습니다. 앞으로도 퍼포스 서버에 장기간에 걸쳐 의존할 작정이므로 최소한 퍼포스 서버 정도는 이들이 권장하는 복원 방법인 체크포인트를 사용하는 것 정도는 미리 연습해 어느 정도 익숙한 상태가 되어 있어야겠다는 교훈을 얻었습니다.