Nguyen Le PhongNguyen Le Phong

Rate limiting và throttling

Một bài giải thích thực tế về rate limiting và throttling: cách hệ thống bảo vệ capacity chung, điều tiết traffic, giao tiếp limit rõ ràng và ngăn overload biến thành outage.

Dấu hiệu đầu tiên không phải một outage ồn ào. Nó chỉ là một con số đỏ nhỏ trên dashboard sau giờ ăn trưa, kiểu con số người ta chỉ để ý vì nó cứ tăng trong lúc mọi người vẫn đang làm việc bình thường. Một campaign mới vừa chạy, vài client bắt đầu retry hơi mạnh tay, và login API bỗng giống như một cánh cửa hẹp ở lối vào một tòa nhà đông người.

Rate limiting và throttling thường được mô tả như cơ chế kiểm soát, nhưng trong thực tế chúng là một cuộc trò chuyện về capacity. Một system chỉ có một lượng CPU, database connection, queue depth, vendor quota và sức chịu của support nhất định. Nếu caller nào cũng có thể dùng bao nhiêu tùy ý vào bất cứ lúc nào, traffic hung hăng nhất thường thắng, còn user yên lặng hơn sẽ gặp lag, timeout hoặc downtime. Limit giúp không gian chung công bằng hơn.

Rate limiting thường trả lời câu hỏi: caller này được gửi bao nhiêu request trong một khoảng thời gian? Có thể là 100 request mỗi phút cho một API key, 10 lần login mỗi giờ cho một account, hoặc 1 export job nặng tại một thời điểm. Throttling nghiêng về việc làm chậm traffic khi system chịu áp lực. Thay vì nhận mọi request cho đến khi mọi thứ đổ xuống, service chủ động delay, reject, queue hoặc giảm việc để toàn bộ system còn sống.

Sự khác nhau giữa hai khái niệm này ít quan trọng hơn ý định phía sau. Mình không cố phạt user vì họ dùng product nhiều. Mình đang bảo vệ lời hứa của product cho tất cả mọi người. Một rate limit tốt giống như mở thêm quầy kiểm vé trước khi rạp đông, rồi giữ mỗi hàng đi với tốc độ nhân viên thật sự phục vụ được. Không có kỷ luật nhỏ đó, hàng ồn nhất có thể chặn cửa của cả rạp.

Limit hữu ích bắt đầu từ unit rõ ràng. Per user, per IP, per account, per organization, per endpoint, per token và per device kể những câu chuyện khác nhau. Một public search endpoint có thể cần limit khác với internal admin export. Password reset flow cần bảo vệ khỏi abuse. Webhook receiver cần chịu được burst vì traffic đến theo từng đợt. Một con số global hiếm khi hợp với mọi đường đi.

Algorithm cũng định hình trải nghiệm. Fixed window đơn giản, nhưng có thể tạo burst khó chịu ở rìa mỗi window. Sliding window mượt hơn, nhưng tốn chi phí tracking hơn. Token bucket cho phép burst ngắn trong khi vẫn giữ average dài hạn. Leaky bucket làm traffic chảy đều hơn. Đây không chỉ là lựa chọn toán học. Chúng quyết định một khách hàng thật có hoàn tất được tác vụ bình thường trong một phút bận rộn hay không.

Response design là một phần của architecture. Caller chỉ nhận generic error sẽ phải đoán chuyện gì xảy ra. API tốt hơn trả status rõ như 429 Too Many Requests, kèm header hữu ích như quota còn lại hoặc thời điểm nên retry, và document client nên làm gì tiếp theo. Nếu product có UI, message nên nghe như người thật: hệ thống đang bận, action vẫn an toàn, và thử lại sau là đủ. Limit rõ làm giảm hoang mang.

Ở đây cũng có lợi ích security thầm lặng. Login attempt, password reset, scraping, brute force request và AI call đắt tiền đều cần boundary. Nhưng security limit có thể làm đau user thật nếu quá thô. Cả một văn phòng có thể dùng chung một NAT IP. Một người đi công tác có thể gặp network không ổn định. Một business customer có thể thật sự chạy một batch lớn. System tốt cần account-level policy, allowlist, support override và observability, thay vì giấu mọi quyết định trong một con số hardcode.

Phần khó chịu là rate limiting thường làm lộ ra những product decision mình từng trì hoãn. Ai được ưu tiên khi overload? Free user và paid user có thể có guarantee khác nhau. Internal job và customer-facing request có thể tranh cùng một database. Background export có thể ít khẩn cấp hơn checkout. Architecture không tự trả lời hết các câu đó. Nó chỉ làm trade-off đủ rõ để team quyết định trước khi incident quyết định thay mình.

Tôi thích rate limiting vì nó chấp nhận một sự thật đơn giản: system khỏe mạnh đôi khi phải nói không. Không giận dữ, không ngẫu nhiên, và không mãi mãi. Nó nói không một cách có đo lường để phần còn lại của service vẫn giữ được lời hứa. Nếu bạn từng thấy một limit nhỏ ngăn một outage lớn, hoặc từng thấy thiếu limit tạo thêm căng thẳng không cần thiết, câu chuyện đó rất đáng mang vào buổi design review tiếp theo.

Bạn thấy bài viết thế nào?