Skip to content

Ensure gRPC serializes/deserializes requests/responses from blocking executors if available #6241

Description

@jrhee17

We received a report that serde logic for gRPC requests/responses may occupy the event loop. Hence, some users may want this logic to be executed from blockingTaskExecutor if specified via GrpcServiceBuilder#useBlockingTaskExecutor.

Ideally, the general rule of thumb should be:

  • Receiving requests: the thread invoking ServerCall.Listener callbacks should be either the eventLoop (if useBlockingTaskExecutor = false) or the blocking executor (if useBlockingTaskExecutor = true). Request serde/decompression should be executed in this thread.
  • Sending responses: the user decides which threads should invoke ServerCall callbacks (e.g. sendHeaders, sendMessage, etc...). This thread is responsible for serde/compressions/etc.

@ikhoon proposed the following approach for simplicity:

The simplest way to achieve this is probably to just:

  1. Move request deserialization to after the blockingTaskExecutor reschedule

final boolean grpcWebText = GrpcSerializationFormats.isGrpcWebText(serializationFormat);
request = marshaller.deserializeRequest(message, grpcWebText);
maybeLogRequestContent(request);
if (unsafeWrapRequestBuffers && buf != null && !grpcWebText) {
GrpcUnsafeBufferUtil.storeBuffer(buf, request, ctx);
}

  1. Move response serialization to be performed before the reschedule:

e.g. the following will be called as soon as sendMessage is called before the reschedule. We should be aware of leaks though.

final HttpData responseBody = toPayload(responseMessage);

ref: https://discord.com/channels/1087271586832318494/1087272728177942629/1372734948967972978

Note: Another idea I was developing was to provide a Executor variant which:

  1. guarantees sequential execution (so no two tasks execute at the same time, but ordering is not necessarily guaranteed based on submission time)
  2. is aware of reentry and does not perform rescheduling
    This approach can also help clean up the logic and remove most of the conditional statements regarding blockingTaskExecutor.

However, this might take a while to develop, so this idea can be tackled later if needed.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions