Scaling Email Infrastructure for Medium Digest

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

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

Bối cảnh

  • Digest là email cá nhân hoá hằng ngày, phải tới 8–9 giờ sáng theo giờ địa phương. Gửi hàng triệu email/ngày; là một trong những tải nặng nhất.
  • Luồng: mỗi 10 phút một job Jenkins phát shard event, fan-out thành 256 shard event con; mỗi cái quét một shard của bảng email user cho múi giờ hiện tại và phát generation event cho từng user; generation gọi service gợi ý Rex rồi phát send event sang Sendgrid. Bắt đầu sinh trước 6 giờ; user mỗi múi giờ chia ngẫu nhiên vào sáu bucket 10 phút để một lần hỏng mất ít digest.
  • Tất cả digest event chạy trên một hàng đợi SQS, giới hạn 500 event/giây.
  • Tháng 9–10/2019: thời gian "rảnh" trong ngày biến mất; tổng event/ngày chạm sức xử lý tối đa/ngày. Cuối 10/2019 vượt ngưỡng, hàng đợi ứ mãi không hết.

Thay đổi chính

  1. Tách send event ra hàng đợi riêng (khoảng 20% event): email đã sinh không phải xếp cuối hàng đợi ứ. Chỉ là băng dán; 6/2020 lại chạm trần.
  2. Chỉ sinh cho user còn hoạt động (45 ngày, quy tắc "sunset"): trước đó cứ mỗi user từng đăng ký là một generation event, nhưng chỉ 1/4 thành email. Event no-op tuy nhanh vẫn tốn suất vì hàng đợi giới hạn theo số event/giây. Thêm cột lastActiveAt, query chỉ user hoạt động; bật sau flag, ramp dần.
  3. Ramp tới 50% thì Rex lỗi nhiều: không còn event no-op "đệm" ở giữa, request tới Rex thành cụm đỉnh mỗi 10 phút, Rex scale lên rồi xuống liên tục (thrashing).
  4. Rải đều: mỗi generation event có delay ngẫu nhiên trong 10 phút (SQS cho delay tới 15 phút); và tách shard event ra hàng đợi riêng để nó chạy đúng giờ, vì khi hàng đợi chung ứ, các lô shard event dồn vào nhau làm hỏng phép rải.
  5. Vẫn lỗi khi chuyển từ múi giờ ít người sang múi giờ đông: hạ rate tối đa 500 → 250 (ít event hơn nên đủ), cấp thêm tài nguyên và chỉnh autoscale cho Rex cùng team Recommendations.
  • Kết quả: giảm 70% generation event; còn khoảng 22,5% vẫn no-op. Theo Medium, sức gửi tăng khoảng 282% (3,8 lần), đủ cho ít nhất vài năm.

Vì sao

  • Tăng rate là cách hiển nhiên nhưng đắt (phải tăng tài nguyên cho service phía sau); Medium chọn tối ưu luồng trước.
  • Bỏ việc vô ích lại đổi hình dạng tải: chính các no-op từng làm phẳng tải cho Rex.

Bài học cho SWE

  • Tách loại event khác nhau ra hàng đợi riêng khi có nhu cầu cụ thể; đừng tách sớm (Event-driven, message queue và Kafka).
  • Lịch chạy theo mốc giờ tạo cụm tải; rải bằng jitter, và giữ nguồn phát sự kiện chạy đúng nhịp (Hot partition và thundering herd).
  • Giới hạn tốc độ của consumer là van backpressure cho service phía sau; hạ nó có thể là cách sửa (Load shedding, rate limit và backpressure).
  • Phụ thuộc vào service của team khác: làm việc trực tiếp với team sở hữu, đừng tự đoán cách scale hệ mình không hiểu (theo Medium, tự làm "không suôn sẻ").
  • Hướng tiếp theo Medium nêu: tăng rate từ từ (10 → 20 → 40 … → 500) thay vì nhảy một phút, hoặc cho phép retry an toàn.

Liên quan