部落格

警示只發一次,而不是每台伺服器各發一次

我們的一項營運警示「低點數」(針對建置者已發布的應用程式)原本只會每天送出最多一次,但有一天下午卻來了五次。原因是:每一份 API 副本都把已送出的警示記在自己的記憶體裡,因此兩台伺服器各送了一次;而每次部署都會清空這些記憶體,讓它們再送一次。同樣的模式也讓一個背景工作重複執行。現在兩者都改為透過共享的儲存空間協調,而且當該儲存空間無法使用時,都會偏向安全的一方。

症狀

Mythex 會在需要關注的時候發送團隊警示:服務停擺、工作卡住,或建置者已發布的應用程式點數即將用完。每個警示都有一個去重時間窗:在這段期間內,相同的警示只會送出一次。對於「低點數」警示來說,這個時間窗是 24 小時。

一個下午收到同一警示五次,表示時間窗沒有發揮作用。更糟的是,這些警示是成對出現的,每對之間大約相隔 11 分鐘。

為什麼會這樣

我們的警示套件把去重資料存在記憶體中:一張「警示鍵 → 上次送出的時間」的表,位於正在執行的程序內。這在只有一個程序且從不重新啟動的情況下沒有問題。但正式環境兩者都不符合:

  • **兩個 API 副本。**正式環境執行兩份 API。每份都有自己的表,因此各自送出一次警示,這就是成對出現的原因。
  • **部署。**每次部署都會用全新的程序和空白的表取代兩個副本。部署之後,警示又會再送一次,彷彿從未送出過。

每對警示之間約 11 分鐘的間隔,來自觸發此警示的工作。它在兩個副本上都會執行,並且會取得一把鎖,因此兩個副本會輪流取得鎖,各自輪到時就觸發警示。

同一段程式碼裡還有第二個較不起眼的錯誤。為了避免表無限制地增長,它在存了 500 個鍵之後會自動修剪:刪除所有超過 15 分鐘的項目,不論它們的時間窗為何。在高負載下,一個原本應持續 24 小時的去重,實際可能只維持四分之一小時。

修正方式

每個警示只有一筆共享紀錄

現在,API 在送出警示之前,會先在 Redis 中認領該警示的去重鍵。Redis 是一種共享的記憶體儲存,兩個副本都會連線到它,而且 API 重新啟動時它不會跟著重置。這項認領是一個單一操作:「只有在鍵不存在時才設定,並在時間窗結束後讓它過期」。哪個副本先認領,就由它送出警示;另一個副本發現鍵已被占用,就保持安靜。部署之後的新程序也能看到這個鍵仍然存在。

鍵與時間窗來自警示套件使用的同一套規則,現在由兩者共用,因此兩個層次對「同一個警示」的判斷不會出現分歧。

失敗時放行,而非封鎖

最重要的決定,是 Redis 無法連線時該怎麼辦。我們選擇仍然送出警示。如果 Redis 停擺,或在 1.5 秒內沒有回應,警示就會照常送出。重複的警示只是雜訊;而漏掉的警示,則代表有人沒有被通知。

P0 等級的最嚴重警示則完全不受去重機制影響。

每個項目保有自己的時間窗

在警示套件本身的記憶體去重中,現在每個項目都會記下它的時間窗何時結束,修剪時只移除時間窗已經結束的項目。一個 24 小時的鍵會保留 24 小時,無論有多少其他警示進來。

同樣的錯誤,出現在別處

同樣的模式也讓一項背景工作重複執行了:一個對帳工作,會讓我們記錄的每個工作區狀態,與沙盒供應商的狀態保持一致。每一輪它都會列出帳號中的每一個沙盒(正式環境中有數千個,大約需要五秒),以及每個執行中或已暫停的專案。兩個副本都每兩分鐘執行一次。

它的寫入具有冪等性,也就是說執行一次和執行兩次的結果相同,因此沒有造成任何損壞。只是白白多做了一倍的工作。現在,每個兩分鐘的時間窗會以我們的用量計量器已經在使用的相同方式,在 Redis 中被認領,只有認領成功的副本會執行這一輪。如果連不上 Redis,這一輪仍會執行:重複做同一件事是無害的,但不做則不行。

我們如何測試

測試模擬了真實的環境。它會把警示模組載入兩次,當作兩個獨立的「副本」,兩者都指向同一個共享儲存空間,各自觸發同一個警示;接著再載入第三個全新的副本,模擬「部署之後」的情況,並再次觸發。最後應該只送出一次警示。其他測試則確認:另一位建置者的警示仍會送出;P0 警示在兩個副本上都會送出;當儲存空間停擺或超過逾時時間而沒有回應時,警示仍然會送出。修剪的修正也有自己的測試:一個 24 小時的鍵,能在 20 分鐘後到達的 600 個其他警示之後依然存在。

至於對帳工作,測試讓兩個副本同時觸發,再加上同一時間窗內的第三次觸發,結果只產生一次列表。

我們的收穫

  • **記憶體中的狀態只屬於單一程序。**任何必須在多台伺服器之間成立的事(「只送出一次」、「只執行一次」),都必須存放在它們共享的地方。
  • **部署就是重新啟動。**只能撐到下一次重新啟動的狀態,無法撐過一個正常的工作日。
  • **決定失敗時的方向。**對警示而言,共享儲存空間停擺,應該意味著送出更多警示,而不是更少。
  • **尋找模式,而不只是個別事件。**警示是最明顯的案例;重複執行的對帳工作則是同一個錯誤,只是悄悄地浪費了運算資源。

這些在 Mythex 內部是看不見的。想了解我們如何讓平台保持穩定,可參閱我們如何把部署的 502 錯誤降到零,以及缺少的交換檔。如果你對「執行多個服務副本」的概念還不熟悉,我們的什麼是伺服器指南可以提供基礎說明。

Mythex · Pricing · 用 AI 打造任何東西 · 網站與應用程式範本 · Mythex 比較 · AI 應用程式開發詞彙表 · 免費的 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 Українська اردو Tiếng Việt 简体中文 繁體中文(香港)