zagane
is a static analysis tool which can find bugs in spanner's code.
zagane
consists of several analyzers.
unstopiter
: it finds iterators which did not stop.unclosetx
: it finds transactions which does not closewraperr
: it finds (*spanner.Client).ReadWriteTransaction calls which returns wrapped errors
You can get zagane
by go get
command.
$ go get -u github.com/gcpug/zagane
zagane
run with go vet
as below when Go is 1.12 and higher.
$ go vet -vettool=$(which zagane) github.com/gcpug/spshovel/...
# github.com/gcpug/spshovel/spanner
spanner/spanner_service.go:29:29: iterator must be stop
When Go is lower than 1.12, just run zagane
command with the package name (import path).
But it cannot accept some options such as --tags
.
$ zagane github.com/gcpug/spshovel/...
~/go/src/github.com/gcpug/spshovel/spanner/spanner_service.go:29:29: iterator must be stop
unstopiter
finds spanner.RowIterator which is not calling Stop method or Do method such as below code.
iter := client.Single().Query(ctx, stmt)
for {
row, err := iter.Next()
// ...
}
This code must be fixed as below.
iter := client.Single().Query(ctx, stmt)
defer iter.Stop()
for {
row, err := iter.Next()
// ...
}
unclosetx
finds spanner.ReadOnlyTransaction which is not calling Close method such as below code.
tx := client.ReadOnlyTransaction()
// ...
This code must be fixed as below.
tx := client.ReadOnlyTransaction()
defer tx.Close()
// ...
When a transaction is created by (*spanner.Client).Single
, unclosetx
ignore it.
wraperr
finds ReadWriteTransaction calls which returns wrapped errors such as the below code.
func f(ctx context.Context, client *spanner.Client) {
client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
stmt := spanner.Statement{SQL: `SELECT 1`}
_, err := client.Single().Query(ctx, stmt).Next()
if err != nil {
return errors.Wrap(err, "wrapped") // want "must not be wrapped"
}
return nil
})
}
This code must be fixed as below.
func f(ctx context.Context, client *spanner.Client) {
client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
stmt := spanner.Statement{SQL: `SELECT 1`}
_, err := client.Single().Query(ctx, stmt).Next()
if err != nil {
return err
}
return nil
})
}
Analyzers ignore nodes which are annotated by staticcheck's style comments as belows.
A ignore comment includes analyzer names and reason of ignoring checking.
If you specify zagane
as analyzer name, all analyzers ignore corresponding code.
//lint:ignore zagane reason
var n int
//lint:ignore unstopiter reason
_, _ = client.Single().Query(ctx, stmt).Next()
You can get analyzers of zagane from zagane.Analyzers. And you can use them with unitchecker.
"zagane" (座金) means "washer" in Japanese.
A washer works between a spanner and other parts.
zagane
also works between Cloud Spanner and your applications.