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

Asynchronous operations in client interceptor onReceiveMetadata is not finished by the time the GRPC call is finished #1984

Closed
CMCDragonkai opened this issue Dec 9, 2021 · 5 comments

Comments

@CMCDragonkai
Copy link

Problem description

I'm using the onReceiveMetadata defined here https://github.com/grpc/proposal/blob/master/L5-node-client-interceptors.md in order to do some filesystem operations at the end of each GRPC Call.

The problem is that my filesystem operations are asynchronous, they are not synchronous calls.

This means that the rest of onReceiveMetadata operations only completes long after I've already finished the GRPC call.

Is there anyway to ensure that all asynchronous operations in onReceiveMetadata is awaited for before the call is finished?

I thought that the next callback that is used would ensure that GRPC call doesn't finish until all asynchronous operations have finished, but that's not the case.

Alternatively if I can ensure that all interceptor asynchronous operations are finished before #1340 client.close() is finished, that would also be useful for my problem.

Example:

function sessionInterceptor(session: Session): Interceptor {
  const interceptor: Interceptor = (
    options: InterceptorOptions,
    nextCall: NextCall,
  ) => {
    const requester = {
      start: async (metadata: grpc.Metadata, _, next) => {
        // Outbound interception
        // This executes before the call is started
        // Set the session token only if the caller hasn't set a Basic token
        if (metadata.get('Authorization').length === 0) {
          const token = await session.readToken();
          if (token != null) {
            encodeAuthFromSession(token, metadata);
          }
        }
        next(metadata, {
          // Inbound interception
          onReceiveMetadata: async (metadata: grpc.Metadata, next) => {
            console.log('INBOUND INTERCEPTION');
            // This executes when the metadata is received from the server
            const token = decodeAuthToSession(metadata);
            console.log('BEFORE TOKEN WRITE');
            if (token != null) {
              await session.writeToken(token);
              console.log('AFTER TOKEN WRITE'); // THIS is output after the call is finished and even when the client is closed
            }
            console.log('CALLING NEXT ON METADATA');
            next(metadata);
          },
        });
      },
    };
    return new grpc.InterceptingCall(nextCall(options), requester);
  };
  return interceptor;
}
@murgatroid99
Copy link
Member

I have published #1986 in version 1.4.4, so this should be fixed now.

@CMCDragonkai
Copy link
Author

Does this mean I can use async await in my start, onReceiveMetadata and onReceiveStatus handlers and expect that these asynchronous ops finish before the grpc call finishes? And by "finish" I mean:

  • For unary call, the final callback
  • For streams, before the end event? - I guess this might be tough because the stream.end(meta) on the server side sends the final status, and when waiting for the end event, does this mean the onReceiveStatus is executed and awaited for before the end event is emitted on the stream?

What about onReceiveMessage? How does that factor in?

@murgatroid99
Copy link
Member

Yes, you can do async operations in all 3 events, and the surface level call will see them happen in order. The onReceiveStatus event causes the end and status events at the surface, so the surface level call will end after everything else.

@CMCDragonkai
Copy link
Author

I see. Just to clarify, surface level call refers unary calls and streaming calls?

@murgatroid99
Copy link
Member

Yes, I meant the surface API objects returned by method calls that represent both unary and streaming calls.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants