Skip to content

Feature request: Support runtime changes to default deadline without recreating stubs #377

@suliao

Description

@suliao

Currently, the default deadline for gRPC client stubs is set at stub creation time via DefaultDeadlineSetupClientInterceptor, and the Duration value
is captured in the interceptor instance. This means that changing the deadline configuration at runtime (e.g., via JMX, Spring Actuator, or dynamic
configuration) requires recreating all affected stubs to pick up the new value.

This limitation creates operational challenges in production environments where adjusting timeouts without service interruption is often necessary.

Current Behavior

  1. Application starts and creates stubs with spring.grpc.client.channels.my-service.default-deadline=5s
  2. Stub is created with a DefaultDeadlineSetupClientInterceptor that captures Duration.ofSeconds(5)
  3. At runtime, configuration is changed to default-deadline=10s
  4. Existing stubs continue using 5s deadline because the Duration is already baked into the interceptor
  5. Only newly created stubs will use the 10s deadline

Code Example

// Application startup
@GrpcClient("my-service")
private MyServiceBlockingStub stub;  // Created with 5s deadline

// Runtime: Change deadline via configuration
// spring.grpc.client.channels.my-service.default-deadline=10s

// Problem: stub still uses 5s deadline
stub.myMethod(request);  // ❌ Still uses 5s, not 10s

Expected Behavior

When the deadline configuration changes at runtime, all RPC calls (including those from existing stub instances) should automatically use the new
deadline value without requiring:

  • Stub recreation
  • Application restart
  • Traffic interruption

Use Cases

  1. Production tuning: Adjusting timeouts based on observed latencies without restarting services
  2. Incident response: Quickly increasing deadlines during degraded downstream service conditions
  3. A/B testing: Experimenting with different timeout values in production
  4. JMX/Actuator integration: Runtime management via operational tools

Proposed Solution

Modify DefaultDeadlineSetupClientInterceptor to read the deadline from GrpcClientProperties on each call, rather than capturing it at construction
time.

Suggested Implementation

public class DefaultDeadlineSetupClientInterceptor implements ClientInterceptor {

    private final GrpcClientProperties properties;
    private final String channelName;

    public DefaultDeadlineSetupClientInterceptor(
            GrpcClientProperties properties,
            String channelName) {
        this.properties = properties;
        this.channelName = channelName;
    }

    @Override
    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
            MethodDescriptor<ReqT, RespT> method,
            CallOptions callOptions,
            Channel next) {

        // Read deadline fresh on each call
        GrpcClientProperties.ChannelConfig config =
            properties.getChannels().get(channelName);

        if (config != null && config.getDefaultDeadline() != null) {
            Duration deadline = config.getDefaultDeadline();

            if (callOptions.getDeadline() == null) {
                callOptions = callOptions.withDeadlineAfter(
                    deadline.toMillis(),
                    TimeUnit.MILLISECONDS
                );
            }
        }

        return next.newCall(method, callOptions);
    }
}

Alternative: Configuration Flag

If backward compatibility is a concern, this could be enabled via configuration:

spring.grpc.client.dynamic-deadline=true

When enabled, the interceptor reads from config on each call. When disabled (default), it uses the current behavior.

Workarounds (Current)

We currently work around this limitation by:

  1. Recreating stubs: Remove cached stubs and recreate with new deadline

    • ⚠️ Causes brief traffic interruption
    • ⚠️ Complex synchronization issues
    • ⚠️ Only works if application doesn't hold stub references
  2. Restart application: Redeploy with new configuration

    • ⚠️ Significant downtime
    • ⚠️ Not practical for production tuning

Environment

  • Spring gRPC version: 0.12.0
  • Spring Boot version: 3.x
  • gRPC Java version: 1.x

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions