-
Notifications
You must be signed in to change notification settings - Fork 25.6k
Add thread pool for write coordination #129450
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
Changes from 5 commits
92c9cc7
31ac057
b7f5b12
ad5d0ed
828cbe5
3249513
6ca7bb9
cb9b06f
fc95a00
e7f336a
902f5c8
8697eae
4f79791
a81788c
1cf9619
9d8e9a0
a085bc1
fc0a515
f76760d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -532,7 +532,7 @@ public void testShortCircuitShardLevelFailureWithIngestNodeHop() throws Exceptio | |||||
| } | ||||||
|
|
||||||
| private static void blockWritePool(ThreadPool threadPool, CountDownLatch finishLatch) { | ||||||
| final var threadCount = threadPool.info(ThreadPool.Names.WRITE).getMax(); | ||||||
| final var threadCount = threadPool.info(ThreadPool.Names.WRITE_COORDINATION).getMax(); | ||||||
| final var startBarrier = new CyclicBarrier(threadCount + 1); | ||||||
| final var blockingTask = new AbstractRunnable() { | ||||||
| @Override | ||||||
|
|
@@ -552,13 +552,13 @@ public boolean isForceExecution() { | |||||
| } | ||||||
| }; | ||||||
| for (int i = 0; i < threadCount; i++) { | ||||||
| threadPool.executor(ThreadPool.Names.WRITE).execute(blockingTask); | ||||||
| threadPool.executor(ThreadPool.Names.WRITE_COORDINATION).execute(blockingTask); | ||||||
| } | ||||||
| safeAwait(startBarrier); | ||||||
| } | ||||||
|
|
||||||
| private static void fillWriteQueue(ThreadPool threadPool) { | ||||||
|
||||||
| private static void fillWriteQueue(ThreadPool threadPool) { | |
| private static void fillWriteCoordinationQueue(ThreadPool threadPool) { |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -68,6 +68,7 @@ public abstract class TransportAbstractBulkAction extends HandledTransportAction | |
| private final IngestService ingestService; | ||
| private final IngestActionForwarder ingestForwarder; | ||
| protected final LongSupplier relativeTimeNanosProvider; | ||
| protected final Executor coordinationExecutor; | ||
| protected final Executor writeExecutor; | ||
| protected final Executor systemWriteExecutor; | ||
| private final ActionType<BulkResponse> bulkAction; | ||
|
|
@@ -92,6 +93,7 @@ public TransportAbstractBulkAction( | |
| this.indexingPressure = indexingPressure; | ||
| this.systemIndices = systemIndices; | ||
| this.projectResolver = projectResolver; | ||
| this.coordinationExecutor = threadPool.executor(ThreadPool.Names.WRITE_COORDINATION); | ||
| this.writeExecutor = threadPool.executor(ThreadPool.Names.WRITE); | ||
| this.systemWriteExecutor = threadPool.executor(ThreadPool.Names.SYSTEM_WRITE); | ||
| this.ingestForwarder = new IngestActionForwarder(transportService); | ||
|
|
@@ -106,8 +108,8 @@ protected void doExecute(Task task, BulkRequest bulkRequest, ActionListener<Bulk | |
| * This is called on the Transport thread so we can check the indexing | ||
| * memory pressure *quickly* but we don't want to keep the transport | ||
| * thread busy. Then, as soon as we have the indexing pressure in we fork | ||
| * to one of the write thread pools. We do this because juggling the | ||
| * bulk request can get expensive for a few reasons: | ||
| * to the coordinator thread pool for coordination tasks. We do this because | ||
| * juggling the bulk request can get expensive for a few reasons: | ||
| * 1. Figuring out which shard should receive a bulk request might require | ||
| * parsing the _source. | ||
| * 2. When dispatching the sub-requests to shards we may have to compress | ||
|
|
@@ -131,14 +133,15 @@ protected void doExecute(Task task, BulkRequest bulkRequest, ActionListener<Bulk | |
| releasable = indexingPressure.markCoordinatingOperationStarted(indexingOps, indexingBytes, isOnlySystem); | ||
| } | ||
| final ActionListener<BulkResponse> releasingListener = ActionListener.runBefore(listener, releasable::close); | ||
| final Executor executor = isOnlySystem ? systemWriteExecutor : writeExecutor; | ||
| ensureClusterStateThenForkAndExecute(task, bulkRequest, executor, releasingListener); | ||
| // Use coordinationExecutor for dispatching coordination tasks | ||
| ensureClusterStateThenForkAndExecute(task, bulkRequest, coordinationExecutor, isOnlySystem, releasingListener); | ||
| } | ||
|
|
||
| private void ensureClusterStateThenForkAndExecute( | ||
| Task task, | ||
| BulkRequest bulkRequest, | ||
| Executor executor, | ||
| boolean isOnlySystem, | ||
| ActionListener<BulkResponse> releasingListener | ||
| ) { | ||
| final ClusterState initialState = clusterService.state(); | ||
|
|
@@ -159,7 +162,7 @@ private void ensureClusterStateThenForkAndExecute( | |
| clusterStateObserver.waitForNextChange(new ClusterStateObserver.Listener() { | ||
| @Override | ||
| public void onNewClusterState(ClusterState state) { | ||
| forkAndExecute(task, bulkRequest, executor, releasingListener); | ||
| forkAndExecute(task, bulkRequest, executor, isOnlySystem, releasingListener); | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -173,21 +176,32 @@ public void onTimeout(TimeValue timeout) { | |
| } | ||
| }, newState -> false == newState.blocks().hasGlobalBlockWithLevel(ClusterBlockLevel.WRITE)); | ||
| } else { | ||
| forkAndExecute(task, bulkRequest, executor, releasingListener); | ||
| forkAndExecute(task, bulkRequest, executor, isOnlySystem, releasingListener); | ||
| } | ||
| } | ||
|
|
||
| private void forkAndExecute(Task task, BulkRequest bulkRequest, Executor executor, ActionListener<BulkResponse> releasingListener) { | ||
| private void forkAndExecute( | ||
| Task task, | ||
| BulkRequest bulkRequest, | ||
| Executor executor, | ||
| boolean isOnlySystem, | ||
| ActionListener<BulkResponse> releasingListener | ||
| ) { | ||
| executor.execute(new ActionRunnable<>(releasingListener) { | ||
| @Override | ||
| protected void doRun() throws IOException { | ||
| applyPipelinesAndDoInternalExecute(task, bulkRequest, executor, releasingListener); | ||
| applyPipelinesAndDoInternalExecute(task, bulkRequest, executor, isOnlySystem, releasingListener); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| private boolean applyPipelines(Task task, BulkRequest bulkRequest, Executor executor, ActionListener<BulkResponse> listener) | ||
| throws IOException { | ||
| private boolean applyPipelines( | ||
| Task task, | ||
| BulkRequest bulkRequest, | ||
| Executor executor, | ||
| boolean isOnlySystem, | ||
| ActionListener<BulkResponse> listener | ||
| ) throws IOException { | ||
| boolean hasIndexRequestsWithPipelines = false; | ||
| ClusterState state = clusterService.state(); | ||
| ProjectId projectId = projectResolver.getProjectId(); | ||
|
|
@@ -276,7 +290,7 @@ private boolean applyPipelines(Task task, BulkRequest bulkRequest, Executor exec | |
| assert arePipelinesResolved : bulkRequest; | ||
| } | ||
| if (clusterService.localNode().isIngestNode()) { | ||
| processBulkIndexIngestRequest(task, bulkRequest, executor, project, l); | ||
| processBulkIndexIngestRequest(task, bulkRequest, executor, isOnlySystem, project, l); | ||
| } else { | ||
| ingestForwarder.forwardIngestRequest(bulkAction, bulkRequest, l); | ||
| } | ||
|
|
@@ -290,6 +304,7 @@ private void processBulkIndexIngestRequest( | |
| Task task, | ||
| BulkRequest original, | ||
| Executor executor, | ||
| boolean isOnlySystem, | ||
| ProjectMetadata metadata, | ||
| ActionListener<BulkResponse> listener | ||
| ) { | ||
|
|
@@ -323,20 +338,21 @@ private void processBulkIndexIngestRequest( | |
| ActionRunnable<BulkResponse> runnable = new ActionRunnable<>(actionListener) { | ||
| @Override | ||
| protected void doRun() throws IOException { | ||
| applyPipelinesAndDoInternalExecute(task, bulkRequest, executor, actionListener); | ||
| applyPipelinesAndDoInternalExecute(task, bulkRequest, executor, isOnlySystem, actionListener); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean isForceExecution() { | ||
| // If we fork back to a write thread we **not** should fail, because tp queue is full. | ||
| // If we fork back to a coordination thread we **not** should fail, because tp queue is full. | ||
| // (Otherwise the work done during ingest will be lost) | ||
| // It is okay to force execution here. Throttling of write requests happens prior to | ||
| // ingest when a node receives a bulk request. | ||
| return true; | ||
| } | ||
| }; | ||
| // If a processor went async and returned a response on a different thread then | ||
| // before we continue the bulk request we should fork back on a write thread: | ||
| // before we continue the bulk request we should fork back on a coordination thread. Otherwise it is fine to perform | ||
| // coordination steps on the write thread | ||
| if (originalThread == Thread.currentThread()) { | ||
| runnable.run(); | ||
| } else { | ||
|
|
@@ -345,7 +361,8 @@ public boolean isForceExecution() { | |
| } | ||
| } | ||
| }, | ||
| executor | ||
| // Use the appropriate write executor for actual ingest processing | ||
| isOnlySystem ? systemWriteExecutor : writeExecutor | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am ok with this, but it seems like for any case where we have ingest processing, we would then still have the coordination happen behind any local write work. At least the PR then avoids some of the wait roundtrips. We could also decide to have both a system-write-coordination pool and a write-coordination pool and use those here? We can look at this in follow-ups ofc. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes I would like to move ingest work to a non-WRITE thread pool in a follow-up as I think there might be a few things to discuss. |
||
| ); | ||
| } | ||
|
|
||
|
|
@@ -401,10 +418,11 @@ private void applyPipelinesAndDoInternalExecute( | |
| Task task, | ||
| BulkRequest bulkRequest, | ||
| Executor executor, | ||
| boolean isOnlySystem, | ||
| ActionListener<BulkResponse> listener | ||
| ) throws IOException { | ||
| final long relativeStartTimeNanos = relativeTimeNanos(); | ||
| if (applyPipelines(task, bulkRequest, executor, listener) == false) { | ||
| if (applyPipelines(task, bulkRequest, executor, isOnlySystem, listener) == false) { | ||
| doInternalExecute(task, bulkRequest, executor, listener, relativeStartTimeNanos); | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename to: