|
@@ -14,7 +14,6 @@ import org.apache.logging.log4j.message.ParameterizedMessage;
|
|
|
import org.apache.lucene.util.CollectionUtil;
|
|
|
import org.elasticsearch.ElasticsearchException;
|
|
|
import org.elasticsearch.action.ActionListener;
|
|
|
-import org.elasticsearch.action.ActionRunnable;
|
|
|
import org.elasticsearch.action.StepListener;
|
|
|
import org.elasticsearch.action.admin.cluster.repositories.get.TransportGetRepositoriesAction;
|
|
|
import org.elasticsearch.action.support.ActionFilters;
|
|
@@ -34,6 +33,7 @@ import org.elasticsearch.repositories.IndexId;
|
|
|
import org.elasticsearch.repositories.RepositoriesService;
|
|
|
import org.elasticsearch.repositories.Repository;
|
|
|
import org.elasticsearch.repositories.RepositoryData;
|
|
|
+import org.elasticsearch.repositories.RepositoryMissingException;
|
|
|
import org.elasticsearch.snapshots.SnapshotException;
|
|
|
import org.elasticsearch.snapshots.SnapshotId;
|
|
|
import org.elasticsearch.snapshots.SnapshotInfo;
|
|
@@ -46,12 +46,15 @@ import org.elasticsearch.threadpool.ThreadPool;
|
|
|
import org.elasticsearch.transport.TransportService;
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
+import java.util.Collection;
|
|
|
import java.util.Collections;
|
|
|
import java.util.HashMap;
|
|
|
import java.util.HashSet;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
import java.util.Set;
|
|
|
+import java.util.concurrent.BlockingQueue;
|
|
|
+import java.util.concurrent.LinkedBlockingQueue;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
import static java.util.Collections.unmodifiableList;
|
|
@@ -211,8 +214,7 @@ public class TransportGetSnapshotsAction extends TransportMasterNodeAction<GetSn
|
|
|
}
|
|
|
|
|
|
if (verbose) {
|
|
|
- threadPool.generic().execute(ActionRunnable.supply(
|
|
|
- listener, () -> snapshots(snapshotsInProgress, repo, new ArrayList<>(toResolve), ignoreUnavailable, task)));
|
|
|
+ snapshots(snapshotsInProgress, repo, toResolve, ignoreUnavailable, task, listener);
|
|
|
} else {
|
|
|
final List<SnapshotInfo> snapshotInfos;
|
|
|
if (repositoryData != null) {
|
|
@@ -235,12 +237,16 @@ public class TransportGetSnapshotsAction extends TransportMasterNodeAction<GetSn
|
|
|
* @param snapshotIds snapshots for which to fetch snapshot information
|
|
|
* @param ignoreUnavailable if true, snapshots that could not be read will only be logged with a warning,
|
|
|
* if false, they will throw an error
|
|
|
- * @return list of snapshots
|
|
|
*/
|
|
|
- private List<SnapshotInfo> snapshots(SnapshotsInProgress snapshotsInProgress, String repositoryName,
|
|
|
- List<SnapshotId> snapshotIds, boolean ignoreUnavailable, CancellableTask task) {
|
|
|
+ private void snapshots(SnapshotsInProgress snapshotsInProgress,
|
|
|
+ String repositoryName,
|
|
|
+ Collection<SnapshotId> snapshotIds,
|
|
|
+ boolean ignoreUnavailable,
|
|
|
+ CancellableTask task,
|
|
|
+ ActionListener<List<SnapshotInfo>> listener) {
|
|
|
if (task.isCancelled()) {
|
|
|
- throw new TaskCancelledException("task cancelled");
|
|
|
+ listener.onFailure(new TaskCancelledException("task cancelled"));
|
|
|
+ return;
|
|
|
}
|
|
|
final Set<SnapshotInfo> snapshotSet = new HashSet<>();
|
|
|
final Set<SnapshotId> snapshotIdsToIterate = new HashSet<>(snapshotIds);
|
|
@@ -252,28 +258,88 @@ public class TransportGetSnapshotsAction extends TransportMasterNodeAction<GetSn
|
|
|
snapshotSet.add(new SnapshotInfo(entry));
|
|
|
}
|
|
|
}
|
|
|
- // then, look in the repository
|
|
|
- final Repository repository = repositoriesService.repository(repositoryName);
|
|
|
- for (SnapshotId snapshotId : snapshotIdsToIterate) {
|
|
|
+ // then, look in the repository if there's any matching snapshots left
|
|
|
+ final List<SnapshotInfo> snapshotInfos;
|
|
|
+ if (snapshotIdsToIterate.isEmpty()) {
|
|
|
+ snapshotInfos = Collections.emptyList();
|
|
|
+ } else {
|
|
|
+ snapshotInfos = Collections.synchronizedList(new ArrayList<>());
|
|
|
+ }
|
|
|
+ final ActionListener<Collection<Void>> allDoneListener = listener.delegateFailure((l, v) -> {
|
|
|
+ final ArrayList<SnapshotInfo> snapshotList = new ArrayList<>(snapshotInfos);
|
|
|
+ snapshotList.addAll(snapshotSet);
|
|
|
+ CollectionUtil.timSort(snapshotList);
|
|
|
+ listener.onResponse(unmodifiableList(snapshotList));
|
|
|
+ });
|
|
|
+ if (snapshotIdsToIterate.isEmpty()) {
|
|
|
+ allDoneListener.onResponse(Collections.emptyList());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 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 BlockingQueue<SnapshotId> queue = new LinkedBlockingQueue<>(snapshotIdsToIterate);
|
|
|
+ final ActionListener<Void> workerDoneListener = new GroupedActionListener<>(allDoneListener, workers).delegateResponse((l, e) -> {
|
|
|
+ queue.clear(); // Stop fetching the remaining snapshots once we've failed fetching one since the response is an error response
|
|
|
+ // anyway in this case
|
|
|
+ l.onFailure(e);
|
|
|
+ });
|
|
|
+ final Repository repository;
|
|
|
+ try {
|
|
|
+ repository = repositoriesService.repository(repositoryName);
|
|
|
+ } catch (RepositoryMissingException e) {
|
|
|
+ listener.onFailure(e);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ for (int i = 0; i < workers; i++) {
|
|
|
+ getOneSnapshotInfo(
|
|
|
+ ignoreUnavailable,
|
|
|
+ repository,
|
|
|
+ queue,
|
|
|
+ snapshotInfos,
|
|
|
+ task,
|
|
|
+ workerDoneListener
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Tries to poll a {@link SnapshotId} to load {@link SnapshotInfo} for from the given {@code queue}. If it finds one in the queue,
|
|
|
+ * loads the snapshot info from the repository and adds it to the given {@code snapshotInfos} collection, then invokes itself again to
|
|
|
+ * try and poll another task from the queue.
|
|
|
+ * If the queue is empty resolves {@code} listener.
|
|
|
+ */
|
|
|
+ private void getOneSnapshotInfo(boolean ignoreUnavailable,
|
|
|
+ Repository repository,
|
|
|
+ BlockingQueue<SnapshotId> queue,
|
|
|
+ Collection<SnapshotInfo> snapshotInfos,
|
|
|
+ CancellableTask task,
|
|
|
+ ActionListener<Void> listener) {
|
|
|
+ final SnapshotId snapshotId = queue.poll();
|
|
|
+ if (snapshotId == null) {
|
|
|
+ listener.onResponse(null);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ threadPool.executor(ThreadPool.Names.SNAPSHOT_META).execute(() -> {
|
|
|
if (task.isCancelled()) {
|
|
|
- throw new TaskCancelledException("task cancelled");
|
|
|
+ listener.onFailure(new TaskCancelledException("task cancelled"));
|
|
|
+ return;
|
|
|
}
|
|
|
try {
|
|
|
- snapshotSet.add(repository.getSnapshotInfo(snapshotId));
|
|
|
+ snapshotInfos.add(repository.getSnapshotInfo(snapshotId));
|
|
|
} catch (Exception ex) {
|
|
|
if (ignoreUnavailable) {
|
|
|
logger.warn(() -> new ParameterizedMessage("failed to get snapshot [{}]", snapshotId), ex);
|
|
|
} else {
|
|
|
- if (ex instanceof SnapshotException) {
|
|
|
- throw ex;
|
|
|
- }
|
|
|
- throw new SnapshotException(repositoryName, snapshotId, "Snapshot could not be read", ex);
|
|
|
+ listener.onFailure(
|
|
|
+ ex instanceof SnapshotException
|
|
|
+ ? ex
|
|
|
+ : new SnapshotException(repository.getMetadata().name(), snapshotId, "Snapshot could not be read", ex)
|
|
|
+ );
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
- final ArrayList<SnapshotInfo> snapshotList = new ArrayList<>(snapshotSet);
|
|
|
- CollectionUtil.timSort(snapshotList);
|
|
|
- return unmodifiableList(snapshotList);
|
|
|
+ getOneSnapshotInfo(ignoreUnavailable, repository, queue, snapshotInfos, task, listener);
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
private boolean isAllSnapshots(String[] snapshots) {
|