Skip to content

Commit

Permalink
Update wording and format, and fix typo
Browse files Browse the repository at this point in the history
  • Loading branch information
CaitinChen committed May 15, 2019
1 parent 8b55946 commit e831426
Showing 1 changed file with 13 additions and 11 deletions.
24 changes: 13 additions & 11 deletions golang-failpoint.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Golang Failpoint 的设计与实现
author: ['龙恒']
date: 2019-04-30
summary: Failpoint 项目是 FreeBSD Failpoints 的 Golang 实现,允许在代码中注入错误或异常行为, 并由环境变量或代码动态激活来触发这些异常行为。Failpoint 能用于各种复杂系统中模拟错误处理来提高系统的容错性、正确性和稳定性。
summary: Failpoint 项目是 FreeBSD Failpoints 的 Golang 实现,允许在代码中注入错误或异常行为,并由环境变量或代码动态激活来触发这些异常行为。Failpoint 能用于各种复杂系统中模拟错误处理来提高系统的容错性、正确性和稳定性。
tags: ['Failpoint']
---

Expand All @@ -11,15 +11,15 @@ tags: ['Failpoint']
**[Failpoint 项目](https://github.com/pingcap/failpoint) 就是为此而生,它是 FreeBSD [failpoints](http:https://www.freebsd.org/cgi/man.cgi?query=fail) 的 Golang 实现,允许在代码中注入错误或异常行为, 并由环境变量或代码动态激活来触发这些异常行为。Failpoint 能用于各种复杂系统中模拟错误处理来提高系统的容错性、正确性和稳定性,比如:**

* 微服务中某个服务出现随机延迟、某个服务不可用。
* 存储系统磁盘 IO 延迟增加、IO 吞吐量过低、落盘时间长。
* 存储系统磁盘 I/O 延迟增加、I/O 吞吐量过低、落盘时间长。
* 调度系统中出现热点,某个调度指令失败。
* 充值系统中模拟第三方重复请求充值成功回调接口。
* 游戏开发中模拟玩家网络不稳定、掉帧、延迟过大等,以及各种异常输入(外挂请求)情况下系统是否正确工作。
* ……

## 为什么要重复造轮子?

Etcd 团队在 2016 年开发了 [gofail](https://github.com/etcd-io/gofail/) 极大地简化了错误注入,为 Golang 生态做出了巨大贡献。我们在 2018 年已经引入了 gofail 进行错误注入测试,但是我们在使用中发现了一些功能性以及便利性的问题,所以我们决定造一个更好的「轮子」。
etcd 团队在 2016 年开发了 [gofail](https://github.com/etcd-io/gofail/) 极大地简化了错误注入,为 Golang 生态做出了巨大贡献。我们在 2018 年已经引入了 gofail 进行错误注入测试,但是我们在使用中发现了一些功能性以及便利性的问题,所以我们决定造一个更好的「轮子」。

### 如何使用 gofail

Expand Down Expand Up @@ -47,7 +47,7 @@ Etcd 团队在 2016 年开发了 [gofail](https://github.com/etcd-io/gofail/)
// gofail: RETURN2:
```

* 使用 gofail enable 转换后的代码
* 使用 `gofail enable` 命令将注释转换为代码

```go
if vFailIfImportedChunk, __fpErr := __fp_FailIfImportedChunk.Acquire(); __fpErr == nil { defer __fp_FailIfImportedChunk.Release(); FailIfImportedChunk, __fpTypeOK := vFailIfImportedChunk.(int); if !__fpTypeOK { goto __badTypeFailIfImportedChunk}
Expand Down Expand Up @@ -122,7 +122,7 @@ fail_point!("transport_on_send_store", |sid| if let Some(sid) = sid {

### Failpoint

Failpoint 是一个代码片段,并且仅在对应的 failpoint name 激活的情况下才会执行,如果通过 `failpoint.Disable("failpoint-name-for-demo")` 禁用后, 那么对应的的 failpoint 永远不会触发。所有 failpoiint 代码片段不会编译到最终的二进制文件中,比如我们模拟文件系统权限控制:
Failpoint 是一个代码片段,并且仅在对应的 failpoint name 激活的情况下才会执行,如果通过 `failpoint.Disable("failpoint-name-for-demo")` 禁用后, 那么对应的的 failpoint 永远不会触发。所有 failpoint 代码片段不会编译到最终的二进制文件中,比如我们模拟文件系统权限控制:

```go
func saveTo(path string) error {
Expand All @@ -140,7 +140,7 @@ AST 重写阶段标记需要被重写的部分,主要有以下功能:
* 提示 Rewriter 重写为一个相等的 IF 语句。
* 标记函数的参数是重写过程中需要用到的参数。
* 标记函数是一个空函数,编译过程会被 inline,进一步被消除。
* 标记函数中注入的 failpoint 是一个闭包,如果闭包访问外部作用于变量,闭包语法允许捕获外部作用域变量,不会出现编译错误, 同时转换后的的代码是一个 IF 语句,IF 语句访问外部作用域变量不会产生任何问题,所以闭包捕获只是为了语法合法,最终不会有任何额外开销。
* 标记函数中注入的 failpoint 是一个闭包,如果闭包访问外部作用域变量,闭包语法允许捕获外部作用域变量,则不会出现编译错误, 同时转换后的的代码是一个 IF 语句,IF 语句访问外部作用域变量不会产生任何问题,所以闭包捕获只是为了语法合法,最终不会有任何额外开销。
* 简单、易读、易写。
* 引入编译器检测,如果 Marker 函数的参数不正确,程序不能通过编译的,进而保证转换后的代码正确性。

Expand All @@ -151,6 +151,7 @@ AST 重写阶段标记需要被重写的部分,主要有以下功能:
* `func Break(label ...string) {}`
* `func Goto(label string) {}`
* `func Continue(label ...string) {}`
* `func Return(results ...interface{}) {}`
* `func Fallthrough() {}`
* `func Return(results ...interface{}) {}`
* `func Label(label string) {}`
Expand Down Expand Up @@ -494,7 +495,7 @@ default:

## Failpoint 命名最佳实践

上面生成的代码中会自动添加一个 `_curpkg_` 调用在 `failpoint-name` 上,是因为名字是全局的,为了避免命名冲突,所以会在最终的名字包包名`_curpkg_` 相当一个宏,在运行的时候自动使用包名进行展开。你并不需要在自己的应用程序中实现 `_curpkg_`,它在 `failpoint-ctl enable` 的自动生成以及自动添加,并在 `failpoint-ctl disable` 的时候被删除。
上面生成的代码中会自动添加一个 `_curpkg_` 调用在 `failpoint-name` 上,是因为名字是全局的,为了避免命名冲突,所以会在最终的名字中包含包名`_curpkg_` 相当一个宏,在运行的时候自动使用包名进行展开。你并不需要在自己的应用程序中实现 `_curpkg_`,它在 `failpoint-ctl` enable 的时候自动生成以及自动添加,并在 `failpoint-ctl` disable 的时候被删除。

```go
package ddl // ddl’s parent package is `github.com/pingcap/tidb`
Expand All @@ -509,11 +510,12 @@ func demo() {

* 保证名字在包内是唯一的。
* 使用一个自解释的名字。
* 可以通过环境变量来激活 failpoint:

可以通过环境变量来激活 failpoint:

```
GO_FAILPOINTS="github.com/pingcap/tidb/ddl/renameTableErr=return(100);github.com/pingcap/tidb/planner/core/illegalPushDown=return(true);github.com/pingcap/pd/server/schedulers/balanceLeaderFailed=return(true)"
```
```
GO_FAILPOINTS="github.com/pingcap/tidb/ddl/renameTableErr=return(100);github.com/pingcap/tidb/planner/core/illegalPushDown=return(true);github.com/pingcap/pd/server/schedulers/balanceLeaderFailed=return(true)"
```

## 致谢

Expand Down

0 comments on commit e831426

Please sign in to comment.