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 SNAPSHOT_META Threadpool for Fetching Repository Metadata #73172

Conversation

original-brownbear
Copy link
Member

Adds new snapshot meta pool that is used to speed up the get snapshots API
by making SnapshotInfo load in parallel. Also use this pool to load
RepositoryData.
A follow-up to this would expand the use of this pool to the snapshot status
API and make it run in parallel as well.

Adds new snapshot meta pool that is used to speed up the get snapshots API
by making `SnapshotInfo` load in parallel. Also use this pool to load
`RepositoryData`.
A follow-up to this would expand the use of this pool to the snapshot status
API and make it run in parallel as well.
@elasticmachine elasticmachine added the Team:Distributed (Obsolete) Meta label for distributed team (obsolete). Replaced by Distributed Indexing/Coordination. label May 17, 2021
@elasticmachine
Copy link
Collaborator

Pinging @elastic/es-distributed (Team:Distributed)

@@ -189,6 +191,8 @@ public ThreadPool(final Settings settings, final ExecutorBuilder<?>... customBui
builders.put(Names.REFRESH, new ScalingExecutorBuilder(Names.REFRESH, 1, halfProcMaxAt10, TimeValue.timeValueMinutes(5)));
builders.put(Names.WARMER, new ScalingExecutorBuilder(Names.WARMER, 1, halfProcMaxAt5, TimeValue.timeValueMinutes(5)));
builders.put(Names.SNAPSHOT, new ScalingExecutorBuilder(Names.SNAPSHOT, 1, halfProcMaxAt5, TimeValue.timeValueMinutes(5)));
builders.put(Names.SNAPSHOT_META, new ScalingExecutorBuilder(Names.SNAPSHOT_META, 1, 2 * allocatedProcessors,
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 only used 5 min here because we're using it everywhere else for now. It seems like this should really be less.
Twice the allocated processors seemed like a reasonable guess here since this is exclusively going to run on master nodes pretty much that don't have endless cores available. This might be a little low though in some cases. We could also go for an absolute value here I guess but in the end these threads will also be doing some heavy deserialisation work here and there so going way beyond the CPU count is questionable. OTOH depending on the repo implementation an upper bound may make sense too due to e.g. S3 SDK connection limits.

=> Happy to hear opinions here or discuss this :)

Copy link
Member

Choose a reason for hiding this comment

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

I have the feeling that having a fixed default upper bound would make it easier to reason about the snapshots related thread pools. Something like halfProcMaxAt5 maybe, so that we don't exceed the number of CPUs ad we don't either exceed by too much the default connection pool size.

// put snapshot info downloads into a task queue instead of pushing them all into the queue to not completely monopolize the
// snapshot meta pool for a single request
final int workers = Math.min(threadPool.info(ThreadPool.Names.SNAPSHOT_META).getMax(), snapshotIdsToIterate.size());
final Executor executor = threadPool.executor(ThreadPool.Names.SNAPSHOT_META);
Copy link
Member Author

Choose a reason for hiding this comment

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

Used the same logic here that we use for file uploads and I believe also pre-warming where we do this kind of fake work-stealing-pool. This could be dried up in a follow-up since we have that same logic in a few places now.

Copy link
Member

@tlrx tlrx left a comment

Choose a reason for hiding this comment

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

LGTM, I only left minor comments.

}

private void getOneSnapshotInfo(boolean ignoreUnavailable,
Repository repository,
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we should retrieve the Repository from the the RepositoriesService for each SnapshotInfo to load, so that if the repository is gone the RepositoryMissing is easier to propagate through listeners (and grouped listener which clears the queue etc). Otherwise a RepositoryMissing might be thrown I think and will be caught at a higher level but we keep fetching snapshot info 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.

We could I guess but it's not going to be a big cleanup/win since this situation is somewhat broken to begin with.
The behavior of the repository after close isn't well defined currently. Depending on the repo implementation the requests can either start failing or in case of FsRepository will just keep going actually because close is a noop there.
Might be worth just fixing that in general at some point?

Copy link
Member

Choose a reason for hiding this comment

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

I agree but I still think it is worth not mixing thrown exceptions and listeners 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.

oh 🤦 now I get your comment. Sorry, I completely misread it for no good reason :( => Fix coming right up.

Copy link
Member Author

@original-brownbear original-brownbear May 18, 2021

Choose a reason for hiding this comment

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

I pushed fb55daa to address this (and random formatting noise) now :) I went with this instead of looking up the repo in the loop, because the latter would be caught and suppressed by ignoreUnvailable which I found weird (albeit practically irrelevant).

Copy link
Member

Choose a reason for hiding this comment

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

fb55daa looks good, thanks! And sorry if I wasn't clear at first :)

}
}

private void getOneSnapshotInfo(boolean ignoreUnavailable,
Copy link
Member

Choose a reason for hiding this comment

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

nit: can we add a bit of Javadoc?

@@ -189,6 +191,8 @@ public ThreadPool(final Settings settings, final ExecutorBuilder<?>... customBui
builders.put(Names.REFRESH, new ScalingExecutorBuilder(Names.REFRESH, 1, halfProcMaxAt10, TimeValue.timeValueMinutes(5)));
builders.put(Names.WARMER, new ScalingExecutorBuilder(Names.WARMER, 1, halfProcMaxAt5, TimeValue.timeValueMinutes(5)));
builders.put(Names.SNAPSHOT, new ScalingExecutorBuilder(Names.SNAPSHOT, 1, halfProcMaxAt5, TimeValue.timeValueMinutes(5)));
builders.put(Names.SNAPSHOT_META, new ScalingExecutorBuilder(Names.SNAPSHOT_META, 1, 2 * allocatedProcessors,
Copy link
Member

Choose a reason for hiding this comment

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

I have the feeling that having a fixed default upper bound would make it easier to reason about the snapshots related thread pools. Something like halfProcMaxAt5 maybe, so that we don't exceed the number of CPUs ad we don't either exceed by too much the default connection pool size.

@@ -96,6 +96,7 @@ private int expectedSize(final String threadPoolName, final int numberOfProcesso
sizes.put(ThreadPool.Names.REFRESH, ThreadPool::halfAllocatedProcessorsMaxTen);
sizes.put(ThreadPool.Names.WARMER, ThreadPool::halfAllocatedProcessorsMaxFive);
sizes.put(ThreadPool.Names.SNAPSHOT, ThreadPool::halfAllocatedProcessorsMaxFive);
sizes.put(ThreadPool.Names.SNAPSHOT_META, ThreadPool::twiceAllocatedProcessors);
Copy link
Member

Choose a reason for hiding this comment

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

Can we document this thread pool?

Copy link
Contributor

@DaveCTurner DaveCTurner left a comment

Choose a reason for hiding this comment

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

One minor question inline.

Should we also move TransportSnapshotsStatusAction and/or TransportMountSearchableSnapshotAction and/or restore operations from GENERIC to SNAPSHOT_META too?

BlockingQueue<SnapshotId> queue,
Collection<SnapshotInfo> snapshotInfos,
CancellableTask task,
Executor executor,
Copy link
Contributor

Choose a reason for hiding this comment

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

This is always the same executor, just wondering why not get it from the threadPool each time rather than passing it in.

Copy link
Member Author

Choose a reason for hiding this comment

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

Right, not sure why I did it this way initially, fixed in 24cf149

@original-brownbear
Copy link
Member Author

original-brownbear commented May 18, 2021

Should we also move TransportSnapshotsStatusAction and/or TransportMountSearchableSnapshotAction and/or restore operations from GENERIC to SNAPSHOT_META too?

The snapshot status I was going to move to the pool as well and parallelize on it in a follow-up. I don't have a strong opinion either way when it comes to the mount action. Though maybe it would be nice to get it off the generic pool as well in case of massively concurrent calls actually. I'd also do that in a separate PR though as I just found an edge case bug in its threading anyway (PR incoming today, maybe just discuss it there?).

Update: see #73196 for the mentioned bug

Copy link
Contributor

@DaveCTurner DaveCTurner left a comment

Choose a reason for hiding this comment

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

LGTM

@original-brownbear
Copy link
Member Author

Thanks David and Tanguy!

@original-brownbear original-brownbear merged commit da24285 into elastic:master May 18, 2021
@original-brownbear original-brownbear deleted the snapshot-metadata-read-threadpool branch May 18, 2021 12:40
original-brownbear added a commit to original-brownbear/elasticsearch that referenced this pull request May 18, 2021
Small and obvious oversight from elastic#73172
original-brownbear added a commit that referenced this pull request May 18, 2021
Small and obvious oversight from #73172
original-brownbear added a commit that referenced this pull request Jun 29, 2021
)

Backport of the recently introduced snapshot pagination and scalability improvements listed below.
Merged as a single backport because the `7.x` and master snapshot status API logic had massively diverged between master and 7.x. With the work in the below PRs, the logic in master and 7.x once again has been aligned very closely again.

#72842
#73172
#73199
#73570 
#73952
#74236 
#74451 (this one is only partly applicable as it was mainly a change to master to align `master` and `7.x` branches)
@original-brownbear original-brownbear restored the snapshot-metadata-read-threadpool branch April 18, 2023 20:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
:Distributed Coordination/Snapshot/Restore Anything directly related to the `_snapshot/*` APIs >enhancement Team:Distributed (Obsolete) Meta label for distributed team (obsolete). Replaced by Distributed Indexing/Coordination. v7.14.0 v8.0.0-alpha1
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants