Skip to content

Commit c7ce5a0

Browse files
authored
Add size-based condition to the index rollover API (#27160)
This is to add a max_size condition to the index rollover API. We use a totalSizeInBytes from DocsStats to evaluate this condition. Closes #27004
1 parent 749c3ec commit c7ce5a0

File tree

12 files changed

+362
-31
lines changed

12 files changed

+362
-31
lines changed

core/src/main/java/org/elasticsearch/action/admin/indices/rollover/Condition.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919

2020
package org.elasticsearch.action.admin.indices.rollover;
2121

22+
import org.elasticsearch.Version;
2223
import org.elasticsearch.common.ParseField;
2324
import org.elasticsearch.common.io.stream.NamedWriteable;
25+
import org.elasticsearch.common.unit.ByteSizeValue;
2426
import org.elasticsearch.common.unit.TimeValue;
2527
import org.elasticsearch.common.xcontent.ObjectParser;
2628

@@ -38,6 +40,9 @@ public abstract class Condition<T> implements NamedWriteable {
3840
new ParseField(MaxAgeCondition.NAME));
3941
PARSER.declareLong((conditions, value) ->
4042
conditions.add(new MaxDocsCondition(value)), new ParseField(MaxDocsCondition.NAME));
43+
PARSER.declareString((conditions, s) ->
44+
conditions.add(new MaxSizeCondition(ByteSizeValue.parseBytesSizeValue(s, MaxSizeCondition.NAME))),
45+
new ParseField(MaxSizeCondition.NAME));
4146
}
4247

4348
protected T value;
@@ -49,6 +54,14 @@ protected Condition(String name) {
4954

5055
public abstract Result evaluate(Stats stats);
5156

57+
/**
58+
* Checks if this condition is available in a specific version.
59+
* This makes sure BWC when introducing a new condition which is not recognized by older versions.
60+
*/
61+
boolean includedInVersion(Version version) {
62+
return true;
63+
}
64+
5265
@Override
5366
public final String toString() {
5467
return "[" + name + ": " + value + "]";
@@ -60,10 +73,12 @@ public final String toString() {
6073
public static class Stats {
6174
public final long numDocs;
6275
public final long indexCreated;
76+
public final ByteSizeValue indexSize;
6377

64-
public Stats(long numDocs, long indexCreated) {
78+
public Stats(long numDocs, long indexCreated, ByteSizeValue indexSize) {
6579
this.numDocs = numDocs;
6680
this.indexCreated = indexCreated;
81+
this.indexSize = indexSize;
6782
}
6883
}
6984

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.action.admin.indices.rollover;
21+
22+
import org.elasticsearch.Version;
23+
import org.elasticsearch.common.io.stream.StreamInput;
24+
import org.elasticsearch.common.io.stream.StreamOutput;
25+
import org.elasticsearch.common.unit.ByteSizeUnit;
26+
import org.elasticsearch.common.unit.ByteSizeValue;
27+
28+
import java.io.IOException;
29+
30+
/**
31+
* A size-based condition for an index size.
32+
* Evaluates to <code>true</code> if the index size is at least {@link #value}.
33+
*/
34+
public class MaxSizeCondition extends Condition<ByteSizeValue> {
35+
public static final String NAME = "max_size";
36+
37+
public MaxSizeCondition(ByteSizeValue value) {
38+
super(NAME);
39+
this.value = value;
40+
}
41+
42+
public MaxSizeCondition(StreamInput in) throws IOException {
43+
super(NAME);
44+
this.value = new ByteSizeValue(in.readVLong(), ByteSizeUnit.BYTES);
45+
}
46+
47+
@Override
48+
public Result evaluate(Stats stats) {
49+
return new Result(this, stats.indexSize.getBytes() >= value.getBytes());
50+
}
51+
52+
@Override
53+
boolean includedInVersion(Version version) {
54+
return version.onOrAfter(Version.V_7_0_0_alpha1);
55+
}
56+
57+
@Override
58+
public String getWriteableName() {
59+
return NAME;
60+
}
61+
62+
@Override
63+
public void writeTo(StreamOutput out) throws IOException {
64+
out.writeVLong(value.getBytes());
65+
}
66+
}

core/src/main/java/org/elasticsearch/action/admin/indices/rollover/RolloverRequest.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.elasticsearch.common.ParseField;
2828
import org.elasticsearch.common.io.stream.StreamInput;
2929
import org.elasticsearch.common.io.stream.StreamOutput;
30+
import org.elasticsearch.common.unit.ByteSizeValue;
3031
import org.elasticsearch.common.unit.TimeValue;
3132
import org.elasticsearch.common.xcontent.ObjectParser;
3233

@@ -106,7 +107,9 @@ public void writeTo(StreamOutput out) throws IOException {
106107
out.writeBoolean(dryRun);
107108
out.writeVInt(conditions.size());
108109
for (Condition condition : conditions) {
109-
out.writeNamedWriteable(condition);
110+
if (condition.includedInVersion(out.getVersion())) {
111+
out.writeNamedWriteable(condition);
112+
}
110113
}
111114
createIndexRequest.writeTo(out);
112115
}
@@ -155,6 +158,13 @@ public void addMaxIndexDocsCondition(long numDocs) {
155158
this.conditions.add(new MaxDocsCondition(numDocs));
156159
}
157160

161+
/**
162+
* Adds a size-based condition to check if the index size is at least <code>size</code>.
163+
*/
164+
public void addMaxIndexSizeCondition(ByteSizeValue size) {
165+
this.conditions.add(new MaxSizeCondition(size));
166+
}
167+
158168
/**
159169
* Sets rollover index creation request to override index settings when
160170
* the rolled over index has to be created

core/src/main/java/org/elasticsearch/action/admin/indices/rollover/RolloverRequestBuilder.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.elasticsearch.action.support.master.MasterNodeOperationRequestBuilder;
2424
import org.elasticsearch.client.ElasticsearchClient;
2525
import org.elasticsearch.common.settings.Settings;
26+
import org.elasticsearch.common.unit.ByteSizeValue;
2627
import org.elasticsearch.common.unit.TimeValue;
2728

2829

@@ -52,6 +53,11 @@ public RolloverRequestBuilder addMaxIndexDocsCondition(long docs) {
5253
return this;
5354
}
5455

56+
public RolloverRequestBuilder addMaxIndexSizeCondition(ByteSizeValue size){
57+
this.request.addMaxIndexSizeCondition(size);
58+
return this;
59+
}
60+
5561
public RolloverRequestBuilder dryRun(boolean dryRun) {
5662
this.request.dryRun(dryRun);
5763
return this;

core/src/main/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverAction.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.elasticsearch.cluster.service.ClusterService;
4444
import org.elasticsearch.common.inject.Inject;
4545
import org.elasticsearch.common.settings.Settings;
46+
import org.elasticsearch.common.unit.ByteSizeValue;
4647
import org.elasticsearch.index.shard.DocsStats;
4748
import org.elasticsearch.threadpool.ThreadPool;
4849
import org.elasticsearch.transport.TransportService;
@@ -195,7 +196,8 @@ static String generateRolloverIndexName(String sourceIndexName, IndexNameExpress
195196
static Set<Condition.Result> evaluateConditions(final Set<Condition> conditions,
196197
final DocsStats docsStats, final IndexMetaData metaData) {
197198
final long numDocs = docsStats == null ? 0 : docsStats.getCount();
198-
final Condition.Stats stats = new Condition.Stats(numDocs, metaData.getCreationDate());
199+
final long indexSize = docsStats == null ? 0 : docsStats.getTotalSizeInBytes();
200+
final Condition.Stats stats = new Condition.Stats(numDocs, metaData.getCreationDate(), new ByteSizeValue(indexSize));
199201
return conditions.stream()
200202
.map(condition -> condition.evaluate(stats))
201203
.collect(Collectors.toSet());

core/src/main/java/org/elasticsearch/indices/IndicesModule.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.elasticsearch.action.admin.indices.rollover.Condition;
2323
import org.elasticsearch.action.admin.indices.rollover.MaxAgeCondition;
2424
import org.elasticsearch.action.admin.indices.rollover.MaxDocsCondition;
25+
import org.elasticsearch.action.admin.indices.rollover.MaxSizeCondition;
2526
import org.elasticsearch.action.resync.TransportResyncReplicationAction;
2627
import org.elasticsearch.index.shard.PrimaryReplicaSyncer;
2728
import org.elasticsearch.common.geo.ShapesAvailability;
@@ -79,6 +80,7 @@ public IndicesModule(List<MapperPlugin> mapperPlugins) {
7980
private void registerBuiltinWritables() {
8081
namedWritables.add(new Entry(Condition.class, MaxAgeCondition.NAME, MaxAgeCondition::new));
8182
namedWritables.add(new Entry(Condition.class, MaxDocsCondition.NAME, MaxDocsCondition::new));
83+
namedWritables.add(new Entry(Condition.class, MaxSizeCondition.NAME, MaxSizeCondition::new));
8284
}
8385

8486
public List<Entry> getNamedWriteables() {

core/src/test/java/org/elasticsearch/action/admin/indices/rollover/ConditionTests.java

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
package org.elasticsearch.action.admin.indices.rollover;
2121

22+
import org.elasticsearch.common.unit.ByteSizeUnit;
23+
import org.elasticsearch.common.unit.ByteSizeValue;
2224
import org.elasticsearch.common.unit.TimeValue;
2325
import org.elasticsearch.test.ESTestCase;
2426

@@ -30,12 +32,12 @@ public void testMaxAge() throws Exception {
3032
final MaxAgeCondition maxAgeCondition = new MaxAgeCondition(TimeValue.timeValueHours(1));
3133

3234
long indexCreatedMatch = System.currentTimeMillis() - TimeValue.timeValueMinutes(61).getMillis();
33-
Condition.Result evaluate = maxAgeCondition.evaluate(new Condition.Stats(0, indexCreatedMatch));
35+
Condition.Result evaluate = maxAgeCondition.evaluate(new Condition.Stats(0, indexCreatedMatch, randomByteSize()));
3436
assertThat(evaluate.condition, equalTo(maxAgeCondition));
3537
assertThat(evaluate.matched, equalTo(true));
3638

3739
long indexCreatedNotMatch = System.currentTimeMillis() - TimeValue.timeValueMinutes(59).getMillis();
38-
evaluate = maxAgeCondition.evaluate(new Condition.Stats(0, indexCreatedNotMatch));
40+
evaluate = maxAgeCondition.evaluate(new Condition.Stats(0, indexCreatedNotMatch, randomByteSize()));
3941
assertThat(evaluate.condition, equalTo(maxAgeCondition));
4042
assertThat(evaluate.matched, equalTo(false));
4143
}
@@ -44,13 +46,33 @@ public void testMaxDocs() throws Exception {
4446
final MaxDocsCondition maxDocsCondition = new MaxDocsCondition(100L);
4547

4648
long maxDocsMatch = randomIntBetween(100, 1000);
47-
Condition.Result evaluate = maxDocsCondition.evaluate(new Condition.Stats(maxDocsMatch, 0));
49+
Condition.Result evaluate = maxDocsCondition.evaluate(new Condition.Stats(maxDocsMatch, 0, randomByteSize()));
4850
assertThat(evaluate.condition, equalTo(maxDocsCondition));
4951
assertThat(evaluate.matched, equalTo(true));
5052

5153
long maxDocsNotMatch = randomIntBetween(0, 99);
52-
evaluate = maxDocsCondition.evaluate(new Condition.Stats(0, maxDocsNotMatch));
54+
evaluate = maxDocsCondition.evaluate(new Condition.Stats(0, maxDocsNotMatch, randomByteSize()));
5355
assertThat(evaluate.condition, equalTo(maxDocsCondition));
5456
assertThat(evaluate.matched, equalTo(false));
5557
}
58+
59+
public void testMaxSize() throws Exception {
60+
MaxSizeCondition maxSizeCondition = new MaxSizeCondition(new ByteSizeValue(randomIntBetween(10, 20), ByteSizeUnit.MB));
61+
62+
Condition.Result result = maxSizeCondition.evaluate(new Condition.Stats(randomNonNegativeLong(), randomNonNegativeLong(),
63+
new ByteSizeValue(0, ByteSizeUnit.MB)));
64+
assertThat(result.matched, equalTo(false));
65+
66+
result = maxSizeCondition.evaluate(new Condition.Stats(randomNonNegativeLong(), randomNonNegativeLong(),
67+
new ByteSizeValue(randomIntBetween(0, 9), ByteSizeUnit.MB)));
68+
assertThat(result.matched, equalTo(false));
69+
70+
result = maxSizeCondition.evaluate(new Condition.Stats(randomNonNegativeLong(), randomNonNegativeLong(),
71+
new ByteSizeValue(randomIntBetween(20, 1000), ByteSizeUnit.MB)));
72+
assertThat(result.matched, equalTo(true));
73+
}
74+
75+
private ByteSizeValue randomByteSize() {
76+
return new ByteSizeValue(randomNonNegativeLong(), ByteSizeUnit.BYTES);
77+
}
5678
}

core/src/test/java/org/elasticsearch/action/admin/indices/rollover/RolloverIT.java

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@
1919

2020
package org.elasticsearch.action.admin.indices.rollover;
2121

22+
import org.elasticsearch.ResourceAlreadyExistsException;
2223
import org.elasticsearch.action.admin.indices.alias.Alias;
2324
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
2425
import org.elasticsearch.cluster.ClusterState;
2526
import org.elasticsearch.cluster.metadata.IndexMetaData;
2627
import org.elasticsearch.common.settings.Settings;
28+
import org.elasticsearch.common.unit.ByteSizeUnit;
29+
import org.elasticsearch.common.unit.ByteSizeValue;
2730
import org.elasticsearch.common.unit.TimeValue;
28-
import org.elasticsearch.ResourceAlreadyExistsException;
2931
import org.elasticsearch.plugins.Plugin;
3032
import org.elasticsearch.test.ESIntegTestCase;
3133
import org.elasticsearch.test.InternalSettingsPlugin;
@@ -36,9 +38,15 @@
3638
import java.util.Collection;
3739
import java.util.Collections;
3840
import java.util.Map;
41+
import java.util.Set;
42+
import java.util.stream.Collectors;
3943

4044
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
45+
import static org.hamcrest.Matchers.containsInAnyOrder;
4146
import static org.hamcrest.Matchers.equalTo;
47+
import static org.hamcrest.Matchers.everyItem;
48+
import static org.hamcrest.Matchers.hasProperty;
49+
import static org.hamcrest.Matchers.is;
4250

4351
@ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST)
4452
public class RolloverIT extends ESIntegTestCase {
@@ -128,15 +136,23 @@ public void testRolloverConditionsNotMet() throws Exception {
128136
index("test_index-0", "type1", "1", "field", "value");
129137
flush("test_index-0");
130138
final RolloverResponse response = client().admin().indices().prepareRolloverIndex("test_alias")
139+
.addMaxIndexSizeCondition(new ByteSizeValue(10, ByteSizeUnit.MB))
131140
.addMaxIndexAgeCondition(TimeValue.timeValueHours(4)).get();
132141
assertThat(response.getOldIndex(), equalTo("test_index-0"));
133142
assertThat(response.getNewIndex(), equalTo("test_index-000001"));
134143
assertThat(response.isDryRun(), equalTo(false));
135144
assertThat(response.isRolledOver(), equalTo(false));
136-
assertThat(response.getConditionStatus().size(), equalTo(1));
137-
final Map.Entry<String, Boolean> conditionEntry = response.getConditionStatus().iterator().next();
138-
assertThat(conditionEntry.getKey(), equalTo(new MaxAgeCondition(TimeValue.timeValueHours(4)).toString()));
139-
assertThat(conditionEntry.getValue(), equalTo(false));
145+
assertThat(response.getConditionStatus().size(), equalTo(2));
146+
147+
148+
assertThat(response.getConditionStatus(), everyItem(hasProperty("value", is(false))));
149+
Set<String> conditions = response.getConditionStatus().stream()
150+
.map(Map.Entry::getKey)
151+
.collect(Collectors.toSet());
152+
assertThat(conditions, containsInAnyOrder(
153+
new MaxSizeCondition(new ByteSizeValue(10, ByteSizeUnit.MB)).toString(),
154+
new MaxAgeCondition(TimeValue.timeValueHours(4)).toString()));
155+
140156
final ClusterState state = client().admin().cluster().prepareState().get().getState();
141157
final IndexMetaData oldIndex = state.metaData().index("test_index-0");
142158
assertTrue(oldIndex.getAliases().containsKey("test_alias"));
@@ -218,4 +234,47 @@ public void testRolloverWithDateMath() {
218234
assertThat(response.isRolledOver(), equalTo(true));
219235
assertThat(response.getConditionStatus().size(), equalTo(0));
220236
}
237+
238+
public void testRolloverMaxSize() throws Exception {
239+
assertAcked(prepareCreate("test-1").addAlias(new Alias("test_alias")).get());
240+
int numDocs = randomIntBetween(10, 20);
241+
for (int i = 0; i < numDocs; i++) {
242+
index("test-1", "doc", Integer.toString(i), "field", "foo-" + i);
243+
}
244+
flush("test-1");
245+
refresh("test_alias");
246+
247+
// A large max_size
248+
{
249+
final RolloverResponse response = client().admin().indices()
250+
.prepareRolloverIndex("test_alias")
251+
.addMaxIndexSizeCondition(new ByteSizeValue(randomIntBetween(100, 50 * 1024), ByteSizeUnit.MB))
252+
.get();
253+
assertThat(response.getOldIndex(), equalTo("test-1"));
254+
assertThat(response.getNewIndex(), equalTo("test-000002"));
255+
assertThat("No rollover with a large max_size condition", response.isRolledOver(), equalTo(false));
256+
}
257+
258+
// A small max_size
259+
{
260+
final RolloverResponse response = client().admin().indices()
261+
.prepareRolloverIndex("test_alias")
262+
.addMaxIndexSizeCondition(new ByteSizeValue(randomIntBetween(1, 20), ByteSizeUnit.BYTES))
263+
.get();
264+
assertThat(response.getOldIndex(), equalTo("test-1"));
265+
assertThat(response.getNewIndex(), equalTo("test-000002"));
266+
assertThat("Should rollover with a small max_size condition", response.isRolledOver(), equalTo(true));
267+
}
268+
269+
// An empty index
270+
{
271+
final RolloverResponse response = client().admin().indices()
272+
.prepareRolloverIndex("test_alias")
273+
.addMaxIndexSizeCondition(new ByteSizeValue(randomNonNegativeLong(), ByteSizeUnit.BYTES))
274+
.get();
275+
assertThat(response.getOldIndex(), equalTo("test-000002"));
276+
assertThat(response.getNewIndex(), equalTo("test-000003"));
277+
assertThat("No rollover with an empty index", response.isRolledOver(), equalTo(false));
278+
}
279+
}
221280
}

0 commit comments

Comments
 (0)