0Pricing
C# Academy · Lesson

Deadlines, Cancellation & Interceptors

Apply timeouts with deadlines, propagate CancellationToken, and add cross-cutting concerns via gRPC interceptors.

Deadlines, Cancellation & Interceptors is a free C# Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the C# Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Reliability in gRPC: Deadlines & Cancellation

Production gRPC services must handle two critical reliability concerns: deadlines (how long the client will wait) and cancellation (stopping work early). Together they prevent resource exhaustion and cascade failures.

Setting a Deadline on a Call

A deadline is an absolute point in time by which the call must complete. Set it with CallOptions. If exceeded, the server receives a cancellation and the client gets StatusCode.DeadlineExceeded.

var deadline = DateTime.UtcNow.AddSeconds(5);

var reply = await client.GetOrderAsync(
    new GetOrderRequest { Id = 42 },
    deadline: deadline);

// Or via CallOptions:
var options = new CallOptions(deadline: deadline);
var reply2 = await client.GetOrderAsync(
    new GetOrderRequest { Id = 42 }, options);

Handling DeadlineExceeded on the Client

Catch RpcException and check the status code to differentiate deadline expiry from other errors.

try
{
    var reply = await client.GetOrderAsync(
        new GetOrderRequest { Id = 42 },
        deadline: DateTime.UtcNow.AddSeconds(3));
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.DeadlineExceeded)
{
    Console.WriteLine("Call timed out");
}
catch (RpcException ex)
{
    Console.WriteLine($"gRPC error: {ex.Status.Detail}");
}

Respecting Deadlines on the Server

The server should check context.CancellationToken during long work. When the deadline passes, this token is cancelled automatically.

public override async Task<ReportResponse> GenerateReport(
    ReportRequest request,
    ServerCallContext context)
{
    var ct = context.CancellationToken;

    // Pass ct to every async call — work stops when deadline hits
    var data = await _db.FetchLargeDatasetAsync(request.Filters, ct);
    var report = await _reportEngine.BuildAsync(data, ct);

    ct.ThrowIfCancellationRequested(); // explicit check before CPU work
    return MapToResponse(report);
}

CancellationToken Propagation

Always propagate the server's cancellation token through every async call chain. If you ignore it, cancelled calls continue consuming resources unnecessarily.

// GOOD: token flows through the call chain
await _db.Products.AsNoTracking()
    .Where(p => p.IsActive)
    .ToListAsync(context.CancellationToken); // cancels if client disconnects

// BAD: ignores cancellation
await _db.Products.ToListAsync(); // keeps running after deadline

gRPC Interceptors: Concept

Interceptors are the gRPC equivalent of ASP.NET Core middleware — they run before/after RPC calls and can inspect or modify requests, responses, and exceptions. Ideal for logging, auth, and retry logic.

Creating a Server-Side Interceptor

Inherit from Interceptor and override the method type you want to intercept. Call continuation to proceed to the actual handler.

using Grpc.Core.Interceptors;

public class LoggingInterceptor : Interceptor
{
    private readonly ILogger<LoggingInterceptor> _logger;
    public LoggingInterceptor(ILogger<LoggingInterceptor> l) => _logger = l;

    public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
        TRequest request,
        ServerCallContext context,
        UnaryServerMethod<TRequest, TResponse> continuation)
    {
        _logger.LogInformation("gRPC call: {Method}", context.Method);
        var sw = System.Diagnostics.Stopwatch.StartNew();
        var response = await continuation(request, context);
        _logger.LogInformation("Completed in {Ms}ms", sw.ElapsedMilliseconds);
        return response;
    }
}

Registering Server Interceptors

Register interceptors globally (all services) or per service in Program.cs. The DI container resolves constructor dependencies.

// Globally for all gRPC services:
builder.Services.AddGrpc(opt =>
    opt.Interceptors.Add<LoggingInterceptor>());

// Or per service:
builder.Services.AddGrpc();
builder.Services.AddSingleton<LoggingInterceptor>();

app.MapGrpcService<OrderService>()
   .AddInterceptor<LoggingInterceptor>();

Client-Side Interceptors

Client interceptors wrap outgoing calls — useful for adding auth headers, retry logic, or correlation IDs without modifying each call site.

public class AuthInterceptor : Interceptor
{
    private readonly ITokenProvider _tokens;
    public AuthInterceptor(ITokenProvider t) => _tokens = t;

    public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
        TRequest request,
        ClientInterceptorContext<TRequest, TResponse> context,
        AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
    {
        var headers = context.Options.Headers ?? new Metadata();
        headers.Add("authorization", $"Bearer {_tokens.GetToken()}");
        var newContext = new ClientInterceptorContext<TRequest, TResponse>(
            context.Method, context.Host,
            context.Options.WithHeaders(headers));
        return continuation(request, newContext);
    }
}

// Attach to channel:
var invoker = channel.Intercept(new AuthInterceptor(tokenProvider));
var client = new OrderService.OrderServiceClient(invoker);

Exception Interceptor

A server-side exception interceptor converts unhandled exceptions to RpcExceptions, preventing internal details from leaking to clients.

public class ExceptionInterceptor : Interceptor
{
    public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
        TRequest request,
        ServerCallContext context,
        UnaryServerMethod<TRequest, TResponse> continuation)
    {
        try
        {
            return await continuation(request, context);
        }
        catch (NotFoundException ex)
        {
            throw new RpcException(new Status(StatusCode.NotFound, ex.Message));
        }
        catch (Exception ex)
        {
            throw new RpcException(new Status(StatusCode.Internal, "Internal error"));
        }
    }
}

Quick Check

What happens when a gRPC call exceeds its deadline?

Recap: Deadlines, Cancellation & Interceptors

Key takeaways:

  • Always set deadlines on client calls to prevent unbounded waits
  • Check context.CancellationToken on the server and pass it to all async calls
  • Interceptors handle cross-cutting concerns (logging, auth, error mapping) without cluttering service code
  • Server interceptors: override Unary/Stream handler methods and call continuation
  • Client interceptors: attach to the channel invoker chain

Frequently asked questions

Is the “Deadlines, Cancellation & Interceptors” lesson free?

Yes — the full text of “Deadlines, Cancellation & Interceptors” is free to read here on the web, and the C# Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the C# Academy course, upgrade to CoddyKit PRO.

What will I learn in “Deadlines, Cancellation & Interceptors”?

Apply timeouts with deadlines, propagate CancellationToken, and add cross-cutting concerns via gRPC interceptors. You practise C# Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start C# Academy?

No prior experience is required. C# Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Deadlines, Cancellation & Interceptors” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this C# Academy lesson?

Yes. Every C# Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. gRPC & Protobuf Fundamentals
  2. Unary & Server Streaming RPCs
  3. Client & Bidirectional Streaming
  4. Deadlines, Cancellation & Interceptors
← Back to C# Academy