Bắt những app bị crash ngay sau khi publish
Ngày 25 tháng 9, ba lần publish được đánh dấu thành công dù API của app bị crash vài giây sau mỗi lần khởi động. Vì chúng “thành công”, không có rollback nào chạy; một kiểm tra nền đã đánh dấu thất bại cho các lần deploy đó vài phút sau, và toàn bộ trang trả về 404. Health check đang đếm số lần crash trong danh sách sự kiện của nền tảng hosting, mà danh sách này chỉ giữ năm sự kiện gần nhất cho mỗi máy, nên một vòng lặp crash sẽ làm đầy danh sách và con số không bao giờ tăng. Giờ health check so sánh thời điểm của lần crash mới nhất thay vì đếm, và một publish bị crash lặp đi lặp lại sẽ thất bại và khôi phục phiên bản trước.
Một publish chờ điều gì
Khi bạn publish một app có backend trên Mythex, phiên bản mới được đưa lên các máy của nền tảng hosting, và một health check quyết định publish đã lên live hay thất bại. Nếu thất bại, một rollback sẽ khôi phục phiên bản trước.
Phần khó là “đã khởi động” không đồng nghĩa với “đang phục vụ”. Nền tảng báo một máy là đã khởi động ngay khi nó chạy. Một app bị crash một giây sau khi khởi động sẽ được khởi động lại ngay lập tức, và chỉ một lát sau lại được báo là đã khởi động. Vì vậy health check đã tích lũy vài lớp kiểm tra theo thời gian:
- Nhìn hai lần. Sau khi một máy được báo là đã khởi động, chờ một chút rồi nhìn lại. Một lần crash khi khởi động thường làm lần nhìn thứ hai thất bại.
- Theo dõi vòng lặp crash. Nếu nền tảng báo một máy đã crash liên tục và nó đã bỏ cuộc khởi động lại, thất bại.
- Đếm số lần crash kể từ khi publish bắt đầu. Hai lần nhìn “đã khởi động” có thể bao trùm một lần khởi động lại. Vì vậy nếu có bất kỳ lần crash nào kể từ khi publish bắt đầu, hãy nhìn lần thứ ba; nếu số lần crash nhiều hơn lần nhìn trước, app không ổn định.
Kiểm tra thứ ba là kiểm tra đã thất bại ở đây.
Vì sao con số không tăng
Thông tin về crash đến từ danh sách sự kiện của mỗi máy: khởi động, thoát, khởi động lại. Nền tảng chỉ giữ năm sự kiện gần nhất cho mỗi máy.
Hãy hình dung một app bị crash sau 2,5 giây mỗi lần khởi động, giống một trong những app đó. Mỗi chu kỳ thêm một lần crash và một lần khởi động. Sau vài chu kỳ, danh sách đầy. Từ đó trở đi, mỗi sự kiện mới đẩy một sự kiện cũ ra. Danh sách trượt về phía trước, nhưng luôn giữ cùng một tỷ lệ: vài lần crash, vài lần khởi động.
Vì vậy “bao nhiêu lần crash kể từ khi publish bắt đầu” trả về cùng một số ở mỗi lần nhìn. Lần nhìn thứ hai thấy, ví dụ, hai lần crash. Lần nhìn thứ ba cũng thấy hai lần crash, dù là những lần crash khác. Kiểm tra kết luận rằng việc crash đã dừng, và cho publish qua.
Điều đó đã xảy ra ba lần ngày 25 tháng 9, trên hai app. Mỗi lần:
- publish được đánh dấu là đã live,
- không có rollback nào chạy, vì không có gì thất bại,
- job đối soát của chúng tôi, một background job kiểm tra các deploy sau khi chúng đã xảy ra, đánh dấu deploy thất bại vài phút sau,
- và toàn bộ trang trả về 404.
Một publish thất bại đúng lúc thì còn phục hồi được: rollback khôi phục phiên bản trước. Một publish thất bại trễ vài phút, sau khi đã được tuyên bố là thành công, thì rollback đã bị bỏ qua.
Chúng tôi phát hiện ra như thế nào
Chúng tôi tái hiện lỗi trên máy local: lấy một API đang hoạt động, để nó ngủ, rồi publish lại với một thay đổi bị crash khi khởi động. Health check đánh dấu nó là đã live.
Điều này khớp với production: trong cả ba lần publish, app chết vài giây sau mỗi lần khởi động, nhưng số lần crash mà kiểm tra so sánh không bao giờ tăng giữa các lần nhìn.
Cách sửa: so sánh lần crash mới nhất, không so sánh số lượng
Thay vì đếm số lần crash, kiểm tra giờ xem thời điểm của lần crash gần nhất kể từ khi publish bắt đầu.
Nếu lần nhìn thứ hai cho thấy có bất kỳ lần crash nào kể từ khi publish bắt đầu, kiểm tra sẽ chờ và nhìn lần thứ ba. Nếu lần crash mới nhất ở lần nhìn thứ ba muộn hơn lần crash mới nhất ở lần nhìn thứ hai, máy đã crash thêm lần nữa ở khoảng giữa. Nó không ổn định, và publish thất bại với thông báo rằng máy đã crash nhiều lần kể từ khi publish và vẫn đang khởi động lại.
Cách này hoạt động bất kể danh sách sự kiện ngắn đến đâu. Một cửa sổ trượt năm sự kiện có thể che giấu số lần crash đã xảy ra, nhưng không thể che giấu việc vừa có một lần crash mới; sự kiện mới nhất luôn là sự kiện được giữ lại.
Với thay đổi này, tái hiện trên máy local hoạt động đúng: publish thất bại, và image trước đó được khôi phục. Cũng có một test mô phỏng hành vi của nền tảng, trong đó mỗi lần nhìn thêm một lần crash và một lần khởi động rồi cắt danh sách còn năm, và kiểm tra rằng kết luận là thất bại, không phải đạt.
Điều chúng tôi rút ra
- Hiểu hình dạng của dữ liệu bạn đang đếm. Một số đếm trên một danh sách có giới hạn sẽ không còn ý nghĩa khi danh sách đầy. Giới hạn đó không hề bị giấu; chúng tôi chỉ chưa thiết kế kiểm tra xoay quanh nó.
- Ưu tiên câu hỏi “có điều gì mới xảy ra không?” hơn “đã xảy ra bao nhiêu?”. Dấu thời gian mới nhất vẫn tồn tại khi bị cắt bớt. Tổng số thì không.
- Một thất bại muộn tệ hơn một thất bại sớm. Cùng một bản build hỏng sẽ vô hại nếu publish thất bại và rollback, nhưng sẽ làm sập một trang nếu nó được tuyên bố thành công trước.
- Tái hiện trong điều kiện thực. Lỗi cần một máy đã crash đủ nhiều lần để làm đầy danh sách sự kiện của nó. Một lần crash nhanh đơn lẻ trong test sẽ không cho thấy lỗi này.
Bản sửa này đi cùng một bản sửa khác trong cùng ngày, đảm bảo rollback khôi phục phiên bản cũ thay vì xóa nó: một lần republish thất bại không bao giờ xóa backend đang chạy. Nếu app của bạn publish thất bại vì nó bị crash khi khởi động, các tài liệu xử lý sự cố khi publish và hướng dẫn của chúng tôi về gỡ lỗi một app do AI xây dựng là những nơi tốt để bắt đầu.