Blog

Cảnh báo chỉ kích hoạt một lần, không phải một lần mỗi server

Một trong những cảnh báo vận hành của chúng tôi, “số credit thấp” cho app đã publish của người xây dựng, được thiết kế để chỉ đến tay chúng tôi tối đa một lần mỗi ngày. Một buổi chiều, nó đã đến năm lần. Nguyên nhân: mỗi bản sao của API ghi nhớ trong bộ nhớ riêng các cảnh báo đã gửi, nên hai server gửi cùng một cảnh báo hai lần, và mỗi lần deploy lại xóa bộ nhớ đó khiến chúng gửi lại. Cùng một mẫu lỗi khiến một background job làm việc hai lần. Giờ cả hai đều phối hợp qua một kho lưu trữ dùng chung, và cả hai đều thất bại theo hướng an toàn nếu kho đó không khả dụng.

Triệu chứng

Mythex gửi cảnh báo nội bộ cho đội ngũ khi có điều cần xem xét: một dịch vụ bị ngừng, một job bị kẹt, hoặc số dư credit của người xây dựng đang thấp trong khi app của họ vẫn publish. Mỗi cảnh báo có một cửa sổ khử trùng lặp, tức là khoảng thời gian trong đó cùng một cảnh báo chỉ được gửi một lần. Với cảnh báo credit thấp, cửa sổ đó là 24 giờ.

Năm bản sao của cùng một cảnh báo trong một buổi chiều có nghĩa là cửa sổ không hoạt động. Tệ hơn, chúng đến thành từng cặp, cách nhau khoảng 11 phút.

Vì sao lại xảy ra

Gói cảnh báo của chúng tôi lưu việc khử trùng lặp trong bộ nhớ: một bảng “khóa cảnh báo → lần gửi gần nhất”, nằm trong tiến trình đang chạy. Cách này chỉ ổn khi có một tiến trình duy nhất và nó không bao giờ khởi động lại. Production không có cả hai điều đó:

  • Hai bản sao API. Production chạy hai bản sao của API. Mỗi bản có bảng riêng, nên mỗi bản gửi cảnh báo một lần. Đó là các cặp.
  • Deploy. Mỗi lần deploy thay thế cả hai bản sao bằng các tiến trình mới với bảng trống. Sau deploy, cảnh báo lại được gửi đi như thể chưa từng gửi.

Khoảng cách khoảng 11 phút giữa hai cảnh báo trong mỗi cặp đến từ job sinh ra cảnh báo này. Job chạy trên cả hai bản sao và giữ một khóa, nên hai bản sao luân phiên giành được nó, và mỗi bản sinh cảnh báo đến lượt mình.

Có một lỗi thứ hai, âm thầm hơn, trong cùng đoạn mã. Để bảng không lớn mãi, nó tự dọn khi có 500 khóa, bằng cách xóa mọi mục cũ hơn 15 phút, bất kể cửa sổ của mục đó là bao lâu. Dưới tải, một cửa sổ khử trùng lặp 24 giờ có thể chỉ kéo dài một phần tư giờ.

Cách sửa

Một bản ghi dùng chung cho mỗi cảnh báo

Trước khi API gửi một cảnh báo, giờ nó sẽ giữ khóa khử trùng lặp của cảnh báo đó trong Redis, một kho lưu trữ trong bộ nhớ dùng chung mà cả hai bản sao cùng kết nối tới và không khởi động lại khi API khởi động lại. Thao tác giữ là một lệnh duy nhất: “chỉ đặt khóa này nếu nó chưa tồn tại, và hết hạn sau cửa sổ”. Bản sao nào giữ được trước sẽ gửi cảnh báo; bản còn lại thấy khóa đã bị giữ và im lặng. Một tiến trình mới sau deploy vẫn thấy khóa còn đó.

Khóa và cửa sổ đến từ cùng một quy tắc mà gói cảnh báo đang dùng, giờ được chia sẻ giữa hai lớp, nên hai lớp không thể bất đồng về việc thế nào là “cùng một cảnh báo”.

Thất bại theo hướng mở, không đóng

Quyết định quan trọng nhất là phải làm gì khi không kết nối được Redis. Chúng tôi chọn vẫn gửi cảnh báo. Nếu Redis ngừng hoạt động, hoặc không phản hồi trong 1,5 giây, cảnh báo vẫn được gửi đi. Một cảnh báo trùng lặp chỉ là nhiễu. Một cảnh báo bị bỏ mất nghĩa là có người không được báo.

Các cảnh báo nghiêm trọng nhất, P0, không bao giờ bị giữ lại bởi cơ chế khử trùng lặp.

Mỗi mục giữ cửa sổ riêng

Trong cơ chế khử trùng lặp trong bộ nhớ của chính gói cảnh báo, mỗi mục giờ lưu thời điểm cửa sổ của nó kết thúc, và việc dọn dẹp chỉ xóa những mục có cửa sổ đã hết. Một khóa 24 giờ sẽ tồn tại 24 giờ, bất kể có bao nhiêu cảnh báo khác đến.

Cùng một lỗi, ở chỗ khác

Cùng mẫu lỗi này cũng đã làm đôi một phần công việc nền: một job đối soát giữ bản ghi trạng thái của mỗi workspace khớp với nhà cung cấp sandbox của chúng tôi. Mỗi lần chạy liệt kê mọi sandbox trong tài khoản (hàng nghìn sandbox trên production, khoảng năm giây), cùng mọi dự án đang chạy hoặc đang tạm dừng. Cả hai bản sao đều chạy nó hai phút một lần.

Các thao tác ghi của nó là idempotent, tức là chạy hai lần cho ra kết quả giống như chạy một lần, nên không có gì bị hỏng. Chỉ là tốn gấp đôi công sức. Giờ mỗi cửa sổ hai phút được giữ khóa trong Redis theo cách mà các bộ đếm sử dụng của chúng tôi đã giữ khóa, và chỉ bản sao nào giữ được khóa mới chạy lần đối soát đó. Nếu không kết nối được Redis, lần đối soát vẫn chạy: làm việc hai lần là vô hại, còn không làm thì không ổn.

Cách chúng tôi kiểm thử

Các bài test mô phỏng đúng môi trường thực tế. Chúng nạp module cảnh báo hai lần, như hai “bản sao” riêng biệt, trỏ cả hai về cùng một kho dùng chung, phát cùng một cảnh báo từ mỗi bản, rồi nạp thêm một bản mới thứ ba để đại diện cho “sau deploy” và phát lại cảnh báo đó. Chính xác một cảnh báo được gửi đi. Các bài test khác kiểm tra rằng cảnh báo của một người xây dựng khác vẫn được gửi, rằng một P0 được gửi từ cả hai bản sao, và cảnh báo vẫn được gửi khi kho bị ngừng hoặc treo quá thời gian chờ. Bản sửa lỗi dọn dẹp có test riêng: một khóa 24 giờ vẫn tồn tại sau 600 cảnh báo khác đến 20 phút sau đó.

Với job đối soát, hai bản sao cùng chạy một lúc, cộng thêm một lần chạy thứ ba trong cùng cửa sổ, và chỉ tạo ra một lần liệt kê.

Điều chúng tôi rút ra

  • Trạng thái trong bộ nhớ thuộc về từng tiến trình. Bất cứ thứ gì phải đúng xuyên suốt nhiều server (“đã gửi một lần”, “đã chạy một lần”) đều cần được lưu ở nơi mà chúng cùng chia sẻ.
  • Deploy chính là khởi động lại. Trạng thái chỉ tồn tại đến lần khởi động lại tiếp theo thì không sống sót qua một ngày làm việc bình thường.
  • Quyết định xem sẽ thất bại theo hướng nào. Với cảnh báo, việc kho dùng chung ngừng hoạt động nên dẫn đến nhiều cảnh báo hơn, không phải ít hơn.
  • Tìm mẫu lỗi, không chỉ một trường hợp. Cảnh báo là trường hợp ồn ào; job đối soát bị nhân đôi là cùng một lỗi, âm thầm tiêu tốn công sức.

Không có điều nào trong số này nhìn thấy được từ bên trong Mythex. Để biết thêm về cách chúng tôi giữ nền tảng ổn định, xem cách chúng tôi đưa deploy về không còn lỗi 502 và file swap bị thiếu. Nếu bạn mới làm quen với việc chạy nhiều bản sao của một dịch vụ, hướng dẫn của chúng tôi về server là gì sẽ giải thích những điều cơ bản.

Mythex · Pricing · Xây dựng mọi thứ với AI · Mẫu website và ứng dụng · So sánh Mythex · Thuật ngữ xây dựng ứng dụng bằng AI · Công cụ miễn phí để xây dựng ứng dụng bằng AI · English

English አማርኛ العربية Български বাংলা Bosanski Català Čeština Dansk Deutsch Ελληνικά Español Español (España) Eesti فارسی Suomi Français Français (Canada) ગુજરાતી हिन्दी Hrvatski Magyar Հայերեն Bahasa Indonesia Íslenska Italiano 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 Lietuvių Latviešu Македонски മലയാളം Монгол मराठी Bahasa Melayu မြန်မာ Norsk bokmål Nederlands ਪੰਜਾਬੀ Polski Português (Brasil) Português (Portugal) Română Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو 简体中文 繁體中文(香港) 繁體中文(台灣)