RFC-0217:開啟包裹追蹤

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

防止套件進行垃圾收集時,使用開放式套件追蹤,而非動態索引。

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

摘要

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

提振精神

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

  • 持續執行多項測試
  • 重複執行及編輯同一項測試
  • 重複執行及編輯同一個暫時元件

經常因為儲存空間不足而受到套件解析錯誤的影響。這種做法會破壞開發人員集中度、降低平台信心,且需要開發人員在重新啟動裝置後手動觸發 GC。

由於目前的 GC 實作方式,這些中斷與解決方法的必要性:

  • 保護部分不應受到保護的套件 (要求重新啟動才能移除保護機制)
  • 無法保護部分應受保護的套件 (這會使自動觸發 GC 危險,因而導致目前可避免自動 GC 觸發條件 / 偏好手動 GC 觸發條件)

我們的目標是改善 GC,讓這些工作流程能夠在工作上運作,而開發人員不必考慮 GC 或儲存空間。

相關人員

講師:

  • hjfreyer@google.com

審查者:

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

顧問:

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

社群媒體化:

這個 RFC 的早期草稿已分享給軟體開發與元件架構團隊的成員。

相關規定

整體 GC 必須具備以下條件:

  1. 使用 RFC 170 的修改項目,保留 OTA 程序的儲存空間使用量和轉送進度保證。
    • 簡單來說,允許系統更新程式到 GC 中繼套件會在 OTA (更新套件和包含傳入二進位檔映像檔的套件) 期間解析,而不需要處理下一個系統版本所需的套件 GC。
  2. 允許連續執行多項測試 (每個測試分別符合裝置大小,且測試皆來自不同的套件),而不必用盡空間。
  3. 不會移除元件正在使用的套件,包括包含執行中元件的套件。

下列做法各有專屬偏好的做法:

  • 主要可透過 SWD 完成實作,因此不需使用新的跨團隊 API (依據開發人員時間目前的需求)
  • 易於驗證 (考量維護 OTA 儲存空間需求的重要性)

超出服務範圍:

  • 保護部分非必要套件 (例如稍後會重複使用) 的方法。我們希望日後選擇採取這些方法,因此選用的方法不應排除這類問題。
  • 確定因應空間不足錯誤自動觸發 GC 的時機 (例如使用 CF/full-resolver/pkg-resolver/pkg-cache 與 fx 測試)。

設計

Software Delivery 堆疊尚未升級,以符合套件集 RFC 中詳述的定義和行為,因此下方說明使用已淘汰的術語,更準確地描述系統的現有行為和提議的行為。

定義

  • 基本套件
    • 「base」或「system_image」套件 (以啟動引數中的雜湊識別),以及 data/static_packages 檔案中列出的套件 (根據雜湊值識別)。
    • 通常是指執行特定設定所需的最少套件組合。
  • 快取套件
    • 「基本」套件 data/cache_packages.json 檔案中列出的套件 (依雜湊值)。
    • 我們希望在沒有網路的情況下提供的一般非基本套件。
  • 保留的索引
    • 這份套件清單 (經雜湊),可在 OTA 期間下載及用於 OTA 程序期間使用,或是下一個系統版本必需。
    • 在 OTA 程序期間,由 system-updater 元件處理,以符合 OTA 儲存空間需求。
  • 動態索引
    • 從套件路徑 (可在套件的 meta/package 檔案中找到) 與用於解析套件的網址路徑,對應至最近針對前述路徑解析的套件雜湊。
    • 啟動時,動態索引會預先填入快取套件 (只要包含相同路徑但之後無法解析其他雜湊的套件,就能防止 GC 保護這些套件)。
    • 如果套件在解譯後位於保留索引中 (由雜湊識別套件),則不會新增至動態索引 (因此不會移除路徑相同但雜湊不同的套件)。
  • 套件 blob
    • 套件所需的所有 blob。
    • metadata.far 和內容 blob,以及所有子套件的套件 blob。
    • 根據這項定義,保護來自 GC 的套件可以保護其所有子套件。子套件本身為套件本身,不一定會受到超級套件提供的保護保護。

目前的演算法

  1. 判斷所有常駐 blob 的集合 Br
  2. 暫停非常駐套件的解決程序
  3. 決定所有受保護的 blob (Bp) 組合,這是以下項目的套件 blob:
    • 基本套件
    • 保留的索引套件
    • 動態索引套件
  4. 指示 blobfs 刪除同組的差異 Br - Bp
  5. 取消暫停非居住套件的解析

建議的演算法

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

讓 pkg 解析器公開名為 fuchsia.pkg.PackageResolver-ota 的額外 fuchsia.pkg/PackageResolver 能力。把這項能力轉送到系統更新程式 (僅應傳送到系統更新程式),「而不是」目前的 fuchsia.pkg.PackageResolver 能力。在解析前,這項能力解析的套件必須位於保留索引中,並從開放式套件索引中排除 (透過新增旗標至 fuchsia.pkg/PackageCache.[Open|Get])。

建立「寫入套件索引」,追蹤目前正在寫入儲存空間的套件。這實際上是動態索引,差別在於它會在解決套件後停止追蹤套件 (在開放套件索引或保留索引的涵蓋範圍內)。

使用相同的 GC 演算法,但將動態索引替換為寫入和開啟套件索引,因此受保護的 blob 現在是下列項目的 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 從目前正在執行中的元件刪除套件。

安全性考量

沒有影響。

隱私權注意事項

沒有影響。

測試

一般來說,我們會針對套件解析度與 GC 之間進行了廣泛測試,特別是針對 OTA 和 GC 之間。系統會檢查這些測試,確認其是否仍具意義且已完成。

說明文件

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

診斷資料

寫入和開啟的套件索引將以檢查功能公開。基礎和快取套件以及保留的索引已納入 pkg-cache 的檢查資料中。

缺點、替代方案和未知

我們認為這個解決方案根據需求,使 GC 的正確性比目前更準確。但也有一些未知數和缺點。

缺點

目前的實作方式會嘗試保護目前未使用但應再次解析的臨時套件,以免日後重新下載 blob。建議的導入方式沒有任何這類保護措施。 這項操作不應破壞任何工作流程,因為即使目前的暫時解析度仍需要網路存取權,才能查看存放區中繼資料 (表示裝置仍應能重新下載 blob),而且 GC 的觸發情況極少,因此需要重新下載 blob,也應該十分罕見。 此外,建議的做法不會防止日後重新新增預測防護。

上述的缺點是,當在依附多個套件,但不持有這些套件目錄的開放連線的工作流程中觸發 GC 時,現在必須小心謹慎。在理論上,目標鎖定工作流程應能夠開啟所有必要套件,但在主機上自動化調度管理工作流程可能較困難。

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

替代選項

在觸發 GC 之前,系統更新工具應先關閉與中繼套件的連線,再提供系統更新工具、開放式套件追蹤作業排除的特殊 fuchsia.pkg/PackageResolver 能力。

開啟的套件索引會以非同步方式更新 (每次其發現連線已關閉時),且系統更新工具無法得知此情況的發生時間。我們可以建立 API 來查詢開放式套件索引,但目標不是用於系統更新程式來無條件地將 GC 轉給中繼套件。如果唯一的開放連線是與 system-updater 等系統更新連線,則目的是要讓系統更新到 GC 與中繼套件 (假設中繼套件同時也是目前連線的基本用戶端套件),而且套件供應機器無法確定其持有者。此外,system-updater 也已透過保留的索引手動追蹤已解析的套件,因此正常來說,與其自動追蹤已排除其解析套件並不合理。

請勿提供從開放式套件追蹤中排除的系統更新能力,fuchsia.pkg/PackageResolver改為提供標準能力,並從開放式包裹追蹤中排除保留的套件。

這個方法可能會導致執行元件取得 GC 的套件。請把握以下幾項重點:

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

未知

開啟套件追蹤功能可保護任何具備開放連線的套件。部分元件保留到套件目錄控點的時間可能會超出我們預期。現在當套件解析度因空間不足錯誤而導致套件解析度失敗時,就會導致與開發人員目前看到的問題類似。請找出這類執行個體 (通常使用主控台指令 (例如 k zx ch 等) 直接使用簡單) 並已修正。使用者裝置這不會造成問題,因為使用者的裝置只會使用臨時解析度。