sinmetalcraft / gcpbox

Google Cloud Platform Utility
MIT License
2 stars 1 forks source link

gRPC Error Wrapper #7

Open sinmetal opened 4 years ago

sinmetal commented 4 years ago

WHAT

gRPCのerrorをいい感じにWrapする方法を考える。 選択肢的には以下のどちらか。 どっちもどっちみたいな気持ち

fmt.Errorf(": %w" err) でwrapしたものを剥いて、status.Status を取り出す

以下のようなやつ

func UnwrapGRPCError(err error) (*status.Status, bool) {
    cerr := err
    for {
        sts, ok := status.FromError(cerr)
        if ok {
            return sts, true
        }
        nerr := errors.Unwrap(cerr)
        if nerr == nil {
            return nil, false
        }
        cerr = nerr
    }
}

https://github.com/sinmetal/til/blob/51fd3cc40761e95b77dee6168be5d436d7cc1f7e/datastore/tx/cloud_datastore_service.go#L45

status.Status を内包する Custom Error を作る

WHY

gRPC の error は Wrap すると、gRPC package の関数では取り出せない

sinmetal commented 4 years ago

自分で https://github.com/grpc/grpc-go/blob/9519efffeb5d1897ae8671568871a6d476986524/status/status.go#L83-L85 に合致するinterfaceを作ってしまうという手もあるらしい

package main
import (
    "errors"
    "fmt"
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/status"
)
type gRPCStatus interface {
    GRPCStatus() *status.Status
}
func main() {
    // original gRPC error
    err := status.Errorf(codes.Unauthenticated, "unauthenticated")
    // wrapping
    nerr := fmt.Errorf("wrapping: %w", err)
    nerr = fmt.Errorf("more wrapping: %w", nerr)
    var e gRPCStatus
    if errors.As(nerr, &e) {
        fmt.Printf("code: %s \n", e.GRPCStatus().Code())
    } else {
        fmt.Println("not a gRPCStatus error")
    }
    // this prints "code: Unauthenticated"
}
sinmetal commented 4 years ago

errors.As() をサポートするissueはあるけど、まだ作られてはいない https://github.com/grpc/grpc-go/issues/2934

sinmetal commented 4 years ago

Spannerが実装しているやつ https://github.com/googleapis/google-cloud-go/blob/863a84d5f50fb68a5ae3de0c1b03efd02ae8f1fb/spanner/errors.go#L83-L99