網誌

警報只響一次,而非每部伺服器各響一次

我們其中一個營運警報(用於通知某個建立工具的已發佈應用程式「額度不足」)原本應該最多每天只送達我們一次。結果有一個下午,它送到了五次。原因是:我們 API 的每個副本各自在自己的記憶體中記住了已經發送過哪些警報,所以兩部伺服器各發送了一次;而每次部署都會清空那份記憶體,讓它們再次發送。同樣的模式也曾令一個後台工作重複執行。現在兩者都透過共享儲存來協調,而且如果該儲存無法使用,兩者都會以安全的一方為準來失效。

症狀

當有事情需要留意時,Mythex 會向團隊發送警報:服務停止運作、工作停滯,或者某個建立工具的額度餘額偏低而其應用程式仍已發佈。每個警報都有一個去重時間窗,即在該期間內,同一警報只會發送一次。對於「額度不足」警報,這個時間窗是 24 小時。

同一個下午收到同一警報的五份副本,代表這個時間窗沒有發揮作用。更糟的是,它們是成對出現的,每對之間大約相隔 11 分鐘。

為何會發生

我們的警報套件把去重資料保存在記憶體中:一張「警報鍵 → 上次發送時間」的表格,存在於運行中的程序內。這在只有一個永不重新啟動的程序時行得通。但正式環境兩者都不符合:

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

每對警報之間約 11 分鐘的間隔,來自觸發此警報的工作。它在兩個副本上都運行,並且會取得一個鎖,所以兩個副本會輪流取得這個鎖,而每個副本輪到時都會觸發一次警報。

同一段程式碼還有第二個較為隱蔽的缺陷。為了防止表格無限增長,當它存放的鍵達到 500 個時,程式就會自行修剪:刪除所有超過 15 分鐘的條目,無論它們的時間窗為何。在高負載下,一個 24 小時的去重時間窗可能只維持了四分之一小時。

修正方法

每個警報一份共享紀錄

在 API 發送警報之前,它現在會先在 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 简体中文 繁體中文(台灣)