RFC-0217:開啟包裹追蹤

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

保護套件免於垃圾收集時,請使用開放式套件追蹤,而非動態索引。

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

摘要

改善套件垃圾收集 (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 程序的儲存空間用量和轉送進度保證。
    • 簡而言之,就是允許系統更新程式在無線下載 (OTA) 期間,對其解析的中繼套件 (更新套件和包含傳入二進位映像檔的套件) 執行垃圾收集作業,但不會對下一個系統版本所需的套件執行垃圾收集作業。
  2. 允許連續執行多項測試 (每項測試都適合在裝置上執行,且都來自不同套件),不會耗盡空間。
  3. 請勿移除元件正在使用的套件,包括含有執行中元件的套件。

使用者非常偏好以下方法:

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

不適用:

  • 保護部分非必要套件免於垃圾收集的機制 (例如,因為預期稍後會再次使用)。我們希望日後能採用這些方法,因此所選方法不應排除這些做法。
  • 判斷何時自動觸發 GC,以回應空間不足錯誤 (例如在 CF/full-resolver/pkg-resolver/pkg-cache 中,而非在 fx 測試中)。

設計

軟體交付堆疊尚未升級,因此不符合套件集 RFC 中詳述的定義和行為,因此下方的說明使用已淘汰的字詞,更準確地描述系統目前和建議的行為。

定義

  • 基本套裝組合
    • 「base」或「system_image」套件 (由啟動引數中的雜湊值識別),以及 data/static_packages 檔案中列出的套件 (由雜湊值識別)。
    • 通常是指執行特定設定所需的最低套件組合。
  • 快取套件
    • 「base」套件 data/cache_packages.json 檔案中列出的套件 (依雜湊)。
    • 一般來說,我們仍希望在沒有網路的情況下提供非基本套件。
  • 保留索引
    • 在 OTA 期間下載 (或即將下載) 的套件清單 (依雜湊值),這些套件會在 OTA 程序中使用,或用於下一個系統版本。
    • 在 OTA 程序期間,由系統更新程式元件操控,以符合 OTA 儲存空間需求。
  • 動態索引
    • 從套件路徑 (位於套件的 meta/package 檔案中,通常與用於解析套件的網址路徑相同) 到最近為該路徑解析的套件雜湊值的對應。
    • 啟動時,系統會預先填入動態索引的快取套件 (只要具有相同路徑但不同雜湊的套件稍後未解析,即可防止 GC 處理這些套件)。
    • 如果套件在解析時位於保留的索引 (以雜湊識別套件),系統就不會將其新增至動態索引 (因此不會逐出路徑相同但雜湊不同的套件)。
  • 套件 Blob
    • 套件所需的所有 Blob。
    • meta.far 和內容 Blob,以及所有子套件的套件 Blob (以遞迴方式)。
    • 根據這項定義,保護套件免於遭到垃圾收集,也會保護其所有子套件。子套件本身可能受到保護,也可能不受超級套件提供的保護措施保護。

目前的演算法

  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能力 (稱為 fuchsia.pkg.PackageResolver-ota)。將這項能力路徑導向系統更新程式 (且僅導向系統更新程式),而非目前的 fuchsia.pkg.PackageResolver 能力。這項能力解析的套件必須先位於保留的索引中,才能進行解析,且會從開放式套件索引中排除 (方法是在 fuchsia.pkg/PackageCache.[Open|Get] 中新增旗標)。

建立「寫入套件索引」,追蹤目前寫入儲存空間的套件。這實際上是動態索引,但一旦套件獲得解決 (此時套件會納入開放套件索引或保留索引),系統就會停止追蹤。

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

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

這符合規定

  1. 根據 RFC 170 的修改內容,維持 OTA 程序的儲存空間用量和轉送進度保證。

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

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

    現在連續從多個不同套件執行測試時,系統會觸發垃圾收集作業,避免發生空間不足錯誤。動態索引過去用於保護每個測試套件最近解析的版本 (套件會在解析時依路徑新增,且只會在重新啟動時移除),因此先前執行的測試永遠不會進行垃圾收集,但現在開放的套件索引會在最後一個連線關閉後,停止保護測試套件。

  3. 請勿移除元件使用的套件,包括含有執行中元件的套件。

    先前,如果啟動元件後解析出不同版本的支援套件,無論元件是否仍在執行,元件的套件都會從動態索引中逐出。現在,由於執行中的元件會保留與其套件目錄的連線,因此元件的套件會受到開放套件索引的 GC 保護。

索引 新增套件動作 移除套件動作
Base 產品組裝 永不
快取 產品組裝 永不
保留 系統更新程式在 OTA 期間設定 系統更新程式會在 OTA 期間更新/清除
寫作 開始解析套件 套件解析度結束
開啟 非 OTA 套件解決方案終止 最後一個套件目錄連線關閉

效能

動態索引使用的記憶體與開機後解析的不同暫時性套件數量成正比 (依套件路徑分組)。開放式套件索引使用的記憶體大小,與開放式連線的暫時性套件數量成正比 (依套件雜湊分組)。由於目前使用暫時性套件的方式,這兩種記憶體用量都很小,且大小相似 (如果執行許多不同的測試套件,動態索引會有效洩漏這些項目,因此開啟套件索引會較小)。記憶體用量差異應小於因重複資料結構而節省的記憶體,這些資料結構用於放送套件目錄。

人體工學

以開放式套件索引取代動態索引,可讓系統更容易瞭解及運作:

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

安全性考量

沒有影響。

隱私權注意事項

沒有影響。

測試

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

說明文件

現有的 GC 文件將會更新。

診斷

寫入和開啟的套件索引會透過「檢查」公開。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. 系統觸發垃圾收集作業,刪除正在執行的測試中的 Blob

不明

開啟套件追蹤功能後,系統會保護任何開啟連線的套件。有些元件可能會比預期更久才釋放套件目錄控制代碼。這會導致類似於開發人員目前遇到的問題,也就是因空間不足錯誤而導致套件解析失敗。您必須找出這類執行個體 (通常可使用 k zx ch 等控制台指令輕鬆完成),並修正問題。使用者裝置不會發生問題,因為使用者裝置上只有系統更新程式會使用暫時性解析度。