Mới54 bài học mới được bổ sung từ 10/09!
Xem nhật ký cập nhật →
Software Development Atlas
Hệ thống Phân tán

Timeout: Vận hành với ngân sách thời gian rõ ràng

Đặt, truyền, quan sát và tinh chỉnh timeout để dependency chậm chỉ giữ tài nguyên trong giới hạn mà không tạo cảm giác chắc chắn giả.

Phát triểnĐã xác minh: 5 thg 11, 2026Đánh giá lại: 180 ngày

Bản đồ học tập phát triển phần mềm bởi Tran Trong Thuc · Về dự án Atlas · Cập nhật lần cuối: 5 thg 11, 2026

Timeout: Vận hành với ngân sách thời gian rõ ràng

Tóm tắt

Tình huống minh họa (giả định): Một dịch vụ gợi ý sản phẩm phản hồi chậm, trong khi gateway sử dụng timeout chung 30 giây cho từng lần gọi. Dù request của người dùng chỉ còn ít thời gian hữu ích, gateway vẫn chờ dependency và giữ tài nguyên lâu hơn cần thiết. Không thể suy ra số worker bị chặn hoặc thời gian sập hệ thống từ một giá trị timeout: cần đo loại timeout, số request đồng thời và độ trễ thực tế.

Một timeout giới hạn thời gian chờ của một thao tác hoặc một giai đoạn; deadline xác định thời điểm toàn bộ request phải dừng chờ để tránh tiếp tục tiêu tốn tài nguyên vô ích. Timeout không chứng minh server chưa thực hiện thao tác.

💡 Quy tắc bỏ túi: Trước khi đặt timeout, xác định nó đo phần nào của request (connect, response, đọc body hay toàn bộ call). Với thao tác có deadline toàn trình, truyền thời gian còn lại tới từng dependency, giữ phần dự phòng cho xử lý cục bộ và dừng công việc khi không còn hữu ích. Deadline là quyết định về mục tiêu/request, không phải giá trị mặc định của từng socket.

  • Phạm vi timeout khác với deadline: Timeout giới hạn thời gian đợi một phase hoặc thao tác; deadline là hạn chót của request end-to-end. SLO độ trễ dùng để thiết kế chính sách nhưng không nhất thiết bằng deadline cứng cho từng request.
  • Bóc tách từng giai đoạn socket: Không có giá trị timeout chung chung nào đủ an toàn. Một kết nối trải qua phân giải DNS, bắt tay TCP connect, thương lượng TLS, ghi request, chờ header phản hồi và đọc body. Connect timeout không chặn được lỗi treo khi đọc body; socket read timeout lại có thể tự động reset mỗi khi có một byte dữ liệu mới truyền tới.
  • Truyền ngân sách còn lại: Khi đã thiết lập deadline end-to-end, mỗi lần gọi tiếp theo phải dùng thời gian còn lại sau khi trừ thời gian đã tiêu thụ và phần dự phòng cục bộ. Không tự cấp một timeout mới dài hơn toàn bộ khoảng còn giá trị.
  • Hủy bỏ (cancellation) không phải hoàn tác (rollback): Khi hết hạn chót, phát tín hiệu hủy ngay lập tức để downstream ngừng lãng phí tài nguyên tính toán, nhưng luôn ghi nhớ rằng việc hủy không tự động hoàn tác các thay đổi đã ghi bền vững vào cơ sở dữ liệu.
  • Lỗi cần tránh: Cấp lại timeout đầy đủ ở từng hop làm tổng thời gian chờ vượt deadline của request; mức ảnh hưởng lên pool và người dùng phụ thuộc mức tải và cách runtime giữ tài nguyên.

Biết chính xác timeout đang bao phủ gì

Một option tên timeout chưa nói lên điều gì nếu chưa biết scope. Client có thể có giới hạn riêng cho DNS resolution, TCP connect, TLS handshake, write request, chờ response headers, read body, idle time hoặc toàn bộ call.

Connect timeout không chặn được body read bị treo. Socket read timeout có thể được reset mỗi khi có byte mới nên không nhất thiết giới hạn tổng thời gian request. Hãy verify semantics của library thay vì tin vào tên option.

Truyền ngân sách còn lại, không cấp lại timeout ban đầu

Nếu incoming request chỉ còn 600 ms, downstream call không được nhận lại 2 giây mới. Hãy truyền deadline hoặc thời gian còn lại và giữ đủ thời gian để local work cùng response cuối hoàn tất.

gRPC deadline propagation dùng đúng mô hình này: elapsed time được trừ trước khi timeout tương đương được truyền sang outgoing RPC.

Một cách reasoning đơn giản:

remaining = request_deadline - now
child_budget = remaining - local_finish_reserve

Nếu child_budget <= 0, fail sớm thay vì khởi động call không còn cơ hội tạo kết quả hữu ích.

Chọn giá trị từ objective và phân phối độ trễ

Timeout là production policy, không phải hằng số copy từ blog. Bắt đầu từ caller SLO và phân phối độ trễ của downstream, bao gồm network overhead. Chọn tỷ lệ timeout giả chấp nhận được, rồi xem percentile tương ứng và thêm padding có lý do.

AWS mô tả trực tiếp cách này: với service-to-service call, chọn xác suất false timeout chấp nhận được rồi quan sát latency percentile tương ứng. Cần cẩn thận khi p50 gần tail latency hoặc khi độ biến thiên Internet chi phối.

Timeout quá cao:

  • thread, socket, memory và request slot bị giữ lâu;
  • dependency chậm mở rộng blast radius;
  • user latency vượt khỏi khoảng còn giá trị.

Timeout quá thấp:

  • tail latency khỏe mạnh bị biến thành lỗi giả;
  • caller tạo thêm work không cần thiết;
  • dao động latency nhỏ có thể thành timeout spike toàn hệ thống.

Fan-out vẫn chỉ có một deadline

Chạy song song không tạo thêm thời gian. Trong một fan-out, nhiều dependency song song vẫn cạnh tranh trong cùng deadline end-to-end.

Phân loại branch theo mức cần thiết. Dependency bắt buộc có thể làm request fail khi hết budget; dependency tùy chọn có thể bị cancel và bỏ khỏi response. Đừng để một branch không quan trọng giữ sống cả request sau khi thời gian hữu ích đã hết.

Cancellation dừng lãng phí; cancellation không rollback

Khi deadline hết, hãy truyền cancellation để downstream dừng work nếu có thể. Nhưng hủy không rollback. gRPC cảnh báo rõ rằng thay đổi xảy ra trước cancellation không tự hoàn tác.

Vì vậy một timed-out write vẫn có thể cần idempotency hoặc reconciliation. Việc xử lý timeout kiểm soát việc chờ và tiêu thụ tài nguyên; Partial Failure giải thích sự không chắc chắn về side effect.

Quan sát theo giai đoạn timeout và dependency

Một metric timeout_count chung là quá yếu. Ít nhất cần ghi:

  • dependency/route và giai đoạn timeout: DNS, connect, TLS, write, headers, read, overall;
  • configured budget và thời gian còn lại lúc dispatch;
  • độ trễ dependency theo percentile và caller SLO;
  • deadline-exceeded cùng cancellation count;
  • saturation như active requests, connection pool, queue hoặc worker.

Trace cần giúp operator phân biệt “không connect được” với “server mất 480 ms nhưng caller chỉ còn 120 ms”.

Tình huống minh họa (giả định)

Một API có user SLO 1 giây. Nó gọi Service A rồi Service B theo chuỗi. Mỗi client dùng default overall timeout 1 giây, và Service B còn có connect/read timeout 1 giây. Khi latency tăng, upstream request tiếp tục chờ lâu hơn budget có ích trong khi worker và socket vẫn bị giữ.

Hậu quả: tail latency vượt SLO, worker và connection pool bão hòa, rồi traffic khỏe mạnh cũng timeout phía sau các request chậm.

Nguyên nhân cốt lõi: mỗi hop tự cấp lại full timeout thay vì chia sẻ deadline còn lại, còn telemetry chỉ báo lỗi timeout chung chung mà không chỉ rõ stage hay dependency.

Cách khắc phục chuẩn: định nghĩa một deadline end-to-end, trừ elapsed time và local reserve trước mỗi call, cấu hình connect với request/read scope có chủ ý, cancel work đã hết giá trị, và tune threshold từ latency percentile cùng target timeout giả rõ ràng. Emit timeout stage, configured budget, remaining budget, dependency latency và saturation metrics.

Tự kiểm tra

Một request còn 300 ms. Service của bạn cần khoảng 60 ms sau khi dependency trả về để serialize và gửi response. Có nên gọi downstream với timeout mới 500 ms không?

Xem giải thích chi tiết

Không. Downstream budget tối đa chỉ khoảng 240 ms, thường còn thấp hơn sau safety margin. Timeout 500 ms mới sẽ phá parent deadline và giữ tài nguyên cho work mà kết quả đã hết giá trị. Hãy truyền thời gian còn lại và giữ reserve cho local completion.

Checklist vận hành timeout

  • Scope: Mỗi timeout đang bao phủ DNS, connect, TLS, write, headers, read, idle hay overall duration?
  • Deadline: Có một deadline end-to-end gắn với usefulness/SLO của caller không?
  • Propagation: Mỗi downstream hop chỉ nhận thời gian còn lại không?
  • Reserve: Có giữ thời gian cho local completion và response delivery không?
  • Tuning: Giá trị có dựa trên phân phối độ trễ đo được và target timeout giả rõ ràng không?
  • Fan-out: Các branch song song có chia sẻ cùng deadline và phân loại required/optional work không?
  • Cancellation: Work hết hạn có được cancel mà không giả định rollback không?
  • Observability: Operator có xác định được stage, dependency, budget, latency và saturation không?

Quy tắc cho agent

Khi được yêu cầu “set timeout”, trước tiên phải khôi phục SLO/deadline end-to-end, exact timeout scope của client, phân phối độ trễ downstream, tỷ lệ timeout giả chấp nhận được, cách truyền remaining budget, local reserve, cancellation behavior và telemetry. Đừng chọn con số trước khi biết nó đang giới hạn điều gì.

Bài này tập trung vào phạm vi timeout, ngân sách thời gian và cancellation. Sau khi biết request còn bao nhiêu thời gian, đọc Retries and Backoff để quyết định có nên dùng ngân sách đó cho lần thử mới hay không. Bài Timeouts, Retries & Backoff là phần tổng hợp lý luận về partial failure, không thay thế checklist vận hành riêng.

Khái niệm liên quan

  • Partial Failure — giải thích vì sao timeout không chứng minh write thất bại.
  • Retries and Backoff — quyết định một attempt khác có nên dùng phần budget còn lại hay không.
  • SLIs and SLOs — cung cấp latency objective mà timeout policy phải phục vụ.
  • Logs, Metrics & Traces — cho thấy stage timeout và resource pressure.

Nguồn

Tài liệu primary được kiểm tra ngày 2026-11-05:

Mục lục bài học