Skip to content

Commit aad3312

Browse files
Async Snapshot Repository Deletes (#40144) (#41571)
Motivated by slow snapshot deletes reported in e.g. #39656 and the fact that these likely are a contributing factor to repositories accumulating stale files over time when deletes fail to finish in time and are interrupted before they can complete. * Makes snapshot deletion async and parallelizes some steps of the delete process that can be safely run concurrently via the snapshot thread poll * I did not take the biggest potential speedup step here and parallelize the shard file deletion because that's probably better handled by moving to bulk deletes where possible (and can still be parallelized via the snapshot pool where it isn't). Also, I wanted to keep the size of the PR manageable. * See #39656 (comment) * Also, as a side effect this gives the `SnapshotResiliencyTests` a little more coverage for master failover scenarios (since parallel access to a blob store repository during deletes is now possible since a delete isn't a single task anymore). * By adding a `ThreadPool` reference to the repository this also lays the groundwork to parallelizing shard snapshot uploads to improve the situation reported in #39657
1 parent 4127d68 commit aad3312

File tree

39 files changed

+248
-203
lines changed

39 files changed

+248
-203
lines changed

modules/repository-url/src/main/java/org/elasticsearch/plugin/repository/url/URLRepositoryPlugin.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.elasticsearch.plugins.RepositoryPlugin;
2727
import org.elasticsearch.repositories.Repository;
2828
import org.elasticsearch.repositories.url.URLRepository;
29+
import org.elasticsearch.threadpool.ThreadPool;
2930

3031
import java.util.Arrays;
3132
import java.util.Collections;
@@ -44,7 +45,9 @@ public List<Setting<?>> getSettings() {
4445
}
4546

4647
@Override
47-
public Map<String, Repository.Factory> getRepositories(Environment env, NamedXContentRegistry namedXContentRegistry) {
48-
return Collections.singletonMap(URLRepository.TYPE, metadata -> new URLRepository(metadata, env, namedXContentRegistry));
48+
public Map<String, Repository.Factory> getRepositories(Environment env, NamedXContentRegistry namedXContentRegistry,
49+
ThreadPool threadPool) {
50+
return Collections.singletonMap(URLRepository.TYPE,
51+
metadata -> new URLRepository(metadata, env, namedXContentRegistry, threadPool));
4952
}
5053
}

modules/repository-url/src/main/java/org/elasticsearch/repositories/url/URLRepository.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.elasticsearch.env.Environment;
3434
import org.elasticsearch.repositories.RepositoryException;
3535
import org.elasticsearch.repositories.blobstore.BlobStoreRepository;
36+
import org.elasticsearch.threadpool.ThreadPool;
3637

3738
import java.net.MalformedURLException;
3839
import java.net.URISyntaxException;
@@ -82,8 +83,8 @@ public class URLRepository extends BlobStoreRepository {
8283
* Constructs a read-only URL-based repository
8384
*/
8485
public URLRepository(RepositoryMetaData metadata, Environment environment,
85-
NamedXContentRegistry namedXContentRegistry) {
86-
super(metadata, environment.settings(), false, namedXContentRegistry);
86+
NamedXContentRegistry namedXContentRegistry, ThreadPool threadPool) {
87+
super(metadata, environment.settings(), false, namedXContentRegistry, threadPool);
8788

8889
if (URL_SETTING.exists(metadata.settings()) == false && REPOSITORIES_URL_SETTING.exists(environment.settings()) == false) {
8990
throw new RepositoryException(metadata.name(), "missing url");

modules/repository-url/src/test/java/org/elasticsearch/repositories/url/URLRepositoryTests.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.elasticsearch.env.TestEnvironment;
2727
import org.elasticsearch.repositories.RepositoryException;
2828
import org.elasticsearch.test.ESTestCase;
29+
import org.elasticsearch.threadpool.ThreadPool;
2930

3031
import java.io.IOException;
3132
import java.nio.file.Path;
@@ -34,12 +35,13 @@
3435
import static org.hamcrest.CoreMatchers.is;
3536
import static org.hamcrest.CoreMatchers.not;
3637
import static org.hamcrest.CoreMatchers.nullValue;
38+
import static org.mockito.Mockito.mock;
3739

3840
public class URLRepositoryTests extends ESTestCase {
3941

4042
private URLRepository createRepository(Settings baseSettings, RepositoryMetaData repositoryMetaData) {
4143
return new URLRepository(repositoryMetaData, TestEnvironment.newEnvironment(baseSettings),
42-
new NamedXContentRegistry(Collections.emptyList())) {
44+
new NamedXContentRegistry(Collections.emptyList()), mock(ThreadPool.class)) {
4345
@Override
4446
protected void assertSnapshotOrGenericThread() {
4547
// eliminate thread name check as we create repo manually on test/main threads

plugins/repository-azure/src/main/java/org/elasticsearch/repositories/azure/AzureRepository.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.elasticsearch.repositories.blobstore.BlobStoreRepository;
3939
import org.elasticsearch.snapshots.SnapshotCreationException;
4040
import org.elasticsearch.snapshots.SnapshotId;
41+
import org.elasticsearch.threadpool.ThreadPool;
4142

4243
import java.net.URISyntaxException;
4344
import java.util.List;
@@ -86,8 +87,8 @@ public static final class Repository {
8687
private final boolean readonly;
8788

8889
public AzureRepository(RepositoryMetaData metadata, Environment environment, NamedXContentRegistry namedXContentRegistry,
89-
AzureStorageService storageService) {
90-
super(metadata, environment.settings(), Repository.COMPRESS_SETTING.get(metadata.settings()), namedXContentRegistry);
90+
AzureStorageService storageService, ThreadPool threadPool) {
91+
super(metadata, environment.settings(), Repository.COMPRESS_SETTING.get(metadata.settings()), namedXContentRegistry, threadPool);
9192
this.chunkSize = Repository.CHUNK_SIZE_SETTING.get(metadata.settings());
9293
this.environment = environment;
9394
this.storageService = storageService;

plugins/repository-azure/src/main/java/org/elasticsearch/repositories/azure/AzureRepositoryPlugin.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import org.elasticsearch.plugins.ReloadablePlugin;
2929
import org.elasticsearch.plugins.RepositoryPlugin;
3030
import org.elasticsearch.repositories.Repository;
31+
import org.elasticsearch.threadpool.ThreadPool;
32+
3133
import java.util.Arrays;
3234
import java.util.Collections;
3335
import java.util.List;
@@ -47,9 +49,10 @@ public AzureRepositoryPlugin(Settings settings) {
4749
}
4850

4951
@Override
50-
public Map<String, Repository.Factory> getRepositories(Environment env, NamedXContentRegistry namedXContentRegistry) {
52+
public Map<String, Repository.Factory> getRepositories(Environment env, NamedXContentRegistry namedXContentRegistry,
53+
ThreadPool threadPool) {
5154
return Collections.singletonMap(AzureRepository.TYPE,
52-
(metadata) -> new AzureRepository(metadata, env, namedXContentRegistry, azureStoreService));
55+
(metadata) -> new AzureRepository(metadata, env, namedXContentRegistry, azureStoreService, threadPool));
5356
}
5457

5558
@Override

plugins/repository-azure/src/test/java/org/elasticsearch/repositories/azure/AzureRepositorySettingsTests.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.elasticsearch.env.Environment;
2929
import org.elasticsearch.env.TestEnvironment;
3030
import org.elasticsearch.test.ESTestCase;
31+
import org.elasticsearch.threadpool.ThreadPool;
3132

3233
import static org.hamcrest.Matchers.is;
3334
import static org.hamcrest.Matchers.nullValue;
@@ -42,7 +43,8 @@ private AzureRepository azureRepository(Settings settings) {
4243
.put(settings)
4344
.build();
4445
final AzureRepository azureRepository = new AzureRepository(new RepositoryMetaData("foo", "azure", internalSettings),
45-
TestEnvironment.newEnvironment(internalSettings), NamedXContentRegistry.EMPTY, mock(AzureStorageService.class));
46+
TestEnvironment.newEnvironment(internalSettings), NamedXContentRegistry.EMPTY, mock(AzureStorageService.class),
47+
mock(ThreadPool.class));
4648
assertThat(azureRepository.getBlobStore(), is(nullValue()));
4749
return azureRepository;
4850
}

plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStoragePlugin.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import org.elasticsearch.plugins.ReloadablePlugin;
2828
import org.elasticsearch.plugins.RepositoryPlugin;
2929
import org.elasticsearch.repositories.Repository;
30+
import org.elasticsearch.threadpool.ThreadPool;
31+
3032
import java.util.Arrays;
3133
import java.util.Collections;
3234
import java.util.List;
@@ -49,9 +51,10 @@ protected GoogleCloudStorageService createStorageService() {
4951
}
5052

5153
@Override
52-
public Map<String, Repository.Factory> getRepositories(Environment env, NamedXContentRegistry namedXContentRegistry) {
54+
public Map<String, Repository.Factory> getRepositories(Environment env, NamedXContentRegistry namedXContentRegistry,
55+
ThreadPool threadPool) {
5356
return Collections.singletonMap(GoogleCloudStorageRepository.TYPE,
54-
(metadata) -> new GoogleCloudStorageRepository(metadata, env, namedXContentRegistry, this.storageService));
57+
metadata -> new GoogleCloudStorageRepository(metadata, env, namedXContentRegistry, this.storageService, threadPool));
5558
}
5659

5760
@Override

plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageRepository.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@
2525
import org.elasticsearch.common.Strings;
2626
import org.elasticsearch.common.blobstore.BlobPath;
2727
import org.elasticsearch.common.settings.Setting;
28-
import org.elasticsearch.common.settings.Settings;
2928
import org.elasticsearch.common.unit.ByteSizeUnit;
3029
import org.elasticsearch.common.unit.ByteSizeValue;
3130
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
3231
import org.elasticsearch.env.Environment;
3332
import org.elasticsearch.repositories.RepositoryException;
3433
import org.elasticsearch.repositories.blobstore.BlobStoreRepository;
34+
import org.elasticsearch.threadpool.ThreadPool;
3535

3636
import java.util.function.Function;
3737

@@ -59,7 +59,6 @@ class GoogleCloudStorageRepository extends BlobStoreRepository {
5959
byteSizeSetting("chunk_size", MAX_CHUNK_SIZE, MIN_CHUNK_SIZE, MAX_CHUNK_SIZE, Property.NodeScope, Property.Dynamic);
6060
static final Setting<String> CLIENT_NAME = new Setting<>("client", "default", Function.identity());
6161

62-
private final Settings settings;
6362
private final GoogleCloudStorageService storageService;
6463
private final BlobPath basePath;
6564
private final ByteSizeValue chunkSize;
@@ -68,9 +67,8 @@ class GoogleCloudStorageRepository extends BlobStoreRepository {
6867

6968
GoogleCloudStorageRepository(RepositoryMetaData metadata, Environment environment,
7069
NamedXContentRegistry namedXContentRegistry,
71-
GoogleCloudStorageService storageService) {
72-
super(metadata, environment.settings(), getSetting(COMPRESS, metadata), namedXContentRegistry);
73-
this.settings = environment.settings();
70+
GoogleCloudStorageService storageService, ThreadPool threadPool) {
71+
super(metadata, environment.settings(), getSetting(COMPRESS, metadata), namedXContentRegistry, threadPool);
7472
this.storageService = storageService;
7573

7674
String basePath = BASE_PATH.get(metadata.settings());

plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsPlugin.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.elasticsearch.plugins.Plugin;
3737
import org.elasticsearch.plugins.RepositoryPlugin;
3838
import org.elasticsearch.repositories.Repository;
39+
import org.elasticsearch.threadpool.ThreadPool;
3940

4041
public final class HdfsPlugin extends Plugin implements RepositoryPlugin {
4142

@@ -110,7 +111,8 @@ private static Void eagerInit() {
110111
}
111112

112113
@Override
113-
public Map<String, Repository.Factory> getRepositories(Environment env, NamedXContentRegistry namedXContentRegistry) {
114-
return Collections.singletonMap("hdfs", (metadata) -> new HdfsRepository(metadata, env, namedXContentRegistry));
114+
public Map<String, Repository.Factory> getRepositories(Environment env, NamedXContentRegistry namedXContentRegistry,
115+
ThreadPool threadPool) {
116+
return Collections.singletonMap("hdfs", (metadata) -> new HdfsRepository(metadata, env, namedXContentRegistry, threadPool));
115117
}
116118
}

plugins/repository-hdfs/src/main/java/org/elasticsearch/repositories/hdfs/HdfsRepository.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
4141
import org.elasticsearch.env.Environment;
4242
import org.elasticsearch.repositories.blobstore.BlobStoreRepository;
43+
import org.elasticsearch.threadpool.ThreadPool;
4344

4445
import java.io.IOException;
4546
import java.io.UncheckedIOException;
@@ -67,8 +68,8 @@ public final class HdfsRepository extends BlobStoreRepository {
6768
private static final ByteSizeValue DEFAULT_BUFFER_SIZE = new ByteSizeValue(100, ByteSizeUnit.KB);
6869

6970
public HdfsRepository(RepositoryMetaData metadata, Environment environment,
70-
NamedXContentRegistry namedXContentRegistry) {
71-
super(metadata, environment.settings(), metadata.settings().getAsBoolean("compress", false), namedXContentRegistry);
71+
NamedXContentRegistry namedXContentRegistry, ThreadPool threadPool) {
72+
super(metadata, environment.settings(), metadata.settings().getAsBoolean("compress", false), namedXContentRegistry, threadPool);
7273

7374
this.environment = environment;
7475
this.chunkSize = metadata.settings().getAsBytesSize("chunk_size", null);

0 commit comments

Comments
 (0)