Skip to content

Commit 4fed1a5

Browse files
joegallogaobinlong
andauthored
Support max_single_primary_size in Resize Action and exposed in ILM (#67705) (#68321)
Co-authored-by: bellengao <[email protected]>
1 parent 4657fd6 commit 4fed1a5

File tree

28 files changed

+534
-107
lines changed

28 files changed

+534
-107
lines changed

client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/ShrinkAction.java

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@
1818
*/
1919
package org.elasticsearch.client.indexlifecycle;
2020

21+
import org.elasticsearch.common.Nullable;
2122
import org.elasticsearch.common.ParseField;
2223
import org.elasticsearch.common.Strings;
24+
import org.elasticsearch.common.unit.ByteSizeValue;
2325
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
26+
import org.elasticsearch.common.xcontent.ObjectParser;
2427
import org.elasticsearch.common.xcontent.ToXContentObject;
2528
import org.elasticsearch.common.xcontent.XContentBuilder;
2629
import org.elasticsearch.common.xcontent.XContentParser;
@@ -31,31 +34,53 @@
3134
public class ShrinkAction implements LifecycleAction, ToXContentObject {
3235
public static final String NAME = "shrink";
3336
private static final ParseField NUMBER_OF_SHARDS_FIELD = new ParseField("number_of_shards");
37+
private static final ParseField MAX_SINGLE_PRIMARY_SIZE = new ParseField("max_single_primary_size");
3438

3539
private static final ConstructingObjectParser<ShrinkAction, Void> PARSER =
36-
new ConstructingObjectParser<>(NAME, true, a -> new ShrinkAction((Integer) a[0]));
40+
new ConstructingObjectParser<>(NAME, true, a -> new ShrinkAction((Integer) a[0], (ByteSizeValue) a[1]));
3741

3842
static {
39-
PARSER.declareInt(ConstructingObjectParser.constructorArg(), NUMBER_OF_SHARDS_FIELD);
43+
PARSER.declareInt(ConstructingObjectParser.optionalConstructorArg(), NUMBER_OF_SHARDS_FIELD);
44+
PARSER.declareField(ConstructingObjectParser.optionalConstructorArg(),
45+
(p, c) -> ByteSizeValue.parseBytesSizeValue(p.text(), MAX_SINGLE_PRIMARY_SIZE.getPreferredName()),
46+
MAX_SINGLE_PRIMARY_SIZE, ObjectParser.ValueType.STRING);
4047
}
4148

42-
private int numberOfShards;
49+
private Integer numberOfShards;
50+
private ByteSizeValue maxSinglePrimarySize;
4351

4452
public static ShrinkAction parse(XContentParser parser) throws IOException {
4553
return PARSER.parse(parser, null);
4654
}
4755

48-
public ShrinkAction(int numberOfShards) {
49-
if (numberOfShards <= 0) {
50-
throw new IllegalArgumentException("[" + NUMBER_OF_SHARDS_FIELD.getPreferredName() + "] must be greater than 0");
56+
public ShrinkAction(@Nullable Integer numberOfShards, ByteSizeValue maxSinglePrimarySize) {
57+
if (numberOfShards != null && maxSinglePrimarySize != null) {
58+
throw new IllegalArgumentException("Cannot set both [number_of_shards] and [max_single_primary_size]");
59+
}
60+
if (numberOfShards == null && maxSinglePrimarySize == null) {
61+
throw new IllegalArgumentException("Either [number_of_shards] or [max_single_primary_size] must be set");
62+
}
63+
if (maxSinglePrimarySize != null) {
64+
if (maxSinglePrimarySize.getBytes() <= 0) {
65+
throw new IllegalArgumentException("[max_single_primary_size] must be greater than 0");
66+
}
67+
this.maxSinglePrimarySize = maxSinglePrimarySize;
68+
} else {
69+
if (numberOfShards <= 0) {
70+
throw new IllegalArgumentException("[" + NUMBER_OF_SHARDS_FIELD.getPreferredName() + "] must be greater than 0");
71+
}
72+
this.numberOfShards = numberOfShards;
5173
}
52-
this.numberOfShards = numberOfShards;
5374
}
5475

55-
int getNumberOfShards() {
76+
Integer getNumberOfShards() {
5677
return numberOfShards;
5778
}
5879

80+
ByteSizeValue getMaxSinglePrimarySize() {
81+
return maxSinglePrimarySize;
82+
}
83+
5984
@Override
6085
public String getName() {
6186
return NAME;
@@ -64,7 +89,12 @@ public String getName() {
6489
@Override
6590
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
6691
builder.startObject();
67-
builder.field(NUMBER_OF_SHARDS_FIELD.getPreferredName(), numberOfShards);
92+
if (numberOfShards != null) {
93+
builder.field(NUMBER_OF_SHARDS_FIELD.getPreferredName(), numberOfShards);
94+
}
95+
if (maxSinglePrimarySize != null) {
96+
builder.field(MAX_SINGLE_PRIMARY_SIZE.getPreferredName(), maxSinglePrimarySize);
97+
}
6898
builder.endObject();
6999
return builder;
70100
}
@@ -74,12 +104,14 @@ public boolean equals(Object o) {
74104
if (this == o) return true;
75105
if (o == null || getClass() != o.getClass()) return false;
76106
ShrinkAction that = (ShrinkAction) o;
77-
return Objects.equals(numberOfShards, that.numberOfShards);
107+
108+
return Objects.equals(numberOfShards, that.numberOfShards) &&
109+
Objects.equals(maxSinglePrimarySize, that.maxSinglePrimarySize);
78110
}
79111

80112
@Override
81113
public int hashCode() {
82-
return Objects.hash(numberOfShards);
114+
return Objects.hash(numberOfShards, maxSinglePrimarySize);
83115
}
84116

85117
@Override

client/rest-high-level/src/main/java/org/elasticsearch/client/indices/ResizeRequest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.elasticsearch.client.Validatable;
2525
import org.elasticsearch.client.ValidationException;
2626
import org.elasticsearch.common.settings.Settings;
27+
import org.elasticsearch.common.unit.ByteSizeValue;
2728
import org.elasticsearch.common.xcontent.ToXContentObject;
2829
import org.elasticsearch.common.xcontent.XContentBuilder;
2930

@@ -45,6 +46,7 @@ public class ResizeRequest extends TimedRequest implements Validatable, ToXConte
4546
private final String targetIndex;
4647
private Settings settings = Settings.EMPTY;
4748
private Set<Alias> aliases = new HashSet<>();
49+
private ByteSizeValue maxSinglePrimarySize;
4850

4951
/**
5052
* Creates a new resize request
@@ -87,6 +89,20 @@ public Set<Alias> getAliases() {
8789
return Collections.unmodifiableSet(this.aliases);
8890
}
8991

92+
/**
93+
* Sets the max single primary shard size of the target index
94+
*/
95+
public void setMaxSinglePrimarySize(ByteSizeValue maxSinglePrimarySize) {
96+
this.maxSinglePrimarySize = maxSinglePrimarySize;
97+
}
98+
99+
/**
100+
* Return the max single primary shard size of the target index
101+
*/
102+
public ByteSizeValue getMaxSinglePrimarySize() {
103+
return maxSinglePrimarySize;
104+
}
105+
90106
@Override
91107
public Optional<ValidationException> validate() {
92108
ValidationException validationException = new ValidationException();

client/rest-high-level/src/test/java/org/elasticsearch/client/IndexLifecycleIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ public void testExplainLifecycle() throws Exception {
156156
Map<String, LifecycleAction> warmActions = new HashMap<>();
157157
warmActions.put(UnfollowAction.NAME, new UnfollowAction());
158158
warmActions.put(AllocateAction.NAME, new AllocateAction(null, null, null, Collections.singletonMap("_name", "node-1")));
159-
warmActions.put(ShrinkAction.NAME, new ShrinkAction(1));
159+
warmActions.put(ShrinkAction.NAME, new ShrinkAction(1, null));
160160
warmActions.put(ForceMergeAction.NAME, new ForceMergeAction(1000));
161161
lifecyclePhases.put("warm", new Phase("warm", TimeValue.timeValueSeconds(1000), warmActions));
162162

client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesRequestConvertersTests.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import org.elasticsearch.common.CheckedFunction;
6565
import org.elasticsearch.common.Strings;
6666
import org.elasticsearch.common.settings.Settings;
67+
import org.elasticsearch.common.unit.ByteSizeValue;
6768
import org.elasticsearch.common.unit.TimeValue;
6869
import org.elasticsearch.common.util.CollectionUtils;
6970
import org.elasticsearch.common.xcontent.XContentType;
@@ -910,6 +911,9 @@ private void resizeTest(ResizeType resizeType, CheckedFunction<ResizeRequest, Re
910911
if (resizeType == ResizeType.SPLIT) {
911912
resizeRequest.setSettings(Settings.builder().put("index.number_of_shards", 2).build());
912913
}
914+
if (resizeType == ResizeType.SHRINK) {
915+
resizeRequest.setMaxSinglePrimarySize(new ByteSizeValue(randomIntBetween(1, 100)));
916+
}
913917

914918
Request request = function.apply(resizeRequest);
915919
Assert.assertEquals(HttpPut.METHOD_NAME, request.getMethod());

client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/ILMDocumentationIT.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ public void testGetLifecyclePolicy() throws IOException, InterruptedException {
264264
PutLifecyclePolicyRequest putRequest = new PutLifecyclePolicyRequest(myPolicyAsPut);
265265

266266
Map<String, Phase> otherPolicyPhases = new HashMap<>(phases);
267-
Map<String, LifecycleAction> warmActions = Collections.singletonMap(ShrinkAction.NAME, new ShrinkAction(1));
267+
Map<String, LifecycleAction> warmActions = Collections.singletonMap(ShrinkAction.NAME, new ShrinkAction(1, null));
268268
otherPolicyPhases.put("warm", new Phase("warm", new TimeValue(30, TimeUnit.DAYS), warmActions));
269269
otherPolicyAsPut = new LifecyclePolicy("other_policy", otherPolicyPhases);
270270

@@ -614,7 +614,7 @@ public void testRetryPolicy() throws Exception {
614614
{
615615
Map<String, Phase> phases = new HashMap<>();
616616
Map<String, LifecycleAction> warmActions = new HashMap<>();
617-
warmActions.put(ShrinkAction.NAME, new ShrinkAction(3));
617+
warmActions.put(ShrinkAction.NAME, new ShrinkAction(3, null));
618618
phases.put("warm", new Phase("warm", TimeValue.ZERO, warmActions));
619619

620620
LifecyclePolicy policy = new LifecyclePolicy("my_policy",

client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1706,11 +1706,19 @@ public void testShrinkIndex() throws Exception {
17061706
request.setWaitForActiveShards(2); // <1>
17071707
request.setWaitForActiveShards(ActiveShardCount.DEFAULT); // <2>
17081708
// end::shrink-index-request-waitForActiveShards
1709-
// tag::shrink-index-request-settings
1710-
request.getTargetIndexRequest().settings(Settings.builder()
1709+
if (randomBoolean()) {
1710+
// tag::shrink-index-request-settings
1711+
request.getTargetIndexRequest().settings(Settings.builder()
17111712
.put("index.number_of_shards", 2) // <1>
17121713
.putNull("index.routing.allocation.require._name")); // <2>
1713-
// end::shrink-index-request-settings
1714+
// end::shrink-index-request-settings
1715+
} else {
1716+
request.getTargetIndexRequest().settings(Settings.builder()
1717+
.putNull("index.routing.allocation.require._name"));
1718+
// tag::shrink-index-request-maxSinglePrimarySize
1719+
request.setMaxSinglePrimarySize(new ByteSizeValue(50, ByteSizeUnit.GB)); // <1>
1720+
// end::shrink-index-request-maxSinglePrimarySize
1721+
}
17141722
// tag::shrink-index-request-aliases
17151723
request.getTargetIndexRequest().alias(new Alias("target_alias")); // <1>
17161724
// end::shrink-index-request-aliases

client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/ShrinkActionTests.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package org.elasticsearch.client.indexlifecycle;
2020

21+
import org.elasticsearch.common.unit.ByteSizeValue;
2122
import org.elasticsearch.common.xcontent.XContentParser;
2223
import org.elasticsearch.test.AbstractXContentTestCase;
2324

@@ -38,7 +39,11 @@ protected ShrinkAction createTestInstance() {
3839
}
3940

4041
static ShrinkAction randomInstance() {
41-
return new ShrinkAction(randomIntBetween(1, 100));
42+
if (randomBoolean()) {
43+
return new ShrinkAction(randomIntBetween(1, 100), null);
44+
} else {
45+
return new ShrinkAction(null, new ByteSizeValue(randomIntBetween(1, 100)));
46+
}
4247
}
4348

4449
@Override
@@ -47,7 +52,17 @@ protected boolean supportsUnknownFields() {
4752
}
4853

4954
public void testNonPositiveShardNumber() {
50-
Exception e = expectThrows(Exception.class, () -> new ShrinkAction(randomIntBetween(-100, 0)));
55+
Exception e = expectThrows(Exception.class, () -> new ShrinkAction(randomIntBetween(-100, 0), null));
5156
assertThat(e.getMessage(), equalTo("[number_of_shards] must be greater than 0"));
5257
}
58+
59+
public void testMaxSinglePrimarySize() {
60+
ByteSizeValue maxSinglePrimarySize1 = new ByteSizeValue(10);
61+
Exception e1 = expectThrows(Exception.class, () -> new ShrinkAction(randomIntBetween(1, 100), maxSinglePrimarySize1));
62+
assertThat(e1.getMessage(), equalTo("Cannot set both [number_of_shards] and [max_single_primary_size]"));
63+
64+
ByteSizeValue maxSinglePrimarySize2 = new ByteSizeValue(0);
65+
Exception e2 = expectThrows(Exception.class, () -> new ShrinkAction(null, maxSinglePrimarySize2));
66+
assertThat(e2.getMessage(), equalTo("[max_single_primary_size] must be greater than 0"));
67+
}
5368
}

docs/java-rest/high-level/indices/shrink_index.asciidoc

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ include-tagged::{doc-tests-file}[{api}-request-settings]
5454
<1> The number of shards on the target of the shrink index request
5555
<2> Remove the allocation requirement copied from the source index
5656

57+
["source","java",subs="attributes,callouts,macros"]
58+
--------------------------------------------------
59+
include-tagged::{doc-tests-file}[{api}-request-maxSinglePrimarySize]
60+
--------------------------------------------------
61+
<1> The max single primary shard size of the target index
62+
5763
["source","java",subs="attributes,callouts,macros"]
5864
--------------------------------------------------
5965
include-tagged::{doc-tests-file}[{api}-request-aliases]
@@ -75,5 +81,3 @@ include-tagged::{doc-tests-file}[{api}-response]
7581
<1> Indicates whether all of the nodes have acknowledged the request
7682
<2> Indicates whether the requisite number of shard copies were started for
7783
each shard in the index before timing out
78-
79-

docs/reference/ilm/actions/ilm-shrink.asciidoc

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,24 @@
44

55
Phases allowed: hot, warm.
66

7-
Sets an index to <<dynamic-index-settings, read-only>>
8-
and shrinks it into a new index with fewer primary shards.
9-
The name of the new index is of the form `shrink-<original-index-name>`.
10-
For example, if the name of the source index is _logs_,
7+
Sets an index to <<dynamic-index-settings, read-only>>
8+
and shrinks it into a new index with fewer primary shards.
9+
The name of the new index is of the form `shrink-<original-index-name>`.
10+
For example, if the name of the source index is _logs_,
1111
the name of the shrunken index is _shrink-logs_.
1212

13-
The shrink action allocates all primary shards of the index to one node so it
13+
The shrink action allocates all primary shards of the index to one node so it
1414
can call the <<indices-shrink-index,Shrink API>> to shrink the index.
15-
After shrinking, it swaps aliases that point to the original index to the new shrunken index.
15+
After shrinking, it swaps aliases that point to the original index to the new shrunken index.
1616

1717
To use the `shrink` action in the `hot` phase, the `rollover` action *must* be present.
1818
If no rollover action is configured, {ilm-init} will reject the policy.
1919

2020
[IMPORTANT]
21-
If the shrink action is used on a <<ccr-put-follow,follower index>>,
21+
If the shrink action is used on a <<ccr-put-follow,follower index>>,
2222
policy execution waits until the leader index rolls over (or is
23-
<<skipping-rollover, otherwise marked complete>>),
24-
then converts the follower index into a regular index with the
23+
<<skipping-rollover, otherwise marked complete>>),
24+
then converts the follower index into a regular index with the
2525
<<ilm-unfollow,unfollow>> action before performing the shrink operation.
2626

2727
If the managed index is part of a <<data-streams, data stream>>,
@@ -40,14 +40,30 @@ managed indices.
4040
[[ilm-shrink-options]]
4141
==== Shrink options
4242
`number_of_shards`::
43-
(Required, integer)
44-
Number of shards to shrink to.
45-
Must be a factor of the number of shards in the source index.
43+
(Optional, integer)
44+
Number of shards to shrink to.
45+
Must be a factor of the number of shards in the source index. This parameter conflicts with
46+
`max_single_primary_size`, only one of them may be set.
47+
48+
`max_single_primary_size`::
49+
(Optional, <<byte-units, byte units>>)
50+
The max single primary shard size for the target index. Used to find the optimum number of shards for the target index.
51+
When this parameter is set, each shard's storage in the target index will not be greater than the parameter.
52+
The shards count of the target index will still be a factor of the source index's shards count, but if the parameter
53+
is less than the single shard size in the source index, the shards count for the target index will be equal to the source index's shards count.
54+
For example, when this parameter is set to 50gb, if the source index has 60 primary shards with totaling 100gb, then the
55+
target index will have 2 primary shards, with each shard size of 50gb; if the source index has 60 primary shards
56+
with totaling 1000gb, then the target index will have 20 primary shards; if the source index has 60 primary shards
57+
with totaling 4000gb, then the target index will still have 60 primary shards. This parameter conflicts
58+
with `number_of_shards` in the `settings`, only one of them may be set.
4659

4760

4861
[[ilm-shrink-ex]]
4962
==== Example
50-
63+
64+
[[ilm-shrink-shards-ex]]
65+
===== Set the number of shards of the new shrunken index explicitly
66+
5167
[source,console]
5268
--------------------------------------------------
5369
PUT _ilm/policy/my_policy
@@ -65,3 +81,25 @@ PUT _ilm/policy/my_policy
6581
}
6682
}
6783
--------------------------------------------------
84+
85+
[[ilm-shrink-size-ex]]
86+
===== Calculate the number of shards of the new shrunken index based on the storage of the
87+
source index and the `max_single_primary_size` parameter
88+
89+
[source,console]
90+
--------------------------------------------------
91+
PUT _ilm/policy/my_policy
92+
{
93+
"policy": {
94+
"phases": {
95+
"warm": {
96+
"actions": {
97+
"shrink" : {
98+
"max_single_primary_size": "50gb"
99+
}
100+
}
101+
}
102+
}
103+
}
104+
}
105+
--------------------------------------------------

docs/reference/indices/shrink-index.asciidoc

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ PUT /my_source_index/_settings
5656
// TEST[s/^/PUT my_source_index\n{"settings":{"index.number_of_shards":2}}\n/]
5757

5858
<1> Removes replica shards for the index.
59-
<2> Relocates the index's shards to the `shrink_node_name` node.
59+
<2> Relocates the index's shards to the `shrink_node_name` node.
6060
See <<shard-allocation-filtering>>.
6161
<3> Prevents write operations to this index. Metadata changes, such as deleting
6262
the index, are still allowed.
@@ -230,3 +230,15 @@ include::{es-repo-dir}/rest-api/common-parms.asciidoc[tag=timeoutparms]
230230
include::{es-repo-dir}/rest-api/common-parms.asciidoc[tag=target-index-aliases]
231231

232232
include::{es-repo-dir}/rest-api/common-parms.asciidoc[tag=target-index-settings]
233+
234+
`max_single_primary_size`::
235+
(Optional, <<byte-units, byte units>>)
236+
The max single primary shard size for the target index. Used to find the optimum number of shards for the target index.
237+
When this parameter is set, each shard's storage in the target index will not be greater than the parameter.
238+
The shards count of the target index will still be a factor of the source index's shards count, but if the parameter
239+
is less than the single shard size in the source index, the shards count for the target index will be equal to the source index's shards count.
240+
For example, when this parameter is set to 50gb, if the source index has 60 primary shards with totaling 100gb, then the
241+
target index will have 2 primary shards, with each shard size of 50gb; if the source index has 60 primary shards
242+
with totaling 1000gb, then the target index will have 20 primary shards; if the source index has 60 primary shards
243+
with totaling 4000gb, then the target index will still have 60 primary shards. This parameter conflicts
244+
with `number_of_shards` in the `settings`, only one of them may be set.

0 commit comments

Comments
 (0)