Migrating billions of records: moving our active DNS database while it's in use

Bài tóm tắt Cloudflare 2024 3. Quy mô lớn Độ khó: Nâng cao

Cloudflare · 2024 · 3. Quy mô lớn · Độ khó: Nâng cao — bài gốc

Bối cảnh

  • Theo W3Techs (10/2024), Cloudflare là authoritative DNS của 14,5% website. Zone lớn nhất có ~4 triệu record, phần lớn zone dưới 100.
  • Ban đầu Cloudflare dựa vào một cluster Postgres cfdb (primary + replica) cho hầu hết control plane. Từ khoảng 2017 nhiều service đã dời dữ liệu ra microservice riêng.
  • 2022: dữ liệu DNS chiếm ~40% dung lượng cfdb. DNS Records API là API được dùng nhiều nhất, mỗi request là một query DB. Khi DNS có spike thì các service không liên quan cũng chậm theo.
  • Cuối 2022: team DNS cùng 25 team khác quyết định tách dữ liệu DNS sang cluster riêng dnsdb.

Thay đổi chính

Chuẩn bị (nhiều năm):

  • Tách "zone" khỏi "DNS record": hai khái niệm vốn gắn chặt, nhưng zone đã thành nơi chứa cấu hình của nhiều sản phẩm khác, nên chỉ dời record.
  • Chặn truy cập trực tiếp vào DB: hơn 20 service viết bằng 5 ngôn ngữ chuyển sang dùng một gRPC API nội bộ cho DNS record. Team DNS kiểm soát được ai đọc gì, với tốc độ nào; rate limit và cache được.
  • Logic trước đây nằm ở mức SQL (kiểm tra giới hạn số record của zone, audit) được chuyển lên tầng ứng dụng.

Yêu cầu migration: không mất dữ liệu và chứng minh được là không mất; downtime dưới 1 phút; chuyển được hai chiều (để rollback); partition bảng mới theo mod(zone_id, n); DB cũ vẫn đọc được dữ liệu đã dời (qua foreign table, làm lối thoát khẩn cấp); tại mỗi thời điểm chỉ một DB được ghi. Họ loại pgLogical vì lo hiệu năng và khả năng audit.

Dữ liệu: cf_rec có 1,7 tỷ record (1,5 TB kèm index), mỗi ngày 3–5 triệu insert, 1 triệu update, 3–5 triệu delete; cf_archived_rec có ~4,3 tỷ record. Không có trigger hay foreign key.

Change Data Capture and Transfer Service (CDCTS), tự viết:

  • Trigger ghi trạng thái mới của mỗi hàng (I/U/D) vào bảng log log_cf_rec.
  • Chép ban đầu theo lô 1 triệu bằng COPY ... | COPY (không file trung gian), đếm số hàng ở hai bên sau mỗi lô; có thể tạm dừng hoặc dừng hẳn bằng một file cờ.
  • Chép thay đổi theo lô, mỗi bước là một transaction: lấy lô, đánh dấu đã chuyển, xoá khỏi log, ghi bảng tổng kết. Ở DB đích, chỉ áp bản thay đổi cuối cùng cho mỗi ID (insert + 30 update thì chỉ còn update cuối). Bảng audit hai bên so số lượng. Chạy mỗi 3 giây trong nhiều tuần trước ngày chuyển.
  • cf_migration_manager: bảng được API poll để biết DB nào đang active và DB có đang khoá ghi không. Khi khoá, API giữ request HTTP lại thay vì trả lỗi. Việc này chỉ làm được vì mọi service đã đi qua gRPC API.

Ngày chuyển (thông báo cửa sổ bảo trì vài giờ cho chắc): giảm chu kỳ chép từ 3s xuống 0,5s → khoá ghi cfdb → read-only → chép nốt thay đổi → bật ghi dnsdb → báo API. Tổng không quá 2 giây; latency API tăng vọt vài phút do backlog rồi trở lại bình thường. Sau đó phát hiện 3 service ít dùng bị sót, được sửa nhanh nhờ foreign table.

Kết quả

  • Tải cfdb giảm mạnh, nhường tài nguyên cho service khác.
  • Request/giây vào DNS Records API tăng hơn gấp đôi từ đó, mà CPU cả hai cluster dưới 10%; số sự cố liên quan DB giảm rõ.
  • Team DNS tự chỉnh cấu hình riêng cho cluster (ngưỡng replication lag), đẩy được gần hết lượt đọc sang replica.

Vì sao

Theo Cloudflare: một DB dùng chung làm tải của DNS ảnh hưởng mọi service khác, và cải tiến ở mức hệ thống đã hết tác dụng.

Bài học cho SWE

  • Trước khi dời dữ liệu, gom mọi truy cập vào một API, để có điểm kiểm soát cho cutover (giữ request) và cho thay đổi sau này. Cùng ý với gateway của Uber (Uber 2020).
  • Tách hai khái niệm gắn chặt (zone và record) là phần khó nhất, giống việc GitHub gỡ JOIN xuyên domain (GitHub 2021).
  • CDC tự viết bằng trigger + lô + audit số lượng hai đầu: chứng minh được không mất dữ liệu. Có lối thoát (foreign table) cho những thứ bị sót.
  • Cutover vài giây, nhưng chuẩn bị mất nhiều năm.

Liên quan