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

[Broker] Use shared executors for broker and geo-replication clients #13839

Merged
merged 3 commits into from
Jan 20, 2022

Conversation

lhotari
Copy link
Member

@lhotari lhotari commented Jan 19, 2022

Motivation

  • currently each broker client and geo-replication client will create a single-threaded internal/IO executor and an external/listener executer for each PulsarClientImpl instance. In addition there will be a separate Timer thread.
  • Instead of adding individual thread pools, it would be more efficient to share executors for all broker and geo-replication clients.

Modifications

  • create shared executors and timer at broker startup
  • reuse executors and timer for the broker client and the geo-replication clients.

doc = "Number of shared threads to use for broker clients."
+ " Default is set to `2 * Runtime.getRuntime().availableProcessors()`"
)
private int brokerClientNumIOThreads = 2 * Runtime.getRuntime().availableProcessors();
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd say that we should prob reuse the broker IO threads here

Copy link
Member Author

Choose a reason for hiding this comment

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

The problem is that broker IO threads is a EventLoopGroup instance which is already used in the Pulsar Client.

this.ioEventLoopGroup = EventLoopUtil.newEventLoopGroup(config.getNumIOThreads(), config.isEnableBusyWait(),
new DefaultThreadFactory("pulsar-io"));

The Pulsar Client has this internal/IO and external/listener executors in addition to the eventLoopGroup.

this.externalExecutorProvider = externalExecutorProvider != null ? externalExecutorProvider :
new ExecutorProvider(conf.getNumListenerThreads(), "pulsar-external-listener");
this.internalExecutorProvider = internalExecutorProvider != null ? internalExecutorProvider :
new ExecutorProvider(conf.getNumIoThreads(), "pulsar-client-internal");

It might be a risky change in Pulsar Client to replace the internal executor to delegate directly to eventLoopGroup.

This is the reason why I think it's necessary to have a separate setting to control the amount of threads used to run the Pulsar Client internal executors. Before this PR each client has had a single threaded internal executor for each client instance.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, I think the confusion comes from the name brokerClientNumIOThreads, since it's client internal executor and not the client event loop which is already shared.

Additionally, the client internal executor shouldn't really be used a lot in broker, since it's used mainly for dispatching the partitioned/multi topic consumers, so we could just default to 1 thread instead.

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, I think the confusion comes from the name brokerClientNumIOThreads, since it's client internal executor and not the client event loop which is already shared.

That's true. The Pulsar Client uses the numIOThreads parameter for configuring the number of threads for the eventLoopGroup and also for the internal/IO executor.

return EventLoopUtil.newEventLoopGroup(conf.getNumIoThreads(), conf.isEnableBusyWait(), threadFactory);

this.internalExecutorProvider = internalExecutorProvider != null ? internalExecutorProvider :
new ExecutorProvider(conf.getNumIoThreads(), "pulsar-client-internal");

This is confusing. The internal executor was introduced in #8208 and that change uses numIOThreads parameter also for configuring the number of threads for the internal executor. @merlimat I wonder what would be a way to improve this situation?

Copy link
Member Author

Choose a reason for hiding this comment

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

Additionally, the client internal executor shouldn't really be used a lot in broker, since it's used mainly for dispatching the partitioned/multi topic consumers, so we could just default to 1 thread instead.

yes, that is true. I wonder if the value needs to be configurable at all? I'll remove brokerClientNumIOThreads key and just use the value 1 as I did for the external executor.

Copy link
Member Author

Choose a reason for hiding this comment

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

I made the changes now. @merlimat PTAL

Copy link
Contributor

Choose a reason for hiding this comment

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

👍

@lhotari lhotari requested a review from merlimat January 20, 2022 04:46
@merlimat merlimat merged commit 4924e6d into apache:master Jan 20, 2022
Comment on lines +331 to +333
// the external executor is not used in the broker client or replication clients since this executor is
// used for consumer listeners.
// since an instance is required, a single threaded shared instance is used for all broker client instances
Copy link
Member

Choose a reason for hiding this comment

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

Doesn't this hint at a problem in the java client? It looks like a listener is declared on consumer initialization. We could change the behavior so that it only gets a thread when there is a listener.

We'll still have the issue of creating an ExecutorProvider per client, so this change still makes sense, but it'd be cheaper for end users to avoid the thread creation for consumers that lack listeners.

I am going to open a PR to update the Java Consumer.

Copy link
Member Author

Choose a reason for hiding this comment

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

Doesn't this hint at a problem in the java client? It looks like a listener is declared on consumer initialization. We could change the behavior so that it only gets a thread when there is a listener.

Yes the client isn't optimal, but I doubt that it causes an actual measurable overhead. I think that the thread is just pinned out of the single shared thread and nothing will get run on it (in the current usage patterns when client is used in the broker).

Copy link
Member

Choose a reason for hiding this comment

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

Yes the client isn't optimal, but I doubt that it causes an actual measurable overhead. I think that the thread is just pinned out of the single shared thread and nothing will get run on it (in the current usage patterns when client is used in the broker).

I was thinking about user applications that have many consumers. I just looked a bit closer, and the upper limit here is NumListenerThreads in the client configuration. That is the max number of threads a client will spawn for consumer listeners. Was this a problem in the broker because we have many clients?

@congbobo184 congbobo184 added release/2.9.4 cherry-picked/branch-2.9 Archived: 2.9 is end of life labels Nov 10, 2022
congbobo184 pushed a commit that referenced this pull request Nov 10, 2022
…13839)

* [Broker] Use shared executors for broker clients and geo-replication clients

* Remove brokerClientNumIOThreads configuration key and default to 1

* Revisit the shared timer creation

- don't ever make it a daemon thread

(cherry picked from commit 4924e6d)
congbobo184 pushed a commit that referenced this pull request Dec 1, 2022
…13839)

* [Broker] Use shared executors for broker clients and geo-replication clients

* Remove brokerClientNumIOThreads configuration key and default to 1

* Revisit the shared timer creation

- don't ever make it a daemon thread

(cherry picked from commit 4924e6d)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/broker cherry-picked/branch-2.9 Archived: 2.9 is end of life doc-not-needed Your PR changes do not impact docs release/2.9.4
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants