zagane is a static analysis tool which can find bugs in spanner's code
zagane
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 close
wraperr: it finds (spanner.Client).ReadWriteTransaction calls which returns wrapped errors
Install
You can get zagane by go install command.
$ go install github.com/gcpug/zagane@latest
How to use
zagane run with go vet as below.
$ go vet -vettool=$(which zagane) github.com/gcpug/spshovel/...
github.com/gcpug/spshovel/spanner
spanner/spanner_service.go:29:29: iterator must be stop
Analyzers
unstopiter
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
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
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
})
}
Ignore Checks
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()
Analyze with golang.org/x/tools/go/analysis
You can get analyzers of zagane from zagane.Analyzers. And you can use them with unitchecker.
Why name is "zagane"?
"zagane" (座金) means "washer" in Japanese. A washer works between a spanner and other parts. zagane also works between Cloud Spanner and your applications.