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 install command.
$ go install github.com/gcpug/zagane@latest
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
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.