C# gRPC RpcException をスローしたときにメソッド呼び出しが終わらなくなる


サービス側の処理で RpcException をスローすることがありますが、クライアント側から呼び出したメソッドが終了しないケースを発見しました。一見何の問題もないように見えてしまうため、注意が必要です。

gRPC のバージョンは 1.16.0 ですが、他のバージョンでも発生するかもしれません。

サービスの実装

RpcException のトレーラーに格納する内容をいったん ServerCallContext に格納し、ServerCallContext の ResponseTrailers プロパティ経由で RpcException に格納します。

RpcExceptionをスローするサービスメソッド(1)
public override Task<Response> GetData(Request request, ServerCallContext context)
{
    // ServerCallContext にトレーラーを格納し、
    context.ResponseTrailers.Add("result", "1");

    // ServerCallContext からトレーラーを取得して例外に格納する。
    throw new RpcException(
        new Status(StatusCode.Unauthenticated, "Unauthenticated.")
        , context.ResponseTrailers
    );
}

ちなみに ServerCallContext.ResponseTrailers を使わなければこの現象は発生しません。通常はこちらのように実装すると思います。

RpcExceptionをスローするサービスメソッド(2)
public override Task<Response> GetData(Request request, ServerCallContext context)
{
    // Metadata にトレーラーを格納し、
    Metadata trailers = new Metadata();
    trailers.Add("result", "1");

    // Metadata を例外に格納する。
    throw new RpcException(
        new Status(StatusCode.Unauthenticated, "Unauthenticated.")
        , trailers
    );
}

クライアントの実装

メソッドの呼び出しが終わらず、ブロックされたままになります。

上記のサービスメソッド(1)を呼び出す
try
{
    // このメソッド呼び出しが終わらない。
    Response response = await rpcClient.GetData(new Request());
}
catch (Exception ex)
{
    // 切断されたりするまで例外はキャッチされない。
    Debug.WriteLine(ex.ToString());
}