一支排程腳本一天跑兩次,大多數時候找不到新影片,安靜結束,不留下任何值得留意的痕跡。如果排程本身根本沒有被觸發,同樣什麼都不會發生。從外面看,這兩種狀態長得一模一樣——都是沒有動靜。
Day 28 處理的是規則被改過一次之後,舊能力還在不在。今天換一個問題:規則沒有人去動它,純粹讓它自己跑一段時間,我怎麼知道它是不是還在正常運作。我拿一支影片輪詢排程腳本的真實 log 來查,結果查的過程中,自己連續腦補錯了三次。沉默看起來都一樣,但沉默有好幾種,不拆開會判斷錯——這篇要講的就是這件事。
這支排程 65 天裡留下 59 份 log,其中 9 次的紀錄是「找到待處理影片」。我原本以為這 9 次都進了處理流程,只是有些沒跑完。查下去才發現,其中 3 次標著 DRY_RUN=1——只是模擬「將處理」,本來就不會真的執行,當然也不會留下收尾紀錄。這不是中斷,是設計上就該如此。
扣掉這 3 次,真正進入處理的是 6 次,其中 4 次有收尾(3 成功、4 失敗),剩下 2 次是真的找到影片、開始處理,卻沒有留下結果。這 2 次才是缺口,不是我一開始算的 5 次。差別在於,我沒先確認「找到影片」跟「真的處理」是不是同一件事,就直接拿數字相減。
這 59 份 log 涵蓋 65 天,缺了整整一週的前半段——07-20 到 07-25,六天完全沒有任何一份 log。我第一個念頭是:排程那幾天大概沒被觸發。
但這個專案自己的 SOP 文件裡早就記著答案:那幾天 repo 放在系統會保護的目錄下,排程程序不被允許存取,連續 14 次執行全部失敗、結束代碼都一樣,畫面上沒有任何提示。排程其實有觸發,只是每次都在寫 log 之前就被擋下,連一份檔案都留不下來。「沒有觸發」跟「觸發了但沒能留下痕跡」,是完全不同的兩種缺口,我一開始沒有分清楚。
六天空窗結束後的第一份 log,開頭第一行是一次鎖競爭事件:排程偵測到另一個實例還在跑,跳過了這一輪。這支腳本用的鎖機制,跟 Day 27 提過的殘鎖 bug 是同一套。我一度很興奮,覺得挖到了伏筆兌現的證據。
仔細看時間才發現不對:這一輪跳過後,6 分鐘後下一輪就正常執行、正常結束,不符合「之後每一輪都被擋掉、靜默停擺」的症狀,反而比較像鎖機制正常運作該有的樣子——真的有另一個實例同時在跑,被正確攔下來了。這則紀錄只能證明鎖機制真的觸發過一次,證明不了那次觸發就是 Day 27 那個 bug。硬要把兩件事接成同一個故事,是我自己在腦補,不是資料告訴我的。
看到一個現象,腦補一個最順的解釋,然後就當成結論——這三次錯法其實是同一件事。要避免這種錯,得靠比較笨的方法:健康計算只認 log 的日期在不在、以及幾種固定字串出現的次數,不去讀、不去猜 log 裡的敘述文字實際在講什麼。這樣算出來的東西比較無聊,但至少不會被我自己的腦補帶偏。
拆開來看,「還活著」不是一個數字,是好幾個獨立的問題:最近一次真正有結果的執行是什麼時候、缺口有幾天且是哪一種缺口、找到工作但沒收尾的次數有多少、外部依賴失敗的頻率。Google SRE Book 提出的「四個黃金信號」是同一個道理:拆成幾個獨立維度看,比合併成一個總分可靠。
這批資料算出來的樣子:65 天裡 59 天有留下 log,缺口 6 天連續、找到工作但沒收尾 2 次、外部依賴失敗 8 天——告警狀態被判成需要留意,理由清楚寫著是連續缺口跟未收尾次數,不是一句「有點不對勁」。告警的門檻是我自己訂的示範規則:連續缺口達 3 天、或未收尾達 2 次才算告警,避免每次空轉都發一次警告。這套判斷我拿兩組合成資料測過,一組乾淨資料要判成沒事、一組塞連續缺口的資料要判成告警,確認它不是寫死的答案。
3 次成功、4 次失敗全部來自同 3 支影片反覆重試,不是 7 個獨立事件。NIST 的統計方法手冊講得很白:故障次數太少時,樣本大小幫不上忙,1000 台裝置只故障 2 次,資訊量反而比 10 台裝置故障 5 次還少。這種規模的樣本,撐不起「這套系統可靠度多少趴」的結論。SRE Book 對 SLO 的定義也提醒同一件事:健康是一個容許區間,不是要求零失敗;一次鎖競爭事件也不能拿來說系統長期不穩定——單一事件跟長期趨勢是不同等級的證據。
今天解決的是這條內容產線自己,長期運作下去還活不活著。系列剩下最後一天要回答另一個問題:這幾天在內容產線上累積出來的一整套架構——觸發、恢復、對帳、環境契約、回歸閘門、存活判斷——搬到完全不同的另一條工作(會議紀錄)上,還撐不撐得住。