先不要用無限重試解決 App Store Connect API 限流。你應先把上傳請求、狀態查詢和後台處理分開,再採用指數退避、幂等任務、Webhook 觸發與有限次數的補償輪詢;若二進位檔交付和 API 狀態管理互相阻塞,就把 Transporter 或 Xcode 上傳鏈路與 API 查詢鏈路拆開。
本週建議動作:先為每次發布建立唯一任務識別碼,停止同一 Build 的重複輪詢;接著用一個真實 TestFlight 構建驗收「已上傳、已處理、可測試」三個狀態。這套方法適合需要持續交付、但不希望一次限流就讓整條任務失敗的團隊。
01先判斷你遇到的是哪一種故障
這篇文章適合三類讀者:
- 獨立開發者:在遠端 Mac 上自動上傳 TestFlight 構建,不想因一次限流重做整個發布。
- 小型團隊:多個 App 或 Runner 共用 App Store Connect API,需要請求隔離與重試邊界。
- 自動化維護者:正在接入 fastlane、腳本或 CI,希望同時追蹤上傳結果與後台處理狀態。
「構建沒有出現」不是一個足夠精確的故障描述。你要先看證據入口:
| 故障現象 | 主要證據入口 | 下一步動作 |
|---|---|---|
| API 回應請求過多 | HTTP 狀態、回應內容、限流相關資訊 | 暫停新增輪詢,保存請求記錄並套用退避 |
| 二進位檔沒有完成交付 | Transporter 或 Xcode 交付記錄 | 檢查上傳流程與檔案驗證,不要只查 API |
| 上傳完成但 Build 未可測試 | Build Uploads 狀態與 App Store Connect 頁面 | 將「交付完成」和「後台處理完成」分成兩個狀態 |
| 任務反覆重啟 | 遠端 Mac 任務記錄、Runner 狀態 | 讀取持久化狀態,避免從頭上傳 |
| 沒有新事件通知 | Webhook 接收記錄與補償查詢記錄 | 只開啟受控的補償輪詢 |
Apple 的限流識別文件可用來核對 API 回應與限流判斷。不要把社群提到的固定額度、固定間隔或未公開的後端門檻當成官方規則。
02第一步:把發布流程拆成四層
遠端 Mac 自動上傳最常見的設計錯誤,是讓「上傳成功」直接等同於「TestFlight 可用」。實際上,至少要保留以下四層邊界:
- 上傳執行:由 Transporter、Xcode 或既有交付工具傳送構建。
- 上傳結果確認:確認交付工具是否完成,以及是否有可核對的 Build。
- Build Processing 查詢:確認 App Store Connect 是否仍在處理、失敗或完成。
- 最終可測試確認:確認構建已符合 TestFlight 測試使用條件,而不是只有檔案存在。
Apple 的上傳構建說明與Build Uploads API 資源文件分別描述交付與 API 資源。這正是你不應用單一輪詢器包辦整個流程的原因。
可用下列方式分工:
- Transporter 或 Xcode:證明「交付動作」是否完成。
- Build Uploads API:取得可追蹤的上傳資源與狀態。
- App Store Connect 頁面:作為人工核對入口,不適合當作唯一自動化訊號。
- Webhook:在狀態事件發生時觸發後續工作,減少持續查詢。
如果你使用的是遠端 Mac,建議把編譯、上傳、狀態管理分成不同工作目錄和任務記錄。你可以先參考遠端 Mac 的 Mac mini 方案,但不要把租用主機本身當成限流修復;真正需要修的是發布狀態機。
03第二步:避免輪詢把一次發布放大
限流往往不是一次請求造成,而是多個元件同時把同一個問題當成自己的責任。常見放大路徑包括:
- 固定頻率輪詢,無論狀態是否改變都繼續發送請求。
- 多個 Job 同時查詢同一個 App、版本或 Build。
- 失敗任務重啟後遺失舊狀態,再次建立完整上傳流程。
- 上傳工具、主腳本和監控程式各自查詢同一個狀態。
- 一個 API 回應延遲,觸發下一層重試,形成重疊工作。
因此,不要直接問「App Store Connect API 應該多久輪詢一次」。正確做法是為每項任務建立請求預算和停止條件。間隔、重試次數及是否繼續查詢,必須依最新官方文件和實際回應決定,不能寫死成通用秒數。
一個脫敏的狀態流轉可以長這樣:
[任務建立]
task_id=<TASK_ID>
app_id=<APP_ID>
build_id=<BUILD_ID>
↓
[上傳執行] ── 交付失敗 ──> [停止,保存交付記錄]
│
└── 交付完成 ──> [等待事件或受控查詢]
│
┌───────────────┴───────────────┐
↓ ↓
[Processing 中] [Processing 失敗]
│ │
[Webhook / 補償查詢] [人工接管]
│
[可測試確認] ──> [任務完成]
任務重啟時,先查詢 <TASK_ID> 的持久化狀態。若 <BUILD_ID> 已有完成交付記錄,就不要重新上傳;若只有建立任務而沒有交付證據,才進入明確定義的補償流程。<KEY_ID>、<ISSUER_ID>、<REQUEST_ID> 和主機名也應以占位符保存於記錄中,避免把憑據或可識別資料寫入公開日誌。
第三步:用有限重試和幂等恢復取代重跑
遇到 HTTP 429 時,先保存完整回應,再停止同一任務新增的查詢。Apple 的錯誤處理文件可用來核對錯誤處理方式;不要自行推導未公開的限流閾值。
你的恢復邏輯至少需要這些欄位:
task_id=<TASK_ID>:一次發布的唯一識別碼。app_id=<APP_ID>、build_id=<BUILD_ID>:避免跨 App 或跨 Build 誤合併。upload_state:尚未開始、進行中、已交付或交付失敗。processing_state:尚未確認、處理中、完成或失敗。last_confirmed_at=<TIMESTAMP>:最後一次有證據的狀態。request_id=<REQUEST_ID>:串起 API 回應與遠端 Mac 記錄。manual_takeover:達到停止條件後交給人工處理。
恢復時依序執行:
- 讀取持久化任務,而不是直接建立新任務。
- 以
<BUILD_ID>核對是否已有交付結果。 - 若交付未完成,停止並檢查上傳工具記錄。
- 若交付完成但處理未結束,等待 Webhook 或進入受控補償查詢。
- 若回應持續失敗、狀態矛盾或沒有可核對證據,停止自動化並保留人工接管入口。
優點與限制
有限重試的優點
- 不會因一次 API 異常而重複上傳相同二進位檔。
- 任務重啟後能從最後可信狀態繼續。
- 每個 App、環境和發布階段都能獨立觀察。
- 失敗原因可回溯到交付、查詢或後台處理其中一層。
需要付出的成本
- 你必須維護狀態儲存,而不能只依賴終端機輸出。
- Webhook 接收端需要驗證事件、記錄事件並處理重複通知。
- 沒有事件的階段仍需保留補償查詢。
- 團隊要定義何時由自動化轉交人工。
第四步:讓 Webhook 和查詢各司其職
Webhook 不是「收到事件就直接發布成功」。它只是一個觸發來源。接收事件後,仍要核對 App、Build 和目前任務狀態,並拒絕把舊事件套用到新任務。
Apple 的Webhook 設定與解析文件及 WebhookEventType 定義可用來核對事件類型與解析方式。設計上可採取以下分工:
- Webhook 到達:更新「收到事件」時間,不能直接覆寫最終狀態。
- 事件核對:確認
<APP_ID>、<BUILD_ID>和<TASK_ID>是否一致。 - 狀態確認:只在需要時做一次 API 查詢,取得可保存的證據。
- 沒有事件:使用有限的補償查詢,不讓所有 Runner 無限等待。
- 重複事件:依事件識別資料去重,不重新建立上傳工作。
這樣做的好處,是將高頻查詢改成事件驅動;缺點是你需要處理事件遺失、重複事件和事件先後順序。不要把 Webhook 設計成另一條無限重試隊列。
06第五步:隔離遠端 Mac 的發布任務
多個 App 共用一台常駐 Mac 時,問題不只在 API。共享重試隊列會讓一個 App 的限流拖慢其他 App,還可能把不同環境的憑據、Build 和日誌混在一起。
建議按以下維度隔離:
- App:不同 Bundle ID 不共用任務狀態。
- 環境:測試、預發布和正式發布分開。
- 階段:編譯、上傳、Processing 確認和可測試確認分開。
- Runner:每個工作程序只能持有自己的任務識別碼。
- 憑據:Keychain、API 金鑰檔案和環境變數不要跨專案共用。
遠端 Mac 的 SSH 連線中斷,也不應等同於發布失敗。SSH 只負責控制工作;任務狀態應寫入持久化位置,讓你重新連線後可以讀取最後確認結果。若你需要常駐打包,可再參考遠端 Mac SSH 斷線後的持續工作方案,但仍要保留獨立的上傳與狀態記錄。
07發布前驗收清單
以下清單要在一次真實 TestFlight 構建上執行,不要只用模擬的成功回應:
- [ ] 為本次發布建立唯一的
<TASK_ID>。 - [ ] 記錄
<APP_ID>、<BUILD_ID>、<REQUEST_ID>,並對敏感欄位脫敏。 - [ ] 保存 Transporter 或 Xcode 的交付結果。
- [ ] 確認交付完成不會直接把任務標記為可測試。
- [ ] 將 Processing 狀態和最終可測試狀態分開記錄。
- [ ] 驗證 Webhook 事件可被核對、去重和保存。
- [ ] 模擬 Runner 重啟,確認不會重複上傳。
- [ ] 模擬 API 限流,確認新增輪詢會停止。
- [ ] 驗證補償查詢有退避、上限和人工接管入口。
- [ ] 檢查不同 App 的任務不會共用同一重試隊列。
| 驗收階段 | 必須保存的證據 | 通過條件 |
|---|---|---|
| 構建生成 | 構建識別資料與遠端 Mac 任務記錄 | 可追溯到唯一任務 |
| 二進位檔交付 | Transporter 或 Xcode 交付結果 | 能判斷是否完成上傳 |
| Build 處理 | API、Webhook 或頁面核對資料 | 不把上傳完成誤判為處理完成 |
| TestFlight 可用 | 最終狀態與人工核對結果 | 測試者可取得正確構建 |
| 任務重啟 | 重啟前後狀態與請求記錄 | 不重複上傳、不無限輪詢 |
| 處理方式 | 適合情況 | 主要風險 |
|---|---|---|
| 保留現有上傳流程,降低查詢頻率 | 上傳穩定,只是狀態查詢過密 | 仍需處理事件遺失 |
| 上傳與 API 查詢拆成兩條鏈路 | 交付工具正常,但查詢重試互相阻塞 | 狀態同步需要持久化 |
| Webhook 加有限補償查詢 | 希望減少持續輪詢 | 需處理重複或延遲事件 |
| 轉人工接管 | 狀態矛盾、證據不足或失敗反覆發生 | 發布流程需要明確交接記錄 |
常見故障的最終判斷
如果一次真實驗收顯示:二進位檔已完成交付、API 查詢可在受控條件下恢復,而且任務重啟不會重複上傳,你可以保留現有方案,只降低自動化查詢頻率並加入 Webhook。
如果上傳工具和 API 查詢共用同一個重試器,或一個 App 的限流會拖住其他 App,就應立即拆分遠端 Mac 任務。不要用增加重試次數來掩蓋狀態設計問題。
若你目前依賴本地電腦、臨時雲端工作階段或共享伺服器,常見缺點是工作階段中斷後狀態不完整、憑據難以隔離,以及沒有穩定的常駐環境承接 SSH 斷線後的任務。對需要持續在線、又不想專門購買一台 Mac 作為發布機的你,租用 VpsMesh 的遠端 Mac 會更容易把上傳、查詢和恢復分開管理;但若你需要長期滿載編譯、實體 USB 裝置或完全掌控硬體,直接購買並維護本地 Mac 仍可能更合適。
你可以先做兩步:把上傳與狀態查詢拆開,再用一次真實 TestFlight 構建驗證重試邊界。只有當這兩步穩定後,才值得把 Webhook、SSH 斷線恢復和多 App 任務隔離納入長期發布流程。