Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to manage request-scoped data and MDC effectively in gRPC unary calls? #1159

Open
Romil-Desai opened this issue Jan 10, 2025 · 0 comments
Labels
question A question about this library or its usage

Comments

@Romil-Desai
Copy link

Romil-Desai commented Jan 10, 2025

I have a Spring Boot application serving HTTP requests in a multitenant architecture. For HTTP requests, client identity is extracted from a header and stored in a ThreadLocal. Since Tomcat ensures a single request is served by a single thread, this approach works well.

Now, the application is being extended to serve gRPC requests (unary calls only). I understand there are concerns about using ThreadLocal in gRPC, even for unary calls. However, my testing shows no issues so far.

To address potential concerns, I explored using the gRPC Context API to propagate request-scoped data, such as client identity, across multiple server interceptors. Below is an example of my implementation:
Example Implementation:

@Override
  public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
      ServerCall<ReqT, RespT> serverCall,
      Metadata metadata,
      ServerCallHandler<ReqT, RespT> serverCallHandler
  ) {
      Context ctx = Context.current()
          .withValue(GrpcContextStorage.grpcRequestKey, Boolean.TRUE)
          .withValue(GrpcContextStorage.grpcRequestDateTimeKey, DateTimeUtil.getCurrentLocalTime());
  
      ServerCall.Listener<ReqT> listener = Contexts.interceptCall(ctx, serverCall, metadata, serverCallHandler);
  
      return new InterceptorForwardingCallListener<>(listener) {
          @Override
          public void onCancel() {
              try {
                  super.onCancel();
              } catch (Exception exe) {
                  _logger.error("Error in onCancel: " + exe.getMessage(), exe);
                  throw exe;
              } finally {
                  MDC.clear(); // Custom cleanup logic
              }
          }
  
          @Override
          public void onComplete() {
              try {
                  super.onComplete();
              } catch (Exception exe) {
                  _logger.error("Error in onComplete: " + exe.getMessage(), exe);
                  throw exe;
              } finally {
                  MDC.clear(); // Custom cleanup logic
              }
          }
      };
  }

Specific Questions:

  1. Using ThreadLocal in gRPC Unary Calls:

Is it acceptable to use ThreadLocal for storing request-scoped data in gRPC unary calls?
If not, why is it discouraged, considering the synchronous nature of unary calls?

  1. Using gRPC Context for Propagation:

In the implementation above, does the creation of a new Context object using Context.withValue() ensure proper isolation
of data between requests?
Is manual cleanup of the previous Context required, or does gRPC handle this automatically?
How can I ensure client-specific data stored in the Context is not inadvertently leaked or reused in subsequent requests?

  1. MDC and Logging in gRPC Unary Calls:

Is it appropriate to use MDC (which relies on ThreadLocal) for logging context in gRPC unary calls?
If so, is overriding onComplete() and onCancel() sufficient to ensure proper cleanup of MDC after the request lifecycle?
Should other methods like onHalfClose() be overridden to handle edge cases?
If MDC is discouraged, what alternative approach should I use for propagating logging context across interceptors?

The application's environment

Which versions do you use?

  • Spring (boot): 2.7.7
  • grpc-spring-boot-starter: 3.1.0.RELEASE
  • java: 17
@Romil-Desai Romil-Desai added the question A question about this library or its usage label Jan 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question A question about this library or its usage
Projects
None yet
Development

No branches or pull requests

1 participant