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
- 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.
- 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. - 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).
- 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.
- 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
- Medium 2018 (hai năm trước: chuyển sang microservice) · Medium 2015
- GitLab 2019 (cùng mẫu: tải dồn vào mốc giờ)
- Event-driven, message queue và Kafka · Hot partition và thundering herd · Load shedding, rate limit và backpressure
Event-driven, message queue và KafkaHot partition và thundering herdLoad shedding, rate limit và backpressure
Được nhắc tới trong
Chủ đề (3)
Công ty (1)
Bài tóm tắt (1)