
Библиотека внедрения сбоев для Go, добавляющая управляемые во время выполнения точки отказа для запуска паник, задержек, возвратов и условных путей ошибок через переменные окружения.
Реализация failpoints для Golang. Fail points используются для добавления точек в коде, где ошибки могут быть внедрены под управлением пользователя. Fail point — это фрагмент кода, который выполняется только тогда, когда соответствующий failpoint активен.
failpoint-ctl)Соберите failpoint-ctl из исходников
git clone https://github.com/pingcap/failpoint.git
cd failpoint
make
ls bin/failpoint-ctl
Внедрите failpoints в вашу программу, например:
package main
import "github.com/pingcap/failpoint"
func main() {
failpoint.Inject("testPanic", func() {
panic("failpoint triggerd")
})
}
Преобразуйте ваш код с помощью failpoint-ctl enable
Соберите с помощью go build
Включите failpoints с помощью переменной окружения GO_FAILPOINTS
GO_FAILPOINTS="main/testPanic=return(true)" ./your-program
Примечание: GO_FAILPOINTS не работает с маркером типа InjectCall.
Если вы используете go run для запуска теста, не забудьте добавить сгенерированный binding__failpoint_binding__.go в вашу команду, например:
GO_FAILPOINTS="main/testPanic=return(true)" go run your-program.go binding__failpoint_binding__.go
failpoint-toolexec)Соберите failpoint-toolexec из исходников
git clone https://github.com/pingcap/failpoint.git
cd failpoint
make
ls bin/failpoint-toolexec
Внедрите failpoints в вашу программу, например:
package main
import "github.com/pingcap/failpoint"
func main() {
failpoint.Inject("testPanic", func() {
panic("failpoint triggerd")
})
}
Используйте отдельный кэш сборки, чтобы избежать смешивания кэшей без failpoint-toolexec, и выполните сборку
GOCACHE=/tmp/failpoint-cache go build -toolexec path/to/failpoint-toolexec
Включите failpoints с помощью переменной окружения GO_FAILPOINTS
GO_FAILPOINTS="main/testPanic=return(true)" ./your-program
Вы также можете использовать go run или go test, например:
GOCACHE=/tmp/failpoint-cache GO_FAILPOINTS="main/testPanic=return(true)" go run -toolexec path/to/failpoint-toolexec your-program.go
Определяйте failpoint в валидном коде Golang, а не в комментариях или где-либо ещё
Failpoint не имеет дополнительных затрат
Код failpoint можно писать и читать, и он должен проверяться компилятором
Сгенерированный код по определению failpoint легко читается
Сохраняйте номера строк такими же, как у кода с внедрением (легче отлаживать)
Поддержка параллельных тестов с context.Context
Failpoint
Failpoint — это фрагмент кода, который выполняется только тогда, когда соответствующий failpoint активен.
Замыкание никогда не будет выполнено, если выполняется failpoint.Disable("failpoint-name-for-demo").
var outerVar = "declare in outer scope"
failpoint.Inject("failpoint-name-for-demo", func(val failpoint.Value) {
fmt.Println("unit-test", val, outerVar)
})
Функции-маркеры
Это просто пустая функция
Её легко писать и читать
Добавляет проверку компилятором для failpoints: код не скомпилируется в обычном режиме, если код failpoint невалиден
Список функций-маркеров
func Inject(fpname string, fpblock func(val Value)) {}func InjectContext(fpname string, ctx context.Context, fpblock func(val Value)) {}func InjectCall(fpname string, args ...any) {}func Break(label ...string) {}func Goto(label string) {}func Continue(label ...string) {}func Fallthrough() {}func Return(results ...interface{}) {}func Label(label string) {}Поддерживаемая переменная окружения failpoint
failpoint можно включить с помощью экспортируемых переменных окружения по следующему шаблону, который очень похож на SYSCTL-переменные failpoint FreeBSD
Примечание: InjectCall нельзя включить с помощью переменных окружения.
[<percent>%][<count>*]<type>[(args...)][-><more terms>]
Аргумент определяет, какое действие выполнить; он может быть одним из:
Вы можете вызвать failpoint.Inject, чтобы внедрить failpoint в место вызова, где failpoint-name используется для запуска failpoint, а failpoint-closure будет развёрнут как тело оператора IF.
failpoint.Inject("failpoint-name", func(val failpoint.Value) {
failpoint.Return("unit-test", val)
})
Преобразованный код выглядит так:
if val, _err_ := failpoint.Eval(_curpkg_("failpoint-name")); _err_ == nil {
return "unit-test", val
}
failpoint.Value — это значение, которое передаётся через failpoint.Enable("failpoint-name", "return(5)"); его можно игнорировать.
failpoint.Inject("failpoint-name", func(_ failpoint.Value) {
fmt.Println("unit-test")
})
ИЛИ
failpoint.Inject("failpoint-name", func() {
fmt.Println("unit-test")
})
Преобразованный код выглядит так:
if _, _err_ := failpoint.Eval(_curpkg_("failpoint-name")); _err_ == nil {
fmt.Println("unit-test")
}
Кроме того, замыкание failpoint может быть функцией, принимающей context.Context. Вы можете делать с context.Context различные настраиваемые вещи, например управлять тем, активен ли failpoint в параллельных тестах или в других случаях. Например,
failpoint.InjectContext(ctx, "failpoint-name", func(val failpoint.Value) {
fmt.Println("unit-test", val)
})
Преобразованный код выглядит так: