Thử lại các hàm không đồng bộ

Tài liệu này mô tả cách bạn có thể yêu cầu các hàm nền không đồng bộ (không phải HTTPS) thử lại khi không thành công.

Lý do khiến các hàm dựa trên sự kiện không hoàn tất

Trong một số ít trường hợp, một hàm có thể thoát sớm do lỗi nội bộ và theo mặc định, hàm đó có thể được thử lại tự động hoặc không.

Thông thường, một hàm dựa trên sự kiện có thể không hoàn tất thành công do lỗi được gửi trong chính mã hàm. Điều này có thể xảy ra vì những lý do sau:

  • Hàm này có lỗi và thời gian chạy sẽ trả về một ngoại lệ.
  • Hàm không thể kết nối với điểm cuối của dịch vụ hoặc hết thời gian chờ khi cố gắng kết nối.
  • Hàm này cố ý gửi một ngoại lệ (ví dụ: khi một tham số không xác thực được).
  • Hàm Node.js trả về một lời hứa bị từ chối hoặc truyền một giá trị không phải null đến một lệnh gọi lại.

Trong bất kỳ trường hợp nào nêu trên, hàm sẽ ngừng thực thi và trả về lỗi. Trình kích hoạt sự kiện tạo ra thông báo có chính sách thử lại mà bạn có thể tuỳ chỉnh để đáp ứng nhu cầu của hàm.

Ngữ nghĩa của việc thử lại

Cloud Functions cung cấp ít nhất một lần thực thi hàm dựa trên sự kiện cho mỗi sự kiện do nguồn sự kiện phát ra. Theo mặc định, nếu một lệnh gọi hàm kết thúc kèm theo lỗi, thì hàm đó sẽ không được gọi lại và sự kiện sẽ bị loại bỏ. Khi bạn bật tính năng thử lại trên một hàm dựa trên sự kiện, Cloud Functions sẽ thử gọi lại một hàm không thành công cho đến khi hàm đó hoàn tất thành công hoặc hết thời gian thử lại.

Khi bạn không bật tính năng thử lại cho một hàm (chế độ mặc định), hàm đó sẽ luôn báo cáo rằng hàm đã thực thi thành công và mã phản hồi 200 OK có thể xuất hiện trong nhật ký của hàm. Điều này xảy ra ngay cả khi hàm gặp lỗi. Để làm rõ thời điểm hàm của bạn gặp lỗi, hãy nhớ báo cáo lỗi một cách thích hợp.

Định cấu hình thử lại từ mã hàm

Với Cloud Functions for Firebase, bạn có thể bật tính năng thử lại trong mã cho một hàm. Để thực hiện việc này cho một sự kiện trong nền, chẳng hạn như tạo một tài liệu Firestore mới, hãy đặt tuỳ chọn chính sách failurePolicy (thế hệ thứ 1) hoặc retry (thế hệ thứ 2) thành true:

thế hệ thứ 1

exports.docCreated = functions
  .runWith({
    // retry on failure
    failurePolicy: true,
  })
  .firestore.document("my-collection/{docId}")
  .onCreate((change, context) => {
    /* ... */
  });

thế hệ thứ 2

const { onDocumentCreated } = require("firebase-functions/firestore");

exports.docCreated = onDocumentCreated(
  {
    // retry on failure
    retry: true,
  },
  "my-collection/{docId}",
  (event) => {
    /* ... */
  },
);

Việc đặt true như minh hoạ sẽ định cấu hình một hàm để thử lại khi không thành công.

Cửa sổ thử lại

Đối với các hàm thế hệ thứ 2, khoảng thời gian thử lại này sẽ hết hạn sau 24 giờ. Đối với các hàm thế hệ thứ 1, mã thông báo sẽ hết hạn sau 7 ngày. Cloud Functions thử lại các hàm dựa trên sự kiện mới tạo bằng cách sử dụng chiến lược thời gian đợi luỹ thừa, với thời gian đợi tăng dần từ 10 đến 600 giây. Chính sách này được áp dụng cho các hàm mới trong lần đầu tiên bạn triển khai. Chính sách này không được áp dụng hồi tố cho các hàm hiện có được triển khai lần đầu trước khi những thay đổi được mô tả trong bản ghi chú phát hành này có hiệu lực, ngay cả khi bạn triển khai lại các hàm đó.

Các phương pháp hay nhất

Phần này mô tả các phương pháp hay nhất để sử dụng tính năng thử lại.

Sử dụng tính năng thử lại để xử lý lỗi tạm thời

Vì hàm của bạn được thử lại liên tục cho đến khi thực thi thành công, nên bạn cần loại bỏ các lỗi vĩnh viễn như lỗi mã thông qua việc kiểm thử trước khi bật tính năng thử lại. Bạn nên sử dụng tính năng thử lại để xử lý các lỗi gián đoạn hoặc tạm thời có khả năng giải quyết cao khi thử lại, chẳng hạn như điểm cuối dịch vụ không ổn định hoặc hết thời gian chờ.

Đặt điều kiện kết thúc để tránh vòng lặp thử lại vô hạn

Tốt nhất là bạn nên bảo vệ hàm của mình khỏi vòng lặp liên tục khi sử dụng tính năng thử lại. Bạn có thể làm việc này bằng cách thêm một điều kiện kết thúc được xác định rõ ràng, trước khi hàm bắt đầu xử lý. Xin lưu ý rằng kỹ thuật này chỉ hoạt động nếu hàm của bạn khởi động thành công và có thể đánh giá điều kiện kết thúc.

Một phương pháp đơn giản nhưng hiệu quả là loại bỏ các sự kiện có dấu thời gian cũ hơn một thời điểm nhất định. Điều này giúp tránh việc thực thi quá mức khi lỗi xảy ra liên tục hoặc kéo dài hơn dự kiến.

Ví dụ: đoạn mã này loại bỏ tất cả sự kiện cũ hơn 10 giây:

const eventAgeMs = Date.now() - Date.parse(event.timestamp);
const eventMaxAgeMs = 10000;
if (eventAgeMs > eventMaxAgeMs) {
  console.log(`Dropping event ${event} with age[ms]: ${eventAgeMs}`);
  callback();
  return;
}

Sử dụng catch với Promise

Nếu hàm của bạn đã bật tính năng thử lại, thì mọi lỗi chưa được xử lý sẽ kích hoạt một lần thử lại. Đảm bảo rằng mã của bạn ghi lại mọi lỗi không cần thử lại.

Sau đây là ví dụ về những việc bạn nên làm:

return doFooAsync().catch((err) => {
    if (isFatal(err)) {
        console.error(`Fatal error ${err}`);
    }
    return Promise.reject(err);
});

Đặt hàm dựa trên sự kiện có thể thử lại thành hàm luỹ đẳng

Các hàm dựa trên sự kiện có thể thử lại phải có tính luỹ đẳng. Sau đây là một số nguyên tắc chung để tạo một hàm có tính chất luỹ đẳng:

  • Nhiều API bên ngoài (chẳng hạn như Stripe) cho phép bạn cung cấp khoá lũy đẳng dưới dạng tham số. Nếu đang sử dụng một API như vậy, bạn nên dùng mã sự kiện làm khoá đảm bảo tính bất biến.
  • Tính lũy đẳng hoạt động hiệu quả với cơ chế phân phối ít nhất một lần, vì tính năng này giúp bạn thử lại một cách an toàn. Vì vậy, một phương pháp hay nhất chung để viết mã đáng tin cậy là kết hợp tính lũy đẳng với các lần thử lại.
  • Đảm bảo rằng mã của bạn có tính chất luỹ đẳng nội bộ. Ví dụ:
    • Đảm bảo rằng các đột biến có thể xảy ra nhiều lần mà không làm thay đổi kết quả.
    • Truy vấn trạng thái cơ sở dữ liệu trong một giao dịch trước khi thay đổi trạng thái.
    • Đảm bảo rằng tất cả các hiệu ứng phụ đều có tính chất luỹ đẳng.
  • Áp dụng quy trình kiểm tra giao dịch bên ngoài hàm, độc lập với mã. Ví dụ: duy trì trạng thái ở một nơi nào đó ghi lại rằng một mã sự kiện nhất định đã được xử lý.
  • Xử lý các lệnh gọi hàm trùng lặp ngoài băng tần. Ví dụ: có một quy trình dọn dẹp riêng biệt để dọn dẹp sau khi gọi hàm trùng lặp.

Định cấu hình chính sách thử lại

Tuỳ thuộc vào nhu cầu của hàm, bạn có thể muốn định cấu hình trực tiếp chính sách thử lại. Điều này cho phép bạn thiết lập bất kỳ tổ hợp nào sau đây:

  • Rút ngắn thời gian thử lại từ 7 ngày xuống còn 10 phút.
  • Thay đổi thời gian đợi tối thiểu và tối đa cho chiến lược thử lại theo thuật toán thời gian đợi luỹ thừa.
  • Thay đổi chiến lược thử lại để thử lại ngay lập tức.
  • Định cấu hình một chủ đề thư không gửi được.
  • Đặt số lần gửi tối đa và tối thiểu.

Cách định cấu hình chính sách thử lại:

  1. Viết một hàm HTTP.
  2. Sử dụng API Pub/Sub để tạo gói thuê bao Pub/Sub, chỉ định URL của hàm làm mục tiêu.

Hãy xem tài liệu của Pub/Sub về cách xử lý lỗi để biết thêm thông tin về cách định cấu hình trực tiếp Pub/Sub.