除鏽壓力測試程式庫

本文件說明如何使用 Rust 壓力測試程式庫編寫壓力測試。程式庫位於 //src/sys/lib/stress-test。這會實作測試迴圈,以及執行這些測試所需的並行和同步處理基本功能。

撰寫壓力測試

定義 GN 版本目標

為測試定義 rustc_binaryfuchsia_componentfuchsia_test_package GN 建構目標:

rustc_binary("filesystem-stressor-bin") {
    deps = [
        ...
        "//src/sys/lib/stress-test",
        ...
    ]
    sources = [
        ...
    ]
}

fuchsia_component("filesystem-stressor") {
    deps = [ ":filesystem-stressor-bin" ]
    manifest = "meta/filesystem-stressor.cml"
    testonly = true
}

fuchsia_test_package("filesystem-stress-tests") {
  test_components = [
    ":filesystem-stressor"
  ]
}

寫出演員

所有執行者都必須實作 Actor 特徵。操作者特徵是 ActorRunner 叫用的一個方法 perform()。叫用時,執行者只能執行一項作業,並將結果傳回執行元件。執行者必須儲存執行作業所需的所有連線。

pub trait Actor: Sync + Send + 'static {
    // ActorRunner invokes this function, instructing the actor
    // to perform exactly one operation and return result.
    async fn perform(&mut self) -> Result<(), ActorError>;
}

執行者可透過傳回結果表示以下資訊:

  • Ok(()):作業成功,並已新增至全域作業數量。

  • Err(ActorError::DoNotCount):這項作業不得計入全域作業數量,

  • Err(ActorError::ResetEnvironment):必須重設環境,且作業不會計入全域作業數量。

當操作者遇到未預期的錯誤時,應會驚慌,進而停止測試。

由於執行者的作業相同環境,其作業可能會發生衝突。例如,在檔案系統壓力測試中,執行者可能會使用同一組檔案運作。如果需要這種衝突,您必須設定執行者來妥善處理這類衝突。如果不是,則操作者應該要恐慌,導致測試停止。

執行者可能會刻意中斷在測試下的系統,進而要求重設環境。舉例來說,對於檔案系統壓力測試,執行者可以隨機中斷檔案系統和基礎區塊裝置之間的連線。在此範例中,其他執行者應使用 ActorError::ResetEnvironment 要求新環境,而環境將會為所有執行者重新建立連線。

pub struct FilesystemActor {
    /// Store a connection to the root of filesystem here
    pub root_directory: Directory
    ...
}

impl FilesystemActor {
    pub fn new(root_directory: Directory) -> Self {
        ...
    }
}

#[async_trait]
impl Actor for FilesystemActor {
    async pub fn perform(&mut self) -> Result<(), ActorError> {
        // Choose exactly one operation to do on the filesystem
        // using the root_directory
        self.root_directory.delete_all_files();
    }
}

寫入環境

環境提供壓力測試的基本設定,包括退場條件、執行者和重設方法。

pub trait Environment: Send + Sync + Debug {
    /// Returns the target number of operations to complete before exiting
    fn target_operations(&self) -> Option<u64>;

    /// Returns the number of seconds to wait before exiting
    fn timeout_seconds(&self) -> Option<u64>;

    /// Return the runners for all the actors
    async fn actor_runners(&mut self) -> Vec<ActorRunner>;

    /// Reset the environment, when an actor requests one
    async fn reset(&mut self);
}

一個環境可以儲存測試的其他設定。您可以在指令列中使用 argh Crate 提供這項設定。

執行元件和環境會共用執行者,因此必須在 Arc<Mutex<dyn Actor>> 包裝中。執行器在執行動作時保留鎖定。這表示環境只能取得執行者在運算之間的鎖定。

當執行者判定正在執行的測試中的目前執行個體故障時,系統就會指示環境重設。環境預期會為自我測試建立新的執行個體,並鎖定執行者,以便更新其與新執行個體的連線。

環境也必須實作 Debug 特徵。壓力測試會在測試開始時及是否出現測試恐慌時記錄環境。列印出重產生測試的重要參數是常見的做法,例如使用的隨機種子值。

#[derive(Debug)]
pub struct FilesystemEnvironment {
    fs_actor: Arc<Mutex<FilesystemActor>>,
    seed: u64,
    ...
}

impl Environment {
    pub fn new() -> Self {
        ...
    }
}

#[async_trait]
impl Environment for FilesystemEnvironment {
    fn target_operations(&self) -> Option<u64> {
        // By specifying None here, the test will run without an operation limit
        None
    }

    fn timeout_seconds(&self) -> Option<u64> {
        // By specifying None here, the test will run without a time limit
        None
    }

    async fn actor_runners(&mut self) -> Vec<ActorRunner> {
        vec![
            ActorRunner::new(
                "filesystem_actor",  // debug name
                60,  // delay (in seconds) between each operation (0 means no delay)
                self.fs_actor.clone()), // actor
            )
        ]
    }

    async fn reset(&mut self) {
        // If the actor is performing an operation, this will remain
        // locked until the operation is complete.
        let actor = self.fs_actor.lock().await;

        // Now the environment can update the actor before it is run again.
        actor.root_directory = ...;

        // Releasing the lock will resume the runner.
    }
}

編寫主要函式

壓力測試的主要功能很簡單,因為大多數的邏輯是在環境和執行者中實作。您可以使用主函式收集指令列引數 (如有)、初始化記錄及設定記錄嚴重性。

#[fuchsia::main]
async fn main() {
    // Create the environment
    let env = FilesystemEnvironment::new();

    // Run the test.
    // Depending on the exit criteria, this may never return.
    stress_test::run_test(env).await;
}

在本機執行壓力測試

由於壓力測試是 fuchsia_test_package 的一部分,因此執行這個程式碼最簡單的方法之一就是使用 fx test 指令:

fx test filesystem-stress-tests

如要使用自訂指令列引數執行測試,請使用 fx shell run

fx shell run fuchsia-pkg://fuchsia.com/filesystem-stress-tests#meta/filesystem-stressor.cm <args>

對基礎架構執行壓力測試

壓力測試會透過附加至 fuchsia_test_packagefuchsia_unittest_package GN 建構目標的 stress-tests 標記進行識別。

fuchsia_test_package("filesystem-stress-tests") {
  test_components = [
    ":filesystem-stressor"
  ]
  test_specs = {
    environments = [
      {
        dimensions = {
          device_type = "QEMU"
        }
        tags = [ "stress-tests" ]
      },
    ]
  }
}

專屬的 core.x64-stress 建構工具會識別這些測試,並執行套件中的每個測試元件,最長可執行 22 小時。

對壓力測試進行偵錯

這個架構使用 rust log Crate 來記錄訊息。測試會在啟動時記錄環境物件,以及是否出現測試恐慌。

--------------------- stressor is starting -----------------------
Environment {
    seed: 268479717856254664270968796173957499835,
    filesystem_actor: { ... }
    ...
}
------------------------------------------------------------------

如果啟用了偵錯記錄功能,系統也會記錄個別操作者的作業和作業數量。

DEBUG: [0][filesystem_actor][389] Sleeping for 2 seconds
DEBUG: [0][filesystem_actor][389] Performing...
DEBUG: [0][filesystem_actor][389] Done!
DEBUG: Counters -> [total:403] {"filesystem_actor": 403}