国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

TiKV 源碼解析(五)fail-rs 介紹

enali / 3214人閱讀

摘要:作者張博康本文為源碼解析系列的第五篇,為大家介紹在測(cè)試中使用的周邊庫(kù)。而對(duì)于行為的情況會(huì)特殊一些,在中并不做實(shí)際的動(dòng)作,而是返回并通過(guò)傳參給閉包產(chǎn)生自定義的返回值。

作者:張博康

本文為 TiKV 源碼解析系列的第五篇,為大家介紹 TiKV 在測(cè)試中使用的周邊庫(kù) fail-rs。

fail-rs 的設(shè)計(jì)啟發(fā)于 FreeBSD 的 failpoints,由 Rust 實(shí)現(xiàn)。通過(guò)代碼或者環(huán)境變量,其允許程序在特定的地方動(dòng)態(tài)地注入錯(cuò)誤或者其他行為。在 TiKV 中通常在測(cè)試中使用 fail point 來(lái)構(gòu)建異常的情況,是一個(gè)非常方便的測(cè)試工具。

Fail point 需求

在我們的集成測(cè)試中,都是簡(jiǎn)單的構(gòu)建一個(gè) KV 實(shí)例,然后發(fā)送請(qǐng)求,檢查返回值和狀態(tài)的改變。這樣的測(cè)試可以較為完整地測(cè)試功能,但是對(duì)于一些需要精細(xì)化控制的測(cè)試就鞭長(zhǎng)莫及了。我們當(dāng)然可以通過(guò) mock 網(wǎng)絡(luò)層提供網(wǎng)絡(luò)的精細(xì)模擬控制,但是對(duì)于諸如磁盤 IO、系統(tǒng)調(diào)度等方面的控制就沒(méi)辦法做到了。

同時(shí),在分布式系統(tǒng)中時(shí)序的關(guān)系是非常關(guān)鍵的,可能兩個(gè)操作的執(zhí)行順行相反,就導(dǎo)致了迥然不同的結(jié)果。尤其對(duì)于數(shù)據(jù)庫(kù)來(lái)說(shuō),保證數(shù)據(jù)的一致性是至關(guān)重要的,因此需要去做一些相關(guān)的測(cè)試。

基于以上原因,我們就需要使用 fail point 來(lái)復(fù)現(xiàn)一些 corner case,比如模擬數(shù)據(jù)落盤特別慢、raftstore 繁忙、特殊的操作處理順序、錯(cuò)誤 panic 等等。

基本用法 示例

在詳細(xì)介紹之前,先舉一個(gè)簡(jiǎn)單的例子給大家一個(gè)直觀的認(rèn)識(shí)。

還是那個(gè)老生常談的 Hello World:

#[macro_use]
extern crate fail;

fn say_hello() {
    fail_point!(“before_print”);
    println!(“Hello World~”);
}

fn main() {
    say_hello();
    fail::cfg("before_print", "panic");
    say_hello();
}

運(yùn)行結(jié)果如下:

Hello World~
thread "main" panicked at "failpoint before_print panic" ...

可以看到最終只打印出一個(gè) Hello World~,而在打印第二個(gè)之前就 panic 了。這是因?yàn)槲覀冊(cè)诘谝淮未蛴⊥旰蟛胖付诉@個(gè) fail point 行為是 panic,因此第一次在 fail point 不做任何事情之后正常輸出,而第二次在執(zhí)行到 fail point 時(shí)就會(huì)根據(jù)配置的行為 panic 掉!

Fail point 行為

當(dāng)然 fail point 不僅僅能注入 panic,還可以是其他的操作,并且可以按照一定的概率出現(xiàn)。描述行為的格式如下:

[%][*][(args...)][->]

pct:行為被執(zhí)行時(shí)有百分之 pct 的機(jī)率觸發(fā)

cnt:行為總共能被觸發(fā)的次數(shù)

type:行為類型

off:不做任何事

return(arg):提前返回,需要 fail point 定義時(shí)指定 expr,arg 會(huì)作為字符串傳給 expr 計(jì)算返回值

sleep(arg):使當(dāng)前線程睡眠 arg 毫秒

panic(arg):使當(dāng)前線程崩潰,崩潰消息為 arg

print(arg):打印出 arg

pause:暫停當(dāng)前線程,直到該 fail point 設(shè)置為其他行為為止

yield:使當(dāng)前線程放棄剩余時(shí)間片

delay(arg):和 sleep 類似,但是讓 CPU 空轉(zhuǎn) arg 毫秒

args:行為的參數(shù)

比如我們想在 before_print 處先 sleep 1s 然后有 1% 的機(jī)率 panic,那么就可以這么寫:

"sleep(1000)->1%panic"
定義 fail point

只需要使用宏 fail_point! 就可以在相應(yīng)代碼中提前定義好 fail point,而具體的行為在之后動(dòng)態(tài)注入。

fail_point!("failpoint_name");
fail_point!("failpoint_name", |_| { // 指定生成自定義返回值的閉包,只有當(dāng) fail point 的行為為 return 時(shí),才會(huì)調(diào)用該閉包并返回結(jié)果
    return Error
});
fail_point!("failpoint_name", a == b, |_| { // 當(dāng)滿足條件時(shí),fail point 才被觸發(fā)
    return Error
})
動(dòng)態(tài)注入 環(huán)境變量

通過(guò)設(shè)置環(huán)境變量指定相應(yīng) fail point 的行為:

FAILPOINTS="=;=;..."

注意,在實(shí)際運(yùn)行的代碼需要先使用 fail::setup() 以環(huán)境變量去設(shè)置相應(yīng) fail point,否則 FAILPOINTS 并不會(huì)起作用。

#[macro_use]
extern crate fail;

fn main() {
    fail::setup(); // 初始化 fail point 設(shè)置
    do_fallible_work();
    fail::teardown(); // 清除所有 fail point 設(shè)置,并且恢復(fù)所有被 fail point 暫停的線程
}
代碼控制

不同于環(huán)境變量方式,代碼控制更加靈活,可以在程序中根據(jù)情況動(dòng)態(tài)調(diào)整 fail point 的行為。這種方式主要應(yīng)用于集成測(cè)試,以此可以很輕松地構(gòu)建出各種異常情況。

fail::cfg("failpoint_name", "actions"); // 設(shè)置相應(yīng)的 fail point 的行為
fail::remove("failpoint_name"); // 解除相應(yīng)的 fail point 的行為
內(nèi)部實(shí)現(xiàn)

以下我們將以 fail-rs v0.2.1 版本代碼為基礎(chǔ),從 API 出發(fā)來(lái)看看其背后的具體實(shí)現(xiàn)。

fail-rs 的實(shí)現(xiàn)非常簡(jiǎn)單,總的來(lái)說(shuō),就是內(nèi)部維護(hù)了一個(gè)全局 map,其保存著相應(yīng) fail point 所對(duì)應(yīng)的行為。當(dāng)程序執(zhí)行到某個(gè) fail point 時(shí),獲取并執(zhí)行該全局 map 中所保存的相應(yīng)的行為。

全局 map 其具體定義在 FailPointRegistry。

struct FailPointRegistry {
    registry: RwLock>>,
}

其中 FailPoint 的定義如下:

struct FailPoint {
    pause: Mutex,
    pause_notifier: Condvar,
    actions: RwLock>,
    actions_str: RwLock,
}

pausepause_notifier 是用于實(shí)現(xiàn)線程的暫停和恢復(fù),感興趣的同學(xué)可以去看看代碼,太過(guò)細(xì)節(jié)在此不展開(kāi)了;actions_str 保存著描述行為的字符串,用于輸出;而 actions 就是保存著 failpoint 的行為,包括概率、次數(shù)、以及具體行為。Action 實(shí)現(xiàn)了 FromStr 的 trait,可以將滿足格式要求的字符串轉(zhuǎn)換成 Action。這樣各個(gè) API 的操作也就顯而易見(jiàn)了,實(shí)際上就是對(duì)于這個(gè)全局 map 的增刪查改:

fail::setup() 讀取環(huán)境變量 FAILPOINTS 的值,以 ; 分割,解析出多個(gè) failpoint name 和相應(yīng)的 actions 并保存在 registry 中。

fail::teardown() 設(shè)置 registry 中所有 fail point 對(duì)應(yīng)的 actions 為空。

fail::cfg(name, actions) 將 name 和對(duì)應(yīng)解析出的 actions 保存在 registry 中。

fail::remove(name) 設(shè)置 registryname 對(duì)應(yīng)的 actions 為空。

而代碼到執(zhí)行到 fail point 的時(shí)候到底發(fā)生了什么呢,我們可以展開(kāi) fail_point! 宏定義看一下:

macro_rules! fail_point {
    ($name:expr) => {{
        $crate::eval($name, |_| {
            panic!("Return is not supported for the fail point "{}"", $name);
        });
    }};
    ($name:expr, $e:expr) => {{
        if let Some(res) = $crate::eval($name, $e) {
            return res;
        }
    }};
    ($name:expr, $cond:expr, $e:expr) => {{
        if $cond {
            fail_point!($name, $e);
        }
    }};
}

現(xiàn)在一切都變得豁然開(kāi)朗了,實(shí)際上就是對(duì)于 eval 函數(shù)的調(diào)用,當(dāng)函數(shù)返回值為 Some 時(shí)則提前返回。而 eval 就是從全局 map 中獲取相應(yīng)的行為,在 p.eval(name) 中執(zhí)行相應(yīng)的動(dòng)作,比如輸出、等待亦或者 panic。而對(duì)于 return 行為的情況會(huì)特殊一些,在 p.eval(name) 中并不做實(shí)際的動(dòng)作,而是返回 Some(arg) 并通過(guò) .map(f) 傳參給閉包產(chǎn)生自定義的返回值。

pub fn eval) -> R>(name: &str, f: F) -> Option {
    let p = {
        let registry = REGISTRY.registry.read().unwrap();
        match registry.get(name) {
            None => return None,
            Some(p) => p.clone(),
        }
    };
    p.eval(name).map(f)
}
小結(jié)

至此,關(guān)于 fail-rs 背后的秘密也就清清楚楚了。關(guān)于在 TiKV 中使用 fail point 的測(cè)試詳見(jiàn) github.com/tikv/tikv/tree/master/tests/failpoints,大家感興趣可以看看在 TiKV 中是如何來(lái)構(gòu)建異常情況的。

同時(shí),fail-rs 計(jì)劃支持 HTTP API,歡迎感興趣的小伙伴提交 PR。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/17973.html

相關(guān)文章

  • TiKV 源碼解析系列文章(一)序

    摘要:而源碼解析系列文章則是會(huì)從源碼層面給大家抽絲剝繭,讓大家知道我們內(nèi)部到底是如何實(shí)現(xiàn)的。我們希望通過(guò)該源碼解析系列,能讓大家對(duì)有一個(gè)更深刻的理解。 作者:唐劉 TiKV 是一個(gè)支持事務(wù)的分布式 Key-Value 數(shù)據(jù)庫(kù),有很多社區(qū)開(kāi)發(fā)者基于 TiKV 來(lái)開(kāi)發(fā)自己的應(yīng)用,譬如 titan、tidis。尤其是在 TiKV 成為 CNCF 的 Sandbox 項(xiàng)目之后,吸引了越來(lái)越多開(kāi)發(fā)者的...

    LeviDing 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<