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

Add ClientTransportFilter #10646

Merged

Conversation

joybestourous
Copy link
Contributor

@joybestourous joybestourous commented Nov 3, 2023

Problem
We're trying to add some stats on the number of connections opened, closed, and active on the client side for grpc-java. On the server side, ServerTransportFilter acts as a reasonable hook for this, but there doesn't seem to be anything comparable on the client side that I can find (if there is, please direct me to it!).

Closes #10647

Solution
Add a ClientTransportFilter. Similarly to ServerTransportFilter this will provide an observability hook and allows direct modification of the transport's attributes.
To wire this in, this PR also adds a filterTransport method to ManagedClientTransport.Listener which handles modifying the transport's attributes. This is called just before transportReady

@joybestourous joybestourous force-pushed the joy.bestourous/clienttransportfilter branch from dc9592d to a40996c Compare November 3, 2023 18:03
@joybestourous joybestourous changed the title Add ClientTransportFilter Add ClientTransportHook Nov 8, 2023
@joybestourous joybestourous marked this pull request as ready for review November 8, 2023 16:18
api/src/main/java/io/grpc/ManagedChannelBuilder.java Outdated Show resolved Hide resolved
* to modify the channels or transport life-cycle event behavior, but they can be useful hooks
* for transport observability. Multiple filters may be registered to the client.
*/
@ExperimentalApi("https://gitub.com/grpc/grpc-java/issues/TODO")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the other comment:

This should be a new ticket with "experimental API" label. Example: #10108.

I think it'd even make sense to use the same ticket to track both ClientTransportHook interface, and channel builder's addTransportHook().

@ejona86 - correct me if I'm wrong here

Co-authored-by: Sergii Tkachenko <hi@sergii.org>
@larry-safran larry-safran added the kokoro:force-run Add this label to a PR to tell Kokoro to re-run all tests. Not generally necessary label Nov 21, 2023
@grpc-kokoro grpc-kokoro removed the kokoro:force-run Add this label to a PR to tell Kokoro to re-run all tests. Not generally necessary label Nov 21, 2023
@joybestourous
Copy link
Contributor Author

👋 @ejona86 any update on this PR? is this still on track for v61 release?

@ejona86
Copy link
Member

ejona86 commented Dec 13, 2023

Sorry I went silent on this. I had been hoping to sneak it into 1.60 but then issues with the release cropped up that ended up taking a lot of my time. (I actually didn't see your comment two days ago either...)

We discussed in our API review and the general verdict was "make it a filter like on server-side." Having it just be a hook caused a whole slew of naming/identity/purpose crises that are all solved by mirroring the existing server-side API. Before my time evaporated I was going to actually make the necessary plumbing change...

The listener could receive and return Attributes, just like is done on server-side. In OkHttpClientTransport, BinderTransport, InProcessTransport, CronetClientTransport look to be pretty easy to support (some places attributes would no longer be final; that's okay). NettyClientHandler could pass the Attributes through ClientTransportLifecycleManager.nofiyReady() and update the attributes on return.

But I think that would be inherently racy, in a way not present on server-side. transportReady() is when the client can start sending RPCs on the transport, so we'd need to synchronize the attribute update. On server-side, the server's transport thread can update the attributes before processing any RPCs from the connection.

So instead of adding Attributes to transportReady(), let's add a new method to ManagedClientTransport.Listener just to do this filtering. It can be called just before transportReady(). With a default implementation that just echos back the same attributes, I hope there's not even many tests that have to be updated.

@ejona86
Copy link
Member

ejona86 commented Dec 13, 2023

(FYI, the branch cut for 1.61 is a week earlier than normal, or maybe a bit more. So it is coming up soon. I'd be fine backporting this to the release, even though it is technically a feature, if we can do so at least a week before the release. If there's any bugs, seem they should only impact you and not destabilize others.)

@joybestourous
Copy link
Contributor Author

Sorry I went silent on this. I had been hoping to sneak it into 1.60 but then issues with the release cropped up that ended up taking a lot of my time. (I actually didn't see your comment two days ago either...)

We discussed in our API review and the general verdict was "make it a filter like on server-side." Having it just be a hook caused a whole slew of naming/identity/purpose crises that are all solved by mirroring the existing server-side API. Before my time evaporated I was going to actually make the necessary plumbing change...

The listener could receive and return Attributes, just like is done on server-side. In OkHttpClientTransport, BinderTransport, InProcessTransport, CronetClientTransport look to be pretty easy to support (some places attributes would no longer be final; that's okay). NettyClientHandler could pass the Attributes through ClientTransportLifecycleManager.nofiyReady() and update the attributes on return.

But I think that would be inherently racy, in a way not present on server-side. transportReady() is when the client can start sending RPCs on the transport, so we'd need to synchronize the attribute update. On server-side, the server's transport thread can update the attributes before processing any RPCs from the connection.

So instead of adding Attributes to transportReady(), let's add a new method to ManagedClientTransport.Listener just to do this filtering. It can be called just before transportReady(). With a default implementation that just echos back the same attributes, I hope there's not even many tests that have to be updated.

Thanks for getting back to me! I addressed these suggestions, ptal and let me know if i understood you correctly. As for backporting this to 1.61, I think we're fine with waiting for 1.62 if you folks are at all worried about the risk. Thanks again!

@joybestourous joybestourous changed the title Add ClientTransportHook Add ClientTransportFilter Dec 18, 2023
Copy link
Member

@ejona86 ejona86 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me, other than some nits.

@ejona86 ejona86 added the kokoro:run Add this label to a PR to tell Kokoro the code is safe and tests can be run label Dec 18, 2023
@ejona86 ejona86 added this to the 1.61 milestone Dec 18, 2023
@grpc-kokoro grpc-kokoro removed the kokoro:run Add this label to a PR to tell Kokoro the code is safe and tests can be run label Dec 18, 2023
@ejona86 ejona86 requested a review from larry-safran December 18, 2023 22:50
Co-authored-by: Eric Anderson <ejona@google.com>
joybestourous and others added 4 commits December 19, 2023 10:50
@sergiitk sergiitk added the kokoro:run Add this label to a PR to tell Kokoro the code is safe and tests can be run label Dec 26, 2023
@grpc-kokoro grpc-kokoro removed the kokoro:run Add this label to a PR to tell Kokoro the code is safe and tests can be run label Dec 26, 2023
@sergiitk sergiitk added the kokoro:run Add this label to a PR to tell Kokoro the code is safe and tests can be run label Dec 26, 2023
@grpc-kokoro grpc-kokoro removed the kokoro:run Add this label to a PR to tell Kokoro the code is safe and tests can be run label Dec 26, 2023
@sergiitk
Copy link
Member

/tmpfs/src/github/grpc-java/cronet/src/main/java/io/grpc/cronet/CronetClientTransport.java:172: error: cannot assign a value to final variable attrs
        attrs = CronetClientTransport.this.listener.filterTransport(attrs);
        ^

@larry-safran
Copy link
Contributor

larry-safran commented Dec 26, 2023 via email

@larry-safran larry-safran added the kokoro:force-run Add this label to a PR to tell Kokoro to re-run all tests. Not generally necessary label Dec 28, 2023
@grpc-kokoro grpc-kokoro removed the kokoro:force-run Add this label to a PR to tell Kokoro to re-run all tests. Not generally necessary label Dec 28, 2023
@larry-safran
Copy link
Contributor

@joybestourous The builds failed because of CronetClientTransportTest. That test uses a mock for clientTransportListener which by default returns null for all of the methods. You will need to put a when clause into setUp() so that clientTransportListener.filterTransport(attr) just returns its argument.

For example:

when(clientTransportListener.filterTransport(any()).thenAnswer(i -> i.getArguments()[0])

@ejona86 ejona86 added the kokoro:run Add this label to a PR to tell Kokoro the code is safe and tests can be run label Jan 3, 2024
@grpc-kokoro grpc-kokoro removed the kokoro:run Add this label to a PR to tell Kokoro the code is safe and tests can be run label Jan 3, 2024
@larry-safran larry-safran merged commit 91d15ce into grpc:master Jan 3, 2024
11 of 12 checks passed
@joybestourous
Copy link
Contributor Author

@joybestourous The builds failed because of CronetClientTransportTest. That test uses a mock for clientTransportListener which by default returns null for all of the methods. You will need to put a when clause into setUp() so that clientTransportListener.filterTransport(attr) just returns its argument.

For example:

when(clientTransportListener.filterTransport(any()).thenAnswer(i -> i.getArguments()[0])

Thanks for bringing this to completion 🙏 sorry I missed these errors my android setup was not working properly.

@larry-safran
Copy link
Contributor

larry-safran commented Jan 3, 2024 via email

@ejona86
Copy link
Member

ejona86 commented Jan 11, 2024

#10805 is suspicious, and shouldn't be necessary. I feel like there's no harm happening at present with making the field volatile, but the fact TSAN is finding issues makes clear something is happening that we don't expect. So we aren't going to backport this into the 1.61 release. We'll probably try to remove the volatile before 1.62, but even if we don't, at that point it will have seen enough testing that we are then more confident it won't cause an issue.

@joybestourous
Copy link
Contributor Author

#10805 is suspicious, and shouldn't be necessary. I feel like there's no harm happening at present with making the field volatile, but the fact TSAN is finding issues makes clear something is happening that we don't expect. So we aren't going to backport this into the 1.61 release. We'll probably try to remove the volatile before 1.62, but even if we don't, at that point it will have seen enough testing that we are then more confident it won't cause an issue.

Oh hm that's super surprising / suspicious. Makes complete sense to put this in 1.62. Thanks for the heads up!

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 12, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Client-side transport hooks
5 participants