diff --git a/docs/changelog/83345.yaml b/docs/changelog/83345.yaml new file mode 100644 index 0000000000000..570dc85b319e2 --- /dev/null +++ b/docs/changelog/83345.yaml @@ -0,0 +1,5 @@ +pr: 83345 +summary: Add min_* conditions to rollover +area: ILM+SLM +type: enhancement +issues: [] diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.rollover/10_basic.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.rollover/10_basic.yml index e989577c98d42..fd7d4c5b857ac 100644 --- a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.rollover/10_basic.yml +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.rollover/10_basic.yml @@ -20,7 +20,7 @@ body: { "foo": "hello world" } # make this doc visible in index stats refresh: true - + - do: get: index: logs_search @@ -149,3 +149,37 @@ conditions: max_docs: 1 +--- +"Will not rollover with only min_* conditions": + - skip: + version: " - 8.3.99" + reason: introduced in 8.4.0 + + # create index with alias and replica + - do: + indices.create: + index: logs-1 + wait_for_active_shards: 1 + body: + aliases: + logs_search: {} + + # index first document and wait for refresh + - do: + index: + index: logs-1 + id: "1" + body: { "foo": "hello world" } + refresh: true + + # perform alias rollover with no result + - do: + catch: bad_request + indices.rollover: + alias: "logs_search" + wait_for_active_shards: 1 + body: + conditions: + min_age: "0s" + min_docs: 1 + - match: { error.reason: "Validation Failed: 1: at least one max_* rollover condition must be set when using min_* conditions;" } diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.rollover/50_min_age_condition.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.rollover/50_min_age_condition.yml new file mode 100644 index 0000000000000..125d00b2faff9 --- /dev/null +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.rollover/50_min_age_condition.yml @@ -0,0 +1,48 @@ +--- +"Rollover with min_age condition": + - skip: + version: " - 8.3.99" + reason: introduced in 8.4.0 + + # create index with alias and replica + - do: + indices.create: + index: logs-1 + wait_for_active_shards: 1 + body: + aliases: + logs_search: {} + + # index first document and wait for refresh + - do: + index: + index: logs-1 + id: "1" + body: { "foo": "hello world" } + refresh: true + + # perform alias rollover with no result + - do: + indices.rollover: + alias: "logs_search" + wait_for_active_shards: 1 + body: + conditions: + max_docs: 1 + min_age: "7d" + + - match: { conditions: { "[max_docs: 1]": true, "[min_age: 7d]": false } } + - match: { rolled_over: false } + + # perform alias rollover + - do: + indices.rollover: + alias: "logs_search" + wait_for_active_shards: 1 + body: + conditions: + max_docs: 1 + min_age: "0s" + + - match: { conditions: { "[max_docs: 1]": true, "[min_age: 0s]": true } } + - match: { rolled_over: true } diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.rollover/55_min_docs_condition.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.rollover/55_min_docs_condition.yml new file mode 100644 index 0000000000000..99fa37ecfdcb6 --- /dev/null +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.rollover/55_min_docs_condition.yml @@ -0,0 +1,48 @@ +--- +"Rollover with min_docs condition": + - skip: + version: " - 8.3.99" + reason: introduced in 8.4.0 + + # create index with alias and replica + - do: + indices.create: + index: logs-1 + wait_for_active_shards: 1 + body: + aliases: + logs_search: {} + + # index first document and wait for refresh + - do: + index: + index: logs-1 + id: "1" + body: { "foo": "hello world" } + refresh: true + + # perform alias rollover with no result + - do: + indices.rollover: + alias: "logs_search" + wait_for_active_shards: 1 + body: + conditions: + max_docs: 1 + min_docs: 10 + + - match: { conditions: { "[max_docs: 1]": true, "[min_docs: 10]": false } } + - match: { rolled_over: false } + + # perform alias rollover + - do: + indices.rollover: + alias: "logs_search" + wait_for_active_shards: 1 + body: + conditions: + max_docs: 1 + min_docs: 1 + + - match: { conditions: { "[max_docs: 1]": true, "[min_docs: 1]": true } } + - match: { rolled_over: true } diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.rollover/60_min_primary_shard_docs_condition.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.rollover/60_min_primary_shard_docs_condition.yml new file mode 100644 index 0000000000000..c4226590d70ff --- /dev/null +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.rollover/60_min_primary_shard_docs_condition.yml @@ -0,0 +1,48 @@ +--- +"Rollover with min_primary_shard_docs condition": + - skip: + version: " - 8.3.99" + reason: introduced in 8.4.0 + + # create index with alias and replica + - do: + indices.create: + index: logs-1 + wait_for_active_shards: 1 + body: + aliases: + logs_search: {} + + # index first document and wait for refresh + - do: + index: + index: logs-1 + id: "1" + body: { "foo": "hello world" } + refresh: true + + # perform alias rollover with no result + - do: + indices.rollover: + alias: "logs_search" + wait_for_active_shards: 1 + body: + conditions: + max_docs: 1 + min_primary_shard_docs: 10 + + - match: { conditions: { "[max_docs: 1]": true, "[min_primary_shard_docs: 10]": false } } + - match: { rolled_over: false } + + # perform alias rollover + - do: + indices.rollover: + alias: "logs_search" + wait_for_active_shards: 1 + body: + conditions: + max_docs: 1 + min_primary_shard_docs: 1 + + - match: { conditions: { "[max_docs: 1]": true, "[min_primary_shard_docs: 1]": true } } + - match: { rolled_over: true } diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.rollover/65_min_primary_shard_size_condition.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.rollover/65_min_primary_shard_size_condition.yml new file mode 100644 index 0000000000000..fde9b8c5a922b --- /dev/null +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.rollover/65_min_primary_shard_size_condition.yml @@ -0,0 +1,48 @@ +--- +"Rollover with min_primary_shard_size condition": + - skip: + version: " - 8.3.99" + reason: introduced in 8.4.0 + + # create index with alias and replica + - do: + indices.create: + index: logs-1 + wait_for_active_shards: 1 + body: + aliases: + logs_search: {} + + # index first document and wait for refresh + - do: + index: + index: logs-1 + id: "1" + body: { "foo": "hello world" } + refresh: true + + # perform alias rollover with no result + - do: + indices.rollover: + alias: "logs_search" + wait_for_active_shards: 1 + body: + conditions: + max_docs: 1 + min_primary_shard_size: "50gb" + + - match: { conditions: { "[max_docs: 1]": true, "[min_primary_shard_size: 50gb]": false } } + - match: { rolled_over: false } + + # perform alias rollover + - do: + indices.rollover: + alias: "logs_search" + wait_for_active_shards: 1 + body: + conditions: + max_docs: 1 + min_primary_shard_size: "0b" + + - match: { conditions: { "[max_docs: 1]": true, "[min_primary_shard_size: 0b]": true } } + - match: { rolled_over: true } diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.rollover/70_min_size_condition.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.rollover/70_min_size_condition.yml new file mode 100644 index 0000000000000..400858d7942a7 --- /dev/null +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.rollover/70_min_size_condition.yml @@ -0,0 +1,48 @@ +--- +"Rollover with min_size condition": + - skip: + version: " - 8.3.99" + reason: introduced in 8.4.0 + + # create index with alias and replica + - do: + indices.create: + index: logs-1 + wait_for_active_shards: 1 + body: + aliases: + logs_search: {} + + # index first document and wait for refresh + - do: + index: + index: logs-1 + id: "1" + body: { "foo": "hello world" } + refresh: true + + # perform alias rollover with no result + - do: + indices.rollover: + alias: "logs_search" + wait_for_active_shards: 1 + body: + conditions: + max_docs: 1 + min_size: "50gb" + + - match: { conditions: { "[max_docs: 1]": true, "[min_size: 50gb]": false } } + - match: { rolled_over: false } + + # perform alias rollover + - do: + indices.rollover: + alias: "logs_search" + wait_for_active_shards: 1 + body: + conditions: + max_docs: 1 + min_size: "0b" + + - match: { conditions: { "[max_docs: 1]": true, "[min_size: 0b]": true } } + - match: { rolled_over: true } diff --git a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/rollover/RolloverIT.java b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/rollover/RolloverIT.java index 75fae401586e0..2f6344c04acf5 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/rollover/RolloverIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/action/admin/indices/rollover/RolloverIT.java @@ -472,6 +472,7 @@ public void testRolloverMaxSize() throws Exception { .indices() .prepareRolloverIndex("test_alias") .addMaxIndexSizeCondition(new ByteSizeValue(randomNonNegativeLong(), ByteSizeUnit.BYTES)) + .addMinIndexDocsCondition(1) .get(); assertThat(response.getOldIndex(), equalTo("test-000002")); assertThat(response.getNewIndex(), equalTo("test-000003")); @@ -532,6 +533,7 @@ public void testRolloverMaxPrimaryShardSize() throws Exception { .indices() .prepareRolloverIndex("test_alias") .addMaxPrimaryShardSizeCondition(new ByteSizeValue(randomNonNegativeLong(), ByteSizeUnit.BYTES)) + .addMinIndexDocsCondition(1) .get(); assertThat(response.getOldIndex(), equalTo("test-000002")); assertThat(response.getNewIndex(), equalTo("test-000003")); @@ -597,6 +599,7 @@ public void testRolloverMaxPrimaryShardDocs() throws Exception { .indices() .prepareRolloverIndex("test_alias") .addMaxPrimaryShardDocsCondition(randomNonNegativeLong()) + .addMinIndexDocsCondition(1) .get(); assertThat(response.getOldIndex(), equalTo("test-000002")); assertThat(response.getNewIndex(), equalTo("test-000003")); diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/Condition.java b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/Condition.java index dd30274a1cc0d..b61e73bbfa26b 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/Condition.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/Condition.java @@ -20,11 +20,21 @@ */ public abstract class Condition implements NamedWriteable, ToXContentFragment { + /** + * Describes the type of condition - a min_* condition (MIN) or max_* condition (MAX). + */ + public enum Type { + MIN, + MAX + } + protected T value; protected final String name; + protected final Type type; - protected Condition(String name) { + protected Condition(String name, Type type) { this.name = name; + this.type = type; } /** @@ -67,6 +77,10 @@ public String name() { return name; } + public Type type() { + return type; + } + /** * Holder for index stats used to evaluate conditions */ diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MaxAgeCondition.java b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MaxAgeCondition.java index 22d68feecf60c..a2c2456f788e0 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MaxAgeCondition.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MaxAgeCondition.java @@ -24,12 +24,12 @@ public class MaxAgeCondition extends Condition { public static final String NAME = "max_age"; public MaxAgeCondition(TimeValue value) { - super(NAME); + super(NAME, Type.MAX); this.value = value; } public MaxAgeCondition(StreamInput in) throws IOException { - super(NAME); + super(NAME, Type.MAX); this.value = TimeValue.timeValueMillis(in.readLong()); } @@ -64,7 +64,7 @@ public static MaxAgeCondition fromXContent(XContentParser parser) throws IOExcep if (parser.nextToken() == XContentParser.Token.VALUE_STRING) { return new MaxAgeCondition(TimeValue.parseTimeValue(parser.text(), NAME)); } else { - throw new IllegalArgumentException("invalid token: " + parser.currentToken()); + throw new IllegalArgumentException("invalid token when parsing " + NAME + " condition: " + parser.currentToken()); } } } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MaxDocsCondition.java b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MaxDocsCondition.java index 0348190eebdef..4bb64c451b074 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MaxDocsCondition.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MaxDocsCondition.java @@ -23,12 +23,12 @@ public class MaxDocsCondition extends Condition { public static final String NAME = "max_docs"; public MaxDocsCondition(Long value) { - super(NAME); + super(NAME, Type.MAX); this.value = value; } public MaxDocsCondition(StreamInput in) throws IOException { - super(NAME); + super(NAME, Type.MAX); this.value = in.readLong(); } @@ -56,7 +56,7 @@ public static MaxDocsCondition fromXContent(XContentParser parser) throws IOExce if (parser.nextToken() == XContentParser.Token.VALUE_NUMBER) { return new MaxDocsCondition(parser.longValue()); } else { - throw new IllegalArgumentException("invalid token: " + parser.currentToken()); + throw new IllegalArgumentException("invalid token when parsing " + NAME + " condition: " + parser.currentToken()); } } } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MaxPrimaryShardDocsCondition.java b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MaxPrimaryShardDocsCondition.java index ed0b2ff13685a..c27b4a7b7e739 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MaxPrimaryShardDocsCondition.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MaxPrimaryShardDocsCondition.java @@ -16,16 +16,20 @@ import java.io.IOException; +/** + * Condition for maximum shard docs. Evaluates to true + * when a primary shard in the index has at least {@link #value} docs + */ public class MaxPrimaryShardDocsCondition extends Condition { public static final String NAME = "max_primary_shard_docs"; public MaxPrimaryShardDocsCondition(Long value) { - super(NAME); + super(NAME, Type.MAX); this.value = value; } public MaxPrimaryShardDocsCondition(StreamInput in) throws IOException { - super(NAME); + super(NAME, Type.MAX); this.value = in.readLong(); } @@ -53,7 +57,7 @@ public static MaxPrimaryShardDocsCondition fromXContent(XContentParser parser) t if (parser.nextToken() == XContentParser.Token.VALUE_NUMBER) { return new MaxPrimaryShardDocsCondition(parser.longValue()); } else { - throw new IllegalArgumentException("invalid token: " + parser.currentToken()); + throw new IllegalArgumentException("invalid token when parsing " + NAME + " condition: " + parser.currentToken()); } } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MaxPrimaryShardSizeCondition.java b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MaxPrimaryShardSizeCondition.java index 131b721965fc2..73ac9cdfb82e8 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MaxPrimaryShardSizeCondition.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MaxPrimaryShardSizeCondition.java @@ -25,12 +25,12 @@ public class MaxPrimaryShardSizeCondition extends Condition { public static final String NAME = "max_primary_shard_size"; public MaxPrimaryShardSizeCondition(ByteSizeValue value) { - super(NAME); + super(NAME, Type.MAX); this.value = value; } public MaxPrimaryShardSizeCondition(StreamInput in) throws IOException { - super(NAME); + super(NAME, Type.MAX); this.value = new ByteSizeValue(in.readVLong(), ByteSizeUnit.BYTES); } @@ -62,7 +62,7 @@ public static MaxPrimaryShardSizeCondition fromXContent(XContentParser parser) t if (parser.nextToken() == XContentParser.Token.VALUE_STRING) { return new MaxPrimaryShardSizeCondition(ByteSizeValue.parseBytesSizeValue(parser.text(), NAME)); } else { - throw new IllegalArgumentException("invalid token: " + parser.currentToken()); + throw new IllegalArgumentException("invalid token when parsing " + NAME + " condition: " + parser.currentToken()); } } } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MaxSizeCondition.java b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MaxSizeCondition.java index 8284c4c0f4c45..0aff7d6e504f0 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MaxSizeCondition.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MaxSizeCondition.java @@ -18,19 +18,19 @@ import java.io.IOException; /** - * A size-based condition for an index size. + * A maximum size-based condition for an index size. * Evaluates to true if the index size is at least {@link #value}. */ public class MaxSizeCondition extends Condition { public static final String NAME = "max_size"; public MaxSizeCondition(ByteSizeValue value) { - super(NAME); + super(NAME, Type.MAX); this.value = value; } public MaxSizeCondition(StreamInput in) throws IOException { - super(NAME); + super(NAME, Type.MAX); this.value = new ByteSizeValue(in.readVLong(), ByteSizeUnit.BYTES); } @@ -62,7 +62,7 @@ public static MaxSizeCondition fromXContent(XContentParser parser) throws IOExce if (parser.nextToken() == XContentParser.Token.VALUE_STRING) { return new MaxSizeCondition(ByteSizeValue.parseBytesSizeValue(parser.text(), NAME)); } else { - throw new IllegalArgumentException("invalid token: " + parser.currentToken()); + throw new IllegalArgumentException("invalid token when parsing " + NAME + " condition: " + parser.currentToken()); } } } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MinAgeCondition.java b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MinAgeCondition.java new file mode 100644 index 0000000000000..ddcfadd53dd74 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MinAgeCondition.java @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.action.admin.indices.rollover; + +import org.elasticsearch.Version; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.core.TimeValue; +import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.XContentParser; + +import java.io.IOException; + +/** + * Condition for index minimum age. Evaluates to true + * when the index is at least {@link #value} old + */ +public class MinAgeCondition extends Condition { + public static final String NAME = "min_age"; + + public MinAgeCondition(TimeValue value) { + super(NAME, Type.MIN); + this.value = value; + } + + public MinAgeCondition(StreamInput in) throws IOException { + super(NAME, Type.MIN); + this.value = in.readTimeValue(); + } + + @Override + public Result evaluate(final Stats stats) { + long indexAge = System.currentTimeMillis() - stats.indexCreated(); + return new Result(this, this.value.getMillis() <= indexAge); + } + + @Override + public String getWriteableName() { + return NAME; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeTimeValue(value); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.field(NAME, value.getStringRep()); + } + + public static MinAgeCondition fromXContent(XContentParser parser) throws IOException { + if (parser.nextToken() == XContentParser.Token.VALUE_STRING) { + return new MinAgeCondition(TimeValue.parseTimeValue(parser.text(), NAME)); + } else { + throw new IllegalArgumentException("invalid token when parsing " + NAME + " condition: " + parser.currentToken()); + } + } + + @Override + boolean includedInVersion(Version version) { + return version.onOrAfter(Version.V_8_4_0); + } +} diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MinDocsCondition.java b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MinDocsCondition.java new file mode 100644 index 0000000000000..9a4fffc17018f --- /dev/null +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MinDocsCondition.java @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.action.admin.indices.rollover; + +import org.elasticsearch.Version; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.XContentParser; + +import java.io.IOException; + +/** + * Condition for minimum index docs. Evaluates to true + * when the index has at least {@link #value} docs + */ +public class MinDocsCondition extends Condition { + public static final String NAME = "min_docs"; + + public MinDocsCondition(Long value) { + super(NAME, Type.MIN); + this.value = value; + } + + public MinDocsCondition(StreamInput in) throws IOException { + super(NAME, Type.MIN); + this.value = in.readLong(); + } + + @Override + public Result evaluate(final Stats stats) { + return new Result(this, this.value <= stats.numDocs()); + } + + @Override + public String getWriteableName() { + return NAME; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeLong(value); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.field(NAME, value); + } + + public static MinDocsCondition fromXContent(XContentParser parser) throws IOException { + if (parser.nextToken() == XContentParser.Token.VALUE_NUMBER) { + return new MinDocsCondition(parser.longValue()); + } else { + throw new IllegalArgumentException("invalid token when parsing " + NAME + " condition: " + parser.currentToken()); + } + } + + @Override + boolean includedInVersion(Version version) { + return version.onOrAfter(Version.V_8_4_0); + } +} diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MinPrimaryShardDocsCondition.java b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MinPrimaryShardDocsCondition.java new file mode 100644 index 0000000000000..e1aee305742f3 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MinPrimaryShardDocsCondition.java @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.action.admin.indices.rollover; + +import org.elasticsearch.Version; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.XContentParser; + +import java.io.IOException; + +/** + * Condition for minimum shard docs. Evaluates to true + * when a primary shard in the index has at least {@link #value} docs + */ +public class MinPrimaryShardDocsCondition extends Condition { + public static final String NAME = "min_primary_shard_docs"; + + public MinPrimaryShardDocsCondition(Long value) { + super(NAME, Type.MIN); + this.value = value; + } + + public MinPrimaryShardDocsCondition(StreamInput in) throws IOException { + super(NAME, Type.MIN); + this.value = in.readLong(); + } + + @Override + public Result evaluate(Stats stats) { + return new Result(this, this.value <= stats.maxPrimaryShardDocs()); + } + + @Override + public String getWriteableName() { + return NAME; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeLong(value); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.field(NAME, value); + } + + public static MinPrimaryShardDocsCondition fromXContent(XContentParser parser) throws IOException { + if (parser.nextToken() == XContentParser.Token.VALUE_NUMBER) { + return new MinPrimaryShardDocsCondition(parser.longValue()); + } else { + throw new IllegalArgumentException("invalid token when parsing " + NAME + " condition: " + parser.currentToken()); + } + } + + @Override + boolean includedInVersion(Version version) { + return version.onOrAfter(Version.V_8_4_0); + } +} diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MinPrimaryShardSizeCondition.java b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MinPrimaryShardSizeCondition.java new file mode 100644 index 0000000000000..0a9c417c0bd6b --- /dev/null +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MinPrimaryShardSizeCondition.java @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.action.admin.indices.rollover; + +import org.elasticsearch.Version; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.XContentParser; + +import java.io.IOException; + +/** + * A size-based condition for the primary shards within an index. + * Evaluates to true if the size of the largest primary shard is at least {@link #value}. + */ +public class MinPrimaryShardSizeCondition extends Condition { + public static final String NAME = "min_primary_shard_size"; + + public MinPrimaryShardSizeCondition(ByteSizeValue value) { + super(NAME, Type.MIN); + this.value = value; + } + + public MinPrimaryShardSizeCondition(StreamInput in) throws IOException { + super(NAME, Type.MIN); + this.value = new ByteSizeValue(in); + } + + @Override + public Result evaluate(Stats stats) { + return new Result(this, stats.maxPrimaryShardSize().getBytes() >= value.getBytes()); + } + + @Override + public String getWriteableName() { + return NAME; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + value.writeTo(out); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.field(NAME, value.getStringRep()); + } + + public static MinPrimaryShardSizeCondition fromXContent(XContentParser parser) throws IOException { + if (parser.nextToken() == XContentParser.Token.VALUE_STRING) { + return new MinPrimaryShardSizeCondition(ByteSizeValue.parseBytesSizeValue(parser.text(), NAME)); + } else { + throw new IllegalArgumentException("invalid token when parsing " + NAME + " condition: " + parser.currentToken()); + } + } + + @Override + boolean includedInVersion(Version version) { + return version.onOrAfter(Version.V_8_4_0); + } +} diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MinSizeCondition.java b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MinSizeCondition.java new file mode 100644 index 0000000000000..56c2b833e23ed --- /dev/null +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MinSizeCondition.java @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.action.admin.indices.rollover; + +import org.elasticsearch.Version; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.XContentParser; + +import java.io.IOException; + +/** + * A minimum size-based condition for an index size. + * Evaluates to true if the index size is at least {@link #value}. + */ +public class MinSizeCondition extends Condition { + public static final String NAME = "min_size"; + + public MinSizeCondition(ByteSizeValue value) { + super(NAME, Type.MIN); + this.value = value; + } + + public MinSizeCondition(StreamInput in) throws IOException { + super(NAME, Type.MIN); + this.value = new ByteSizeValue(in); + } + + @Override + public Result evaluate(Stats stats) { + return new Result(this, stats.indexSize().getBytes() >= value.getBytes()); + } + + @Override + public String getWriteableName() { + return NAME; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + value.writeTo(out); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.field(NAME, value.getStringRep()); + } + + public static MinSizeCondition fromXContent(XContentParser parser) throws IOException { + if (parser.nextToken() == XContentParser.Token.VALUE_STRING) { + return new MinSizeCondition(ByteSizeValue.parseBytesSizeValue(parser.text(), NAME)); + } else { + throw new IllegalArgumentException("invalid token when parsing " + NAME + " condition: " + parser.currentToken()); + } + } + + @Override + boolean includedInVersion(Version version) { + return version.onOrAfter(Version.V_8_4_0); + } +} diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/RolloverRequest.java b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/RolloverRequest.java index 1f1bd241afa88..9916acbef125f 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/RolloverRequest.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/RolloverRequest.java @@ -28,13 +28,14 @@ import org.elasticsearch.xcontent.XContentParser; import java.io.IOException; +import java.util.Collections; import java.util.Map; import static org.elasticsearch.action.ValidateActions.addValidationError; /** * Request class to swap index under an alias or increment data stream generation upon satisfying conditions - * + *

* Note: there is a new class with the same name for the Java HLRC that uses a typeless format. * Any changes done to this class should also go to that client class. */ @@ -49,6 +50,11 @@ public class RolloverRequest extends AcknowledgedRequest implem private static final ParseField MAX_SIZE_CONDITION = new ParseField(MaxSizeCondition.NAME); private static final ParseField MAX_PRIMARY_SHARD_SIZE_CONDITION = new ParseField(MaxPrimaryShardSizeCondition.NAME); private static final ParseField MAX_PRIMARY_SHARD_DOCS_CONDITION = new ParseField(MaxPrimaryShardDocsCondition.NAME); + private static final ParseField MIN_AGE_CONDITION = new ParseField(MinAgeCondition.NAME); + private static final ParseField MIN_DOCS_CONDITION = new ParseField(MinDocsCondition.NAME); + private static final ParseField MIN_SIZE_CONDITION = new ParseField(MinSizeCondition.NAME); + private static final ParseField MIN_PRIMARY_SHARD_SIZE_CONDITION = new ParseField(MinPrimaryShardSizeCondition.NAME); + private static final ParseField MIN_PRIMARY_SHARD_DOCS_CONDITION = new ParseField(MinPrimaryShardDocsCondition.NAME); static { CONDITION_PARSER.declareString( @@ -77,6 +83,32 @@ public class RolloverRequest extends AcknowledgedRequest implem (conditions, value) -> conditions.put(MaxPrimaryShardDocsCondition.NAME, new MaxPrimaryShardDocsCondition(value)), MAX_PRIMARY_SHARD_DOCS_CONDITION ); + CONDITION_PARSER.declareString( + (conditions, s) -> conditions.put(MinAgeCondition.NAME, new MinAgeCondition(TimeValue.parseTimeValue(s, MinAgeCondition.NAME))), + MIN_AGE_CONDITION + ); + CONDITION_PARSER.declareLong( + (conditions, value) -> conditions.put(MinDocsCondition.NAME, new MinDocsCondition(value)), + MIN_DOCS_CONDITION + ); + CONDITION_PARSER.declareString( + (conditions, s) -> conditions.put( + MinSizeCondition.NAME, + new MinSizeCondition(ByteSizeValue.parseBytesSizeValue(s, MinSizeCondition.NAME)) + ), + MIN_SIZE_CONDITION + ); + CONDITION_PARSER.declareString( + (conditions, s) -> conditions.put( + MinPrimaryShardSizeCondition.NAME, + new MinPrimaryShardSizeCondition(ByteSizeValue.parseBytesSizeValue(s, MinPrimaryShardSizeCondition.NAME)) + ), + MIN_PRIMARY_SHARD_SIZE_CONDITION + ); + CONDITION_PARSER.declareLong( + (conditions, value) -> conditions.put(MinPrimaryShardDocsCondition.NAME, new MinPrimaryShardDocsCondition(value)), + MIN_PRIMARY_SHARD_DOCS_CONDITION + ); PARSER.declareField( (parser, request, context) -> CONDITION_PARSER.parse(parser, request.conditions, null), @@ -160,6 +192,16 @@ public ActionRequestValidationException validate() { if (rolloverTarget == null) { validationException = addValidationError("rollover target is missing", validationException); } + + // if the request has any conditions, then at least one condition must be a max_* condition + boolean noMaxConditions = conditions.values().stream().noneMatch(c -> Condition.Type.MAX == c.type()); + if (conditions.size() > 0 && noMaxConditions) { + validationException = addValidationError( + "at least one max_* rollover condition must be set when using min_* conditions", + validationException + ); + } + return validationException; } @@ -274,12 +316,67 @@ public void addMaxPrimaryShardDocsCondition(long numDocs) { this.conditions.put(maxPrimaryShardDocsCondition.name, maxPrimaryShardDocsCondition); } + /** + * Adds required condition to check if the index is at least age old + */ + public void addMinIndexAgeCondition(TimeValue age) { + MinAgeCondition minAgeCondition = new MinAgeCondition(age); + if (this.conditions.containsKey(minAgeCondition.name)) { + throw new IllegalArgumentException(minAgeCondition.name + " condition is already set"); + } + this.conditions.put(minAgeCondition.name, minAgeCondition); + } + + /** + * Adds required condition to check if the index has at least numDocs + */ + public void addMinIndexDocsCondition(long numDocs) { + MinDocsCondition minDocsCondition = new MinDocsCondition(numDocs); + if (this.conditions.containsKey(minDocsCondition.name)) { + throw new IllegalArgumentException(minDocsCondition.name + " condition is already set"); + } + this.conditions.put(minDocsCondition.name, minDocsCondition); + } + + /** + * Adds a size-based required condition to check if the index size is at least size. + */ + public void addMinIndexSizeCondition(ByteSizeValue size) { + MinSizeCondition minSizeCondition = new MinSizeCondition(size); + if (this.conditions.containsKey(minSizeCondition.name)) { + throw new IllegalArgumentException(minSizeCondition + " condition is already set"); + } + this.conditions.put(minSizeCondition.name, minSizeCondition); + } + + /** + * Adds a size-based required condition to check if the size of the largest primary shard is at least size. + */ + public void addMinPrimaryShardSizeCondition(ByteSizeValue size) { + MinPrimaryShardSizeCondition minPrimaryShardSizeCondition = new MinPrimaryShardSizeCondition(size); + if (this.conditions.containsKey(minPrimaryShardSizeCondition.name)) { + throw new IllegalArgumentException(minPrimaryShardSizeCondition + " condition is already set"); + } + this.conditions.put(minPrimaryShardSizeCondition.name, minPrimaryShardSizeCondition); + } + + /** + * Adds a size-based required condition to check if the docs of the largest primary shard has at least numDocs + */ + public void addMinPrimaryShardDocsCondition(long numDocs) { + MinPrimaryShardDocsCondition minPrimaryShardDocsCondition = new MinPrimaryShardDocsCondition(numDocs); + if (this.conditions.containsKey(minPrimaryShardDocsCondition.name)) { + throw new IllegalArgumentException(minPrimaryShardDocsCondition.name + " condition is already set"); + } + this.conditions.put(minPrimaryShardDocsCondition.name, minPrimaryShardDocsCondition); + } + public boolean isDryRun() { return dryRun; } public Map> getConditions() { - return conditions; + return Collections.unmodifiableMap(conditions); } public String getRolloverTarget() { @@ -290,6 +387,32 @@ public String getNewIndexName() { return newIndexName; } + /** + * Given the results of evaluating each individual condition, determine whether the rollover request should proceed -- that is, + * whether the conditions are met. + * + * If there are no conditions at all, then the request is unconditional (i.e. a command), and the conditions are met. + * + * If the request has conditions, then all min_* conditions and at least one max_* condition must have a true result. + * + * @param conditionResults a map of individual conditions and their associated evaluation results + * + * @return where the conditions for rollover are satisfied or not + */ + public boolean areConditionsMet(Map conditionResults) { + boolean allMinConditionsMet = conditions.values() + .stream() + .filter(c -> Condition.Type.MIN == c.type()) + .allMatch(c -> conditionResults.getOrDefault(c.toString(), false)); + + boolean anyMaxConditionsMet = conditions.values() + .stream() + .filter(c -> Condition.Type.MAX == c.type()) + .anyMatch(c -> conditionResults.getOrDefault(c.toString(), false)); + + return conditionResults.size() == 0 || (allMinConditionsMet && anyMaxConditionsMet); + } + /** * Returns the inner {@link CreateIndexRequest}. Allows to configure mappings, settings and aliases for the new index. */ diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/RolloverRequestBuilder.java b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/RolloverRequestBuilder.java index 27300b6213f56..9ff7cb067b6b7 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/RolloverRequestBuilder.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/RolloverRequestBuilder.java @@ -55,6 +55,31 @@ public RolloverRequestBuilder addMaxPrimaryShardDocsCondition(long docs) { return this; } + public RolloverRequestBuilder addMinIndexAgeCondition(TimeValue age) { + this.request.addMinIndexAgeCondition(age); + return this; + } + + public RolloverRequestBuilder addMinIndexDocsCondition(long docs) { + this.request.addMinIndexDocsCondition(docs); + return this; + } + + public RolloverRequestBuilder addMinIndexSizeCondition(ByteSizeValue size) { + this.request.addMinIndexSizeCondition(size); + return this; + } + + public RolloverRequestBuilder addMinPrimaryShardSizeCondition(ByteSizeValue size) { + this.request.addMinPrimaryShardSizeCondition(size); + return this; + } + + public RolloverRequestBuilder addMinPrimaryShardDocsCondition(long docs) { + this.request.addMinPrimaryShardDocsCondition(docs); + return this; + } + public RolloverRequestBuilder dryRun(boolean dryRun) { this.request.dryRun(dryRun); return this; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverAction.java index 2c2ac77296445..d0248a09f505e 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverAction.java @@ -153,32 +153,24 @@ protected void masterOperation( buildStats(metadata.index(trialSourceIndexName), statsResponse) ); - // If this is a dry run, return with the results without invoking a cluster state update - if (rolloverRequest.isDryRun()) { - listener.onResponse( - new RolloverResponse(trialSourceIndexName, trialRolloverIndexName, trialConditionResults, true, false, false, false) - ); - return; - } - - final List> trialMetConditions = rolloverRequest.getConditions() - .values() - .stream() - .filter(condition -> trialConditionResults.get(condition.toString())) - .toList(); - final RolloverResponse trialRolloverResponse = new RolloverResponse( trialSourceIndexName, trialRolloverIndexName, trialConditionResults, - false, + rolloverRequest.isDryRun(), false, false, false ); + // If this is a dry run, return with the results without invoking a cluster state update + if (rolloverRequest.isDryRun()) { + listener.onResponse(trialRolloverResponse); + return; + } + // Pre-check the conditions to see whether we should submit a new cluster state task - if (trialConditionResults.size() == 0 || trialMetConditions.size() > 0) { + if (rolloverRequest.areConditionsMet(trialConditionResults)) { String source = "rollover_index source [" + trialRolloverIndexName + "] to target [" + trialRolloverIndexName + "]"; RolloverTask rolloverTask = new RolloverTask(rolloverRequest, statsResponse, trialRolloverResponse, listener); ClusterStateTaskConfig config = ClusterStateTaskConfig.build(Priority.NORMAL, rolloverRequest.masterNodeTimeout()); @@ -315,13 +307,13 @@ public ClusterState executeTask( rolloverRequest.getConditions().values(), buildStats(currentState.metadata().index(sourceIndexName), rolloverTask.statsResponse()) ); - final List> metConditions = rolloverRequest.getConditions() - .values() - .stream() - .filter(condition -> postConditionResults.get(condition.toString())) - .toList(); - if (postConditionResults.size() == 0 || metConditions.size() > 0) { + if (rolloverRequest.areConditionsMet(postConditionResults)) { + final List> metConditions = rolloverRequest.getConditions() + .values() + .stream() + .filter(condition -> postConditionResults.get(condition.toString())) + .toList(); // Perform the actual rollover final var rolloverResult = rolloverService.rolloverClusterState( diff --git a/server/src/main/java/org/elasticsearch/cluster/coordination/ElasticsearchNodeCommand.java b/server/src/main/java/org/elasticsearch/cluster/coordination/ElasticsearchNodeCommand.java index cf60c8b7222e7..6e996bc29a52e 100644 --- a/server/src/main/java/org/elasticsearch/cluster/coordination/ElasticsearchNodeCommand.java +++ b/server/src/main/java/org/elasticsearch/cluster/coordination/ElasticsearchNodeCommand.java @@ -237,7 +237,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws public static class UnknownCondition extends Condition { public UnknownCondition(String name, Object value) { - super(name); + super(name, null); this.value = value; } @@ -262,5 +262,11 @@ public Result evaluate(Stats stats) { assert false; throw new UnsupportedOperationException(); } + + @Override + public Type type() { + assert false; + throw new UnsupportedOperationException(); + } } } diff --git a/server/src/main/java/org/elasticsearch/indices/IndicesModule.java b/server/src/main/java/org/elasticsearch/indices/IndicesModule.java index 2d2f21b42bd8c..181852c2c3bc9 100644 --- a/server/src/main/java/org/elasticsearch/indices/IndicesModule.java +++ b/server/src/main/java/org/elasticsearch/indices/IndicesModule.java @@ -14,6 +14,11 @@ import org.elasticsearch.action.admin.indices.rollover.MaxPrimaryShardDocsCondition; import org.elasticsearch.action.admin.indices.rollover.MaxPrimaryShardSizeCondition; import org.elasticsearch.action.admin.indices.rollover.MaxSizeCondition; +import org.elasticsearch.action.admin.indices.rollover.MinAgeCondition; +import org.elasticsearch.action.admin.indices.rollover.MinDocsCondition; +import org.elasticsearch.action.admin.indices.rollover.MinPrimaryShardDocsCondition; +import org.elasticsearch.action.admin.indices.rollover.MinPrimaryShardSizeCondition; +import org.elasticsearch.action.admin.indices.rollover.MinSizeCondition; import org.elasticsearch.action.resync.TransportResyncReplicationAction; import org.elasticsearch.common.inject.AbstractModule; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; @@ -94,6 +99,11 @@ public IndicesModule(List mapperPlugins) { public static List getNamedWriteables() { return Arrays.asList( + new NamedWriteableRegistry.Entry(Condition.class, MinAgeCondition.NAME, MinAgeCondition::new), + new NamedWriteableRegistry.Entry(Condition.class, MinDocsCondition.NAME, MinDocsCondition::new), + new NamedWriteableRegistry.Entry(Condition.class, MinSizeCondition.NAME, MinSizeCondition::new), + new NamedWriteableRegistry.Entry(Condition.class, MinPrimaryShardSizeCondition.NAME, MinPrimaryShardSizeCondition::new), + new NamedWriteableRegistry.Entry(Condition.class, MinPrimaryShardDocsCondition.NAME, MinPrimaryShardDocsCondition::new), new NamedWriteableRegistry.Entry(Condition.class, MaxAgeCondition.NAME, MaxAgeCondition::new), new NamedWriteableRegistry.Entry(Condition.class, MaxDocsCondition.NAME, MaxDocsCondition::new), new NamedWriteableRegistry.Entry(Condition.class, MaxSizeCondition.NAME, MaxSizeCondition::new), @@ -104,6 +114,31 @@ public static List getNamedWriteables() { public static List getNamedXContents() { return Arrays.asList( + new NamedXContentRegistry.Entry( + Condition.class, + new ParseField(MinAgeCondition.NAME), + (p, c) -> MinAgeCondition.fromXContent(p) + ), + new NamedXContentRegistry.Entry( + Condition.class, + new ParseField(MinDocsCondition.NAME), + (p, c) -> MinDocsCondition.fromXContent(p) + ), + new NamedXContentRegistry.Entry( + Condition.class, + new ParseField(MinSizeCondition.NAME), + (p, c) -> MinSizeCondition.fromXContent(p) + ), + new NamedXContentRegistry.Entry( + Condition.class, + new ParseField(MinPrimaryShardSizeCondition.NAME), + (p, c) -> MinPrimaryShardSizeCondition.fromXContent(p) + ), + new NamedXContentRegistry.Entry( + Condition.class, + new ParseField(MinPrimaryShardDocsCondition.NAME), + (p, c) -> MinPrimaryShardDocsCondition.fromXContent(p) + ), new NamedXContentRegistry.Entry( Condition.class, new ParseField(MaxAgeCondition.NAME), diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/ConditionTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/ConditionTests.java index ae3cbdfedcdad..f99a589b6a997 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/ConditionTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/ConditionTests.java @@ -147,6 +147,136 @@ public void testMaxPrimaryShardDocs() { assertThat(evaluate.matched(), equalTo(false)); } + public void testMinAge() { + final MinAgeCondition minAgeCondition = new MinAgeCondition(TimeValue.timeValueHours(1)); + + long indexCreatedMatch = System.currentTimeMillis() - TimeValue.timeValueMinutes(61).getMillis(); + Condition.Result evaluate = minAgeCondition.evaluate( + new Condition.Stats(0, indexCreatedMatch, randomByteSize(), randomByteSize(), randomNonNegativeLong()) + ); + assertThat(evaluate.condition(), equalTo(minAgeCondition)); + assertThat(evaluate.matched(), equalTo(true)); + + long indexCreatedNotMatch = System.currentTimeMillis() - TimeValue.timeValueMinutes(59).getMillis(); + evaluate = minAgeCondition.evaluate( + new Condition.Stats(0, indexCreatedNotMatch, randomByteSize(), randomByteSize(), randomNonNegativeLong()) + ); + assertThat(evaluate.condition(), equalTo(minAgeCondition)); + assertThat(evaluate.matched(), equalTo(false)); + } + + public void testMinDocs() { + final MinDocsCondition minDocsCondition = new MinDocsCondition(100L); + + long minDocsMatch = randomIntBetween(100, 1000); + Condition.Result evaluate = minDocsCondition.evaluate( + new Condition.Stats(minDocsMatch, 0, randomByteSize(), randomByteSize(), randomNonNegativeLong()) + ); + assertThat(evaluate.condition(), equalTo(minDocsCondition)); + assertThat(evaluate.matched(), equalTo(true)); + + long minDocsNotMatch = randomIntBetween(0, 99); + evaluate = minDocsCondition.evaluate( + new Condition.Stats(minDocsNotMatch, 0, randomByteSize(), randomByteSize(), randomNonNegativeLong()) + ); + assertThat(evaluate.condition(), equalTo(minDocsCondition)); + assertThat(evaluate.matched(), equalTo(false)); + } + + public void testMinSize() { + MinSizeCondition minSizeCondition = new MinSizeCondition(ByteSizeValue.ofMb(randomIntBetween(10, 20))); + + Condition.Result result = minSizeCondition.evaluate( + new Condition.Stats( + randomNonNegativeLong(), + randomNonNegativeLong(), + ByteSizeValue.ofMb(0), + randomByteSize(), + randomNonNegativeLong() + ) + ); + assertThat(result.matched(), equalTo(false)); + + result = minSizeCondition.evaluate( + new Condition.Stats( + randomNonNegativeLong(), + randomNonNegativeLong(), + ByteSizeValue.ofMb(randomIntBetween(0, 9)), + randomByteSize(), + randomNonNegativeLong() + ) + ); + assertThat(result.matched(), equalTo(false)); + + result = minSizeCondition.evaluate( + new Condition.Stats( + randomNonNegativeLong(), + randomNonNegativeLong(), + ByteSizeValue.ofMb(randomIntBetween(20, 1000)), + randomByteSize(), + randomNonNegativeLong() + ) + ); + assertThat(result.matched(), equalTo(true)); + } + + public void testMinPrimaryShardSize() { + MinPrimaryShardSizeCondition minPrimaryShardSizeCondition = new MinPrimaryShardSizeCondition( + ByteSizeValue.ofMb(randomIntBetween(10, 20)) + ); + + Condition.Result result = minPrimaryShardSizeCondition.evaluate( + new Condition.Stats( + randomNonNegativeLong(), + randomNonNegativeLong(), + randomByteSize(), + ByteSizeValue.ofMb(0), + randomNonNegativeLong() + ) + ); + assertThat(result.matched(), equalTo(false)); + + result = minPrimaryShardSizeCondition.evaluate( + new Condition.Stats( + randomNonNegativeLong(), + randomNonNegativeLong(), + randomByteSize(), + ByteSizeValue.ofMb(randomIntBetween(0, 9)), + randomNonNegativeLong() + ) + ); + assertThat(result.matched(), equalTo(false)); + + result = minPrimaryShardSizeCondition.evaluate( + new Condition.Stats( + randomNonNegativeLong(), + randomNonNegativeLong(), + randomByteSize(), + ByteSizeValue.ofMb(randomIntBetween(20, 1000)), + randomNonNegativeLong() + ) + ); + assertThat(result.matched(), equalTo(true)); + } + + public void testMinPrimaryShardDocs() { + final MinPrimaryShardDocsCondition minPrimaryShardDocsCondition = new MinPrimaryShardDocsCondition(100L); + + long minPrimaryShardDocsMatch = randomIntBetween(100, 1000); + Condition.Result evaluate = minPrimaryShardDocsCondition.evaluate( + new Condition.Stats(randomNonNegativeLong(), 0, randomByteSize(), randomByteSize(), minPrimaryShardDocsMatch) + ); + assertThat(evaluate.condition(), equalTo(minPrimaryShardDocsCondition)); + assertThat(evaluate.matched(), equalTo(true)); + + long minPrimaryShardDocsNotMatch = randomIntBetween(0, 99); + evaluate = minPrimaryShardDocsCondition.evaluate( + new Condition.Stats(randomNonNegativeLong(), 0, randomByteSize(), randomByteSize(), minPrimaryShardDocsNotMatch) + ); + assertThat(evaluate.condition(), equalTo(minPrimaryShardDocsCondition)); + assertThat(evaluate.matched(), equalTo(false)); + } + public void testEqualsAndHashCode() { MaxAgeCondition maxAgeCondition = new MaxAgeCondition(new TimeValue(randomNonNegativeLong())); EqualsHashCodeTestUtils.checkEqualsAndHashCode( @@ -182,6 +312,41 @@ public void testEqualsAndHashCode() { condition -> new MaxPrimaryShardDocsCondition(condition.value), condition -> new MaxPrimaryShardDocsCondition(randomNonNegativeLong()) ); + + MinAgeCondition minAgeCondition = new MinAgeCondition(new TimeValue(randomNonNegativeLong())); + EqualsHashCodeTestUtils.checkEqualsAndHashCode( + minAgeCondition, + condition -> new MinAgeCondition(condition.value), + condition -> new MinAgeCondition(new TimeValue(randomNonNegativeLong())) + ); + + MinDocsCondition minDocsCondition = new MinDocsCondition(randomLong()); + EqualsHashCodeTestUtils.checkEqualsAndHashCode( + minDocsCondition, + condition -> new MinDocsCondition(condition.value), + condition -> new MinDocsCondition(randomLong()) + ); + + MinSizeCondition minSizeCondition = new MinSizeCondition(randomByteSize()); + EqualsHashCodeTestUtils.checkEqualsAndHashCode( + minSizeCondition, + condition -> new MinSizeCondition(condition.value), + condition -> new MinSizeCondition(randomByteSize()) + ); + + MinPrimaryShardSizeCondition minPrimaryShardSizeCondition = new MinPrimaryShardSizeCondition(randomByteSize()); + EqualsHashCodeTestUtils.checkEqualsAndHashCode( + minPrimaryShardSizeCondition, + condition -> new MinPrimaryShardSizeCondition(condition.value), + condition -> new MinPrimaryShardSizeCondition(randomByteSize()) + ); + + MinPrimaryShardDocsCondition minPrimaryShardDocsCondition = new MinPrimaryShardDocsCondition(randomNonNegativeLong()); + EqualsHashCodeTestUtils.checkEqualsAndHashCode( + minPrimaryShardDocsCondition, + condition -> new MinPrimaryShardDocsCondition(condition.value), + condition -> new MinPrimaryShardDocsCondition(randomNonNegativeLong()) + ); } private static ByteSizeValue randomByteSize() { diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/RolloverRequestTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/RolloverRequestTests.java index 05cb665824b3b..fcc507e924ca3 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/RolloverRequestTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/RolloverRequestTests.java @@ -34,7 +34,8 @@ import org.junit.Before; import java.io.IOException; -import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.Consumer; @@ -62,11 +63,16 @@ public void testConditionsParsing() throws Exception { .field("max_size", "45gb") .field("max_primary_shard_size", "55gb") .field("max_primary_shard_docs", 10) + .field("min_age", "10d") + .field("min_docs", 100) + .field("min_size", "45gb") + .field("min_primary_shard_size", "55gb") + .field("min_primary_shard_docs", 10) .endObject() .endObject(); request.fromXContent(false, createParser(builder)); Map> conditions = request.getConditions(); - assertThat(conditions.size(), equalTo(5)); + assertThat(conditions.size(), equalTo(10)); MaxAgeCondition maxAgeCondition = (MaxAgeCondition) conditions.get(MaxAgeCondition.NAME); assertThat(maxAgeCondition.value.getMillis(), equalTo(TimeValue.timeValueHours(24 * 10).getMillis())); MaxDocsCondition maxDocsCondition = (MaxDocsCondition) conditions.get(MaxDocsCondition.NAME); @@ -81,6 +87,14 @@ public void testConditionsParsing() throws Exception { MaxPrimaryShardDocsCondition.NAME ); assertThat(maxPrimaryShardDocsCondition.value, equalTo(10L)); + MinAgeCondition minAgeCondition = (MinAgeCondition) conditions.get(MinAgeCondition.NAME); + assertThat(minAgeCondition.value.getMillis(), equalTo(TimeValue.timeValueHours(24 * 10).getMillis())); + MinDocsCondition minDocsCondition = (MinDocsCondition) conditions.get(MinDocsCondition.NAME); + assertThat(minDocsCondition.value, equalTo(100L)); + MinPrimaryShardSizeCondition minPrimaryShardSizeCondition = (MinPrimaryShardSizeCondition) conditions.get( + MinPrimaryShardSizeCondition.NAME + ); + assertThat(minPrimaryShardSizeCondition.value.getBytes(), equalTo(ByteSizeUnit.GB.toBytes(55))); } public void testParsingWithIndexSettings() throws Exception { @@ -148,7 +162,13 @@ public void testSerialize() throws Exception { originalRequest.addMaxIndexDocsCondition(randomNonNegativeLong()); originalRequest.addMaxIndexAgeCondition(TimeValue.timeValueNanos(randomNonNegativeLong())); originalRequest.addMaxIndexSizeCondition(new ByteSizeValue(randomNonNegativeLong())); + originalRequest.addMaxPrimaryShardSizeCondition(new ByteSizeValue(randomNonNegativeLong())); originalRequest.addMaxPrimaryShardDocsCondition(randomNonNegativeLong()); + originalRequest.addMinIndexDocsCondition(randomNonNegativeLong()); + originalRequest.addMinIndexAgeCondition(TimeValue.timeValueNanos(randomNonNegativeLong())); + originalRequest.addMinIndexSizeCondition(new ByteSizeValue(randomNonNegativeLong())); + originalRequest.addMinPrimaryShardSizeCondition(new ByteSizeValue(randomNonNegativeLong())); + originalRequest.addMinPrimaryShardDocsCondition(randomNonNegativeLong()); try (BytesStreamOutput out = new BytesStreamOutput()) { originalRequest.writeTo(out); BytesReference bytes = out.bytes(); @@ -159,7 +179,7 @@ public void testSerialize() throws Exception { for (Map.Entry> entry : cloneRequest.getConditions().entrySet()) { Condition condition = originalRequest.getConditions().get(entry.getKey()); // here we compare the string representation as there is some information loss when serializing - // and de-serializing MaxAgeCondition + // and de-serializing MaxAgeCondition/MinAgeCondition assertEquals(condition.toString(), entry.getValue().toString()); } } @@ -189,12 +209,36 @@ public void testSameConditionCanOnlyBeAddedOnce() { } public void testValidation() { - RolloverRequest rolloverRequest = new RolloverRequest(); - assertNotNull(rolloverRequest.getCreateIndexRequest()); - ActionRequestValidationException validationException = rolloverRequest.validate(); - assertNotNull(validationException); - assertEquals(1, validationException.validationErrors().size()); - assertEquals("rollover target is missing", validationException.validationErrors().get(0)); + { + RolloverRequest rolloverRequest = new RolloverRequest(); + assertNotNull(rolloverRequest.getCreateIndexRequest()); + ActionRequestValidationException validationException = rolloverRequest.validate(); + assertNotNull(validationException); + assertEquals(1, validationException.validationErrors().size()); + assertEquals("rollover target is missing", validationException.validationErrors().get(0)); + } + + { + RolloverRequest rolloverRequest = new RolloverRequest("alias-index", "new-index-name"); + rolloverRequest.addMinIndexDocsCondition(1L); + ActionRequestValidationException validationException = rolloverRequest.validate(); + assertNotNull(validationException); + assertEquals(1, validationException.validationErrors().size()); + assertEquals( + "at least one max_* rollover condition must be set when using min_* conditions", + validationException.validationErrors().get(0) + ); + } + + { + RolloverRequest rolloverRequest = new RolloverRequest("alias-index", "new-index-name"); + if (randomBoolean()) { + rolloverRequest.addMaxIndexAgeCondition(TimeValue.timeValueHours(1)); + rolloverRequest.addMinIndexDocsCondition(1L); + } + ActionRequestValidationException validationException = rolloverRequest.validate(); + assertNull(validationException); + } } public void testParsingWithType() throws Exception { @@ -265,11 +309,52 @@ public void testTypedRequestWithoutIncludeTypeName() throws IOException { } } - private static List> conditionsGenerator = new ArrayList<>(); - static { - conditionsGenerator.add((request) -> request.addMaxIndexDocsCondition(randomNonNegativeLong())); - conditionsGenerator.add((request) -> request.addMaxIndexSizeCondition(new ByteSizeValue(randomNonNegativeLong()))); - conditionsGenerator.add((request) -> request.addMaxIndexAgeCondition(new TimeValue(randomNonNegativeLong()))); + public void testConditionsAreMet() throws Exception { + RolloverRequest rolloverRequest = new RolloverRequest(); + assertTrue(rolloverRequest.areConditionsMet(Collections.emptyMap())); + + TimeValue age = TimeValue.timeValueSeconds(5); + rolloverRequest.addMaxIndexAgeCondition(age); + MaxAgeCondition maxAgeCondition = new MaxAgeCondition(age); + assertFalse(rolloverRequest.areConditionsMet(Map.of(maxAgeCondition.toString(), false))); + assertTrue(rolloverRequest.areConditionsMet(Map.of(maxAgeCondition.toString(), true))); + + rolloverRequest.addMaxIndexDocsCondition(100L); + MaxDocsCondition maxDocsCondition = new MaxDocsCondition(100L); + assertFalse(rolloverRequest.areConditionsMet(Map.of(maxAgeCondition.toString(), false))); + assertTrue(rolloverRequest.areConditionsMet(Map.of(maxAgeCondition.toString(), true))); + assertFalse(rolloverRequest.areConditionsMet(Map.of(maxDocsCondition.toString(), false))); + assertTrue(rolloverRequest.areConditionsMet(Map.of(maxDocsCondition.toString(), true))); + + MinDocsCondition minDocsCondition = new MinDocsCondition(1L); + rolloverRequest.addMinIndexDocsCondition(1L); + assertFalse(rolloverRequest.areConditionsMet(Map.of(maxAgeCondition.toString(), false))); + assertFalse(rolloverRequest.areConditionsMet(Map.of(maxAgeCondition.toString(), true))); + assertFalse(rolloverRequest.areConditionsMet(Map.of(maxDocsCondition.toString(), false))); + assertFalse(rolloverRequest.areConditionsMet(Map.of(maxDocsCondition.toString(), true))); + assertFalse(rolloverRequest.areConditionsMet(Map.of(minDocsCondition.toString(), true))); + assertTrue(rolloverRequest.areConditionsMet(Map.of(maxAgeCondition.toString(), true, minDocsCondition.toString(), true))); + + MinAgeCondition minAgeCondition = new MinAgeCondition(age); + rolloverRequest.addMinIndexAgeCondition(age); + assertFalse(rolloverRequest.areConditionsMet(Map.of(maxAgeCondition.toString(), true, minDocsCondition.toString(), true))); + assertTrue( + rolloverRequest.areConditionsMet( + Map.of(maxAgeCondition.toString(), true, minDocsCondition.toString(), true, minAgeCondition.toString(), true) + ) + ); } + private static final List> conditionsGenerator = Arrays.asList( + (request) -> request.addMaxIndexDocsCondition(randomNonNegativeLong()), + (request) -> request.addMaxIndexSizeCondition(new ByteSizeValue(randomNonNegativeLong())), + (request) -> request.addMaxIndexAgeCondition(new TimeValue(randomNonNegativeLong())), + (request) -> request.addMaxPrimaryShardSizeCondition(new ByteSizeValue(randomNonNegativeLong())), + (request) -> request.addMaxPrimaryShardDocsCondition(randomNonNegativeLong()), + (request) -> request.addMinIndexDocsCondition(randomNonNegativeLong()), + (request) -> request.addMinIndexSizeCondition(new ByteSizeValue(randomNonNegativeLong())), + (request) -> request.addMinIndexAgeCondition(new TimeValue(randomNonNegativeLong())), + (request) -> request.addMinPrimaryShardSizeCondition(new ByteSizeValue(randomNonNegativeLong())), + (request) -> request.addMinPrimaryShardDocsCondition(randomNonNegativeLong()) + ); } diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/RolloverResponseTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/RolloverResponseTests.java index fbd5ced1e0a37..65382cdcfc688 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/RolloverResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/RolloverResponseTests.java @@ -55,6 +55,11 @@ private static Map randomResults(boolean allowNoItems) { conditionSuppliers.add(() -> new MaxSizeCondition(new ByteSizeValue(randomNonNegativeLong()))); conditionSuppliers.add(() -> new MaxPrimaryShardSizeCondition(new ByteSizeValue(randomNonNegativeLong()))); conditionSuppliers.add(() -> new MaxPrimaryShardDocsCondition(randomNonNegativeLong())); + conditionSuppliers.add(() -> new MinAgeCondition(new TimeValue(randomNonNegativeLong()))); + conditionSuppliers.add(() -> new MinDocsCondition(randomNonNegativeLong())); + conditionSuppliers.add(() -> new MinSizeCondition(new ByteSizeValue(randomNonNegativeLong()))); + conditionSuppliers.add(() -> new MinPrimaryShardSizeCondition(new ByteSizeValue(randomNonNegativeLong()))); + conditionSuppliers.add(() -> new MinPrimaryShardDocsCondition(randomNonNegativeLong())); } @Override diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverActionTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverActionTests.java index 01f572ecabcb7..da6c23571ca7a 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverActionTests.java @@ -108,12 +108,24 @@ public void testEvaluateConditions() { ByteSizeValue.ofMb(randomIntBetween(10, 100)) ); MaxPrimaryShardDocsCondition maxPrimaryShardDocsCondition = new MaxPrimaryShardDocsCondition(10L); + MinAgeCondition minAgeCondition = new MinAgeCondition(TimeValue.timeValueHours(2)); + MinDocsCondition minDocsCondition = new MinDocsCondition(100L); + MinSizeCondition minSizeCondition = new MinSizeCondition(ByteSizeValue.ofMb(randomIntBetween(10, 100))); + MinPrimaryShardSizeCondition minPrimaryShardSizeCondition = new MinPrimaryShardSizeCondition( + ByteSizeValue.ofMb(randomIntBetween(10, 100)) + ); + MinPrimaryShardDocsCondition minPrimaryShardDocsCondition = new MinPrimaryShardDocsCondition(10L); final Set> conditions = Set.of( maxAgeCondition, maxDocsCondition, maxSizeCondition, maxPrimaryShardSizeCondition, - maxPrimaryShardDocsCondition + maxPrimaryShardDocsCondition, + minAgeCondition, + minDocsCondition, + minSizeCondition, + minPrimaryShardSizeCondition, + minPrimaryShardDocsCondition ); long matchMaxDocs = randomIntBetween(100, 1000); @@ -129,7 +141,7 @@ public void testEvaluateConditions() { ); Map results = evaluateConditions(conditions, null); - assertThat(results.size(), equalTo(5)); + assertThat(results.size(), equalTo(10)); for (Boolean matched : results.values()) { assertThat(matched, equalTo(false)); } @@ -138,7 +150,7 @@ public void testEvaluateConditions() { conditions, new Condition.Stats(matchMaxDocs, indexCreated, ByteSizeValue.ofMb(120), ByteSizeValue.ofMb(120), matchMaxPrimaryShardDocs) ); - assertThat(results.size(), equalTo(5)); + assertThat(results.size(), equalTo(10)); for (Boolean matched : results.values()) { assertThat(matched, equalTo(true)); } @@ -147,7 +159,7 @@ public void testEvaluateConditions() { conditions, new Condition.Stats(notMatchMaxDocs, indexCreated, notMatchMaxSize, ByteSizeValue.ofMb(0), notMatchMaxPrimaryShardDocs) ); - assertThat(results.size(), equalTo(5)); + assertThat(results.size(), equalTo(10)); for (Map.Entry entry : results.entrySet()) { if (entry.getKey().equals(maxAgeCondition.toString())) { assertThat(entry.getValue(), equalTo(true)); @@ -159,6 +171,16 @@ public void testEvaluateConditions() { assertThat(entry.getValue(), equalTo(false)); } else if (entry.getKey().equals(maxPrimaryShardDocsCondition.toString())) { assertThat(entry.getValue(), equalTo(false)); + } else if (entry.getKey().equals(minAgeCondition.toString())) { + assertThat(entry.getValue(), equalTo(true)); + } else if (entry.getKey().equals(minDocsCondition.toString())) { + assertThat(entry.getValue(), equalTo(false)); + } else if (entry.getKey().equals(minSizeCondition.toString())) { + assertThat(entry.getValue(), equalTo(false)); + } else if (entry.getKey().equals(minPrimaryShardSizeCondition.toString())) { + assertThat(entry.getValue(), equalTo(false)); + } else if (entry.getKey().equals(minPrimaryShardDocsCondition.toString())) { + assertThat(entry.getValue(), equalTo(false)); } else { fail("unknown condition result found " + entry.getKey()); } @@ -173,12 +195,24 @@ public void testEvaluateWithoutStats() { new ByteSizeValue(randomNonNegativeLong()) ); MaxPrimaryShardDocsCondition maxPrimaryShardDocsCondition = new MaxPrimaryShardDocsCondition(randomNonNegativeLong()); + MinAgeCondition minAgeCondition = new MinAgeCondition(TimeValue.timeValueHours(randomIntBetween(1, 3))); + MinDocsCondition minDocsCondition = new MinDocsCondition(randomNonNegativeLong()); + MinSizeCondition minSizeCondition = new MinSizeCondition(new ByteSizeValue(randomNonNegativeLong())); + MinPrimaryShardSizeCondition minPrimaryShardSizeCondition = new MinPrimaryShardSizeCondition( + new ByteSizeValue(randomNonNegativeLong()) + ); + MinPrimaryShardDocsCondition minPrimaryShardDocsCondition = new MinPrimaryShardDocsCondition(randomNonNegativeLong()); final Set> conditions = Set.of( maxAgeCondition, maxDocsCondition, maxSizeCondition, maxPrimaryShardSizeCondition, - maxPrimaryShardDocsCondition + maxPrimaryShardDocsCondition, + minAgeCondition, + minDocsCondition, + minSizeCondition, + minPrimaryShardSizeCondition, + minPrimaryShardDocsCondition ); final Settings settings = Settings.builder() @@ -193,7 +227,7 @@ public void testEvaluateWithoutStats() { .settings(settings) .build(); Map results = evaluateConditions(conditions, buildStats(metadata, null)); - assertThat(results.size(), equalTo(5)); + assertThat(results.size(), equalTo(10)); for (Map.Entry entry : results.entrySet()) { if (entry.getKey().equals(maxAgeCondition.toString())) { @@ -206,6 +240,16 @@ public void testEvaluateWithoutStats() { assertThat(entry.getValue(), equalTo(false)); } else if (entry.getKey().equals(maxPrimaryShardDocsCondition.toString())) { assertThat(entry.getValue(), equalTo(false)); + } else if (entry.getKey().equals(minAgeCondition.toString())) { + assertThat(entry.getValue(), equalTo(true)); + } else if (entry.getKey().equals(minDocsCondition.toString())) { + assertThat(entry.getValue(), equalTo(false)); + } else if (entry.getKey().equals(minSizeCondition.toString())) { + assertThat(entry.getValue(), equalTo(false)); + } else if (entry.getKey().equals(minPrimaryShardSizeCondition.toString())) { + assertThat(entry.getValue(), equalTo(false)); + } else if (entry.getKey().equals(minPrimaryShardDocsCondition.toString())) { + assertThat(entry.getValue(), equalTo(false)); } else { fail("unknown condition result found " + entry.getKey()); } @@ -220,12 +264,24 @@ public void testEvaluateWithoutMetadata() { ByteSizeValue.ofMb(randomIntBetween(10, 100)) ); MaxPrimaryShardDocsCondition maxPrimaryShardDocsCondition = new MaxPrimaryShardDocsCondition(10L); + MinAgeCondition minAgeCondition = new MinAgeCondition(TimeValue.timeValueHours(2)); + MinDocsCondition minDocsCondition = new MinDocsCondition(100L); + MinSizeCondition minSizeCondition = new MinSizeCondition(ByteSizeValue.ofMb(randomIntBetween(10, 100))); + MinPrimaryShardSizeCondition minPrimaryShardSizeCondition = new MinPrimaryShardSizeCondition( + ByteSizeValue.ofMb(randomIntBetween(10, 100)) + ); + MinPrimaryShardDocsCondition minPrimaryShardDocsCondition = new MinPrimaryShardDocsCondition(10L); final Set> conditions = Set.of( maxAgeCondition, maxDocsCondition, maxSizeCondition, maxPrimaryShardSizeCondition, - maxPrimaryShardDocsCondition + maxPrimaryShardDocsCondition, + minAgeCondition, + minDocsCondition, + minSizeCondition, + minPrimaryShardSizeCondition, + minPrimaryShardDocsCondition ); final Settings settings = Settings.builder() @@ -241,7 +297,7 @@ public void testEvaluateWithoutMetadata() { .build(); IndicesStatsResponse indicesStats = randomIndicesStatsResponse(new IndexMetadata[] { metadata }); Map results = evaluateConditions(conditions, buildStats(null, indicesStats)); - assertThat(results.size(), equalTo(5)); + assertThat(results.size(), equalTo(10)); results.forEach((k, v) -> assertFalse(v)); } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/IndexLifecycleFeatureSetUsage.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/IndexLifecycleFeatureSetUsage.java index 892113df53085..72de1f0e32cc2 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/IndexLifecycleFeatureSetUsage.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/IndexLifecycleFeatureSetUsage.java @@ -231,6 +231,11 @@ public static final class ActionConfigStats implements ToXContentObject, Writeab private final Long rolloverMaxPrimaryShardDocs; private final ByteSizeValue rolloverMaxPrimaryShardSize; private final ByteSizeValue rolloverMaxSize; + private final TimeValue rolloverMinAge; + private final Long rolloverMinDocs; + private final Long rolloverMinPrimaryShardDocs; + private final ByteSizeValue rolloverMinPrimaryShardSize; + private final ByteSizeValue rolloverMinSize; private final Integer setPriorityPriority; private final ByteSizeValue shrinkMaxPrimaryShardSize; private final Integer shrinkNumberOfShards; @@ -251,6 +256,11 @@ public static final class Builder { private Long rolloverMaxPrimaryShardDocs; private ByteSizeValue rolloverMaxPrimaryShardSize; private ByteSizeValue rolloverMaxSize; + private TimeValue rolloverMinAge; + private Long rolloverMinDocs; + private Long rolloverMinPrimaryShardDocs; + private ByteSizeValue rolloverMinPrimaryShardSize; + private ByteSizeValue rolloverMinSize; private Integer setPriorityPriority; private ByteSizeValue shrinkMaxPrimaryShardSize; private Integer shrinkNumberOfShards; @@ -265,6 +275,11 @@ public Builder(ActionConfigStats existing) { this.rolloverMaxPrimaryShardDocs = existing.rolloverMaxPrimaryShardDocs; this.rolloverMaxPrimaryShardSize = existing.rolloverMaxPrimaryShardSize; this.rolloverMaxSize = existing.rolloverMaxSize; + this.rolloverMinAge = existing.rolloverMinAge; + this.rolloverMinDocs = existing.rolloverMinDocs; + this.rolloverMinPrimaryShardDocs = existing.rolloverMinPrimaryShardDocs; + this.rolloverMinPrimaryShardSize = existing.rolloverMinPrimaryShardSize; + this.rolloverMinSize = existing.rolloverMinSize; this.setPriorityPriority = existing.setPriorityPriority; this.shrinkMaxPrimaryShardSize = existing.shrinkMaxPrimaryShardSize; this.shrinkNumberOfShards = existing.shrinkNumberOfShards; @@ -290,8 +305,9 @@ public Builder setRolloverMaxDocs(Long rolloverMaxDocs) { return this; } - public void setRolloverMaxPrimaryShardDocs(Long rolloverMaxPrimaryShardDocs) { + public Builder setRolloverMaxPrimaryShardDocs(Long rolloverMaxPrimaryShardDocs) { this.rolloverMaxPrimaryShardDocs = rolloverMaxPrimaryShardDocs; + return this; } public Builder setRolloverMaxPrimaryShardSize(ByteSizeValue rolloverMaxPrimaryShardSize) { @@ -304,6 +320,31 @@ public Builder setRolloverMaxSize(ByteSizeValue rolloverMaxSize) { return this; } + public Builder setRolloverMinAge(TimeValue rolloverMinAge) { + this.rolloverMinAge = rolloverMinAge; + return this; + } + + public Builder setRolloverMinDocs(Long rolloverMinDocs) { + this.rolloverMinDocs = rolloverMinDocs; + return this; + } + + public Builder setRolloverMinPrimaryShardDocs(Long rolloverMinPrimaryShardDocs) { + this.rolloverMinPrimaryShardDocs = rolloverMinPrimaryShardDocs; + return this; + } + + public Builder setRolloverMinPrimaryShardSize(ByteSizeValue rolloverMinPrimaryShardSize) { + this.rolloverMinPrimaryShardSize = rolloverMinPrimaryShardSize; + return this; + } + + public Builder setRolloverMinSize(ByteSizeValue rolloverMinSize) { + this.rolloverMinSize = rolloverMinSize; + return this; + } + public Builder setPriority(Integer priority) { this.setPriorityPriority = priority; return this; @@ -328,6 +369,11 @@ public ActionConfigStats build() { rolloverMaxPrimaryShardDocs, rolloverMaxPrimaryShardSize, rolloverMaxSize, + rolloverMinAge, + rolloverMinDocs, + rolloverMinPrimaryShardDocs, + rolloverMinPrimaryShardSize, + rolloverMinSize, setPriorityPriority, shrinkMaxPrimaryShardSize, shrinkNumberOfShards @@ -343,6 +389,11 @@ public ActionConfigStats( Long rolloverMaxPrimaryShardDocs, ByteSizeValue rolloverMaxPrimaryShardSize, ByteSizeValue rolloverMaxSize, + TimeValue rolloverMinAge, + Long rolloverMinDocs, + Long rolloverMinPrimaryShardDocs, + ByteSizeValue rolloverMinPrimaryShardSize, + ByteSizeValue rolloverMinSize, Integer setPriorityPriority, ByteSizeValue shrinkMaxPrimaryShardSize, Integer shrinkNumberOfShards @@ -354,6 +405,11 @@ public ActionConfigStats( this.rolloverMaxPrimaryShardDocs = rolloverMaxPrimaryShardDocs; this.rolloverMaxPrimaryShardSize = rolloverMaxPrimaryShardSize; this.rolloverMaxSize = rolloverMaxSize; + this.rolloverMinAge = rolloverMinAge; + this.rolloverMinDocs = rolloverMinDocs; + this.rolloverMinPrimaryShardDocs = rolloverMinPrimaryShardDocs; + this.rolloverMinPrimaryShardSize = rolloverMinPrimaryShardSize; + this.rolloverMinSize = rolloverMinSize; this.setPriorityPriority = setPriorityPriority; this.shrinkMaxPrimaryShardSize = shrinkMaxPrimaryShardSize; this.shrinkNumberOfShards = shrinkNumberOfShards; @@ -374,6 +430,19 @@ public ActionConfigStats(StreamInput in) throws IOException { } else { this.rolloverMaxPrimaryShardDocs = null; } + if (in.getVersion().onOrAfter(Version.V_8_4_0)) { + this.rolloverMinAge = in.readOptionalTimeValue(); + this.rolloverMinDocs = in.readOptionalVLong(); + this.rolloverMinPrimaryShardSize = in.readOptionalWriteable(ByteSizeValue::new); + this.rolloverMinSize = in.readOptionalWriteable(ByteSizeValue::new); + this.rolloverMinPrimaryShardDocs = in.readOptionalVLong(); + } else { + this.rolloverMinAge = null; + this.rolloverMinDocs = null; + this.rolloverMinPrimaryShardSize = null; + this.rolloverMinSize = null; + this.rolloverMinPrimaryShardDocs = null; + } } @Override @@ -390,6 +459,13 @@ public void writeTo(StreamOutput out) throws IOException { if (out.getVersion().onOrAfter(Version.V_8_2_0)) { out.writeOptionalVLong(rolloverMaxPrimaryShardDocs); } + if (out.getVersion().onOrAfter(Version.V_8_4_0)) { + out.writeOptionalTimeValue(rolloverMinAge); + out.writeOptionalVLong(rolloverMinDocs); + out.writeOptionalWriteable(rolloverMinPrimaryShardSize); + out.writeOptionalWriteable(rolloverMinSize); + out.writeOptionalVLong(rolloverMinPrimaryShardDocs); + } } @Override @@ -409,7 +485,12 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws || rolloverMaxDocs != null || rolloverMaxPrimaryShardDocs != null || rolloverMaxSize != null - || rolloverMaxPrimaryShardSize != null) { + || rolloverMaxPrimaryShardSize != null + || rolloverMinAge != null + || rolloverMinDocs != null + || rolloverMinPrimaryShardDocs != null + || rolloverMinSize != null + || rolloverMinPrimaryShardSize != null) { builder.startObject(RolloverAction.NAME); if (rolloverMaxAge != null) { builder.field(RolloverAction.MAX_AGE_FIELD.getPreferredName(), rolloverMaxAge.getStringRep()); @@ -435,6 +516,30 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws rolloverMaxPrimaryShardSize.getBytes() ); } + if (rolloverMinAge != null) { + builder.field(RolloverAction.MIN_AGE_FIELD.getPreferredName(), rolloverMinAge.getStringRep()); + builder.field(RolloverAction.MIN_AGE_FIELD.getPreferredName() + "_millis", rolloverMinAge.getMillis()); + } + if (rolloverMinDocs != null) { + builder.field(RolloverAction.MIN_DOCS_FIELD.getPreferredName(), rolloverMinDocs); + } + if (rolloverMinPrimaryShardDocs != null) { + builder.field(RolloverAction.MIN_PRIMARY_SHARD_DOCS_FIELD.getPreferredName(), rolloverMinPrimaryShardDocs); + } + if (rolloverMinSize != null) { + builder.field(RolloverAction.MIN_SIZE_FIELD.getPreferredName(), rolloverMinSize.getStringRep()); + builder.field(RolloverAction.MIN_SIZE_FIELD.getPreferredName() + "_bytes", rolloverMinSize.getBytes()); + } + if (rolloverMinPrimaryShardSize != null) { + builder.field( + RolloverAction.MIN_PRIMARY_SHARD_SIZE_FIELD.getPreferredName(), + rolloverMinPrimaryShardSize.getStringRep() + ); + builder.field( + RolloverAction.MIN_PRIMARY_SHARD_SIZE_FIELD.getPreferredName() + "_bytes", + rolloverMinPrimaryShardSize.getBytes() + ); + } builder.endObject(); } if (setPriorityPriority != null) { @@ -485,6 +590,26 @@ public ByteSizeValue getRolloverMaxSize() { return rolloverMaxSize; } + public TimeValue getRolloverMinAge() { + return rolloverMinAge; + } + + public Long getRolloverMinDocs() { + return rolloverMinDocs; + } + + public Long getRolloverMinPrimaryShardDocs() { + return rolloverMinPrimaryShardDocs; + } + + public ByteSizeValue getRolloverMinPrimaryShardSize() { + return rolloverMinPrimaryShardSize; + } + + public ByteSizeValue getRolloverMinSize() { + return rolloverMinSize; + } + public Integer getSetPriorityPriority() { return setPriorityPriority; } @@ -507,8 +632,13 @@ public boolean equals(Object o) { && Objects.equals(rolloverMaxAge, that.rolloverMaxAge) && Objects.equals(rolloverMaxDocs, that.rolloverMaxDocs) && Objects.equals(rolloverMaxPrimaryShardDocs, that.rolloverMaxPrimaryShardDocs) - && Objects.equals(rolloverMaxPrimaryShardSize, that.rolloverMaxPrimaryShardSize) && Objects.equals(rolloverMaxSize, that.rolloverMaxSize) + && Objects.equals(rolloverMaxPrimaryShardSize, that.rolloverMaxPrimaryShardSize) + && Objects.equals(rolloverMinAge, that.rolloverMinAge) + && Objects.equals(rolloverMinDocs, that.rolloverMinDocs) + && Objects.equals(rolloverMinPrimaryShardDocs, that.rolloverMinPrimaryShardDocs) + && Objects.equals(rolloverMinSize, that.rolloverMinSize) + && Objects.equals(rolloverMinPrimaryShardSize, that.rolloverMinPrimaryShardSize) && Objects.equals(setPriorityPriority, that.setPriorityPriority) && Objects.equals(shrinkMaxPrimaryShardSize, that.shrinkMaxPrimaryShardSize) && Objects.equals(shrinkNumberOfShards, that.shrinkNumberOfShards); @@ -522,8 +652,13 @@ public int hashCode() { rolloverMaxAge, rolloverMaxDocs, rolloverMaxPrimaryShardDocs, - rolloverMaxPrimaryShardSize, rolloverMaxSize, + rolloverMaxPrimaryShardSize, + rolloverMinAge, + rolloverMinDocs, + rolloverMinPrimaryShardDocs, + rolloverMinSize, + rolloverMinPrimaryShardSize, setPriorityPriority, shrinkMaxPrimaryShardSize, shrinkNumberOfShards diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/RolloverAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/RolloverAction.java index 9989f440a93c1..ea5330853a373 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/RolloverAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/RolloverAction.java @@ -39,6 +39,11 @@ public class RolloverAction implements LifecycleAction { public static final ParseField MAX_DOCS_FIELD = new ParseField("max_docs"); public static final ParseField MAX_AGE_FIELD = new ParseField("max_age"); public static final ParseField MAX_PRIMARY_SHARD_DOCS_FIELD = new ParseField("max_primary_shard_docs"); + public static final ParseField MIN_SIZE_FIELD = new ParseField("min_size"); + public static final ParseField MIN_PRIMARY_SHARD_SIZE_FIELD = new ParseField("min_primary_shard_size"); + public static final ParseField MIN_DOCS_FIELD = new ParseField("min_docs"); + public static final ParseField MIN_AGE_FIELD = new ParseField("min_age"); + public static final ParseField MIN_PRIMARY_SHARD_DOCS_FIELD = new ParseField("min_primary_shard_docs"); public static final String LIFECYCLE_ROLLOVER_ALIAS = "index.lifecycle.rollover_alias"; public static final Setting LIFECYCLE_ROLLOVER_ALIAS_SETTING = Setting.simpleString( LIFECYCLE_ROLLOVER_ALIAS, @@ -50,7 +55,18 @@ public class RolloverAction implements LifecycleAction { private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( NAME, - a -> new RolloverAction((ByteSizeValue) a[0], (ByteSizeValue) a[1], (TimeValue) a[2], (Long) a[3], (Long) a[4]) + a -> new RolloverAction( + (ByteSizeValue) a[0], + (ByteSizeValue) a[1], + (TimeValue) a[2], + (Long) a[3], + (Long) a[4], + (ByteSizeValue) a[5], + (ByteSizeValue) a[6], + (TimeValue) a[7], + (Long) a[8], + (Long) a[9] + ) ); static { @@ -74,6 +90,26 @@ public class RolloverAction implements LifecycleAction { ); PARSER.declareLong(ConstructingObjectParser.optionalConstructorArg(), MAX_DOCS_FIELD); PARSER.declareLong(ConstructingObjectParser.optionalConstructorArg(), MAX_PRIMARY_SHARD_DOCS_FIELD); + PARSER.declareField( + ConstructingObjectParser.optionalConstructorArg(), + (p, c) -> ByteSizeValue.parseBytesSizeValue(p.text(), MIN_SIZE_FIELD.getPreferredName()), + MIN_SIZE_FIELD, + ValueType.VALUE + ); + PARSER.declareField( + ConstructingObjectParser.optionalConstructorArg(), + (p, c) -> ByteSizeValue.parseBytesSizeValue(p.text(), MIN_PRIMARY_SHARD_SIZE_FIELD.getPreferredName()), + MIN_PRIMARY_SHARD_SIZE_FIELD, + ValueType.VALUE + ); + PARSER.declareField( + ConstructingObjectParser.optionalConstructorArg(), + (p, c) -> TimeValue.parseTimeValue(p.text(), MIN_AGE_FIELD.getPreferredName()), + MIN_AGE_FIELD, + ValueType.VALUE + ); + PARSER.declareLong(ConstructingObjectParser.optionalConstructorArg(), MIN_DOCS_FIELD); + PARSER.declareLong(ConstructingObjectParser.optionalConstructorArg(), MIN_PRIMARY_SHARD_DOCS_FIELD); } private final ByteSizeValue maxSize; @@ -81,6 +117,11 @@ public class RolloverAction implements LifecycleAction { private final Long maxDocs; private final TimeValue maxAge; private final Long maxPrimaryShardDocs; + private final ByteSizeValue minSize; + private final ByteSizeValue minPrimaryShardSize; + private final Long minDocs; + private final TimeValue minAge; + private final Long minPrimaryShardDocs; public static RolloverAction parse(XContentParser parser) { return PARSER.apply(parser, null); @@ -91,29 +132,33 @@ public RolloverAction( @Nullable ByteSizeValue maxPrimaryShardSize, @Nullable TimeValue maxAge, @Nullable Long maxDocs, - @Nullable Long maxPrimaryShardDocs + @Nullable Long maxPrimaryShardDocs, + @Nullable ByteSizeValue minSize, + @Nullable ByteSizeValue minPrimaryShardSize, + @Nullable TimeValue minAge, + @Nullable Long minDocs, + @Nullable Long minPrimaryShardDocs ) { if (maxSize == null && maxPrimaryShardSize == null && maxAge == null && maxDocs == null && maxPrimaryShardDocs == null) { - throw new IllegalArgumentException("At least one rollover condition must be set."); + throw new IllegalArgumentException("At least one max_* rollover condition must be set."); } + this.maxSize = maxSize; this.maxPrimaryShardSize = maxPrimaryShardSize; this.maxAge = maxAge; this.maxDocs = maxDocs; this.maxPrimaryShardDocs = maxPrimaryShardDocs; + + this.minSize = minSize; + this.minPrimaryShardSize = minPrimaryShardSize; + this.minAge = minAge; + this.minDocs = minDocs; + this.minPrimaryShardDocs = minPrimaryShardDocs; } public RolloverAction(StreamInput in) throws IOException { - if (in.readBoolean()) { - maxSize = new ByteSizeValue(in); - } else { - maxSize = null; - } - if (in.readBoolean()) { - maxPrimaryShardSize = new ByteSizeValue(in); - } else { - maxPrimaryShardSize = null; - } + maxSize = in.readOptionalWriteable(ByteSizeValue::new); + maxPrimaryShardSize = in.readOptionalWriteable(ByteSizeValue::new); maxAge = in.readOptionalTimeValue(); maxDocs = in.readOptionalVLong(); if (in.getVersion().onOrAfter(Version.V_8_2_0)) { @@ -121,25 +166,37 @@ public RolloverAction(StreamInput in) throws IOException { } else { maxPrimaryShardDocs = null; } + if (in.getVersion().onOrAfter(Version.V_8_4_0)) { + minSize = in.readOptionalWriteable(ByteSizeValue::new); + minPrimaryShardSize = in.readOptionalWriteable(ByteSizeValue::new); + minAge = in.readOptionalTimeValue(); + minDocs = in.readOptionalVLong(); + minPrimaryShardDocs = in.readOptionalVLong(); + } else { + minSize = null; + minPrimaryShardSize = null; + minAge = null; + minDocs = null; + minPrimaryShardDocs = null; + } } @Override public void writeTo(StreamOutput out) throws IOException { - boolean hasMaxSize = maxSize != null; - out.writeBoolean(hasMaxSize); - if (hasMaxSize) { - maxSize.writeTo(out); - } - boolean hasMaxPrimaryShardSize = maxPrimaryShardSize != null; - out.writeBoolean(hasMaxPrimaryShardSize); - if (hasMaxPrimaryShardSize) { - maxPrimaryShardSize.writeTo(out); - } + out.writeOptionalWriteable(maxSize); + out.writeOptionalWriteable(maxPrimaryShardSize); out.writeOptionalTimeValue(maxAge); out.writeOptionalVLong(maxDocs); if (out.getVersion().onOrAfter(Version.V_8_2_0)) { out.writeOptionalVLong(maxPrimaryShardDocs); } + if (out.getVersion().onOrAfter(Version.V_8_4_0)) { + out.writeOptionalWriteable(minSize); + out.writeOptionalWriteable(minPrimaryShardSize); + out.writeOptionalTimeValue(minAge); + out.writeOptionalVLong(minDocs); + out.writeOptionalVLong(minPrimaryShardDocs); + } } @Override @@ -167,6 +224,26 @@ public Long getMaxPrimaryShardDocs() { return maxPrimaryShardDocs; } + public ByteSizeValue getMinSize() { + return minSize; + } + + public ByteSizeValue getMinPrimaryShardSize() { + return minPrimaryShardSize; + } + + public TimeValue getMinAge() { + return minAge; + } + + public Long getMinDocs() { + return minDocs; + } + + public Long getMinPrimaryShardDocs() { + return minPrimaryShardDocs; + } + @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); @@ -185,6 +262,21 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws if (maxPrimaryShardDocs != null) { builder.field(MAX_PRIMARY_SHARD_DOCS_FIELD.getPreferredName(), maxPrimaryShardDocs); } + if (minSize != null) { + builder.field(MIN_SIZE_FIELD.getPreferredName(), minSize.getStringRep()); + } + if (minPrimaryShardSize != null) { + builder.field(MIN_PRIMARY_SHARD_SIZE_FIELD.getPreferredName(), minPrimaryShardSize.getStringRep()); + } + if (minAge != null) { + builder.field(MIN_AGE_FIELD.getPreferredName(), minAge.getStringRep()); + } + if (minDocs != null) { + builder.field(MIN_DOCS_FIELD.getPreferredName(), minDocs); + } + if (minPrimaryShardDocs != null) { + builder.field(MIN_PRIMARY_SHARD_DOCS_FIELD.getPreferredName(), minPrimaryShardDocs); + } builder.endObject(); return builder; } @@ -210,7 +302,12 @@ public List toSteps(Client client, String phase, Step.StepKey nextStepKey) maxPrimaryShardSize, maxAge, maxDocs, - maxPrimaryShardDocs + maxPrimaryShardDocs, + minSize, + minPrimaryShardSize, + minAge, + minDocs, + minPrimaryShardDocs ); RolloverStep rolloverStep = new RolloverStep(rolloverStepKey, waitForActiveShardsKey, client); WaitForActiveShardsStep waitForActiveShardsStep = new WaitForActiveShardsStep(waitForActiveShardsKey, updateDateStepKey); @@ -230,7 +327,18 @@ public List toSteps(Client client, String phase, Step.StepKey nextStepKey) @Override public int hashCode() { - return Objects.hash(maxSize, maxPrimaryShardSize, maxAge, maxDocs, maxPrimaryShardDocs); + return Objects.hash( + maxSize, + maxPrimaryShardSize, + maxAge, + maxDocs, + maxPrimaryShardDocs, + minSize, + minPrimaryShardSize, + minAge, + minDocs, + minPrimaryShardDocs + ); } @Override @@ -246,7 +354,12 @@ public boolean equals(Object obj) { && Objects.equals(maxPrimaryShardSize, other.maxPrimaryShardSize) && Objects.equals(maxAge, other.maxAge) && Objects.equals(maxDocs, other.maxDocs) - && Objects.equals(maxPrimaryShardDocs, other.maxPrimaryShardDocs); + && Objects.equals(maxPrimaryShardDocs, other.maxPrimaryShardDocs) + && Objects.equals(minSize, other.minSize) + && Objects.equals(minPrimaryShardSize, other.minPrimaryShardSize) + && Objects.equals(minAge, other.minAge) + && Objects.equals(minDocs, other.minDocs) + && Objects.equals(minPrimaryShardDocs, other.minPrimaryShardDocs); } @Override diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/WaitForRolloverReadyStep.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/WaitForRolloverReadyStep.java index bed56c3a24392..b153d8d9530b2 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/WaitForRolloverReadyStep.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/WaitForRolloverReadyStep.java @@ -39,6 +39,11 @@ public class WaitForRolloverReadyStep extends AsyncWaitStep { private final TimeValue maxAge; private final Long maxDocs; private final Long maxPrimaryShardDocs; + private final ByteSizeValue minSize; + private final ByteSizeValue minPrimaryShardSize; + private final TimeValue minAge; + private final Long minDocs; + private final Long minPrimaryShardDocs; public WaitForRolloverReadyStep( StepKey key, @@ -48,7 +53,12 @@ public WaitForRolloverReadyStep( ByteSizeValue maxPrimaryShardSize, TimeValue maxAge, Long maxDocs, - Long maxPrimaryShardDocs + Long maxPrimaryShardDocs, + ByteSizeValue minSize, + ByteSizeValue minPrimaryShardSize, + TimeValue minAge, + Long minDocs, + Long minPrimaryShardDocs ) { super(key, nextStepKey, client); this.maxSize = maxSize; @@ -56,6 +66,11 @@ public WaitForRolloverReadyStep( this.maxAge = maxAge; this.maxDocs = maxDocs; this.maxPrimaryShardDocs = maxPrimaryShardDocs; + this.minSize = minSize; + this.minPrimaryShardSize = minPrimaryShardSize; + this.minAge = minAge; + this.minDocs = minDocs; + this.minPrimaryShardDocs = minPrimaryShardDocs; } @Override @@ -198,12 +213,27 @@ public void evaluateCondition(Metadata metadata, Index index, Listener listener, if (maxPrimaryShardDocs != null) { rolloverRequest.addMaxPrimaryShardDocsCondition(maxPrimaryShardDocs); } + if (minSize != null) { + rolloverRequest.addMinIndexSizeCondition(minSize); + } + if (minPrimaryShardSize != null) { + rolloverRequest.addMinPrimaryShardSizeCondition(minPrimaryShardSize); + } + if (minAge != null) { + rolloverRequest.addMinIndexAgeCondition(minAge); + } + if (minDocs != null) { + rolloverRequest.addMinIndexDocsCondition(minDocs); + } + if (minPrimaryShardDocs != null) { + rolloverRequest.addMinPrimaryShardDocsCondition(minPrimaryShardDocs); + } getClient().admin() .indices() .rolloverIndex( rolloverRequest, ActionListener.wrap( - response -> listener.onResponse(response.getConditionStatus().values().stream().anyMatch(i -> i), EmptyInfo.INSTANCE), + response -> listener.onResponse(rolloverRequest.areConditionsMet(response.getConditionStatus()), EmptyInfo.INSTANCE), listener::onFailure ) ); @@ -225,13 +255,45 @@ Long getMaxDocs() { return maxDocs; } - public Long getMaxPrimaryShardDocs() { + Long getMaxPrimaryShardDocs() { return maxPrimaryShardDocs; } + ByteSizeValue getMinSize() { + return minSize; + } + + ByteSizeValue getMinPrimaryShardSize() { + return minPrimaryShardSize; + } + + TimeValue getMinAge() { + return minAge; + } + + Long getMinDocs() { + return minDocs; + } + + Long getMinPrimaryShardDocs() { + return minPrimaryShardDocs; + } + @Override public int hashCode() { - return Objects.hash(super.hashCode(), maxSize, maxPrimaryShardSize, maxAge, maxDocs, maxPrimaryShardDocs); + return Objects.hash( + super.hashCode(), + maxSize, + maxPrimaryShardSize, + maxAge, + maxDocs, + maxPrimaryShardDocs, + minSize, + minPrimaryShardSize, + minAge, + minDocs, + minPrimaryShardDocs + ); } @Override @@ -248,7 +310,12 @@ public boolean equals(Object obj) { && Objects.equals(maxPrimaryShardSize, other.maxPrimaryShardSize) && Objects.equals(maxAge, other.maxAge) && Objects.equals(maxDocs, other.maxDocs) - && Objects.equals(maxPrimaryShardDocs, other.maxPrimaryShardDocs); + && Objects.equals(maxPrimaryShardDocs, other.maxPrimaryShardDocs) + && Objects.equals(minSize, other.minSize) + && Objects.equals(minPrimaryShardSize, other.minPrimaryShardSize) + && Objects.equals(minAge, other.minAge) + && Objects.equals(minDocs, other.minDocs) + && Objects.equals(minPrimaryShardDocs, other.minPrimaryShardDocs); } // We currently have no information to provide for this AsyncWaitStep, so this is an empty object diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/ActionConfigStatsTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/ActionConfigStatsTests.java index e41f4eb930339..2a503515bf8ee 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/ActionConfigStatsTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/ActionConfigStatsTests.java @@ -41,19 +41,32 @@ public static ActionConfigStats createRandomInstance() { builder.setRolloverMaxPrimaryShardDocs(randomLongBetween(0, Long.MAX_VALUE)); } if (randomBoolean()) { - ByteSizeValue randomByteSize = ByteSizeValue.ofBytes(randomLongBetween(0, 1024L * 1024L * 1024L * 50L)); - builder.setRolloverMaxPrimaryShardSize(randomByteSize); + builder.setRolloverMaxPrimaryShardSize(randomByteSize()); } if (randomBoolean()) { - ByteSizeValue randomByteSize = ByteSizeValue.ofBytes(randomLongBetween(0, 1024L * 1024L * 1024L * 50L)); - builder.setRolloverMaxSize(randomByteSize); + builder.setRolloverMaxSize(randomByteSize()); + } + if (randomBoolean()) { + TimeValue randomAge = TimeValue.parseTimeValue(randomTimeValue(), "action_config_stats_tests"); + builder.setRolloverMinAge(randomAge); + } + if (randomBoolean()) { + builder.setRolloverMinDocs(randomLongBetween(0, Long.MAX_VALUE)); + } + if (randomBoolean()) { + builder.setRolloverMinPrimaryShardDocs(randomLongBetween(0, Long.MAX_VALUE)); + } + if (randomBoolean()) { + builder.setRolloverMinPrimaryShardSize(randomByteSize()); + } + if (randomBoolean()) { + builder.setRolloverMinSize(randomByteSize()); } if (randomBoolean()) { builder.setPriority(randomIntBetween(0, 50)); } if (randomBoolean()) { - ByteSizeValue randomByteSize = ByteSizeValue.ofBytes(randomLongBetween(0, 1024L * 1024L * 1024L * 50L)); - builder.setShrinkMaxPrimaryShardSize(randomByteSize); + builder.setShrinkMaxPrimaryShardSize(randomByteSize()); } if (randomBoolean()) { builder.setShrinkNumberOfShards(randomIntBetween(0, 50)); @@ -69,7 +82,7 @@ protected Writeable.Reader instanceReader() { @Override protected ActionConfigStats mutateInstance(ActionConfigStats instance) throws IOException { ActionConfigStats.Builder builder = ActionConfigStats.builder(instance); - switch (between(0, 9)) { + switch (between(0, 14)) { case 0 -> { int numberOfReplicas = randomValueOtherThan(instance.getAllocateNumberOfReplicas(), () -> randomIntBetween(0, 10000)); builder.setAllocateNumberOfReplicas(numberOfReplicas); @@ -86,25 +99,41 @@ protected ActionConfigStats mutateInstance(ActionConfigStats instance) throws IO builder.setRolloverMaxAge(randomAge); } case 3 -> builder.setRolloverMaxDocs(randomLongBetween(0, Long.MAX_VALUE)); - case 4 -> { - ByteSizeValue randomByteSize = ByteSizeValue.ofBytes(randomLongBetween(0, 1024L * 1024L * 1024L * 50L)); - builder.setRolloverMaxPrimaryShardSize(randomByteSize); - } + case 4 -> builder.setRolloverMaxPrimaryShardDocs(randomLongBetween(0, Long.MAX_VALUE)); case 5 -> { - ByteSizeValue randomMaxByteSize = ByteSizeValue.ofBytes(randomLongBetween(0, 1024L * 1024L * 1024L * 50L)); - builder.setRolloverMaxSize(randomMaxByteSize); + builder.setRolloverMaxPrimaryShardSize(randomByteSize()); + } + case 6 -> { + builder.setRolloverMaxSize(randomByteSize()); } - case 6 -> builder.setPriority(randomValueOtherThan(instance.getSetPriorityPriority(), () -> randomIntBetween(0, 50))); case 7 -> { - ByteSizeValue randomPrimaryByteSize = ByteSizeValue.ofBytes(randomLongBetween(0, 1024L * 1024L * 1024L * 50L)); - builder.setShrinkMaxPrimaryShardSize(randomPrimaryByteSize); + TimeValue randomAge = randomValueOtherThan( + instance.getRolloverMinAge(), + () -> TimeValue.parseTimeValue(randomTimeValue(), "action_config_stats_tests") + ); + builder.setRolloverMinAge(randomAge); } - case 8 -> builder.setShrinkNumberOfShards( + case 8 -> builder.setRolloverMinDocs(randomLongBetween(0, Long.MAX_VALUE)); + case 9 -> builder.setRolloverMinPrimaryShardDocs(randomLongBetween(0, Long.MAX_VALUE)); + case 10 -> { + builder.setRolloverMinPrimaryShardSize(randomByteSize()); + } + case 11 -> { + builder.setRolloverMinSize(randomByteSize()); + } + case 12 -> builder.setPriority(randomValueOtherThan(instance.getSetPriorityPriority(), () -> randomIntBetween(0, 50))); + case 13 -> { + builder.setShrinkMaxPrimaryShardSize(randomByteSize()); + } + case 14 -> builder.setShrinkNumberOfShards( randomValueOtherThan(instance.getShrinkNumberOfShards(), () -> randomIntBetween(0, 50)) ); - case 9 -> builder.setRolloverMaxPrimaryShardDocs(randomLongBetween(0, Long.MAX_VALUE)); default -> throw new IllegalStateException("Illegal randomization branch"); } return builder.build(); } + + private static ByteSizeValue randomByteSize() { + return ByteSizeValue.ofBytes(randomLongBetween(0, 1024L * 1024L * 1024L * 50L)); + } } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/PhaseCacheManagementTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/PhaseCacheManagementTests.java index 42ec31e8b9470..049a04836f575 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/PhaseCacheManagementTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/PhaseCacheManagementTests.java @@ -82,7 +82,7 @@ public void testRefreshPhaseJson() throws IOException { String indexName = meta.getIndex().getName(); Map actions = new HashMap<>(); - actions.put("rollover", new RolloverAction(null, null, null, 1L, null)); + actions.put("rollover", new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); actions.put("set_priority", new SetPriorityAction(100)); Phase hotPhase = new Phase("hot", TimeValue.ZERO, actions); Map phases = Collections.singletonMap("hot", hotPhase); @@ -318,7 +318,7 @@ public void testIndexCanBeSafelyUpdated() { IndexMetadata meta = mkMeta().putCustom(ILM_CUSTOM_METADATA_KEY, exState.asMap()).build(); Map actions = new HashMap<>(); - actions.put("rollover", new RolloverAction(null, null, null, 1L, null)); + actions.put("rollover", new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); actions.put("set_priority", new SetPriorityAction(100)); Phase hotPhase = new Phase("hot", TimeValue.ZERO, actions); Map phases = Collections.singletonMap("hot", hotPhase); @@ -391,7 +391,10 @@ public void testIndexCanBeSafelyUpdated() { IndexMetadata meta = mkMeta().putCustom(ILM_CUSTOM_METADATA_KEY, exState.asMap()).build(); Map actions = new HashMap<>(); - actions.put("rollover", new RolloverAction(null, null, TimeValue.timeValueSeconds(5), null, null)); + actions.put( + "rollover", + new RolloverAction(null, null, TimeValue.timeValueSeconds(5), null, null, null, null, null, null, null) + ); Phase hotPhase = new Phase("hot", TimeValue.ZERO, actions); Map phases = Collections.singletonMap("hot", hotPhase); LifecyclePolicy newPolicy = new LifecyclePolicy("my-policy", phases); @@ -422,7 +425,7 @@ public void testIndexCanBeSafelyUpdated() { IndexMetadata meta = mkMeta().putCustom(ILM_CUSTOM_METADATA_KEY, exState.asMap()).build(); Map actions = new HashMap<>(); - actions.put("rollover", new RolloverAction(null, null, null, 1L, null)); + actions.put("rollover", new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); actions.put("set_priority", new SetPriorityAction(100)); Phase hotPhase = new Phase("hot", TimeValue.ZERO, actions); Map phases = Collections.singletonMap("hot", hotPhase); @@ -443,7 +446,7 @@ public void testIndexCanBeSafelyUpdated() { IndexMetadata meta = mkMeta().putCustom(ILM_CUSTOM_METADATA_KEY, exState.asMap()).build(); Map actions = new HashMap<>(); - actions.put("rollover", new RolloverAction(null, null, null, 1L, null)); + actions.put("rollover", new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); actions.put("set_priority", new SetPriorityAction(100)); Phase hotPhase = new Phase("hot", TimeValue.ZERO, actions); Map phases = Collections.singletonMap("hot", hotPhase); @@ -482,14 +485,14 @@ public void testUpdateIndicesForPolicy() throws IOException { assertTrue(eligibleToCheckForRefresh(meta)); Map oldActions = new HashMap<>(); - oldActions.put("rollover", new RolloverAction(null, null, null, 1L, null)); + oldActions.put("rollover", new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); oldActions.put("set_priority", new SetPriorityAction(100)); Phase oldHotPhase = new Phase("hot", TimeValue.ZERO, oldActions); Map oldPhases = Collections.singletonMap("hot", oldHotPhase); LifecyclePolicy oldPolicy = new LifecyclePolicy("my-policy", oldPhases); Map actions = new HashMap<>(); - actions.put("rollover", new RolloverAction(null, null, null, 1L, null)); + actions.put("rollover", new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); actions.put("set_priority", new SetPriorityAction(100)); Phase hotPhase = new Phase("hot", TimeValue.ZERO, actions); Map phases = Collections.singletonMap("hot", hotPhase); @@ -509,7 +512,7 @@ public void testUpdateIndicesForPolicy() throws IOException { assertThat(updatedState, equalTo(existingState)); actions = new HashMap<>(); - actions.put("rollover", new RolloverAction(null, null, null, 2L, null)); + actions.put("rollover", new RolloverAction(null, null, null, 2L, null, null, null, null, null, null)); actions.put("set_priority", new SetPriorityAction(150)); hotPhase = new Phase("hot", TimeValue.ZERO, actions); phases = Collections.singletonMap("hot", hotPhase); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/RolloverActionTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/RolloverActionTests.java index b5f38994ffc60..dd28da61f9c81 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/RolloverActionTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/RolloverActionTests.java @@ -46,7 +46,31 @@ public static RolloverAction randomInstance() { Long maxPrimaryShardDocs = (maxSize == null && maxPrimaryShardSize == null && maxAge == null && maxDocs == null || randomBoolean()) ? randomNonNegativeLong() : null; - return new RolloverAction(maxSize, maxPrimaryShardSize, maxAge, maxDocs, maxPrimaryShardDocs); + ByteSizeUnit minSizeUnit = randomFrom(ByteSizeUnit.values()); + ByteSizeValue minSize = randomBoolean() ? null : new ByteSizeValue(randomNonNegativeLong() / minSizeUnit.toBytes(1), minSizeUnit); + ByteSizeUnit minPrimaryShardSizeUnit = randomFrom(ByteSizeUnit.values()); + ByteSizeValue minPrimaryShardSize = randomBoolean() + ? null + : new ByteSizeValue(randomNonNegativeLong() / minPrimaryShardSizeUnit.toBytes(1), minPrimaryShardSizeUnit); + Long minDocs = randomBoolean() ? null : randomNonNegativeLong(); + TimeValue minAge = (minDocs == null || randomBoolean()) + ? TimeValue.parseTimeValue(randomPositiveTimeValue(), "rollover_action_test") + : null; + Long minPrimaryShardDocs = (minSize == null && minPrimaryShardSize == null && minAge == null && minDocs == null || randomBoolean()) + ? randomNonNegativeLong() + : null; + return new RolloverAction( + maxSize, + maxPrimaryShardSize, + maxAge, + maxDocs, + maxPrimaryShardDocs, + minSize, + minPrimaryShardSize, + minAge, + minDocs, + minPrimaryShardDocs + ); } @Override @@ -61,7 +85,12 @@ protected RolloverAction mutateInstance(RolloverAction instance) throws IOExcept TimeValue maxAge = instance.getMaxAge(); Long maxDocs = instance.getMaxDocs(); Long maxPrimaryShardDocs = instance.getMaxPrimaryShardDocs(); - switch (between(0, 4)) { + ByteSizeValue minSize = instance.getMinSize(); + ByteSizeValue minPrimaryShardSize = instance.getMinPrimaryShardSize(); + TimeValue minAge = instance.getMinAge(); + Long minDocs = instance.getMinDocs(); + Long minPrimaryShardDocs = instance.getMinPrimaryShardDocs(); + switch (between(0, 9)) { case 0 -> maxSize = randomValueOtherThan(maxSize, () -> { ByteSizeUnit maxSizeUnit = randomFrom(ByteSizeUnit.values()); return new ByteSizeValue(randomNonNegativeLong() / maxSizeUnit.toBytes(1), maxSizeUnit); @@ -76,17 +105,42 @@ protected RolloverAction mutateInstance(RolloverAction instance) throws IOExcept ); case 3 -> maxDocs = maxDocs == null ? randomNonNegativeLong() : maxDocs + 1; case 4 -> maxPrimaryShardDocs = maxPrimaryShardDocs == null ? randomNonNegativeLong() : maxPrimaryShardDocs + 1; + case 5 -> minSize = randomValueOtherThan(minSize, () -> { + ByteSizeUnit minSizeUnit = randomFrom(ByteSizeUnit.values()); + return new ByteSizeValue(randomNonNegativeLong() / minSizeUnit.toBytes(1), minSizeUnit); + }); + case 6 -> minPrimaryShardSize = randomValueOtherThan(minPrimaryShardSize, () -> { + ByteSizeUnit minPrimaryShardSizeUnit = randomFrom(ByteSizeUnit.values()); + return new ByteSizeValue(randomNonNegativeLong() / minPrimaryShardSizeUnit.toBytes(1), minPrimaryShardSizeUnit); + }); + case 7 -> minAge = randomValueOtherThan( + minAge, + () -> TimeValue.parseTimeValue(randomPositiveTimeValue(), "rollover_action_test") + ); + case 8 -> minDocs = minDocs == null ? randomNonNegativeLong() : minDocs + 1; + case 9 -> minPrimaryShardDocs = minPrimaryShardDocs == null ? randomNonNegativeLong() : minPrimaryShardDocs + 1; default -> throw new AssertionError("Illegal randomisation branch"); } - return new RolloverAction(maxSize, maxPrimaryShardSize, maxAge, maxDocs, maxPrimaryShardDocs); + return new RolloverAction( + maxSize, + maxPrimaryShardSize, + maxAge, + maxDocs, + maxPrimaryShardDocs, + minSize, + minPrimaryShardSize, + minAge, + minDocs, + minPrimaryShardDocs + ); } public void testNoConditions() { IllegalArgumentException exception = expectThrows( IllegalArgumentException.class, - () -> new RolloverAction(null, null, null, null, null) + () -> new RolloverAction(null, null, null, null, null, null, null, null, null, null) ); - assertEquals("At least one rollover condition must be set.", exception.getMessage()); + assertEquals("At least one max_* rollover condition must be set.", exception.getMessage()); } public void testToSteps() { @@ -124,17 +178,22 @@ public void testToSteps() { assertEquals(action.getMaxAge(), firstStep.getMaxAge()); assertEquals(action.getMaxDocs(), firstStep.getMaxDocs()); assertEquals(action.getMaxPrimaryShardDocs(), firstStep.getMaxPrimaryShardDocs()); + assertEquals(action.getMinSize(), firstStep.getMinSize()); + assertEquals(action.getMinPrimaryShardSize(), firstStep.getMinPrimaryShardSize()); + assertEquals(action.getMinAge(), firstStep.getMinAge()); + assertEquals(action.getMinDocs(), firstStep.getMinDocs()); + assertEquals(action.getMinPrimaryShardDocs(), firstStep.getMinPrimaryShardDocs()); assertEquals(nextStepKey, fifthStep.getNextStepKey()); } public void testBwcSerializationWithMaxPrimaryShardDocs() throws Exception { // In case of serializing to node with older version, replace maxPrimaryShardDocs with maxDocs. - RolloverAction instance = new RolloverAction(null, null, null, null, 1L); + RolloverAction instance = new RolloverAction(null, null, null, null, 1L, null, null, null, null, null); RolloverAction deserializedInstance = copyInstance(instance, Version.V_8_1_0); assertThat(deserializedInstance.getMaxPrimaryShardDocs(), nullValue()); - // But not if maxSize is also specified: - instance = new RolloverAction(null, null, null, 2L, 1L); + // But not if maxDocs is also specified: + instance = new RolloverAction(null, null, null, 2L, 1L, null, null, null, null, null); deserializedInstance = copyInstance(instance, Version.V_8_1_0); assertThat(deserializedInstance.getMaxPrimaryShardDocs(), nullValue()); assertThat(deserializedInstance.getMaxDocs(), equalTo(instance.getMaxDocs())); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java index e1e817477b159..f4ae83b04498f 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/TimeseriesLifecycleTypeTests.java @@ -63,7 +63,18 @@ public class TimeseriesLifecycleTypeTests extends ESTestCase { private static final WaitForSnapshotAction TEST_WAIT_FOR_SNAPSHOT_ACTION = new WaitForSnapshotAction("policy"); private static final ForceMergeAction TEST_FORCE_MERGE_ACTION = new ForceMergeAction(1, null); - private static final RolloverAction TEST_ROLLOVER_ACTION = new RolloverAction(new ByteSizeValue(1), null, null, null, null); + private static final RolloverAction TEST_ROLLOVER_ACTION = new RolloverAction( + new ByteSizeValue(1), + null, + null, + null, + null, + null, + null, + null, + null, + null + ); private static final ShrinkAction TEST_SHRINK_ACTION = new ShrinkAction(1, null); private static final ReadOnlyAction TEST_READ_ONLY_ACTION = new ReadOnlyAction(); private static final SetPriorityAction TEST_PRIORITY_ACTION = new SetPriorityAction(0); @@ -316,7 +327,7 @@ public void testValidateActionsFollowingSearchableSnapshot() { TimeValue.ZERO, Map.of( RolloverAction.NAME, - new RolloverAction(null, null, null, 1L, null), + new RolloverAction(null, null, null, 1L, null, null, null, null, null, null), SearchableSnapshotAction.NAME, new SearchableSnapshotAction(randomAlphaOfLengthBetween(4, 10)) ) @@ -1092,7 +1103,12 @@ private ConcurrentMap convertActionNamesToActions(Strin ByteSizeValue.parseBytesSizeValue("0b", "test"), TimeValue.ZERO, 1L, - 1L + 1L, + null, + null, + null, + null, + null ); case ShrinkAction.NAME -> new ShrinkAction(1, null); case FreezeAction.NAME -> FreezeAction.INSTANCE; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/WaitForRolloverReadyStepTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/WaitForRolloverReadyStepTests.java index e177cb99bbb06..99aef721bfd04 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/WaitForRolloverReadyStepTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/WaitForRolloverReadyStepTests.java @@ -16,6 +16,11 @@ import org.elasticsearch.action.admin.indices.rollover.MaxPrimaryShardDocsCondition; import org.elasticsearch.action.admin.indices.rollover.MaxPrimaryShardSizeCondition; import org.elasticsearch.action.admin.indices.rollover.MaxSizeCondition; +import org.elasticsearch.action.admin.indices.rollover.MinAgeCondition; +import org.elasticsearch.action.admin.indices.rollover.MinDocsCondition; +import org.elasticsearch.action.admin.indices.rollover.MinPrimaryShardDocsCondition; +import org.elasticsearch.action.admin.indices.rollover.MinPrimaryShardSizeCondition; +import org.elasticsearch.action.admin.indices.rollover.MinSizeCondition; import org.elasticsearch.action.admin.indices.rollover.RolloverInfo; import org.elasticsearch.action.admin.indices.rollover.RolloverRequest; import org.elasticsearch.action.admin.indices.rollover.RolloverResponse; @@ -27,6 +32,7 @@ import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.core.TimeValue; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xcontent.ToXContentObject; import org.mockito.Mockito; @@ -61,6 +67,17 @@ protected WaitForRolloverReadyStep createRandomInstance() { ? TimeValue.parseTimeValue(randomPositiveTimeValue(), "rollover_action_test") : null; Long maxPrimaryShardDocs = randomBoolean() ? null : randomNonNegativeLong(); + ByteSizeUnit minSizeUnit = randomFrom(ByteSizeUnit.values()); + ByteSizeValue minSize = randomBoolean() ? null : new ByteSizeValue(randomNonNegativeLong() / minSizeUnit.toBytes(1), minSizeUnit); + ByteSizeUnit minPrimaryShardSizeUnit = randomFrom(ByteSizeUnit.values()); + ByteSizeValue minPrimaryShardSize = randomBoolean() + ? null + : new ByteSizeValue(randomNonNegativeLong() / minPrimaryShardSizeUnit.toBytes(1), minPrimaryShardSizeUnit); + Long minDocs = randomBoolean() ? null : randomNonNegativeLong(); + TimeValue minAge = (minDocs == null || randomBoolean()) + ? TimeValue.parseTimeValue(randomPositiveTimeValue(), "rollover_action_test") + : null; + Long minPrimaryShardDocs = randomBoolean() ? null : randomNonNegativeLong(); return new WaitForRolloverReadyStep( stepKey, nextStepKey, @@ -69,7 +86,12 @@ protected WaitForRolloverReadyStep createRandomInstance() { maxPrimaryShardSize, maxAge, maxDocs, - maxPrimaryShardDocs + maxPrimaryShardDocs, + minSize, + minPrimaryShardSize, + minAge, + minDocs, + minPrimaryShardDocs ); } @@ -82,8 +104,13 @@ protected WaitForRolloverReadyStep mutateInstance(WaitForRolloverReadyStep insta TimeValue maxAge = instance.getMaxAge(); Long maxDocs = instance.getMaxDocs(); Long maxPrimaryShardDocs = instance.getMaxPrimaryShardDocs(); + ByteSizeValue minSize = instance.getMinSize(); + ByteSizeValue minPrimaryShardSize = instance.getMinPrimaryShardSize(); + TimeValue minAge = instance.getMinAge(); + Long minDocs = instance.getMinDocs(); + Long minPrimaryShardDocs = instance.getMinPrimaryShardDocs(); - switch (between(0, 6)) { + switch (between(0, 11)) { case 0 -> key = new Step.StepKey(key.getPhase(), key.getAction(), key.getName() + randomAlphaOfLength(5)); case 1 -> nextKey = new Step.StepKey(key.getPhase(), key.getAction(), key.getName() + randomAlphaOfLength(5)); case 2 -> maxSize = randomValueOtherThan(maxSize, () -> { @@ -100,6 +127,20 @@ protected WaitForRolloverReadyStep mutateInstance(WaitForRolloverReadyStep insta ); case 5 -> maxDocs = randomValueOtherThan(maxDocs, () -> randomNonNegativeLong()); case 6 -> maxPrimaryShardDocs = randomValueOtherThan(maxPrimaryShardDocs, () -> randomNonNegativeLong()); + case 7 -> minSize = randomValueOtherThan(minSize, () -> { + ByteSizeUnit minSizeUnit = randomFrom(ByteSizeUnit.values()); + return new ByteSizeValue(randomNonNegativeLong() / minSizeUnit.toBytes(1), minSizeUnit); + }); + case 8 -> minPrimaryShardSize = randomValueOtherThan(minPrimaryShardSize, () -> { + ByteSizeUnit minPrimaryShardSizeUnit = randomFrom(ByteSizeUnit.values()); + return new ByteSizeValue(randomNonNegativeLong() / minPrimaryShardSizeUnit.toBytes(1), minPrimaryShardSizeUnit); + }); + case 9 -> minAge = randomValueOtherThan( + minAge, + () -> TimeValue.parseTimeValue(randomPositiveTimeValue(), "rollover_action_test") + ); + case 10 -> minDocs = randomValueOtherThan(minDocs, ESTestCase::randomNonNegativeLong); + case 11 -> minPrimaryShardDocs = randomValueOtherThan(minPrimaryShardDocs, () -> randomNonNegativeLong()); default -> throw new AssertionError("Illegal randomisation branch"); } return new WaitForRolloverReadyStep( @@ -110,7 +151,12 @@ protected WaitForRolloverReadyStep mutateInstance(WaitForRolloverReadyStep insta maxPrimaryShardSize, maxAge, maxDocs, - maxPrimaryShardDocs + maxPrimaryShardDocs, + minSize, + minPrimaryShardSize, + minAge, + minDocs, + minPrimaryShardDocs ); } @@ -124,7 +170,12 @@ protected WaitForRolloverReadyStep copyInstance(WaitForRolloverReadyStep instanc instance.getMaxPrimaryShardSize(), instance.getMaxAge(), instance.getMaxDocs(), - instance.getMaxPrimaryShardDocs() + instance.getMaxPrimaryShardDocs(), + instance.getMinSize(), + instance.getMinPrimaryShardSize(), + instance.getMinAge(), + instance.getMinDocs(), + instance.getMinPrimaryShardDocs() ); } @@ -273,6 +324,21 @@ private void mockRolloverIndexCall(String rolloverTarget, WaitForRolloverReadySt if (step.getMaxPrimaryShardDocs() != null) { expectedConditions.add(new MaxPrimaryShardDocsCondition(step.getMaxPrimaryShardDocs())); } + if (step.getMinSize() != null) { + expectedConditions.add(new MinSizeCondition(step.getMinSize())); + } + if (step.getMinPrimaryShardSize() != null) { + expectedConditions.add(new MinPrimaryShardSizeCondition(step.getMinPrimaryShardSize())); + } + if (step.getMinAge() != null) { + expectedConditions.add(new MinAgeCondition(step.getMinAge())); + } + if (step.getMinDocs() != null) { + expectedConditions.add(new MinDocsCondition(step.getMinDocs())); + } + if (step.getMinPrimaryShardDocs() != null) { + expectedConditions.add(new MinPrimaryShardDocsCondition(step.getMinPrimaryShardDocs())); + } assertRolloverIndexRequest(request, rolloverTarget, expectedConditions); Map conditionResults = expectedConditions.stream() .collect(Collectors.toMap(Condition::toString, condition -> true)); @@ -478,6 +544,21 @@ public void testPerformActionNotComplete() { if (step.getMaxPrimaryShardDocs() != null) { expectedConditions.add(new MaxPrimaryShardDocsCondition(step.getMaxPrimaryShardDocs())); } + if (step.getMinSize() != null) { + expectedConditions.add(new MinSizeCondition(step.getMinSize())); + } + if (step.getMinPrimaryShardSize() != null) { + expectedConditions.add(new MinPrimaryShardSizeCondition(step.getMinPrimaryShardSize())); + } + if (step.getMinAge() != null) { + expectedConditions.add(new MinAgeCondition(step.getMinAge())); + } + if (step.getMinDocs() != null) { + expectedConditions.add(new MinDocsCondition(step.getMinDocs())); + } + if (step.getMinPrimaryShardDocs() != null) { + expectedConditions.add(new MinPrimaryShardDocsCondition(step.getMinPrimaryShardDocs())); + } assertRolloverIndexRequest(request, alias, expectedConditions); Map conditionResults = expectedConditions.stream() .collect(Collectors.toMap(Condition::toString, condition -> false)); @@ -537,6 +618,21 @@ public void testPerformActionFailure() { if (step.getMaxPrimaryShardDocs() != null) { expectedConditions.add(new MaxPrimaryShardDocsCondition(step.getMaxPrimaryShardDocs())); } + if (step.getMinSize() != null) { + expectedConditions.add(new MinSizeCondition(step.getMinSize())); + } + if (step.getMinPrimaryShardSize() != null) { + expectedConditions.add(new MinPrimaryShardSizeCondition(step.getMinPrimaryShardSize())); + } + if (step.getMinAge() != null) { + expectedConditions.add(new MinAgeCondition(step.getMinAge())); + } + if (step.getMinDocs() != null) { + expectedConditions.add(new MinDocsCondition(step.getMinDocs())); + } + if (step.getMinPrimaryShardDocs() != null) { + expectedConditions.add(new MinPrimaryShardDocsCondition(step.getMinPrimaryShardDocs())); + } assertRolloverIndexRequest(request, alias, expectedConditions); listener.onFailure(exception); return null; diff --git a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/MigrateToDataTiersIT.java b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/MigrateToDataTiersIT.java index 336f5fe91bcf3..b76ae3fe5bbcd 100644 --- a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/MigrateToDataTiersIT.java +++ b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/MigrateToDataTiersIT.java @@ -140,7 +140,12 @@ public void testMigrateToDataTiersAction() throws Exception { // let's also have a policy that doesn't need migrating String rolloverOnlyPolicyName = "rollover-policy"; - createNewSingletonPolicy(client(), rolloverOnlyPolicyName, "hot", new RolloverAction(null, null, null, 1L, null)); + createNewSingletonPolicy( + client(), + rolloverOnlyPolicyName, + "hot", + new RolloverAction(null, null, null, 1L, null, null, null, null, null, null) + ); String rolloverIndexPrefix = "rolloverpolicytest_index"; for (int i = 1; i <= 2; i++) { diff --git a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/TimeSeriesRestDriver.java b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/TimeSeriesRestDriver.java index 48f21f23ccecb..1bfebdadb89a4 100644 --- a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/TimeSeriesRestDriver.java +++ b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/TimeSeriesRestDriver.java @@ -174,7 +174,7 @@ public static void rolloverMaxOneDocCondition(RestClient client, String indexAbs public static void createFullPolicy(RestClient client, String policyName, TimeValue hotTime) throws IOException { Map hotActions = new HashMap<>(); hotActions.put(SetPriorityAction.NAME, new SetPriorityAction(100)); - hotActions.put(RolloverAction.NAME, new RolloverAction(null, null, null, 1L, null)); + hotActions.put(RolloverAction.NAME, new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); Map warmActions = new HashMap<>(); warmActions.put(SetPriorityAction.NAME, new SetPriorityAction(50)); warmActions.put(ForceMergeAction.NAME, new ForceMergeAction(1, null)); diff --git a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/ChangePolicyForIndexIT.java b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/ChangePolicyForIndexIT.java index d23999449f9bf..f076f379cd01c 100644 --- a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/ChangePolicyForIndexIT.java +++ b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/ChangePolicyForIndexIT.java @@ -64,7 +64,11 @@ public void testChangePolicyForIndex() throws Exception { Map phases1 = new HashMap<>(); phases1.put( "hot", - new Phase("hot", TimeValue.ZERO, singletonMap(RolloverAction.NAME, new RolloverAction(null, null, null, 1L, null))) + new Phase( + "hot", + TimeValue.ZERO, + singletonMap(RolloverAction.NAME, new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)) + ) ); phases1.put( "warm", @@ -78,7 +82,11 @@ public void testChangePolicyForIndex() throws Exception { Map phases2 = new HashMap<>(); phases2.put( "hot", - new Phase("hot", TimeValue.ZERO, singletonMap(RolloverAction.NAME, new RolloverAction(null, null, null, 1000L, null))) + new Phase( + "hot", + TimeValue.ZERO, + singletonMap(RolloverAction.NAME, new RolloverAction(null, null, null, 1000L, null, null, null, null, null, null)) + ) ); phases2.put( "warm", @@ -170,7 +178,7 @@ public void testILMHonoursTheCachedPhaseAfterPolicyUpdate() throws Exception { String indexName = "test-000001"; String policyName = "rolloverPolicy"; String alias = "thealias"; - createNewSingletonPolicy(client(), policyName, "hot", new RolloverAction(null, null, null, 1L, null)); + createNewSingletonPolicy(client(), policyName, "hot", new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); createIndexWithSettings( client(), diff --git a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/TimeSeriesDataStreamsIT.java b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/TimeSeriesDataStreamsIT.java index 65f4538a5b705..6a3bbde470213 100644 --- a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/TimeSeriesDataStreamsIT.java +++ b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/TimeSeriesDataStreamsIT.java @@ -72,7 +72,7 @@ public void refreshAbstractions() { } public void testRolloverAction() throws Exception { - createNewSingletonPolicy(client(), policyName, "hot", new RolloverAction(null, null, null, 1L, null)); + createNewSingletonPolicy(client(), policyName, "hot", new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); createComposableTemplate(client(), template, dataStream + "*", getTemplate(policyName)); @@ -95,7 +95,7 @@ public void testRolloverAction() throws Exception { } public void testRolloverIsSkippedOnManualDataStreamRollover() throws Exception { - createNewSingletonPolicy(client(), policyName, "hot", new RolloverAction(null, null, null, 2L, null)); + createNewSingletonPolicy(client(), policyName, "hot", new RolloverAction(null, null, null, 2L, null, null, null, null, null, null)); createComposableTemplate(client(), template, dataStream + "*", getTemplate(policyName)); diff --git a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/TimeSeriesLifecycleActionsIT.java b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/TimeSeriesLifecycleActionsIT.java index 25ea0b1bf7995..4fb3ba4301a2a 100644 --- a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/TimeSeriesLifecycleActionsIT.java +++ b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/TimeSeriesLifecycleActionsIT.java @@ -177,7 +177,7 @@ public void testUpdatePolicyToNotContainFailedStep() throws Exception { assertTrue(indexExists(index)); // updating the policy to not contain the delete phase at all - createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 1L, null)); + createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); // ILM must honour the cached delete phase and eventually delete the index Request request = new Request("PUT", index + "/_settings"); @@ -570,7 +570,7 @@ public void testNonexistentPolicy() throws Exception { client().performRequest(templateRequest); policy = randomAlphaOfLengthBetween(5, 20); - createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 1L, null)); + createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); index = indexPrefix + "-000001"; final StringEntity putIndex = new StringEntity(""" @@ -694,7 +694,7 @@ public void testRemoveAndReaddPolicy() throws Exception { String originalIndex = index + "-000001"; String secondIndex = index + "-000002"; // Set up a policy with rollover - createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 1L, null)); + createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); createIndexWithSettings( client(), originalIndex, @@ -776,7 +776,7 @@ public void testWaitForActiveShardsStep() throws Exception { ); // create policy - createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 1L, null)); + createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); // update policy on index updatePolicy(client(), originalIndex, policy); Request createIndexTemplate = new Request("PUT", "_template/rolling_indexes"); @@ -804,7 +804,7 @@ public void testWaitForActiveShardsStep() throws Exception { } public void testHistoryIsWrittenWithSuccess() throws Exception { - createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 1L, null)); + createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); Request createIndexTemplate = new Request("PUT", "_template/rolling_indexes"); createIndexTemplate.setJsonEntity(""" { @@ -838,7 +838,7 @@ public void testHistoryIsWrittenWithSuccess() throws Exception { public void testHistoryIsWrittenWithFailure() throws Exception { createIndexWithSettings(client(), index + "-1", alias, Settings.builder(), false); - createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 1L, null)); + createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); updatePolicy(client(), index + "-1", policy); // Index a document @@ -913,7 +913,7 @@ public void testRetryableInitializationStep() throws Exception { public void testRefreshablePhaseJson() throws Exception { String index = "refresh-index"; - createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 100L, null)); + createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 100L, null, null, null, null, null, null)); Request createIndexTemplate = new Request("PUT", "_template/rolling_indexes"); createIndexTemplate.setJsonEntity(""" { @@ -943,7 +943,7 @@ public void testRefreshablePhaseJson() throws Exception { assertBusy(() -> assertThat(getStepKeyForIndex(client(), index + "-1").getName(), equalTo(WaitForRolloverReadyStep.NAME))); // Update the policy to allow rollover at 1 document instead of 100 - createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 1L, null)); + createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); // Index should now have been able to roll over, creating the new index and proceeding to the "complete" step assertBusy(() -> assertThat(indexExists(index + "-000002"), is(true))); diff --git a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/TimeseriesMoveToStepIT.java b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/TimeseriesMoveToStepIT.java index c4f6e86e20421..651dffacdfe0e 100644 --- a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/TimeseriesMoveToStepIT.java +++ b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/TimeseriesMoveToStepIT.java @@ -189,7 +189,7 @@ public void testMoveToStepRereadsPolicy() throws Exception { client(), policy, "hot", - new RolloverAction(null, null, TimeValue.timeValueHours(1), null, null), + new RolloverAction(null, null, TimeValue.timeValueHours(1), null, null, null, null, null, null, null), TimeValue.ZERO ); @@ -211,7 +211,13 @@ public void testMoveToStepRereadsPolicy() throws Exception { TimeUnit.SECONDS ); - createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 1L, null), TimeValue.ZERO); + createNewSingletonPolicy( + client(), + policy, + "hot", + new RolloverAction(null, null, null, 1L, null, null, null, null, null, null), + TimeValue.ZERO + ); // Move to the same step, which should re-read the policy Request moveToStepRequest = new Request("POST", "_ilm/move/test-1"); diff --git a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/ReadonlyActionIT.java b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/ReadonlyActionIT.java index b2a39f2a2e1df..ec0b685dd5bd0 100644 --- a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/ReadonlyActionIT.java +++ b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/ReadonlyActionIT.java @@ -73,7 +73,7 @@ public void testReadOnlyInTheHotPhase() throws Exception { // add a policy Map hotActions = Map.of( RolloverAction.NAME, - new RolloverAction(null, null, null, 1L, null), + new RolloverAction(null, null, null, 1L, null, null, null, null, null, null), ReadOnlyAction.NAME, new ReadOnlyAction() ); diff --git a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/RolloverActionIT.java b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/RolloverActionIT.java index eab4b442868e0..c4384e06dbf47 100644 --- a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/RolloverActionIT.java +++ b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/RolloverActionIT.java @@ -67,7 +67,7 @@ public void testRolloverAction() throws Exception { ); // create policy - createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 1L, null)); + createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); // update policy on index updatePolicy(client(), originalIndex, policy); // index document {"foo": "bar"} to trigger rollover @@ -118,7 +118,7 @@ public void testRolloverActionWithIndexingComplete() throws Exception { client().performRequest(updateAliasRequest); // create policy - createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 1L, null)); + createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); // update policy on index updatePolicy(client(), originalIndex, policy); // index document {"foo": "bar"} to trigger rollover @@ -148,7 +148,12 @@ public void testRolloverActionWithMaxPrimaryShardSize() throws Exception { index(client(), originalIndex, "_id", "foo", "bar"); // create policy - createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, ByteSizeValue.ofBytes(1), null, null, null)); + createNewSingletonPolicy( + client(), + policy, + "hot", + new RolloverAction(null, ByteSizeValue.ofBytes(1), null, null, null, null, null, null, null, null) + ); // update policy on index updatePolicy(client(), originalIndex, policy); @@ -176,7 +181,7 @@ public void testRolloverActionWithMaxPrimaryDocsSize() throws Exception { index(client(), originalIndex, "_id", "foo", "bar"); // create policy - createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, null, 1L)); + createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, null, 1L, null, null, null, null, null)); // update policy on index updatePolicy(client(), originalIndex, policy); @@ -191,7 +196,12 @@ public void testRolloverActionWithMaxPrimaryDocsSize() throws Exception { public void testILMRolloverRetriesOnReadOnlyBlock() throws Exception { String firstIndex = index + "-000001"; - createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, TimeValue.timeValueSeconds(1), null, null)); + createNewSingletonPolicy( + client(), + policy, + "hot", + new RolloverAction(null, null, TimeValue.timeValueSeconds(1), null, null, null, null, null, null, null) + ); // create the index as readonly and associate the ILM policy to it createIndexWithSettings( @@ -231,7 +241,7 @@ public void testILMRolloverOnManuallyRolledIndex() throws Exception { String thirdIndex = index + "-000003"; // Set up a policy with rollover - createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 2L, null)); + createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 2L, null, null, null, null, null, null)); Request createIndexTemplate = new Request("PUT", "_template/rolling_indexes"); createIndexTemplate.setJsonEntity(""" { @@ -289,7 +299,12 @@ public void testRolloverStepRetriesUntilRolledOverIndexIsDeleted() throws Except String index = this.index + "-000001"; String rolledIndex = this.index + "-000002"; - createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, TimeValue.timeValueSeconds(1), null, null)); + createNewSingletonPolicy( + client(), + policy, + "hot", + new RolloverAction(null, null, TimeValue.timeValueSeconds(1), null, null, null, null, null, null, null) + ); // create the rolled index so the rollover of the first index fails createIndexWithSettings( @@ -371,7 +386,7 @@ public void testRolloverStepRetriesUntilRolledOverIndexIsDeleted() throws Except public void testUpdateRolloverLifecycleDateStepRetriesWhenRolloverInfoIsMissing() throws Exception { String index = this.index + "-000001"; - createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 1L, null)); + createNewSingletonPolicy(client(), policy, "hot", new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); createIndexWithSettings( client(), diff --git a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/SearchableSnapshotActionIT.java b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/SearchableSnapshotActionIT.java index 49742e57835b2..849b00464f0fb 100644 --- a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/SearchableSnapshotActionIT.java +++ b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/SearchableSnapshotActionIT.java @@ -250,7 +250,7 @@ public void testCreateInvalidPolicy() { TimeValue.ZERO, Map.of( RolloverAction.NAME, - new RolloverAction(null, null, null, 1L, null), + new RolloverAction(null, null, null, 1L, null, null, null, null, null, null), SearchableSnapshotAction.NAME, new SearchableSnapshotAction(randomAlphaOfLengthBetween(4, 10)) ) @@ -281,7 +281,7 @@ public void testUpdatePolicyToAddPhasesYieldsInvalidActionsToBeSkipped() throws TimeValue.ZERO, Map.of( RolloverAction.NAME, - new RolloverAction(null, null, null, 1L, null), + new RolloverAction(null, null, null, 1L, null, null, null, null, null, null), SearchableSnapshotAction.NAME, new SearchableSnapshotAction(snapshotRepo) ) @@ -360,7 +360,7 @@ public void testRestoredIndexManagedByLocalPolicySkipsIllegalActions() throws Ex TimeValue.ZERO, Map.of( RolloverAction.NAME, - new RolloverAction(null, null, null, 1L, null), + new RolloverAction(null, null, null, 1L, null, null, null, null, null, null), SearchableSnapshotAction.NAME, new SearchableSnapshotAction(snapshotRepo) ) @@ -452,7 +452,7 @@ public void testIdenticalSearchableSnapshotActionIsNoop() throws Exception { String index = "myindex-" + randomAlphaOfLength(4).toLowerCase(Locale.ROOT) + "-000001"; createSnapshotRepo(client(), snapshotRepo, randomBoolean()); Map hotActions = new HashMap<>(); - hotActions.put(RolloverAction.NAME, new RolloverAction(null, null, null, 1L, null)); + hotActions.put(RolloverAction.NAME, new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); hotActions.put(SearchableSnapshotAction.NAME, new SearchableSnapshotAction(snapshotRepo, randomBoolean())); createPolicy( client(), @@ -620,7 +620,7 @@ public void testSearchableSnapshotsInHotPhasePinnedToHotNodes() throws Exception TimeValue.ZERO, Map.of( RolloverAction.NAME, - new RolloverAction(null, null, null, 1L, null), + new RolloverAction(null, null, null, 1L, null, null, null, null, null, null), SearchableSnapshotAction.NAME, new SearchableSnapshotAction(snapshotRepo, randomBoolean()) ) diff --git a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/ShrinkActionIT.java b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/ShrinkActionIT.java index 8c70150376267..0dfbecde9847f 100644 --- a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/ShrinkActionIT.java +++ b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/ShrinkActionIT.java @@ -206,7 +206,7 @@ public void testShrinkActionInTheHotPhase() throws Exception { // add a policy Map hotActions = Map.of( RolloverAction.NAME, - new RolloverAction(null, null, null, 1L, null), + new RolloverAction(null, null, null, 1L, null, null, null, null, null, null), ShrinkAction.NAME, new ShrinkAction(expectedFinalShards, null) ); diff --git a/x-pack/plugin/ilm/qa/with-security/src/javaRestTest/java/org/elasticsearch/xpack/security/PermissionsIT.java b/x-pack/plugin/ilm/qa/with-security/src/javaRestTest/java/org/elasticsearch/xpack/security/PermissionsIT.java index 8084e170dc13d..88e0ff750fe4e 100644 --- a/x-pack/plugin/ilm/qa/with-security/src/javaRestTest/java/org/elasticsearch/xpack/security/PermissionsIT.java +++ b/x-pack/plugin/ilm/qa/with-security/src/javaRestTest/java/org/elasticsearch/xpack/security/PermissionsIT.java @@ -275,7 +275,12 @@ public void testWhenUserLimitedByOnlyAliasOfIndexCanWriteToIndexWhichWasRolledov * - Create role with just write and manage privileges on alias * - Create user and assign newly created role. */ - createNewSingletonPolicy(adminClient(), "foo-policy", "hot", new RolloverAction(null, null, null, 2L, null)); + createNewSingletonPolicy( + adminClient(), + "foo-policy", + "hot", + new RolloverAction(null, null, null, 2L, null, null, null, null, null, null) + ); createIndexTemplate("foo-template", "foo-logs-*", "foo_alias", "foo-policy"); createIndexAsAdmin("foo-logs-000001", "foo_alias", randomBoolean()); createRole("foo_alias_role", "foo_alias"); diff --git a/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/ILMMultiNodeIT.java b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/ILMMultiNodeIT.java index 6873fc7a82730..03f9d9fc26cdf 100644 --- a/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/ILMMultiNodeIT.java +++ b/x-pack/plugin/ilm/src/internalClusterTest/java/org/elasticsearch/xpack/ilm/ILMMultiNodeIT.java @@ -64,7 +64,7 @@ public void testShrinkOnTiers() throws Exception { startWarmOnlyNode(); ensureGreen(); - RolloverAction rolloverAction = new RolloverAction(null, null, null, 1L, null); + RolloverAction rolloverAction = new RolloverAction(null, null, null, 1L, null, null, null, null, null, null); Phase hotPhase = new Phase("hot", TimeValue.ZERO, Collections.singletonMap(rolloverAction.getWriteableName(), rolloverAction)); ShrinkAction shrinkAction = new ShrinkAction(1, null); Phase warmPhase = new Phase("warm", TimeValue.ZERO, Collections.singletonMap(shrinkAction.getWriteableName(), shrinkAction)); diff --git a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycleUsageTransportAction.java b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycleUsageTransportAction.java index ab0e5467b962e..5a86e16577e95 100644 --- a/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycleUsageTransportAction.java +++ b/x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/IndexLifecycleUsageTransportAction.java @@ -119,8 +119,14 @@ private void collectActionConfigurations(String actionName, LifecycleAction acti RolloverAction rolloverAction = (RolloverAction) action; consumer.setRolloverMaxAge(rolloverAction.getMaxAge()); consumer.setRolloverMaxDocs(rolloverAction.getMaxDocs()); + consumer.setRolloverMaxPrimaryShardDocs(rolloverAction.getMaxPrimaryShardDocs()); consumer.setRolloverMaxPrimaryShardSize(rolloverAction.getMaxPrimaryShardSize()); consumer.setRolloverMaxSize(rolloverAction.getMaxSize()); + consumer.setRolloverMinAge(rolloverAction.getMinAge()); + consumer.setRolloverMinDocs(rolloverAction.getMinDocs()); + consumer.setRolloverMinPrimaryShardDocs(rolloverAction.getMinPrimaryShardDocs()); + consumer.setRolloverMinPrimaryShardSize(rolloverAction.getMinPrimaryShardSize()); + consumer.setRolloverMinSize(rolloverAction.getMinSize()); } case SetPriorityAction.NAME -> { SetPriorityAction setPriorityAction = (SetPriorityAction) action; diff --git a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleTransitionTests.java b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleTransitionTests.java index b58f3659a01a6..d07e20d0261c1 100644 --- a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleTransitionTests.java +++ b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/ilm/IndexLifecycleTransitionTests.java @@ -603,7 +603,21 @@ public void testValidateTransitionToCachedStepMissingFromPolicy() { try (Client client = new NoOpClient(getTestName())) { Step.StepKey currentStepKey = new Step.StepKey("hot", RolloverAction.NAME, WaitForRolloverReadyStep.NAME); Step.StepKey nextStepKey = new Step.StepKey("hot", RolloverAction.NAME, RolloverStep.NAME); - Step currentStep = new WaitForRolloverReadyStep(currentStepKey, nextStepKey, client, null, null, null, 1L, null); + Step currentStep = new WaitForRolloverReadyStep( + currentStepKey, + nextStepKey, + client, + null, + null, + null, + 1L, + null, + null, + null, + null, + null, + null + ); try { IndexLifecycleTransition.validateTransition( meta, @@ -906,7 +920,7 @@ public void testRefreshPhaseJson() throws IOException { String index = meta.getIndex().getName(); Map actions = new HashMap<>(); - actions.put("rollover", new RolloverAction(null, null, null, 1L, null)); + actions.put("rollover", new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); actions.put("set_priority", new SetPriorityAction(100)); Phase hotPhase = new Phase("hot", TimeValue.ZERO, actions); Map phases = Collections.singletonMap("hot", hotPhase); @@ -1057,7 +1071,7 @@ public void testMoveStateToNextActionAndUpdateCachedPhase() { IndexMetadata meta = buildIndexMetadata("my-policy", currentExecutionState); Map actions = new HashMap<>(); - actions.put("rollover", new RolloverAction(null, null, null, 1L, null)); + actions.put("rollover", new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); actions.put("set_priority", new SetPriorityAction(100)); Phase hotPhase = new Phase("hot", TimeValue.ZERO, actions); Map phases = Collections.singletonMap("hot", hotPhase); @@ -1105,7 +1119,7 @@ public void testMoveStateToNextActionAndUpdateCachedPhase() { // the expected new state is that the index is moved into the next action (could be the complete one) and the cached phase // definition is updated Map actionsWitoutSetPriority = new HashMap<>(); - actionsWitoutSetPriority.put("rollover", new RolloverAction(null, null, null, 1L, null)); + actionsWitoutSetPriority.put("rollover", new RolloverAction(null, null, null, 1L, null, null, null, null, null, null)); Phase hotPhaseNoSetPriority = new Phase("hot", TimeValue.ZERO, actionsWitoutSetPriority); Map phasesWithoutSetPriority = Collections.singletonMap("hot", hotPhaseNoSetPriority); LifecyclePolicyMetadata updatedPolicyMetadata = new LifecyclePolicyMetadata(