dan-kli
dan-kli

Reputation: 893

How do I set a universal deadline to gRPC clients created by the gRPC client factory?

Following along the gRPC client factory integration in .NET I can register a gRPC client like this:

builder.Services.AddGrpcClient<Greeter.GreeterClient>(o =>
{
    o.Address = new Uri("https://localhost:5001");
});

Now I would like to set a universal deadline for all calls that are made with this client. Maybe something like this:

int deadline = ReadFromConfig();

builder.Services.AddGrpcClient<Greeter.GreeterClient>(o =>
{
    o.Address = new Uri("https://localhost:5001");
    o.CallOptionsActions.Add(o => () =>
        o.CallOptions.Deadline = DateTime.UtcNow.AddSeconds(deadline))
});

However, the deadline in the call options is read-only here. It seems like I cannot set it here. Am I doing it wrong, or is it only possible to set a deadline when sending out the request?

Upvotes: 2

Views: 382

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1064324

Completely untested, but how about an Interceptor ?

var channel = GrpcChannel.ForAddress("https://whatever");
var invoker = channel.Intercept(new DeadlineInterceptor(TimeSpan.FromSeconds(10)));
// TODO: create your client from "invoker", not "channel"


class DeadlineInterceptor(TimeSpan deadline) : Interceptor
{
    private void ApplyDeadline<TRequest, TResponse>(ref ClientInterceptorContext<TRequest, TResponse> context)
        where TRequest : class
        where TResponse : class
    {
        if (context.Options.Deadline is null)
        {
            context = new(context.Method, context.Host, context.Options.WithDeadline(DateTime.UtcNow.Add(deadline)));
        }
    }

    public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
    {
        ApplyDeadline(ref context);
        return base.AsyncClientStreamingCall(context, continuation);
    }

    public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncDuplexStreamingCallContinuation<TRequest, TResponse> continuation)
    {
        ApplyDeadline(ref context);
        return base.AsyncDuplexStreamingCall(context, continuation);
    }

    public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncServerStreamingCallContinuation<TRequest, TResponse> continuation)
    {
        ApplyDeadline(ref context);
        return base.AsyncServerStreamingCall(request, context, continuation);
    }

    public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
    {
        ApplyDeadline(ref context);
        return base.AsyncUnaryCall(request, context, continuation);
    }

    public override TResponse BlockingUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, BlockingUnaryCallContinuation<TRequest, TResponse> continuation)
    {
        ApplyDeadline(ref context);
        return base.BlockingUnaryCall(request, context, continuation);
    }

    // note no need to intercept server methods
}

Upvotes: 3

Related Questions