RFC-0217:開啟包裹追蹤

RFC-0217:開放包裹追蹤
狀態已接受
區域
  • 軟體推送
說明

如要保護套件免於遭到垃圾收集,請使用開放式套件追蹤功能,而非動態索引。

問題
Gerrit 變更
作者
審查人員
提交日期 (年-月-日)2023-04-18
審查日期 (年-月-日)2023-05-05

摘要

改善套件垃圾收集 (GC),提升執行測試或重新啟動經過修改的暫時性元件時的開發人員體驗。

提振精神

常見的開發人員工作流程,例如:

  • 連續執行多項測試
  • Repeatedly執行及編輯相同的測試
  • 重複執行及編輯相同的暫時性元件

由於儲存空間不足,因此經常會因套件解析錯誤而中斷。這會影響開發人員的專注力,降低對平台的信任度,並要求開發人員手動觸發 GC,可能需要在重新啟動裝置後才能執行。

由於目前的 GC 實作方式,因此需要進行這些中斷和解決方法:

  • 保護不應受到保護的部分套件 (需要重新啟動才能移除保護措施)
  • 未保護部分應受保護的套件 (導致自動觸發 GC 的危險性,導致目前避免自動 GC 觸發 / 偏好手動 GC 觸發的立場)

目標是改善 GC,讓這些工作流程能夠正常運作,而開發人員完全不必考慮 GC 或儲存空間。

相關人員

協助人員:

  • hjfreyer@google.com

審查者:

  • wittrock@google.com (軟體推送)
  • geb@google.com (元件架構)
  • crjohns@google.com (Testing)

諮詢:

  • senj@google.com、etrylezaar@google.com、jamesr@google.com

社會化:

我們已將本 RFC 的早期草稿提供給軟體開發和元件架構團隊成員。

需求條件

GC 整體必須符合下列條件:

  1. 使用 RFC 170 的修改內容,維持OTA 程序的儲存空間用量和前進進度保證。
    • 簡而言之,允許系統更新器在 OTA 期間解析的 GC 中繼套件 (更新套件和包含傳入二進位映像檔的套件),而無需 GC 下一個系統版本所需的套件。
  2. 允許連續執行多個測試 (每個測試都分別適合裝置,且都來自不同的套件),且不會耗盡空間。
  3. 不會移除元件正在使用的套件,包括包含執行中元件的套件。

我們強烈偏好以下方法:

  • 實作方式簡單,主要由 SWD 負責,因此不需要跨團隊的 API (考量目前開發人員的時間需求)
  • 驗證簡單 (考量到維持 OTA 儲存空間需求的重要性)

不適用:

  • 保護某些非必要套件免於 GC 的方法 (例如,因為這些套件預計會在日後再次使用)。我們希望日後能有選擇採用這些方法的選項,因此所選方法不應排除這些方法。
  • 判斷何時自動觸發 GC,以回應空間不足的錯誤 (例如在 CF/full-resolver/pkg-resolver/pkg-cache 中,相較於在 fx 測試中)。

設計

軟體提交堆疊尚未升級至符合「套件集 RFC」中詳細說明的定義和行為,因此下文說明會使用已淘汰的用語,更準確地描述系統目前和建議的行為。

定義

  • 基本套件
    • 「base」或「system_image」套件 (在啟動引數中以雜湊值標示),以及 data/static_packages 檔案中列出的套件 (以雜湊值標示)。
    • 一般而言,這是執行特定設定所需的最低套件組合。
  • 快取包裝
    • 「base」套件的 data/cache_packages.json 檔案中列出的套件 (按雜湊排序)。
    • 一般來說,非基本套件在沒有網路連線的情況下仍可使用。
  • 保留索引
    • 在 OTA 期間已/將下載的套件清單 (按雜湊排序),這些套件會在 OTA 程序中使用,或在下一個系統版本中必備。
    • 在 OTA 程序期間由 system-updater 元件操控,以符合 OTA 儲存空間需求。
  • 動態索引
    • 從套件路徑 (位於套件的 meta/package 檔案中,通常與用於解析套件的網址路徑相同) 對應至套件的雜湊,該雜湊是最近為該路徑解析的套件。
    • 在開機時,動態索引會預先填入快取套件 (只要套件具有相同路徑但未在稍後解析不同的雜湊值,就會受到 GC 保護)。
    • 如果套件在解析時位於保留索引 (以雜湊值識別套件) 中,則不會新增至動態索引 (因此不會將具有相同路徑但雜湊值不同的套件淘汰)。
  • 套件 Blob
    • 套件所需的所有 Blob。
    • 遞迴地處理 meta.far 和內容資料塊,以及所有子套件的套件資料塊。
    • 根據這個定義,如果要保護某個套件不受 GC 影響,就必須保護其所有子套件。子套件和套件本身一樣,不一定會受到超級套件提供的保護。

目前的演算法

  1. 判斷所有常駐 Blob 的集合,Br
  2. 暫停非駐留套件的解析
  3. 判斷所有受保護 Blob 的集合 Bp,這是以下項目的套件 Blob:
    • 基本套件
    • 保留索引套件
    • 動態索引套件
  4. 請 blobfs 刪除集合差異 Br - Bp
  5. 解除暫停非駐留套件的解析

建議的演算法

建立「Open Package Index」,追蹤哪些套件具有含有開放 fuchsia.io/[Node|Directory] 連線的 [子]目錄。如需可能的實作方式,請參閱 https://fxrev.dev/817432 (順帶一提,這個實作方式會去除用於提供套件目錄的資料結構,應該可節省至少 1 MB 的記憶體)。在 pkg-cache (提供所有暫時性套件的套件目錄的元件) 中使用開放的套件索引。

讓 pkg-resolver 公開名為 fuchsia.pkg.PackageResolver-ota 的額外 fuchsia.pkg/PackageResolver 能力。將此能力路由至系統更新器 (且僅路由至系統更新器),而非目前的 fuchsia.pkg.PackageResolver 能力。透過這項能力解析的套件必須在解析前位於保留索引中,並會從開放的套件索引中排除 (透過在 fuchsia.pkg/PackageCache.[Open|Get] 中新增標記)。

建立「Writing Package Index」,追蹤目前寫入儲存空間的套件。這實際上是動態索引,但在解決套件後,系統會停止追蹤套件 (此時會由開放套件索引或保留索引涵蓋)。

使用相同的 GC 演算法,但將動態索引替換為寫入和開啟的套件索引,因此受保護的 Blob 現為:

  • 基本套件
  • 快取套件
  • 保留的索引套件
  • 編寫套件索引套件
  • 開啟套件索引套件

這符合規定

  1. 使用 RFC 170 的修改內容,維持OTA 程序的儲存空間用量和前向進度保證。

    在 OTA 程序中解析的所有套件都會從開放套件索引中排除 (類似於 OTA 解析目前會先加入保留索引,再從動態索引中排除),因此仍會符合儲存空間使用量和前進進度規定。

  2. 允許連續執行多個測試 (每個測試都分別適合裝置,且都來自不同的套件),且不會耗盡空間。

    當您連續執行多個不同套件的測試時,現在可以觸發 GC 來防止空間不足錯誤。動態索引用於保護每個測試套件的最新解析版本 (套件會在解析時透過路徑新增,且僅會在重新啟動時移除),因此先前執行的測試不會 GC,但現在開放的套件索引會在測試套件的最後一個連線關閉後停止保護測試套件。

  3. 不要移除元件正在使用的套件,包括包含執行中元件的套件。

    先前,如果元件已啟動,且系統解析了不同版本的支援套件,則元件的套件會從動態索引中移除,無論元件是否仍在執行。由於執行中的元件會保留至其套件目錄的連線,因此元件的套件會受到開放套件索引的保護,不會遭 GC 回收。

索引 套件新增動作 套件移除動作
Base 產品組裝 永不
快取 產品組裝 永不
保留 OTA 期間的系統更新器設定 系統更新器在 OTA 期間更新/清除
寫作 套件解析作業開始 套件解析結束
開啟 非 OTA 套件解析結束 關閉上次連線至套件目錄

成效

動態索引會使用記憶體,其用量與自啟動以來解析的不同暫時性套件數量成正比 (依套件路徑分組)。開啟的套件索引會使用記憶體,其數量與含有開啟連線的暫時性套件數量成正比 (按套件雜湊分組)。由於目前使用暫時性套件的方式,這些記憶體足跡的大小都很小且相近 (在執行許多不同的測試套件時,開放套件索引會變小,因為動態索引會有效洩漏這些項目)。記憶體占用空間的任何差異,都應小於透過去除用於提供套件目錄的資料結構而節省的記憶體。

人體工學

將動態索引替換為開放套件索引,可讓系統更容易理解及操作:

  • 開啟的套件索引狀態 (以及 GC 的行為) 只取決於目前正在使用的套件,而非動態索引狀態,後者取決於自啟動以來發生的每個套件解析順序。
  • 與動態索引不同,開放式套件索引不依賴套件的路徑 (位於套件的 meta/package 檔案中)。使用者通常不會將套件路徑視為概念 (他們經常會知道套件網址的路徑元件,但 meta/package 路徑可能不同),而現在 GC 行為將不再依賴該路徑。這項修正可解決以下問題:來自不同存放區但具有相同套件路徑的無關套件會競爭 GC 保護 (透過從動態索引中移除彼此)。這也會移除套件路徑上最後一個剩餘的依附元件。
  • 使用者不必再擔心 GC 會刪除目前執行中元件的套件。

安全性考量

沒有影響。

隱私權注意事項

沒有影響。

測試

我們會針對套件解析和 GC 之間的互動進行廣泛測試,特別是 OTA 和 GC。我們會檢查這些測試,確保它們仍有意義且完整。

說明文件

現有的 GC 說明文件將會更新。

診斷

您可以使用「Inspect」檢視寫入和開放的套件索引。基礎和快取套件以及保留的索引已包含在 pkg-cache 的檢查資料中。

缺點、替代方案和未知事項

我們認為,這個解決方案可根據需求,讓 GC 比目前更準確。不過,仍有一些未知因素和缺點。

缺點

目前的實作方式會嘗試保護目前未使用但預期會再次解析的暫存套件,以免日後需要重新下載 blob。而所提的實作方式並未提供任何這類防護機制。這應該不會中斷任何工作流程,因為即使在目前的實作中,暫時性解析度仍需要網路存取權才能檢查存放區中繼資料 (也就是說,裝置仍應能夠重新下載 Blob),而且 GC 很少觸發,因此需要重新下載 Blob 的情況也應該很少。此外,建議的做法不會阻止日後重新加入預測保護功能。

上述缺點的後果是,如果工作流程依賴多個套件,但未保留與這些套件目錄的連線,那麼在工作流程中觸發 GC 時,必須小心處理。理論上,在目標上的工作流程應可保留所有必要的套件,但在主機上協調的工作流程可能會更難做到這一點。

與目前的實作方式不同,在解析不同版本的套件 (由 meta/package 中的路徑識別) 時,快取套件仍會受到保護。也就是說,在解析其他版本並觸發 GC 後,快取備用機制 (如果網路無法使用時會用到) 仍會成功。舉例來說,如果非快取版本以非預期的方式編輯設定檔,就會發生這種情況。這項問題在今天就可能發生,因為如果 GC 未觸發或很少觸發,就會發生這個問題。

替代方案

請不要為系統更新器提供不包含在開放式套件追蹤功能中的特殊 fuchsia.pkg/PackageResolver 能力,而是在系統更新器觸發 GC 之前,先關閉與中繼套件的連線。

開啟的套件索引會以非同步方式更新 (只要發現連線已關閉),而系統更新器無法得知這類情況何時發生。我們可以建立 API 來查詢開放的套件索引,但目標並非讓系統更新程式無條件 GC 中繼套件,而是讓系統更新程式在只有與系統更新程式建立的開放連線的情況下,GC 中繼套件 (請考慮中繼套件也是目前系統的基本套件),且套件服務機制不知道誰持有連線的用戶端端點。此外,系統更新程式已透過保留索引手動追蹤已解析的套件,因此將其解析結果排除在自動追蹤範圍內是合理的做法。

請勿為系統更新器提供特殊的 fuchsia.pkg/PackageResolver 能力,因為這類功能會從開放套件追蹤中排除。請繼續為系統更新器提供標準能力,並將保留的套件從開放套件追蹤中排除。

這種做法可能會導致執行元件的套件遭到 GC。請把握以下幾項重點:

  1. 開發人員編輯測試
  2. 系統會自動將 OTA 置於階段,且測試會位於系統的快取套件中,因此會新增至保留索引
  3. 開發人員執行測試時,測試套件不會加入開放套件索引,因為該套件位於保留索引中
  4. 系統會自動為其他系統版本排程另一次 OTA (可能是因為第一次嘗試失敗),從而從保留索引中移除測試套件
  5. 觸發 GC,從執行中的測試中刪除 blob

未知

公開套件追蹤功能可保護任何有公開連線的套件。可能有元件會保留套件目錄句柄,時間長於預期。這會導致與開發人員目前遇到的問題類似,當套件解析因空間不足錯誤而失敗時,就會發生這種問題。您必須找出這類執行個體 (通常只要使用 k zx ch 等主控台指令即可輕鬆完成),並加以修正。這對使用者裝置而言並非問題,因為在使用者裝置上,只有系統更新器會使用暫時性解析度。