Trong phát triển web/app, không phải lỗi nào cũng là lỗi “chết hẳn”. Một API bên thứ ba có thể chậm vài giây, database có thể nghẽn tạm thời, mạng nội bộ có thể rớt trong chốc lát. Vì vậy, timeout và retry là hai kỹ thuật rất hữu ích. Nhưng nếu dùng sai, chúng có thể khiến hệ thống chậm hơn, tốn tài nguyên hơn, thậm chí làm sự cố lan rộng. Timeout: đừng chờ vô hạn Timeout là giới hạn thời gian tối đa mà ứng dụng sẽ chờ một thao tác hoàn tất. Ví dụ: gọi cổng thanh toán, gửi email, truy vấn dịch vụ vận chuyển, gọi API nội bộ. Nếu không đặt timeout, một request có thể treo rất lâu, giữ connection, giữ thread hoặc worker. Khi lượng người dùng tăng, những request treo này tích tụ và làm ứng dụng cạn tài nguyên. Một vài lời khuyên thực tế: - Luôn đặt timeout khi gọi HTTP API, database, cache, message broker hoặc dịch vụ ngoài. - Timeout nên phù hợp với ngữ cảnh. Gợi ý tìm kiếm sản phẩm không nên chờ lâu như xử lý thanh toán. - Phân biệt connect timeout và read timeout nếu thư viện hỗ trợ. Kết nối không được thì nên fail nhanh. - Đừng đặt timeout quá cao chỉ để “cho chắc”. Timeout cao có thể che giấu vấn đề hiệu năng thật sự. Retry: thử lại, nhưng phải có kiểm soát Retry giúp xử lý lỗi tạm thời. Ví dụ API trả lỗi 503, mạng mất trong khoảnh khắc, hoặc service đang deploy lại. Tuy nhiên, retry không nên áp dụng cho mọi thứ. Không nên retry mù quáng với các thao tác có tác dụng phụ như tạo đơn hàng, trừ tiền, gửi email, tạo tài khoản. Nếu cần retry, hãy kết hợp idempotency key hoặc cơ chế chống xử lý trùng. Một mẫu retry hợp lý thường có: - Số lần thử giới hạn, ví dụ 2 hoặc 3 lần, không phải vô hạn. - Khoảng chờ tăng dần, còn gọi là exponential backoff. - Thêm jitter, tức một chút ngẫu nhiên vào thời gian chờ, để tránh nhiều request cùng retry một lúc. - Chỉ retry với lỗi phù hợp: timeout, lỗi mạng tạm thời, HTTP 429, 502, 503, 504 tùy trường hợp. - Không retry với lỗi validation, sai quyền, sai dữ liệu, ví dụ 400, 401, 403. Ví dụ thực tế Giả sử ứng dụng thương mại điện tử cần gọi API vận chuyển để lấy phí ship. Nếu API vận chuyển chậm, ta có thể đặt timeout 2 giây. Nếu lỗi tạm thời, thử lại tối đa 2 lần với khoảng chờ ngắn. Nếu vẫn thất bại, giao diện có thể hiển thị: “Chưa lấy được phí vận chuyển, vui lòng thử lại” thay vì để toàn bộ trang treo. Ngược lại, với thao tác tạo thanh toán, retry cần cẩn thận hơn. Nếu request đầu tiên đã đến được cổng thanh toán nhưng phản hồi bị mất, lần retry tiếp theo có thể tạo giao dịch trùng nếu không có mã tham chiếu duy nhất. Trong trường hợp này, hệ thống nên gửi một transaction_id hoặc idempotency key cố định cho cùng một yêu cầu thanh toán. Circuit breaker: khi bên kia đang lỗi nặng, đừng tiếp tục đập cửa Ngoài timeout và retry, các hệ thống lớn thường dùng thêm circuit breaker. Ý tưởng là: nếu một dịch vụ đang lỗi liên tục, tạm thời ngừng gọi nó trong một khoảng thời gian ngắn. Thay vào đó, trả về phản hồi dự phòng hoặc thông báo lỗi thân thiện. Ví dụ, dịch vụ gợi ý sản phẩm bị lỗi. Thay vì mọi request đều cố gọi và chờ timeout, ứng dụng có thể tạm bỏ khối gợi ý, vẫn cho người dùng xem trang sản phẩm chính. Điều này giúp phần quan trọng của hệ thống tiếp tục hoạt động. Những sai lầm thường gặp - Không đặt timeout vì môi trường dev chạy nhanh nên tưởng không cần. - Retry quá nhiều lần làm tăng tải lên dịch vụ đang gặp sự cố. - Retry cả lỗi do dữ liệu sai, khiến log nhiễu và khó điều tra. - Không ghi log đủ thông tin: gọi service nào, timeout bao lâu, retry lần thứ mấy, lỗi gì. - Không có fallback, khiến một dịch vụ phụ bị lỗi kéo hỏng toàn bộ trải nghiệm. Gợi ý áp dụng Nếu dự án của bạn chưa có chiến lược rõ ràng, hãy bắt đầu từ những việc đơn giản: - Kiểm tra toàn bộ nơi gọi API bên ngoài và bổ sung timeout. - Chuẩn hóa chính sách retry theo từng loại thao tác. - Với tác vụ quan trọng có tác dụng phụ, dùng idempotency key. - Ghi log cho các lần timeout và retry, nhưng tránh log quá nhiều dữ liệu nhạy cảm. - Thiết kế phản hồi dự phòng cho các tính năng không bắt buộc. Timeout và retry không chỉ là cấu hình kỹ thuật nhỏ. Chúng là một phần quan trọng của thiết kế hệ thống ổn định. Làm tốt từ đầu sẽ giúp ứng dụng chịu lỗi tốt hơn, người dùng ít gặp màn hình treo hơn, và đội phát triển cũng dễ điều tra sự cố hơn khi có vấn đề xảy ra.