Postmortem of database outage of January 31

Bài tóm tắt GitLab 2017 2. Tăng trưởng Độ khó: Trung bình

GitLab · 2017 · 2. Tăng trưởng · Độ khó: Trung bình — bài gốc

Bài ngoài sheet. Một trong số ít postmortem công khai về mất dữ liệu production thật, do chính GitLab viết (CEO đứng tên xin lỗi).

Bối cảnh

  • Đầu 2017, GitLab.com chạy một primary + một secondary hot standby PostgreSQL; secondary chỉ để failover, mọi tải dồn vào primary. Primary đã là điểm hỏng chung trong vài sự cố trước (bloat, polling của CI giữ khoá hàng).
  • Năm cơ chế "dự phòng": pg_dump hằng ngày lên S3, LVM snapshot hằng ngày (dùng để chép production sang staging), snapshot đĩa Azure (chỉ bật cho máy NFS, không cho DB), và replication (chỉ để failover, không phải DR).

Chuyện gì đã xảy ra

  • Tải DB tăng vọt (spam, cộng một job xoá cứng một nhân viên bị báo cáo lạm dụng nhầm). Secondary trễ replication tới mức WAL cần thiết đã bị primary xoá; không có WAL archiving nên phải dựng lại secondary bằng tay: xoá thư mục dữ liệu rồi chạy pg_basebackup.
  • pg_basebackup treo không báo gì (thực ra là hành vi bình thường: nó chờ primary bắt đầu gửi, nhưng runbook và tài liệu không nói). Thêm hai cấu hình sai lộ ra giữa chừng (max_wal_senders mặc định 3; max_connections = 8000 làm Postgres không khởi động lại được khi tăng max_wal_senders).
  • Một kỹ sư xoá thư mục dữ liệu, tưởng đang ở secondary nhưng là primary; dừng sau một hai giây thì khoảng 300 GB đã mất. Lúc đó cả primary lẫn secondary đều không còn dữ liệu.
  • Không backup nào dùng được: bucket S3 của pg_dump trống, vì job chạy trên máy ứng dụng nên package Omnibus chọn pg_dump 9.2 cho DB 9.6 và lỗi ngay; email cảnh báo lỗi cron bị từ chối vì thiếu DMARC, nên không ai biết. Snapshot đĩa Azure không bật cho DB. Chỉ còn LVM snapshot làm thủ công 6 giờ trước để nạp staging.
  • Khôi phục từ bản staging: chép dữ liệu từ đĩa mạng rẻ tiền (khoảng 60 Mbps) mất khoảng 18 giờ; phải khôi phục riêng webhook (bản staging đã xoá webhook để tránh bắn nhầm) và tăng mọi sequence thêm 100.000 để không dùng lại ID cũ.
  • Mất dữ liệu DB khoảng 6 giờ (17:20–23:30 UTC): ước tính khoảng 5.000 project, 5.000 comment, 700 user mới; repo Git và wiki không mất vì nằm riêng. Quá trình khôi phục được livestream trên YouTube và ghi công khai vào Google Doc.

Vì sao (5 Whys của GitLab)

  • Ngừng dịch vụ: thao tác thủ công, không tự động hoá và không có tài liệu, chạy trên máy nhầm; replication đứt vì tải tăng; tải tăng vì spam và hệ xử lý báo cáo lạm dụng quá dễ xoá nhầm người.
  • Khôi phục lâu: backup hỏng âm thầm, snapshot không bật cho DB, và không ai sở hữu việc kiểm tra backup định kỳ.

Thay đổi sau sự cố

  • Thêm nhiều secondary và cân tải đọc; LVM snapshot mỗi giờ thay vì mỗi ngày; dashboard công khai theo dõi backup; quy trình chạy thử và lùi migration trên staging; DR cho cả phần ngoài DB, có chủ sở hữu.
  • Theo GitLab, ưu tiên là khôi phục nhanh khi đã sai và làm rõ đang đứng trên máy nào, hơn là chặn lệnh (alias rm chỉ chặn một kiểu lỗi trong rất nhiều kiểu mất dữ liệu).
  • Hai năm sau, GitLab dùng thêm một replica trễ 8 giờ để khôi phục xoá nhầm (GitLab 2019).

Bài học cho SWE

  • Replication không phải backup: một lệnh sai hoặc lỗi logic lan sang replica ngay.
  • Backup chưa từng khôi phục thử thì coi như không có; cảnh báo lỗi phải đi qua kênh mà chính nó được giám sát.
  • Thao tác thủ công trên DB production dưới áp lực là chỗ sai dễ nhất: runbook, tên máy dễ phân biệt, tự động hoá việc dựng lại replica.
  • Thời gian khôi phục do chỗ chậm nhất quyết định (ở đây là đĩa rẻ của staging), nên cần đo RTO thật chứ không đoán.

Liên quan