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

Introduce IoHandleEventLoopGroup / IoHandleEventLoop and its implemen… #13991

Merged
merged 23 commits into from Apr 29, 2024

Conversation

normanmaurer
Copy link
Member

…tations MultiThreadIoHandleEventLoopGroup / SingleThreadIoHandleEventLoop

Motivation:

At the moment each transport has its own EventLoopGroup and EventLoop implementation. Most of these are final because they are tightly coubled with the specific Channel implementations. This is unfortunate for multiple reasons, as for example:

  • It's hard to instrument EventLoop* implementations as these are not extensible at all or even if they are the same logic needs to be added to multiple EventLoop* implementations.
  • Registration / Deregistration is limited to Channels which remove flexibility as for example some Operation Systems allowing to also handle other IO (for example file IO) with something like an EventLoop (one example would be io_uring).
  • A lot of code-duplication between different EventLoop* implementations exists as most share kind of the same logic of running non IO tasks.

Modifications:

  • Add the concept of IoHandleEventLoopGroup / IoHandleEventLoop and the corrosponding IoHandle, IoHandler and IoHandlerFactory. This allows to only have a custom implement for IO handling per transport while share the same code for execution of tasks, scheduled task and ultimatly IO.
  • Add the implementations MultiThreadIoHandleEventLoopGroup and SingleThreadIoHandleEventLoop that can be used by all (or most) transport implementations by having these just provide their own IoHandler / IoHandlerFactory implementations.

Result:

More code sharing and easier of customizing EventLoops.

@normanmaurer
Copy link
Member Author

normanmaurer commented Apr 21, 2024

I am opening this a draft mainly because I wanted to show and proof that we can back port some of the changes that we did in main without breaking too much things. I think even thought this PR introduce some API breaking it shouldn’t affect almost any user. To proof this I did not change how we create the different EventLoopGroups in our testsuite and everything still works.

So while the old way of constructing Eventloops still work the new way would be a bit different:

Old API:

new EpollEventLoop()

New API:

new MultiThreadIoHandleEventLoopGroup(EpollHandler.newFactory();

The benefit of the new way is that it is quite easy to instrument / doctorate an EventLoop/EventLoopGroup now. Basically you can just extend MultiThreadIoHandleEventLoopGroup / SingleThreadIoHandleEventLoop and add all the customization once while be able to reuse it with any transport implementation.
This includes things like:

  • able to get a count of registered channels / handles
  • understand how long the io processing vs task processing took.
  • how many channels / handles were processed per loop run
  • customize / decorate promises

Beside this IoHandleEventLoop can support not only Channel but also other subtypes of IoHandle. This would for example allow us to add file io support with io_uring and still use the same eventloop as for sockets etc. I am sure there are more things we could do by adding this kind of flexibility.

With all this said I still think the change is too dangerous for a new 4.1.x release. With this in mind I wondered if we might want to aim for releasing 4.2.0 with the following change:

  • This PR
  • Change the minimum java version to 8
  • Maybe a few more low risk changes.

I would love to hear what other project members and the community think about this ?

@normanmaurer
Copy link
Member Author

@normanmaurer
Copy link
Member Author

If interested I can also change all the creation code of EventLoopGroups in the testsuite and examples. I just wanted to not do this for now to show how small the impact for most people should be

…tations MultiThreadIoHandleEventLoopGroup / SingleThreadIoHandleEventLoop

Motivation:

At the moment each transport has its own EventLoopGroup and EventLoop implementation. Most of these are final because they are tightly coubled with the specific Channel implementations.
This is unfortunate for multiple reasons, as for example:

- It's hard to instrument EventLoop* implementations as these are not extensible at all or even if they are the same logic needs to be added to multiple EventLoop* implementations.
- Registration / Deregistration is limited to Channels which remove flexibility as for example some Operation Systems allowing to also handle other IO (for example file IO) with
  something like an EventLoop (one example would be io_uring).
- A lot of code-duplication between different EventLoop* implementations exists as most share kind of the same logic of running non IO tasks.

Modifications:

- Add the concept of IoHandleEventLoopGroup / IoHandleEventLoop and the corrosponding IoHandle, IoHandler and IoHandlerFactory. This allows to only have a custom implement for IO handling per transport while share the same code for execution of tasks, scheduled task and ultimatly IO.
- Add the implementations MultiThreadIoHandleEventLoopGroup and SingleThreadIoHandleEventLoop that can be used by all (or most) transport implementations by having these just provide their own IoHandler / IoHandlerFactory implementations.

Result:

More code sharing and easier of customizing EventLoops.
@He-Pin
Copy link
Contributor

He-Pin commented Apr 22, 2024

Do you plan to align with the design in Netty 5?

for (EventExecutor e: this) {
((EpollEventLoop) e).setIoRatio(ioRatio);
}
// NOOP
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, need to document that this does nothing now

@normanmaurer
Copy link
Member Author

Will look into the build timeout later today

@normanmaurer
Copy link
Member Author

Do you plan to align with the design in Netty 5?

What design you talk about ? The ideas was to introduce the same concept with minimal real world breakage

@He-Pin
Copy link
Contributor

He-Pin commented Apr 22, 2024

Do you plan to align with the design in Netty 5?

What design you talk about ? The ideas was to introduce the same concept with minimal real world breakage

I mean split the ioHandler with the event loop, I will take some time to look into this.

@normanmaurer
Copy link
Member Author

Do you plan to align with the design in Netty 5?

What design you talk about ? The ideas was to introduce the same concept with minimal real world breakage

I mean split the ioHandler with the event loop, I will take some time to look into this.

That's exactly what this PR does ;)

@normanmaurer
Copy link
Member Author

Also I think if we would switch to java8 I could maybe do something more flexible to support different IoHandle in the future.

@violetagg
Copy link
Member

violetagg commented Apr 22, 2024

@normanmaurer I don't know whether we should discuss this here in this PR but

With all this said I still think the change is too dangerous for a new 4.1.x release. With this in mind I wondered if we might
want to aim for releasing 4.2.0 with the following change:

This PR
Change the minimum java version to 8
Maybe a few more low risk changes.

Have you considered moving out of incubation some of the projects? (io_uring, QUIC, HTTP/3)?

@normanmaurer
Copy link
Member Author

@normanmaurer I don't know whether we should discuss this here in this PR but

With all this said I still think the change is too dangerous for a new 4.1.x release. With this in mind I wondered if we might
want to aim for releasing 4.2.0 with the following change:

This PR
Change the minimum java version to 8
Maybe a few more low risk changes.

Have you considered moving out of incubation some of the projects? (io_uring, QUIC, HTTP/3)?

Yes but let's focus on this one first :)

@He-Pin
Copy link
Contributor

He-Pin commented Apr 22, 2024

I would like to +1 for moving to Java 8 on Netty 4.2.x, as Netty 4.1.x has a very long history, With Java 8's default method, lambda completableFuture, etc., we can achieve more than it is now.

We can submit a PR and wait to see if people complain about moving to Java 8 on Netty 4.2.x

pom.xml Show resolved Hide resolved
@normanmaurer
Copy link
Member Author

I would like to +1 for moving to Java 8 on Netty 4.2.x, as Netty 4.1.x has a very long history, With Java 8's default method, lambda completableFuture, etc., we can achieve more than it is now.

We can submit a PR and wait to see if people complain about moving to Java 8 on Netty 4.2.x

I think we should definitely try or even use 11. Let's discuss about this as a followup.

@normanmaurer
Copy link
Member Author

I will port things over for for sure

/**
* {@link EventLoop} which uses epoll under the covers. Only works on Linux!
*/
public class EpollHandler implements IoHandler {
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this class need to be public?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes it needs to be... that said we should make it final. All the IoHandler implementations need to be public as you use these to create the factory to inject in the MultiThreadIoEventLoopGroup.

For example:

var group = new MultiThreadIoEventLoopGroup(NioHandler.newFactory());

Another possibility would be to not make these public but provide a IoHandlerFactory implementation that is public.

Like this:

var group = new MultiThreadIoEventLoopGroup(new NioHandlerFactory());

This factory would then instance the handles.

@chrisvest @He-Pin @yawkat not sure what is better ?

Copy link
Contributor

Choose a reason for hiding this comment

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

current approach seems fine to me

Copy link
Contributor

Choose a reason for hiding this comment

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

I see. The factory seems nicer as it limits the escape of additional API but it's not a huge deal either way.

Copy link
Contributor

Choose a reason for hiding this comment

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

As we are using Java 8 now, so final class with factory would be better.

@normanmaurer
Copy link
Member Author

normanmaurer commented Apr 27, 2024

@He-Pin @violetagg @bryce-anderson @idelpivnitskiy @chrisvest @franz1981 @vietj @yawkat @trustin I just pushed more changes... I think from an API point of view I am now consider this done (and everything compiles / pass tests).

So what changed from the original PR design:

  • Rename *IoHandleEventLoop* to *IoEventLoop* to make things less verbose
  • renamed method registerForIo(...) to just register(...) and let it return an IoRegistration. This registration can then be used to modify which events some is interested in.
  • Channel does not extend IoHandle anymore.
  • Added IoOpt interface and added implements for all transports. These IoOpt implementations can be used with the IoRegistration to manipulate for which events an IoHandle is interested to get notified.
  • IoHandle has now a handle(....) method that is called for the events and so we could decouple the specific IoHandler implementations completely from the respective Channel implementations.
  • Added deprecations

The nice thing about the new API is really that it is trivial for advanced users to write their own IoHandle implementations and register it with our existing implementations without the need to touch either the *IoEventLoop*s or the IoHandler implementations for the various transports. This allows a lot of more advanced use cases and would for example allow users to write their own File support for io_uring once we use the new API there without the need for us to do it ourselves and a lot of other things.

The concept that is used now with IoHandler and IoHandle is kind of similar to what you get when using libev. So it's a lower-level API that can be used and is also used by ourselves.

I know the changes are non-trivial so thanks to everyone who takes the time for review and providing feedback :)

Copy link
Contributor

@chrisvest chrisvest left a comment

Choose a reason for hiding this comment

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

A few comments

pom.xml Outdated Show resolved Hide resolved
pom.xml Show resolved Hide resolved
transport/src/main/java/io/netty/channel/IoHandler.java Outdated Show resolved Hide resolved
@normanmaurer normanmaurer merged commit 596a1f0 into 4.2 Apr 29, 2024
17 checks passed
@normanmaurer normanmaurer deleted the eventloop_rework branch April 29, 2024 18:11
@normanmaurer
Copy link
Member Author

Thanks a lot for all the reviews.... This is now merged :)

@normanmaurer normanmaurer added this to the 4.2.0.Final milestone Apr 29, 2024
normanmaurer added a commit that referenced this pull request Apr 30, 2024
…ucting it

Motiviation:

#13991 introduced a new way of how EventLoopGroups should be constructed. We should update our code to use the new way in examples and tests

Modifications:

Rewrite all tests / example to use new way of constructing

Result:
Cleanup
normanmaurer added a commit that referenced this pull request Apr 30, 2024
…ucting it

Motiviation:

#13991 introduced a new way of how EventLoopGroups should be constructed. We should update our code to use the new way in examples and tests

Modifications:

Rewrite all tests / example to use new way of constructing

Result:
Cleanup
normanmaurer added a commit that referenced this pull request Apr 30, 2024
…ucting it

Motiviation:

#13991 introduced a new way of how EventLoopGroups should be constructed. We should update our code to use the new way in examples and tests

Modifications:

Rewrite all tests / example to use new way of constructing

Result:
Cleanup
normanmaurer added a commit that referenced this pull request Apr 30, 2024
…ucting it

Motiviation:

#13991 introduced a new way of how EventLoopGroups should be constructed. We should update our code to use the new way in examples and tests

Modifications:

Rewrite all tests / example to use new way of constructing

Result:
Cleanup
normanmaurer added a commit that referenced this pull request Apr 30, 2024
…ucting it

Motiviation:

#13991 introduced a new way of how EventLoopGroups should be constructed. We should update our code to use the new way in examples and tests

Modifications:

Rewrite all tests / example to use new way of constructing

Result:
Cleanup
normanmaurer added a commit that referenced this pull request May 2, 2024
…ructing it (#14020)

Motiviation:

#13991 introduced a new way of how
EventLoopGroups should be constructed. We should update our code to use
the new way in examples and tests

Modifications:

Rewrite all tests / example to use new way of constructing

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

Successfully merging this pull request may close these issues.

None yet

6 participants