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

Multi node pipeline executor #4070

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

dolavb
Copy link

@dolavb dolavb commented Jan 22, 2025

This allow clients to provide an ExecutorService at pipeline creation instead of having a new ExecutorService created at every pipeline Sync call. An Executor service creation will create new threads, which are expensive resource to creates. In a high throughput application developed internally, we are writing at a rate of ~100k set per seconds, on a six node cluster, on an instance equivalent to an EC2 m5.12xlarge. The creation of threads uses 40% of CPU, and adds substantial latency. This new approach will allow clients to send a pooled Executor that is tuned to there load patterns.

This change is non breaking, but will come with a slight optimization for the clients currently using the created thread pool. In the current approach even if a pipeline has a single connection to close the Executor service will create MULTI_NODE_PIPELINE_SYNC_WORKERS threads. In the default mode would mean wasting 2 thread creation.


From: https://stackoverflow.com/questions/5483047/why-is-creating-a-thread-said-to-be-expensive

Thread lifecycle overhead. Thread creation and teardown are not free. The actual overhead varies across platforms, but thread creation takes time, introducing latency into request processing, and requires some processing activity by the JVM and OS. If requests are frequent and lightweight, as in most server applications, creating a new thread for each request can consume significant computing resources.

From Java Concurrency in Practice
By Brian Goetz, Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes, Doug Lea
Print ISBN-10: 0-321-34960-1

Java thread creation is expensive because there is a fair bit of work involved:

  • A large block of memory has to be allocated and initialized for the thread stack.
  • System calls need to be made to create / register the native thread with the host OS.
  • Descriptors need to be created, initialized and added to JVM-internal data structures.

dolavb and others added 2 commits January 17, 2025 13:50
This allows passing an ExecutorService when creating a ClusterPipeline. The previous
parallelization approach for pipeline syncing/closing would create a new executor
service for each sync operation, resulting in excessive thread creation and
termination. On an EC2 m5.12xlarge instance with ~100k single writes/sec, this
thread creation consumed 40% CPU and increased operation latency.

The change also optimizes thread usage when no ExecutorService is provided.
Previously, even a single pipeline within a multipipeline would create 3 threads
for syncing. This improvement removes that overhead, though callers are encouraged
to provide their own ExecutorService for optimal CPU usage and latency.
@sazzad16
Copy link
Collaborator

@dolavb Thank you for your effort to improve Jedis.
Your concern is justified. But we have our hands full ATM. We'll try to get to this PR ASAP.

@dolavb dolavb marked this pull request as ready for review January 24, 2025 01:51
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.

2 participants