GitHub Actions 與 Workers 為什麼會卡住發布:走出 Live Schedule 協調地獄

我一開始想做的事,其實很單純。就這樣而已。結果不知不覺,發文之前先開起會來了。這次換誰負責? 用 Workers 還是 Actions? 預算還夠嗎? 上一次的鎖還在嗎? 標記上寫的是誰? 已經重試幾次了?

閱讀功能說明

收聽會朗讀正文;速讀會依序顯示短語,速度可調。語言練習可對照現有的不同語言版本。收藏儲存在本瀏覽器中,可從播放器的收藏清單再次開啟。用打磚塊來讀會把正文的文字變成磚塊,邊打邊讀。

分享這篇文章
廣告
廣告

我一開始想做的事,其實很單純。

寫好新文章,就盡快發出去。

就這樣而已。

結果不知不覺,發文之前先開起會來了。

這次換誰負責? 用 Workers 還是 Actions? 預算還夠嗎? 上一次的鎖還在嗎? 標記上寫的是誰? 已經重試幾次了?

文章在等。

系統也在等。

每個環節都覺得「這不關我的事」。

在公司,還能把大家叫進會議室;分散式系統卻只會默默停下來。

這就是 Live Schedule 地獄。

1. 明明只想馬上發文,卻變成在選負責人

想把發文流程做得更穩,很自然就會一直加保險。

Workers 停了,就改用 Actions。 Actions 的額度吃緊,就改回 Workers。 加一把鎖,避免同一件事做兩次。 加一個標記,避免兩邊碰到同一份工作。 失敗就算重試次數。 再加一個專門盯著的角色。

單獨看,每一項都沒錯。

問題是把它們一個接一個串起來的時候。

在問「這篇文章能發嗎」之前,得先回答一個大得多的問題:「誰有資格發?」

最危險的情況,是只剩負責人的標記還留著。

Workers 那邊已經停了。 可是紀錄上還寫著「Workers 負責」。 後來 Actions 醒來,看到「Workers 負責」,就自己跳過。

沒有哪個環節壞掉。 也沒有哪個環節在做事。

這就是分散式系統版的「承辦人目前不在座位上」。

2. 真正卡住的不是執行能力,而是協調

有一次執行,Actions 這邊明明還有處理能力,也還有額度。

主要的發文處理卻沒有跑。

原因很簡單:判斷結果是「這件事歸 Workers」。

這點很重要。

不是 CPU 不夠。 不是所有 API 都掛了。 也不是 Actions 啟動不了。

問題出在協調,不在執行。

擋住的不是做事的力氣,而是「誰來做」的協調。

這樣一來,就算一個一個修 bug 也很難收尾。

修資料抓取的量。 修存回 main 失敗的問題。 修重試。 修負責人切換。 結果又留下舊標記。 接著又留下舊的鎖。 監看的角色發現了問題,卻沒有發文的權限。

看起來像打地鼠,但地鼠都是從同一個洞冒出來的。

管理這一層太聰明了。

3. schedule 是時鐘,不是待辦清單

GitHub Actions 的 schedule 很方便,不過官方文件也寫了,負載高的時候可能會延遲,負載夠高時,排隊中的部分工作甚至可能被丟掉。

所以 schedule 適合「大概這個時間叫醒我」。

但如果要它扛起「這份稿子一定會有人接手,而且抓著直到成功」的責任,角色就不對了。

時鐘會響。

時鐘不會幫你抓著稿子。

訊息佇列就不一樣,它保存的是工作本身,也能處理失敗:重試、延後,還有把一直失敗的工作另外放到「失敗收容區」(dead-letter queue)。

這裡不是說「一定要引進新服務」。

而是把分工講清楚:

  • schedule = 定時叫醒的機制
  • 待處理清單 = 未完成工作的唯一依據
  • 執行者 = 真正做事的一方
  • 短期的鎖 = 避免兩邊同時做的鑰匙
  • 重試與失敗收容區 = 失敗工作的去處

讓時鐘兼任人事部、倉庫和失物招領,時鐘大概會哭。

4. 以「還有什麼沒做完」為準,而不是「誰負責」

在複雜的分工裡,狀態的重心很容易變成「負責人」。

但發文流程真正想知道的是:

這篇文章發了沒有。

幾乎就只有這一件事。

所以以待處理清單為準就夠了。

新稿子一進來,就放進待處理清單。 開始發文時,上一把短期的鎖。 成功了,就改成「已發布」。 失敗了,就退回待處理清單。 失敗到規定次數,就移到失敗收容區並發出警告。

「Workers 負責」「Actions 負責」只是輔助資訊,不能變成永久的否決權。

就算寫著「Workers 負責」,如果一段時間都沒有進展,別的執行者也可以接手。

這樣,已經失效的負責人標記就無法把工作當人質。

5. 主線是「事件一來就啟動」,schedule 只負責撿漏

想要馬上發文,卻還要等下一次定時,就本末倒置了。

新稿子一到,就叫醒執行者。

把這當作主線。

然後用 schedule 去撿那些過了幾分鐘到幾十分鐘還留著的舊待處理項目。

也就是說,

事件觸發是主線,schedule 是保險。

這樣平常很快。

就算漏接了事件通知,schedule 之後也能撿回來。

反過來,如果把 schedule 當主線,一延遲,發文本身就晚了;再加上負責人判斷,就會出現「醒了卻什麼都不做」。

時間到了。

檢查也過了。

然後說一句「今天不是我負責」就下班了。

那你來幹嘛?

6. 備援要做成「回收舊的未完成工作」,而不是「更換負責人」

設計備援時,常見的陷阱是正式把工作從 A 交給 B。

交接需要更新狀態。

更新一旦失敗,交接本身就變成故障。

其實可以更簡單。

平常由 Workers 處理。 成功就結束。 失敗,待處理清單裡就還留著。

Actions 這類備援執行者定期檢查,如果

  • 有待處理項目
  • 已經超過一定時間
  • 沒有有效的鎖
  • 自己能執行

就把它接過來。

不需要「Workers 交接過來的證明」。

要看的是工作還沒做完這個事實。

這種做法裡,成果有沒有產出,比分工表是否一致更重要。

對文章發布系統來說,這樣比較自然。

7. Live Schedule 不必丟掉,但別把它當成維生設備

Live Schedule 本身沒有問題。

有人在看的時候,調整優先順序。 調查異常。 提出改善方案。 加快修復。 放進新的企劃。

這些用途它都很強。

問題是,它一停,文章工廠也跟著停。

Live 應該是加速器,不能變成一壞就全部停擺的單點故障。

即使沒有 Live,

新稿子 → 待處理清單 → 執行者 → 檢查 → 發文

也要能自己轉起來。

Live 來了,就更快。

不在也不會死。

就像不能把人的心臟接到「負責人有沒有在 Slack 回覆」上一樣。

8. 把完成條件從「聰明的系統做好了」改回「放進一篇,就出一篇」

這類系統,只要想改,可以改一輩子。

公平分配負責人。 最佳化免費額度。 防止重複。 預測預算。 優先順序。 監控。 自動復原。 數據分析。

每一樣都很有趣。

而全部做完的話,文章就發不出去了。

所以最初的完成條件可以粗糙得多。

放進一篇新稿子,不用任何特別操作,就會發出一篇。

失敗了,也留在待處理清單裡。 別的執行者會來接手。 只有反覆失敗的,才交給人處理。

等這條簡單的直線穩定了,再把各種最佳化加回來。

明明只是「想快點發新文章」,卻不知不覺自己做起類似分散式共識的東西。

總有這樣的日子。

但是有辦法收場。

把複雜度砍掉。

發文系統的價值,不在於選負責人選得多漂亮。

而在於文章真的發到外面去。

結論

Live Schedule 地獄的根源,並不是 schedule 本身。

而是讓「負責人的狀態」壓過了「工作還沒完成的狀態」。

要把設計改回來,做到這樣就夠了:

  • 以待處理清單作為未完成工作的唯一依據
  • 事件一來就立刻啟動
  • 鎖要短
  • 備援執行者去回收舊的待處理工作
  • schedule 當作保險
  • 失敗到規定次數的,隔離到失敗收容區
  • Live 只當作可有可無的加速器

與其要聰明的工廠,不如要不停工的工廠。

先從這裡開始。

資料


今天讀這篇

每一篇都回答讀完本文後常有的下一個問題。

瀏覽全部文章更多「工作」文章

如果這篇文章對你有幫助,可以支持本站。 支持本站

廣告
廣告
廣告

再來一篇?有沒有好玩的?

讀完順便看看:幾篇相近的,還有幾篇完全不同但很有意思的。

  1. 相近的話題賣掉獵人執照就能退休,為什麼還要工作?小傑一行接委託的理由
  2. 為什麼「身為老公」「身為男友」「身為家人」總是吵起來
  3. 完全不同,但很有趣吉伊卡哇為什麼總是在吃東西?美食、雜草、怪物、打工和 10+ 的恐怖
  4. 9度500mL的罐裝酒真的只算「一罐」嗎?相當於2.6罐啤酒
  5. 晴王麝香葡萄只要199日圓?日本超市「每100公克」標價為什麼這麼容易看錯
  6. 《冰之城牆》小雪為什麼這麼讓人有共鳴?表面沒事,回家卻想個不停

尋找其他文章

所有文章

Mendoi-chan

本站經營者

Mendoi-chan

把工作與日常生活中的麻煩整理成清楚的結構與下一步行動。