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 debug channel callbacks #52

Merged
merged 10 commits into from
Jan 17, 2025
Merged

Add debug channel callbacks #52

merged 10 commits into from
Jan 17, 2025

Conversation

glbrntt
Copy link
Collaborator

@glbrntt glbrntt commented Jan 9, 2025

Motivation:

Sometimes it can be useful to modify the channel pipeline or inspect the channel when debugging. v1 has hooks to allow users to do this which proved helpful a number of times. v2 should offer similar behaviour.

Modifications:

  • Add 'DebugChannelCallbacks' to the client and server config and wire them up appropriately.
  • Add tests. Extract 'TransportKind' from some tests as it was being recreated a number of times.

Result:

Users can modify the channel pipeline if they need to.

@glbrntt glbrntt added the 🆕 semver/minor Adds new public API. label Jan 9, 2025
@glbrntt glbrntt requested a review from gjcairo January 9, 2025 10:25
Motivation:

Sometimes it can be useful to modify the channel pipeline or inspect the
channel when debugging. v1 has hooks to allow users to do this which
proved helpful a number of times. v2 should offer similar behaviour.

Modifications:

- Add 'DebugChannelCallbacks' to the client and server config and wire
  them up appropriately.
- Add tests. Extract 'TransportKind' from some tests as it was being
  recreated a number of times.

Result:

Users can modify the channel pipeline if they need to.
@glbrntt
Copy link
Collaborator Author

glbrntt commented Jan 9, 2025

API breakages are expected; we added new parameters to these:

  💔 API breakage: constructor HTTP2ClientTransport.Posix.Config.init(http2:backoff:connection:compression:) has been removed
  💔 API breakage: constructor HTTP2ServerTransport.Posix.Config.init(http2:rpc:connection:compression:) has been removed

Comment on lines 178 to 181
public var onCreateTCPConnection: (@Sendable (_ channel: any Channel) async throws -> Void)?

/// A callback invoked with each new HTTP/2 stream.
public var onCreateHTTP2Stream: (@Sendable (_ channel: any Channel) async throws -> Void)?
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are these actually async throws? They're neither in the init below, so one of the two appears to be wrong.

Same for the server callbacks.

Copy link
Collaborator Author

@glbrntt glbrntt Jan 13, 2025

Choose a reason for hiding this comment

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

Oof. Not sure what happened there! I can only assume the autogenerated memberwise initialiser didn't work as expected. These APIs should line up.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Regarding whether they should be async throws vs. EventLoopFuture<Void>, I flip-flopped a bit on this. I think async throws is more appropriate: this API doesn't have to be used for configuring a channel pipeline; it's just that most uses will configure the pipeline.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I thought about this some more and think I've changed my position on this. We're slotting directly into NIO land here so exposing the natural NIO API makes more sense and I think users of this API are more likely to configure the channel pipeline than anything else. So yeah, I think you're right that we should make this EventLoopFuture<Void>.

self.whenComplete { result in
switch result {
case .success(let value):
Task {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I understand this should be used for debugging only, so maybe the fact we're spawning a Task here isn't so bad. But I wonder if we can avoid this anyways - one downside is this for example won't respect task executor preferences, and that may cause some unintended consequences even when debugging.

So I guess this is related to my comment above: do we actually want these callbacks to be async?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

... this for example won't respect task executor preferences,

Is that actually true? (I don't know either way.)

If so the workaround is straightforward: call withTaskExecutorPreference within the callback.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It is true as far as I understand. The proposal states that unstructured tasks (using both Task {} and Task.detached {}) do not inherit task executor preference. There's a separate Task(executorPreference: someExecutor) {} constructor to do this.

You could call withTaskExecutorPreference, but unless there is some magic behind the scenes, this will just cause an extra isolation domain hop, since we'll have to hop to the main executor first if we're not there already to execute the Task's closure, and then hop to the preferred executor isolation domain to execute whatever's inside the callback.

@glbrntt glbrntt requested a review from gjcairo January 14, 2025 11:22
@glbrntt glbrntt merged commit 33a1aec into grpc:main Jan 17, 2025
21 of 28 checks passed
@glbrntt glbrntt deleted the debug-hooks branch January 17, 2025 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🆕 semver/minor Adds new public API.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants