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 new protocol for ChannelHandler to get buffered bytes in the channel handler #2918

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

johnnzhou
Copy link
Contributor

@johnnzhou johnnzhou commented Oct 13, 2024

Add new protocol to get buffered bytes from ChannelHandlers and ChannelPipeline API to query the buffered bytes from ChannelHandlers

Motivation:

In #2849, a new ChannelOption is introduced to retrieve the number of buffered outbound bytes in a Channel. However, this solution does not account for bytes that may be buffered within individual ChannelHandlers. This PR builds on #2849 by adding functionality to audit buffered bytes residing in ChannelHandlers and exposing this information through new ChannelPipeline APIs.

Modifications:

  • Two new protocols for ChannelHandler to audit buffered bytes for inbound and outbound.
    • NIOOutboundBufferedBytesAuditableChannelHandler
    • NIOInboundBufferedBytesAuditableChannelHandler
  • New ChannelPipeline APIs
    • auditOutboundBufferedBytes()
    • auditOutboundBufferedBytes(in: ChannelHandlerContext)
    • auditInboundBufferedBytes()
    • auditInboundBufferedBytes(in: ChannelHandlerContext)

Result:

Users can now easily query the amount of bytes buffered in ChannelHandlers using the new ChannelPipeline APIs, enhancing the visibility of ChannelHandler performance.


I'd like to make PR to gather some feedback from the community regarding the design before proceeding with further implementation. I'm planning to add more test cases and make existing ChannelHandlers in SwiftNIO project conform to new protocols.

@Lukasa Lukasa added the semver/minor Adds new public API. label Oct 14, 2024
@Lukasa
Copy link
Contributor

Lukasa commented Oct 14, 2024

Thanks so much for opening this @johnnzhou! cc @weissi @Joannis @FranzBusch who are all going to care about this API.

public protocol OutboundBufferedBytesAuditableChannelHandler {
/// Returns the number of bytes buffered in the channel handler, which are queued to be sent to
/// the next outbound channel handler.
func auditOutboundBufferedBytes() -> Int
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor nit: these should probably be computed properties, not functions.

Copy link
Member

Choose a reason for hiding this comment

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

Only if we want to add an O(1) requirement. I don't we think we want though because some ChannelHandlers have n child ChannelHandlers which means they wouldn't be able to conform, strictly speaking.

It would also be unnecessarily different from the other overloads that take an argument.


/// A `OutboundBufferedBytesAuditableChannelHandler` is a `ChannelHandler` that
/// audits and reports the number of bytes buffered for outbound direction.
public protocol OutboundBufferedBytesAuditableChannelHandler {
Copy link
Contributor

Choose a reason for hiding this comment

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

Per our API evolution rules, these will need to be prefixed with NIO:

Suggested change
public protocol OutboundBufferedBytesAuditableChannelHandler {
public protocol NIOOutboundBufferedBytesAuditableChannelHandler {


/// A `InboundBufferedBytesAuditableChannelHandler` is a `ChannelHandler` that
/// audits and reports the number of bytes buffered for inbound direction.
public protocol InboundBufferedBytesAuditableChannelHandler {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
public protocol InboundBufferedBytesAuditableChannelHandler {
public protocol NIOInboundBufferedBytesAuditableChannelHandler {

@@ -1492,6 +1492,8 @@ public enum ChannelPipelineError: Error {
case alreadyRemoved
/// `ChannelHandler` was not found.
case notFound
/// `ChannelHandler` is not auditable.
case notAuditable
Copy link
Contributor

Choose a reason for hiding this comment

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

We're not going to be able to add a new error case here: we'll either need a new error, or we'll need to find a way to use one we already have.

@@ -2089,3 +2091,171 @@ extension ChannelPipeline: CustomDebugStringConvertible {
return handlers
}
}

extension ChannelPipeline {
Copy link
Contributor

Choose a reason for hiding this comment

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

These APIs will definitely want syncOperations versions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added in f5f85b5

///
/// - parameters:
/// - name: the name of the channel handler whose outbound buffered bytes will be audited.
public func auditOutboundBufferedBytes(name: String) -> EventLoopFuture<Int> {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure this API is pulling its weight: users can always obtain channel handlers by name and call the relevant functions directly.

///
/// - parameters:
/// - handler: the channel handler object whose outbound buffered bytes will be audited.
public func auditOutboundBufferedBytes(handler: ChannelHandler) -> EventLoopFuture<Int> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here: this API is just a function call on the handler directly, I don't think any extra API surface is needed for this.

}
}

private func audit0(context: ChannelHandlerContext, direction: AuditDirection) -> Result<Int, Error> {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure this needs an error result for not being able to do the type conversion, it might just want to return Int?

Copy link
Member

Choose a reason for hiding this comment

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

It could just throw if it really wants to propagate an error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Given that it is not a good design choice to add an error in ChannelPipelineError and error conveys the same outcome as Int?, I agree it is better to return Int? directly.

Copy link
Member

@FranzBusch FranzBusch left a comment

Choose a reason for hiding this comment

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

In general I am happy to see an API added for this. I am a bit unsure if the word audit is the right one here. Could we just drop that from all the APIs here? What do others think?

@johnnzhou
Copy link
Contributor Author

I am a bit unsure if the word audit is the right one here. Could we just drop that from all the APIs here? What do others think?

Hi @FranzBusch, thanks for this suggestion. I originally designed this functionality as a way for users to inspect the number of bytes buffered in the channel handlers. Perhaps audit is more suited for financial contexts.

How about we use the word count instead? For instance, we could rename the protocol to NIOOutboundBufferedBytesCountableChannelHandler and the public method to countOutboundBufferedBytes. This change would preserve the idea of inspection while emphasizing the action of counting, making it more intuitive.

What do you think?

@Joannis
Copy link

Joannis commented Oct 16, 2024

Would this PR also (intend to) support child channels, such as in HTTP/2.

@FranzBusch
Copy link
Member

How about we use the word count instead? For instance, we could rename the protocol to NIOOutboundBufferedBytesCountableChannelHandler and the public method to countOutboundBufferedBytes. This change would preserve the idea of inspection while emphasizing the action of counting, making it more intuitive.

Do we need count even here? What about NIOOutboundByteBufferingChannelHandler? The current name of the property sounds good to me.

Would this PR also (intend to) support child channels, such as in HTTP/2.

Depends what you expect. Every ChannelHandler that does multiplexing such as the H2Handler needs to decide how to treat. When a handler in a stream channel asks about the outbound buffered bytes it could return both the buffer from just the stream or the whole connection. We need to decide and implement that on a case-by-case basis.

@johnnzhou
Copy link
Contributor Author

Do we need count even here? What about NIOOutboundByteBufferingChannelHandler? The current name of the property sounds good to me.

I'm generally ok with NIOOutboundByteBufferingChannelHandler. However, my main concern is that this name might be a bit misleading. Two new protocols are not intended to help provide buffering mechanism or manage bytes storage in the channel handlers. Instead, their main function is to collect the number of bytes buffered in the channel handler and allow users to make informed decision on their network load.

Would this PR also (intend to) support child channels, such as in HTTP/2.

Depends what you expect. Every ChannelHandler that does multiplexing such as the H2Handler needs to decide how to treat. When a handler in a stream channel asks about the outbound buffered bytes it could return both the buffer from just the stream or the whole connection. We need to decide and implement that on a case-by-case basis.

My original plan is to implement this protocol for channel handlers provided by NIO. But maybe we need to make separate PRs for each handler?

@FranzBusch
Copy link
Member

I'm generally ok with NIOOutboundByteBufferingChannelHandler. However, my main concern is that this name might be a bit misleading. Two new protocols are not intended to help provide buffering mechanism or manage bytes storage in the channel handlers. Instead, their main function is to collect the number of bytes buffered in the channel handler and allow users to make informed decision on their network load.

I understand where you are coming from. I personally still prefer that name but I would like to hear what @Lukasa @glbrntt or @weissi think.

My original plan is to implement this protocol for channel handlers provided by NIO. But maybe we need to make separate PRs for each handler?

We should definitely separate the API additions in this PR from the adoption.

@Lukasa
Copy link
Contributor

Lukasa commented Oct 17, 2024

I'm happy for us to go with NIOOutboundByteBufferingChannelHandler

@johnnzhou
Copy link
Contributor Author

johnnzhou commented Oct 17, 2024

Sounds good. I will make the change accordingly. I will add more test cases in other commits.

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.

5 participants