Back home

Độ tin cậy của thời gian chạy Rust/Wasm yêu cầu xử lý cả quá trình khôi phục hoảng loạn và hủy bỏ

Khi phiên bản Wasm được chia sẻ bắt đầu chấp nhận cuộc gọi trong một thời gian dài, sự cố sẽ chuyển từ một lỗi duy nhất sang vấn đề khôi phục trạng thái và cách ly lỗi.

Ban đầu, Wasm có thể dễ dàng được coi là một lớp chuyển: mã có thể được lập trình, trang có thể chạy, hiệu suất ổn và mọi thứ dường như giống nhau. Nó thực sự bắt đầu trở nên khó khăn, thường là sau khi bản demo được thông qua. Khi các mô-đun như trình soạn thảo, trình kết xuất và trình phân tích cú pháp tài liệu chuyển từ thử nghiệm một trang sang thời gian chạy thường trú dài hạn, các mô hình lỗi sẽ ngay lập tức thay đổi.

Tại thời điểm này, hoảng loạn và hủy bỏ không còn là các nhánh ngoại lệ trong lớp ngôn ngữ. Những gì họ quyết định là: liệu phiên bản này có thể tiếp tục nhận công việc tiếp theo hay không, trạng thái trong bộ nhớ có bị ô nhiễm hay không, liệu lớp máy chủ có nên loại bỏ phiên bản đó ngay lập tức hay không và liệu nhóm phiên bản có cần được lấp đầy hay không. Khi nhóm di động di chuyển một hạt nhân đã chạy trong các vùng chứa gốc trong một thời gian dài lên Web, lớp thay đổi này dễ bị đánh giá thấp nhất.

Sau khi Demo xong, mô hình lỗi mới bắt đầu.

Sự cố trong một cuộc gọi không phải là điều khó hiểu. Một lần bấm nút sẽ kích hoạt cuộc gọi Wasm. Nếu thất bại, một lỗi sẽ được báo cáo cho hoạt động. Hãy làm mới trang và thử lại. Chi phí vẫn có thể kiểm soát được.

Sự cố xảy ra sau khi thời gian chạy bắt đầu sử dụng lại các phiên bản. Khi cùng một phiên bản Wasm liên tục mở nhiều tài liệu, nhận nhiều vòng sự kiện đầu vào và thực hiện nhiều lệnh gọi cầu nối JS, phạm vi ảnh hưởng của sự hoảng loạn và hủy bỏ không còn dừng lại ở hành động hiện tại. Một lỗi không hoàn chỉnh có thể kéo các yêu cầu tiếp theo xuống.

Những rủi ro như vậy thường không được bộc lộ ngay trong ngày đầu tiên. Trong giai đoạn đầu tiên, bạn thường chỉ thấy các báo cáo lỗi rải rác: thỉnh thoảng kết xuất không thành công, quá trình xuất nào đó bị kẹt và một tài liệu nào đó ở trạng thái không chính xác sau khi bị đóng và mở lại. Nếu bạn kiểm tra sâu hơn, các manh mối sẽ dần dần hội tụ về hiện tượng tương tự: mặc dù lỗi xảy ra trong chuỗi cuộc gọi nhưng thiệt hại vẫn nằm ở phiên bản dùng chung.

Tại thời điểm này, trọng tâm của cuộc thảo luận không còn là “Liệu mã Rust có hoảng loạn hay không” mà là “Liệu thời gian chạy này có đủ điều kiện để tiếp tục phục vụ cuộc gọi tiếp theo sau cơn hoảng loạn hay không”.

Hoảng loạn có thể bị bắt, hủy bỏ chỉ có thể thay đổi trường hợp.

Điều quan trọng nhất cần phân biệt trong Rust/Wasm là hai ngữ nghĩa thất bại là hoảng loạn và hủy bỏ.

Sự hoảng loạn cũng có cơ hội thoát ra khỏi những ranh giới đã được thiết lập. Miễn là lớp liên kết và lớp máy chủ đồng ý trước về phương thức khôi phục thì cuộc gọi hiện tại có thể thất bại và các trạng thái khác trong phiên bản cũng có thể được duy trì. phá thai hoàn toàn không phải là cách hay. Điều đó có nghĩa là việc thực thi hiện tại đã đạt đến trạng thái không thể phục hồi. Nếu bạn tiếp tục sử dụng cùng một phiên bản để nhận yêu cầu, về cơ bản bạn đang đánh cược rằng bộ nhớ và tài nguyên sẽ không bị hỏng giữa chừng.

Khi cả hai được trộn lẫn với nhau trong thời gian chạy, các vấn đề chắc chắn sẽ xảy ra trong quá trình xử lý tiếp theo:

  • Việc hủy bỏ nuốt như một ngoại lệ thông thường và nhóm phiên bản sẽ tiếp tục sử dụng lại các đối tượng đã mất độ tin cậy.
  • Xử lý mọi sự hoảng loạn như thể phiên bản đó phải bị phá hủy và thông lượng sẽ bị giảm một cách không cần thiết
  • Máy chủ JS chỉ biết “cuộc gọi không thành công”, nhưng không biết nên thử lại, mất phiên bản hay cắt phiên hiện tại

Đây cũng là điều thực tế nhất về độ tin cậy của thời gian chạy Wasm: ngữ nghĩa khôi phục phải được xác định trước khi có thể thực hiện cách ly và lập kế hoạch tiếp theo.

Nếu lớp liên kết không cung cấp ngữ nghĩa khôi phục, lớp máy chủ sẽ ở trạng thái xấu và tiếp tục chấp nhận công việc.

Nơi nguy hiểm nhất cho loại vấn đề này không phải ở mã doanh nghiệp mà là ở lớp ràng buộc dường như đã được “xử lý”. Lớp máy chủ thường chỉ nhìn thấy một đối tượng lỗi bị ném ra và ghi lại nó như một lỗi cuộc gọi thông thường. Nhật ký vẫn ở đó và trang không bị lỗi ngay lập tức, nhưng hệ thống có thể đã để lại trạng thái xấu bên trong phiên bản.

Điều thực sự cần được khắc phục không chỉ là thử/bắt mà còn là các hành động xử lý sau khi thất bại. Logic tương tự như sau mới bắt đầu được đưa vào thiết kế độ tin cậy:

async function runWithRecovery(instance, input) {
  try {
    return await instance.exports.handle(input)
  } catch (error) {
    if (isAbort(error)) {
      pool.replace(instance.id)
    }
    throw error
  }
}

Trọng tâm của đoạn mã này không phải là cú pháp mà là một phán đoán đơn giản: liệu lỗi hiện tại có đánh dấu trường hợp này là đối tượng không đáng tin cậy hay không. Nếu câu trả lời là có, hành động khôi phục không nên dừng lại ở việc đưa ra lỗi mà nên tiếp tục loại bỏ phiên bản, xây dựng lại tài nguyên và cắt luồng yêu cầu.

Miễn là lớp này không được xác định rõ ràng, hệ thống sẽ có vẻ như đang xử lý lỗi, nhưng những gì nó thực sự đang làm là đưa thời gian chạy có khả năng bị hỏng trở lại quy trình sản xuất.

Phiên bản dùng chung sẽ khuếch đại vấn đề khôi phục thành vấn đề chiến lược tổng hợp

Sau khi Wasm được đưa vào sản phẩm thực, hiếm khi chỉ có “một phiên bản cho đến khi đóng trang”. Phổ biến hơn là nhóm phiên bản, nhóm công nhân hoặc tài liệu nền trước và tác vụ nền chia sẻ một tập hợp tài nguyên thời gian chạy. Ở giai đoạn này, chi phí phục hồi do hoảng loạn và hủy bỏ sẽ trực tiếp viết lại chiến lược gộp.

Nếu việc khởi tạo phiên bản tốn kém thì hệ thống đương nhiên sẽ có xu hướng sử dụng lại nó nhiều nhất có thể. Nhưng một khi việc tái sử dụng được thiết lập, việc cách ly lỗi phải được nâng cấp đồng thời:

  • Những trạng thái nào chỉ có thể bị treo trong một cuộc gọi và sẽ bị loại bỏ cùng với cuộc gọi sau khi thất bại
  • Bộ nhớ đệm nào được phép giữ lại trong các cuộc gọi và bộ nhớ đệm nào phải bị vô hiệu hóa hoàn toàn sau khi gặp phải tình trạng hủy bỏ
  • Sau khi phiên bản được thay thế, các tác vụ trong hàng đợi sẽ được di chuyển như thế nào? Việc thử lại có gây ra tác dụng phụ hai lần không?

Đây không phải là những câu trả lời mà lớp ngôn ngữ sẽ tự động gửi. Chúng là những thiết kế thời gian chạy.

Vì điều này, nếu cuộc thảo luận về độ tin cậy của Rust/Wasm chỉ dừng lại ở “liệu ​​có thể bắt được sự hoảng loạn không?” thì rất dễ đánh giá thấp vấn đề. Điều thực sự làm gia tăng khoảng cách về chi phí bảo trì là liệu nhóm phiên bản có thể duy trì ranh giới tin cậy rõ ràng sau khi xảy ra lỗi hay không.

Ranh giới áp dụng có liên quan chặt chẽ đến vòng đời

Bộ thiết kế khôi phục này không bắt buộc đối với mọi dự án Wasm.

Nếu mô-đun chỉ là công cụ ngoại tuyến một lần hoặc toàn bộ phiên bản được tái chế khi trang bị hủy thì sự khác biệt giữa hoảng loạn và hủy bỏ sẽ vẫn tồn tại, nhưng lợi ích khôi phục sẽ nhỏ hơn nhiều. Thường chỉ cần làm mới trang trực tiếp và chạy lại tác vụ trực tiếp là đủ.

Khi hệ thống có các đặc điểm sau, ngữ nghĩa khôi phục sẽ nhanh chóng thay đổi từ “mục tối ưu hóa” thành “mục cơ sở hạ tầng”:

  • Phiên bản tồn tại trong một thời gian dài và không bị hủy cùng với vòng đời của một trang
  • Cùng một phiên bản liên tục thực hiện nhiều vòng cuộc gọi
  • Lớp hosting cần sử dụng tính năng gộp chung để đổi lấy thời gian khởi động và thông lượng
  • Bảo vệ trạng thái phiên, trạng thái bộ đệm và các tác vụ được xếp hàng đợi sau khi thất bại

Khi các nhóm di động di chuyển các khả năng gốc lên Web, ranh giới này có nhiều khả năng gặp phải nhất. Mối quan hệ cô lập ban đầu được thiết lập theo mặc định trong quy trình Ứng dụng thường phải được điền lại sau khi đạt đến ranh giới máy chủ JS/Wasm.

Wasm giúp mã gốc vào trình duyệt dễ dàng hơn nhưng nó không mang theo ngữ nghĩa phục hồi thời gian chạy. Ngay khi hệ thống bắt đầu chia sẻ phiên bản, sử dụng lại trạng thái và chấp nhận cuộc gọi dài hạn, việc hoảng loạn và hủy bỏ phải được coi là hai sự kiện thời gian chạy khác nhau. Cái trước quan tâm đến cách kết thúc cuộc gọi hiện tại và cái sau quan tâm đến việc liệu phiên bản này có thể tiếp tục tồn tại trong nhóm hay không. Nếu phán đoán này không được đưa ra trước thì việc ghép mã càng thành công thì càng khó giải quyết những thất bại tiếp theo.