Skip to content

Commit 519da17

Browse files
authored
Add max_multipart_parts setting to S3 repository (#113989) (#114161)
1 parent bad8abe commit 519da17

File tree

4 files changed

+73
-4
lines changed

4 files changed

+73
-4
lines changed

docs/changelog/113989.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 113989
2+
summary: Add `max_multipart_parts` setting to S3 repository
3+
area: Snapshot/Restore
4+
type: enhancement
5+
issues: []

docs/reference/snapshot-restore/repository-s3.asciidoc

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -261,9 +261,11 @@ multiple deployments may share the same bucket.
261261

262262
`chunk_size`::
263263

264-
(<<byte-units,byte value>>) Big files can be broken down into chunks during snapshotting if needed.
265-
Specify the chunk size as a value and unit, for example:
266-
`1TB`, `1GB`, `10MB`. Defaults to the maximum size of a blob in the S3 which is `5TB`.
264+
(<<byte-units,byte value>>) The maximum size of object that {es} will write to the repository
265+
when creating a snapshot. Files which are larger than `chunk_size` will be chunked into several
266+
smaller objects. {es} may also split a file across multiple objects to satisfy other constraints
267+
such as the `max_multipart_parts` limit. Defaults to `5TB` which is the
268+
https://docs.aws.amazon.com/AmazonS3/latest/userguide/qfacts.html[maximum size of an object in AWS S3].
267269

268270
`compress`::
269271

@@ -292,6 +294,14 @@ include::repository-shared-settings.asciidoc[]
292294
size allowed by S3. Defaults to `100mb` or `5%` of JVM heap, whichever is
293295
smaller.
294296

297+
`max_multipart_parts` ::
298+
299+
(integer) The maximum number of parts that {es} will write during a multipart upload of a single
300+
object. Files which are larger than `buffer_size × max_multipart_parts` will be chunked into
301+
several smaller objects. {es} may also split a file across multiple objects to satisfy other
302+
constraints such as the `chunk_size` limit. Defaults to `10000` which is the
303+
https://docs.aws.amazon.com/AmazonS3/latest/userguide/qfacts.html[maximum number of parts in a multipart upload in AWS S3].
304+
295305
`canned_acl`::
296306

297307
The S3 repository supports all

modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@ class S3Repository extends MeteredBlobStoreRepository {
141141
MAX_FILE_SIZE_USING_MULTIPART
142142
);
143143

144+
/**
145+
* Maximum parts number for multipart upload. (see https://docs.aws.amazon.com/AmazonS3/latest/userguide/qfacts.html)
146+
*/
147+
static final Setting<Integer> MAX_MULTIPART_PARTS = Setting.intSetting("max_multipart_parts", 10_000, 1, 10_000);
148+
144149
/**
145150
* Sets the S3 storage class type for the backup files. Values may be standard, reduced_redundancy,
146151
* standard_ia, onezone_ia and intelligent_tiering. Defaults to standard.
@@ -254,7 +259,9 @@ class S3Repository extends MeteredBlobStoreRepository {
254259
}
255260

256261
this.bufferSize = BUFFER_SIZE_SETTING.get(metadata.settings());
257-
this.chunkSize = CHUNK_SIZE_SETTING.get(metadata.settings());
262+
var maxChunkSize = CHUNK_SIZE_SETTING.get(metadata.settings());
263+
var maxPartsNum = MAX_MULTIPART_PARTS.get(metadata.settings());
264+
this.chunkSize = objectSizeLimit(maxChunkSize, bufferSize, maxPartsNum);
258265

259266
// We make sure that chunkSize is bigger or equal than/to bufferSize
260267
if (this.chunkSize.getBytes() < bufferSize.getBytes()) {
@@ -303,6 +310,20 @@ private static Map<String, String> buildLocation(RepositoryMetadata metadata) {
303310
return Map.of("base_path", BASE_PATH_SETTING.get(metadata.settings()), "bucket", BUCKET_SETTING.get(metadata.settings()));
304311
}
305312

313+
/**
314+
* Calculates S3 object size limit based on 2 constraints: maximum object(chunk) size
315+
* and maximum number of parts for multipart upload.
316+
* https://docs.aws.amazon.com/AmazonS3/latest/userguide/qfacts.html
317+
*
318+
* @param chunkSize s3 object size
319+
* @param bufferSize s3 multipart upload part size
320+
* @param maxPartsNum s3 multipart upload max parts number
321+
*/
322+
private static ByteSizeValue objectSizeLimit(ByteSizeValue chunkSize, ByteSizeValue bufferSize, int maxPartsNum) {
323+
var bytes = Math.min(chunkSize.getBytes(), bufferSize.getBytes() * maxPartsNum);
324+
return ByteSizeValue.ofBytes(bytes);
325+
}
326+
306327
/**
307328
* Holds a reference to delayed repository operation {@link Scheduler.Cancellable} so it can be cancelled should the repository be
308329
* closed concurrently.

modules/repository-s3/src/test/java/org/elasticsearch/repositories/s3/S3RepositoryTests.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,4 +175,37 @@ public void testAnalysisFailureDetail() {
175175
}
176176
}
177177

178+
// ensures that chunkSize is limited to chunk_size setting, when buffer_size * parts_num is bigger
179+
public void testChunkSizeLimit() {
180+
var meta = new RepositoryMetadata(
181+
"dummy-repo",
182+
"mock",
183+
Settings.builder()
184+
.put(S3Repository.BUCKET_SETTING.getKey(), "bucket")
185+
.put(S3Repository.CHUNK_SIZE_SETTING.getKey(), "1GB")
186+
.put(S3Repository.BUFFER_SIZE_SETTING.getKey(), "100MB")
187+
.put(S3Repository.MAX_MULTIPART_PARTS.getKey(), 10_000) // ~1TB
188+
.build()
189+
);
190+
try (var repo = createS3Repo(meta)) {
191+
assertEquals(ByteSizeValue.ofGb(1), repo.chunkSize());
192+
}
193+
}
194+
195+
// ensures that chunkSize is limited to buffer_size * parts_num, when chunk_size setting is bigger
196+
public void testPartsNumLimit() {
197+
var meta = new RepositoryMetadata(
198+
"dummy-repo",
199+
"mock",
200+
Settings.builder()
201+
.put(S3Repository.BUCKET_SETTING.getKey(), "bucket")
202+
.put(S3Repository.CHUNK_SIZE_SETTING.getKey(), "5TB")
203+
.put(S3Repository.BUFFER_SIZE_SETTING.getKey(), "100MB")
204+
.put(S3Repository.MAX_MULTIPART_PARTS.getKey(), 10_000)
205+
.build()
206+
);
207+
try (var repo = createS3Repo(meta)) {
208+
assertEquals(ByteSizeValue.ofMb(1_000_000), repo.chunkSize());
209+
}
210+
}
178211
}

0 commit comments

Comments
 (0)