Day 12 講過每個片段各自重試、超過上限就記錄失敗並放棄。這代表下載迴圈跑完之後,不能直接假設「所有片段都成功了」——可能有一兩個片段最終還是失敗、被放棄了。
❌ 反例:下載迴圈跑完直接合併
async def process(segments, directory):
for segment in segments:
await download_one(segment) # 個別失敗只是記錄下來,不會中斷迴圈
merge(find_files(directory)) # 直接合併,沒驗證是不是真的都成功了
如果有片段最終下載失敗,這段程式碼會直接合併不完整的片段集合,產出一份看起來像是完成了、實際上缺了幾段的輸出——這種問題不會在合併階段報錯,只會在事後播放或使用時才被發現內容不對。
✅ 正例:合併前先確認片段數量吻合預期
async def process(segments, directory):
for segment in segments:
await download_one(segment)
files = find_files(directory)
if len(files) != len(segments):
raise IncompleteDownloadError(
f'expected {len(segments)} segments, got {len(files)}'
)
merge(files)
先明確比對「實際下載到的片段數量」跟「原本應該有的片段數量」,不吻合就直接中斷、不進行合併,並且給出明確的錯誤訊息,而不是讓一份不完整的結果被悄悄產出。
這是 fail fast 原則的具體體現:與其讓一個不完整的中間狀態繼續往下流動、在更後面的階段才造成模糊不清的問題,不如在能確認出錯的當下就立刻中斷,並且給出足夠明確的錯誤訊息。合併是這個流程裡「一旦執行就很難走回頭路」的一步(合併完的檔案可能會覆蓋掉還沒確認完整的暫存片段),在這一步之前做最後一次確認,是划算的投資。
你的流程裡有沒有類似「幾個步驟跑完,直接進到下一個不可逆的步驟」的地方?在那個不可逆的步驟之前,有沒有做過最後一次確認?
明天是一個真實案例:檔案數量對了,內容卻對不上——只信任檔案數量,原來還不夠。