0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GoのbufconnでモックしたgRPCサーバーを使ってテストをするとUnavailableになる

Posted at

TL;DR

grpc v1.63.0でDialContext関数がdeprecatedになったため、NewClient関数に置き換えたことが原因。
targetに与えるURLのスキームとしてpassthroughを明示的に与えることで解決する。

//v1.63.0以前
conn, err := grpc.DialContext(
    context.Background(),
    "bufnet",
    grpc.WithContextDialer(dial),
    grpc.WithTransportCredentials(insecure.NewCredentials()),
)
//v1.63.0以降
conn, err := grpc.NewClient(
    "passthrough://bufnet",
    grpc.WithContextDialer(dial),
    grpc.WithTransportCredentials(insecure.NewCredentials()),
)

経緯

Goで書いたgRPCサーバーのインターセプターのテストを行うため、bufconnを用いてサーバーのモックを行っている。grpcライブラリをv1.63.0にアップデートすると、クライアント部分のDialContext()がdeprecatedになっていたため,ドキュメントの指示通りNewClient()に置き換えた。この変更をした後テストが通らなくなったため調べたところ、リクエスト結果がUnavailableになっていることが分かった。

client := pb.NewTestServiceClient(conn)
_, err = client.EmptyCall(context.Background(), &pb.Empty{})
slog.Error(err.Error())
//->rpc error: code = Unavailable desc = name resolver error: produced zero addresses

DialContextとNewClientの違い

二つの関数の違いを知るため、ドキュメントを参照した。これによると

One subtle difference between NewClient and Dial and DialContext is that the former uses "dns" as the default name resolver, while the latter use "passthrough" for backward compatibility. This distinction should not matter to most users, but could matter to legacy users that specify a custom dialer and expect it to receive the target string directly.

つまり、bufconnを利用する場合ネームリゾルバがpassthroughである必要があり、NewClient()ではデフォルトのネームリゾルバがdnsとなっているため正常に通信できなくなっている可能性が高い。
次に、ソースコードを見てみると

func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *ClientConn, err error) {
	// At the end of this method, we kick the channel out of idle, rather than
	// waiting for the first rpc.
	opts = append([]DialOption{withDefaultScheme("passthrough")}, opts...)
    cc, err := NewClient(target, opts...)
    //略

このように、DialContext()も内部ではNewClient()を呼び出しており、その呼び出しの前にwithDefaultScheme("passthrough")でpassthroughを設定していることがわかる。withDefaultScheme()はプライベートであるためpassthroughを指定する方法を他に捜したところ、URLをpassthrough://addressのようにすることでリゾルバを設定できることが分かった。

参考

https://github.com/grpc/grpc-go/blob/master/clientconn.go
https://github.com/grpc/grpc-go/blob/master/clientconn_parsed_target_test.go

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?