Skip to content

Commit 11f4e9c

Browse files
imotovdadoonet
authored andcommitted
Added retry mechanism for S3 connection errors
Closes #95
1 parent ba185e0 commit 11f4e9c

File tree

12 files changed

+1012
-191
lines changed

12 files changed

+1012
-191
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ The following settings are supported:
115115
* `chunk_size`: Big files can be broken down into chunks during snapshotting if needed. The chunk size can be specified in bytes or by using size value notation, i.e. `1g`, `10m`, `5k`. Defaults to `100m`.
116116
* `compress`: When set to `true` metadata files are stored in compressed format. This setting doesn't affect index files that are already compressed by default. Defaults to `false`.
117117
* `server_side_encryption`: When set to `true` files are encrypted on server side using AES256 algorithm. Defaults to `false`.
118+
* `max_retries`: Number of retries in case of S3 errors. Defaults to `3`.
118119

119120
The S3 repositories are using the same credentials as the rest of the AWS services provided by this plugin (`discovery`).
120121
See [Generic Configuration](#generic-configuration) for details.

src/main/java/org/elasticsearch/cloud/aws/AwsModule.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,29 @@
2020
package org.elasticsearch.cloud.aws;
2121

2222
import org.elasticsearch.common.inject.AbstractModule;
23+
import org.elasticsearch.common.settings.Settings;
2324

2425
/**
2526
*
2627
*/
2728
public class AwsModule extends AbstractModule {
2829

30+
private final Settings settings;
31+
32+
public static final String S3_SERVICE_TYPE_KEY = "cloud.aws.s3service.type";
33+
34+
public AwsModule(Settings settings) {
35+
this.settings = settings;
36+
}
37+
2938
@Override
3039
protected void configure() {
31-
bind(AwsS3Service.class).asEagerSingleton();
40+
bind(AwsS3Service.class).to(getS3ServiceClass(settings)).asEagerSingleton();
3241
bind(AwsEc2Service.class).asEagerSingleton();
3342
}
34-
}
43+
44+
public static Class<? extends AwsS3Service> getS3ServiceClass(Settings settings) {
45+
return settings.getAsClass(S3_SERVICE_TYPE_KEY, InternalAwsS3Service.class);
46+
}
47+
48+
}

src/main/java/org/elasticsearch/cloud/aws/AwsS3Service.java

Lines changed: 4 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -19,176 +19,14 @@
1919

2020
package org.elasticsearch.cloud.aws;
2121

22-
import java.util.HashMap;
23-
import java.util.Map;
24-
25-
import com.amazonaws.ClientConfiguration;
26-
import com.amazonaws.Protocol;
27-
import com.amazonaws.auth.*;
28-
import com.amazonaws.internal.StaticCredentialsProvider;
2922
import com.amazonaws.services.s3.AmazonS3;
30-
import com.amazonaws.services.s3.AmazonS3Client;
31-
import org.elasticsearch.ElasticsearchException;
32-
import org.elasticsearch.ElasticsearchIllegalArgumentException;
33-
import org.elasticsearch.common.collect.Tuple;
34-
import org.elasticsearch.common.component.AbstractLifecycleComponent;
35-
import org.elasticsearch.common.inject.Inject;
36-
import org.elasticsearch.common.settings.Settings;
37-
import org.elasticsearch.common.settings.SettingsFilter;
23+
import org.elasticsearch.common.component.LifecycleComponent;
3824

3925
/**
4026
*
4127
*/
42-
public class AwsS3Service extends AbstractLifecycleComponent<AwsS3Service> {
43-
44-
/**
45-
* (acceskey, endpoint) -> client
46-
*/
47-
private Map<Tuple<String, String>, AmazonS3Client> clients = new HashMap<Tuple<String,String>, AmazonS3Client>();
48-
49-
@Inject
50-
public AwsS3Service(Settings settings, SettingsFilter settingsFilter) {
51-
super(settings);
52-
53-
settingsFilter.addFilter(new AwsSettingsFilter());
54-
}
55-
56-
public synchronized AmazonS3 client() {
57-
String endpoint = getDefaultEndpoint();
58-
String account = componentSettings.get("access_key", settings.get("cloud.account"));
59-
String key = componentSettings.get("secret_key", settings.get("cloud.key"));
60-
61-
return getClient(endpoint, account, key);
62-
}
63-
64-
public synchronized AmazonS3 client(String region, String account, String key) {
65-
String endpoint;
66-
if (region == null) {
67-
endpoint = getDefaultEndpoint();
68-
} else {
69-
endpoint = getEndpoint(region);
70-
logger.debug("using s3 region [{}], with endpoint [{}]", region, endpoint);
71-
}
72-
if (account == null || key == null) {
73-
account = componentSettings.get("access_key", settings.get("cloud.account"));
74-
key = componentSettings.get("secret_key", settings.get("cloud.key"));
75-
}
76-
77-
return getClient(endpoint, account, key);
78-
}
79-
80-
81-
private synchronized AmazonS3 getClient(String endpoint, String account, String key) {
82-
Tuple<String, String> clientDescriptor = new Tuple<String, String>(endpoint, account);
83-
AmazonS3Client client = clients.get(clientDescriptor);
84-
if (client != null) {
85-
return client;
86-
}
87-
88-
ClientConfiguration clientConfiguration = new ClientConfiguration();
89-
String protocol = componentSettings.get("protocol", "http").toLowerCase();
90-
if ("http".equals(protocol)) {
91-
clientConfiguration.setProtocol(Protocol.HTTP);
92-
} else if ("https".equals(protocol)) {
93-
clientConfiguration.setProtocol(Protocol.HTTPS);
94-
} else {
95-
throw new ElasticsearchIllegalArgumentException("No protocol supported [" + protocol + "], can either be [http] or [https]");
96-
}
97-
98-
String proxyHost = componentSettings.get("proxy_host");
99-
if (proxyHost != null) {
100-
String portString = componentSettings.get("proxy_port", "80");
101-
Integer proxyPort;
102-
try {
103-
proxyPort = Integer.parseInt(portString, 10);
104-
} catch (NumberFormatException ex) {
105-
throw new ElasticsearchIllegalArgumentException("The configured proxy port value [" + portString + "] is invalid", ex);
106-
}
107-
clientConfiguration.withProxyHost(proxyHost).setProxyPort(proxyPort);
108-
}
109-
110-
AWSCredentialsProvider credentials;
111-
112-
if (account == null && key == null) {
113-
credentials = new AWSCredentialsProviderChain(
114-
new EnvironmentVariableCredentialsProvider(),
115-
new SystemPropertiesCredentialsProvider(),
116-
new InstanceProfileCredentialsProvider()
117-
);
118-
} else {
119-
credentials = new AWSCredentialsProviderChain(
120-
new StaticCredentialsProvider(new BasicAWSCredentials(account, key))
121-
);
122-
}
123-
client = new AmazonS3Client(credentials, clientConfiguration);
124-
125-
if (endpoint != null) {
126-
client.setEndpoint(endpoint);
127-
}
128-
clients.put(clientDescriptor, client);
129-
return client;
130-
}
131-
132-
private String getDefaultEndpoint() {
133-
String endpoint = null;
134-
if (componentSettings.get("s3.endpoint") != null) {
135-
endpoint = componentSettings.get("s3.endpoint");
136-
logger.debug("using explicit s3 endpoint [{}]", endpoint);
137-
} else if (componentSettings.get("region") != null) {
138-
String region = componentSettings.get("region").toLowerCase();
139-
endpoint = getEndpoint(region);
140-
logger.debug("using s3 region [{}], with endpoint [{}]", region, endpoint);
141-
}
142-
return endpoint;
143-
}
144-
145-
private static String getEndpoint(String region) {
146-
if ("us-east".equals(region)) {
147-
return "s3.amazonaws.com";
148-
} else if ("us-east-1".equals(region)) {
149-
return "s3.amazonaws.com";
150-
} else if ("us-west".equals(region)) {
151-
return "s3-us-west-1.amazonaws.com";
152-
} else if ("us-west-1".equals(region)) {
153-
return "s3-us-west-1.amazonaws.com";
154-
} else if ("us-west-2".equals(region)) {
155-
return "s3-us-west-2.amazonaws.com";
156-
} else if ("ap-southeast".equals(region)) {
157-
return "s3-ap-southeast-1.amazonaws.com";
158-
} else if ("ap-southeast-1".equals(region)) {
159-
return "s3-ap-southeast-1.amazonaws.com";
160-
} else if ("ap-southeast-2".equals(region)) {
161-
return "s3-ap-southeast-2.amazonaws.com";
162-
} else if ("ap-northeast".equals(region)) {
163-
return "s3-ap-northeast-1.amazonaws.com";
164-
} else if ("ap-northeast-1".equals(region)) {
165-
return "s3-ap-northeast-1.amazonaws.com";
166-
} else if ("eu-west".equals(region)) {
167-
return "s3-eu-west-1.amazonaws.com";
168-
} else if ("eu-west-1".equals(region)) {
169-
return "s3-eu-west-1.amazonaws.com";
170-
} else if ("sa-east".equals(region)) {
171-
return "s3-sa-east-1.amazonaws.com";
172-
} else if ("sa-east-1".equals(region)) {
173-
return "s3-sa-east-1.amazonaws.com";
174-
} else {
175-
throw new ElasticsearchIllegalArgumentException("No automatic endpoint could be derived from region [" + region + "]");
176-
}
177-
}
178-
179-
@Override
180-
protected void doStart() throws ElasticsearchException {
181-
}
182-
183-
@Override
184-
protected void doStop() throws ElasticsearchException {
185-
}
186-
187-
@Override
188-
protected void doClose() throws ElasticsearchException {
189-
for (AmazonS3Client client : clients.values()) {
190-
client.shutdown();
191-
}
192-
}
28+
public interface AwsS3Service extends LifecycleComponent<AwsS3Service> {
29+
AmazonS3 client();
19330

31+
AmazonS3 client(String region, String account, String key);
19432
}

0 commit comments

Comments
 (0)