Expose index age in ILM explain output (#44457)
* Expose index age in ILM explain output This adds the index's age to the ILM explain output, for example: ``` { "indices" : { "ilm-000001" : { "index" : "ilm-000001", "managed" : true, "policy" : "full-lifecycle", "lifecycle_date" : "2019-07-16T19:48:22.294Z", "lifecycle_date_millis" : 1563306502294, "age" : "1.34m", "phase" : "hot", "phase_time" : "2019-07-16T19:48:22.487Z", ... etc ... } } } ``` This age can be used to tell when ILM will transition the index to the next phase, based on that phase's `min_age`. Resolves #38988 * Expose age in getters and in HLRC
This commit is contained in:
parent
af093a4095
commit
fe2ef66e45
|
@ -23,6 +23,7 @@ import org.elasticsearch.common.ParseField;
|
|||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
@ -52,6 +53,7 @@ public class IndexLifecycleExplainResponse implements ToXContentObject {
|
|||
private static final ParseField STEP_TIME_FIELD = new ParseField("step_time");
|
||||
private static final ParseField STEP_INFO_FIELD = new ParseField("step_info");
|
||||
private static final ParseField PHASE_EXECUTION_INFO = new ParseField("phase_execution");
|
||||
private static final ParseField AGE_FIELD = new ParseField("age");
|
||||
|
||||
public static final ConstructingObjectParser<IndexLifecycleExplainResponse, Void> PARSER = new ConstructingObjectParser<>(
|
||||
"index_lifecycle_explain_response", true,
|
||||
|
@ -205,6 +207,14 @@ public class IndexLifecycleExplainResponse implements ToXContentObject {
|
|||
return phaseExecutionInfo;
|
||||
}
|
||||
|
||||
public TimeValue getAge() {
|
||||
if (lifecycleDate == null) {
|
||||
return TimeValue.MINUS_ONE;
|
||||
} else {
|
||||
return TimeValue.timeValueMillis(System.currentTimeMillis() - lifecycleDate);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
|
@ -214,6 +224,7 @@ public class IndexLifecycleExplainResponse implements ToXContentObject {
|
|||
builder.field(POLICY_NAME_FIELD.getPreferredName(), policyName);
|
||||
if (lifecycleDate != null) {
|
||||
builder.timeField(LIFECYCLE_DATE_MILLIS_FIELD.getPreferredName(), LIFECYCLE_DATE_FIELD.getPreferredName(), lifecycleDate);
|
||||
builder.field(AGE_FIELD.getPreferredName(), getAge().toHumanReadableString(2));
|
||||
}
|
||||
if (phase != null) {
|
||||
builder.field(PHASE_FIELD.getPreferredName(), phase);
|
||||
|
|
|
@ -52,6 +52,11 @@ public class ExplainLifecycleResponseTests extends AbstractXContentTestCase<Expl
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean assertToXContentEquivalence() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NamedXContentRegistry xContentRegistry() {
|
||||
List<NamedXContentRegistry.Entry> entries = new ArrayList<>(ClusterModule.getNamedXWriteables());
|
||||
|
|
|
@ -103,6 +103,11 @@ public class IndexLifecycleExplainResponseTests extends AbstractXContentTestCase
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean assertToXContentEquivalence() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Predicate<String> getRandomFieldsExcludeFilter() {
|
||||
return (field) ->
|
||||
|
|
|
@ -98,12 +98,13 @@ that the index is managed and in the `new` phase:
|
|||
"managed": true, <1>
|
||||
"policy": "my_policy", <2>
|
||||
"lifecycle_date_millis": 1538475653281, <3>
|
||||
"age": "15s", <4>
|
||||
"phase": "new",
|
||||
"phase_time_millis": 1538475653317, <4>
|
||||
"phase_time_millis": 1538475653317, <5>
|
||||
"action": "complete",
|
||||
"action_time_millis": 1538475653317, <5>
|
||||
"action_time_millis": 1538475653317, <6>
|
||||
"step": "complete",
|
||||
"step_time_millis": 1538475653317 <6>
|
||||
"step_time_millis": 1538475653317 <7>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,9 +115,10 @@ that the index is managed and in the `new` phase:
|
|||
ILM the other fields will not be shown
|
||||
<2> The name of the policy which ILM is using for this index
|
||||
<3> The timestamp used for the `min_age`
|
||||
<4> When the index entered the current phase
|
||||
<5> When the index entered the current action
|
||||
<6> When the index entered the current step
|
||||
<4> The age of the index (used for calculating when to enter the next phase)
|
||||
<5> When the index entered the current phase
|
||||
<6> When the index entered the current action
|
||||
<7> When the index entered the current step
|
||||
|
||||
Once the policy is running on the index, the response includes a
|
||||
`phase_execution` object that shows the definition of the current phase.
|
||||
|
@ -133,6 +135,7 @@ phase completes.
|
|||
"policy": "my_lifecycle3",
|
||||
"lifecycle_date_millis": 1538475653281,
|
||||
"lifecycle_date": "2018-10-15T13:45:21.981Z",
|
||||
"age": "25.14s",
|
||||
"phase": "hot",
|
||||
"phase_time_millis": 1538475653317,
|
||||
"phase_time": "2018-10-15T13:45:22.577Z",
|
||||
|
@ -181,6 +184,7 @@ information for the step that's being performed on the index.
|
|||
"policy": "my_lifecycle3",
|
||||
"lifecycle_date_millis": 1538475653281,
|
||||
"lifecycle_date": "2018-10-15T13:45:21.981Z",
|
||||
"age": "4.12m",
|
||||
"phase": "warm",
|
||||
"phase_time_millis": 1538475653317,
|
||||
"phase_time": "2018-10-15T13:45:22.577Z",
|
||||
|
@ -241,6 +245,7 @@ the step that failed and the step info provides information about the error.
|
|||
"policy": "my_lifecycle3",
|
||||
"lifecycle_date_millis": 1538475653281,
|
||||
"lifecycle_date": "2018-10-15T13:45:21.981Z",
|
||||
"age": "50.1d",
|
||||
"phase": "hot",
|
||||
"phase_time_millis": 1538475653317,
|
||||
"phase_time": "2018-10-15T13:45:22.577Z",
|
||||
|
|
|
@ -76,20 +76,21 @@ Which returns the following information:
|
|||
"managed" : true, <1>
|
||||
"policy" : "shrink-the-index", <2>
|
||||
"lifecycle_date_millis" : 1541717265865,
|
||||
"phase" : "warm", <3>
|
||||
"age": "5.1d", <3>
|
||||
"phase" : "warm", <4>
|
||||
"phase_time_millis" : 1541717272601,
|
||||
"action" : "shrink", <4>
|
||||
"action" : "shrink", <5>
|
||||
"action_time_millis" : 1541717272601,
|
||||
"step" : "ERROR", <5>
|
||||
"step" : "ERROR", <6>
|
||||
"step_time_millis" : 1541717272688,
|
||||
"failed_step" : "shrink", <6>
|
||||
"failed_step" : "shrink", <7>
|
||||
"step_info" : {
|
||||
"type" : "illegal_argument_exception", <7>
|
||||
"reason" : "the number of target shards [4] must be less that the number of source shards [2]" <8>
|
||||
"type" : "illegal_argument_exception", <8>
|
||||
"reason" : "the number of target shards [4] must be less that the number of source shards [2]" <9>
|
||||
},
|
||||
"phase_execution" : {
|
||||
"policy" : "shrink-the-index",
|
||||
"phase_definition" : { <9>
|
||||
"phase_definition" : { <10>
|
||||
"min_age" : "5d",
|
||||
"actions" : {
|
||||
"shrink" : {
|
||||
|
@ -108,13 +109,14 @@ Which returns the following information:
|
|||
// TESTRESPONSE[skip:no way to know if we will get this response immediately]
|
||||
<1> this index is managed by ILM
|
||||
<2> the policy in question, in this case, "shrink-the-index"
|
||||
<3> what phase the index is currently in
|
||||
<4> what action the index is currently on
|
||||
<5> what step the index is currently on, in this case, because there is an error, the index is in the "ERROR" step
|
||||
<6> the name of the step that failed to execute, in this case "shrink"
|
||||
<7> the error class that occurred during this step
|
||||
<8> the error message that occurred during the execution failure
|
||||
<9> the definition of the phase (in this case, the "warm" phase) that the index is currently on
|
||||
<3> the current age for the index
|
||||
<4> what phase the index is currently in
|
||||
<5> what action the index is currently on
|
||||
<6> what step the index is currently on, in this case, because there is an error, the index is in the "ERROR" step
|
||||
<7> the name of the step that failed to execute, in this case "shrink"
|
||||
<8> the error class that occurred during this step
|
||||
<9> the error message that occurred during the execution failure
|
||||
<10> the definition of the phase (in this case, the "warm" phase) that the index is currently on
|
||||
|
||||
The index here has been moved to the error step because the shrink definition in
|
||||
the policy is using an incorrect number of shards. So rectifying that in the
|
||||
|
|
|
@ -173,15 +173,16 @@ managed indices.
|
|||
"managed": true, <1>
|
||||
"policy": "datastream_policy", <2>
|
||||
"lifecycle_date_millis": 1538475653281,
|
||||
"phase": "hot", <3>
|
||||
"age": "30s", <3>
|
||||
"phase": "hot", <4>
|
||||
"phase_time_millis": 1538475653317,
|
||||
"action": "rollover", <4>
|
||||
"action": "rollover", <5>
|
||||
"action_time_millis": 1538475653317,
|
||||
"step": "attempt-rollover", <5>
|
||||
"step": "attempt-rollover", <6>
|
||||
"step_time_millis": 1538475653317,
|
||||
"phase_execution": {
|
||||
"policy": "datastream_policy",
|
||||
"phase_definition": { <6>
|
||||
"phase_definition": { <7>
|
||||
"min_age": "0ms",
|
||||
"actions": {
|
||||
"rollover": {
|
||||
|
@ -190,7 +191,7 @@ managed indices.
|
|||
}
|
||||
}
|
||||
},
|
||||
"version": 1, <7>
|
||||
"version": 1, <8>
|
||||
"modified_date_in_millis": 1539609701576
|
||||
}
|
||||
}
|
||||
|
@ -201,12 +202,13 @@ managed indices.
|
|||
// TESTRESPONSE[skip:no way to know if we will get this response immediately]
|
||||
<1> this index is managed by ILM
|
||||
<2> the policy in question, in this case, "datastream_policy"
|
||||
<3> what phase the index is currently in
|
||||
<4> what action the index is currently on
|
||||
<5> what step the index is currently on
|
||||
<6> the definition of the phase
|
||||
<3> the current age of the index
|
||||
<4> what phase the index is currently in
|
||||
<5> what action the index is currently on
|
||||
<6> what step the index is currently on
|
||||
<7> the definition of the phase
|
||||
(in this case, the "hot" phase) that the index is currently on
|
||||
<7> the version of the policy being used to execute the current phase
|
||||
<8> the version of the policy being used to execute the current phase
|
||||
|
||||
You can read about the full details of this response in the
|
||||
<<ilm-explain-lifecycle, explain API docs>>. For now, let's focus on how
|
||||
|
|
|
@ -198,6 +198,7 @@ GET my_index/_ilm/explain
|
|||
"managed": true,
|
||||
"policy": "my_executing_policy",
|
||||
"lifecycle_date_millis": 1538475653281,
|
||||
"age": "30s",
|
||||
"phase": "hot",
|
||||
"phase_time_millis": 1538475653317,
|
||||
"action": "rollover",
|
||||
|
@ -275,6 +276,7 @@ GET my_index/_ilm/explain
|
|||
"managed": true,
|
||||
"policy": "my_executing_policy",
|
||||
"lifecycle_date_millis": 1538475653281,
|
||||
"age": "30s",
|
||||
"phase": "hot",
|
||||
"phase_time_millis": 1538475653317,
|
||||
"action": "rollover",
|
||||
|
@ -354,6 +356,7 @@ GET my_index/_ilm/explain
|
|||
"managed": true,
|
||||
"policy": "my_executing_policy",
|
||||
"lifecycle_date_millis": 1538475653281,
|
||||
"age": "30s",
|
||||
"phase": "hot",
|
||||
"phase_time_millis": 1538475653317,
|
||||
"action": "rollover",
|
||||
|
@ -408,6 +411,7 @@ GET my_index/_ilm/explain
|
|||
"managed": true,
|
||||
"policy": "my_executing_policy",
|
||||
"lifecycle_date_millis": 1538475653281,
|
||||
"age": "30s",
|
||||
"phase": "warm",
|
||||
"phase_time_millis": 1538475653317,
|
||||
"action": "forcemerge",
|
||||
|
|
|
@ -43,7 +43,7 @@ public abstract class AbstractStreamableXContentTestCase<T extends ToXContent &
|
|||
.shuffleFieldsExceptions(getShuffleFieldsExceptions())
|
||||
.randomFieldsExcludeFilter(getRandomFieldsExcludeFilter())
|
||||
.assertEqualsConsumer(this::assertEqualInstances)
|
||||
.assertToXContentEquivalence(true)
|
||||
.assertToXContentEquivalence(assertToXContentEquivalence())
|
||||
.test();
|
||||
}
|
||||
|
||||
|
@ -83,6 +83,16 @@ public abstract class AbstractStreamableXContentTestCase<T extends ToXContent &
|
|||
return Strings.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not to assert equivalence of the {@link org.elasticsearch.common.xcontent.XContent} of the test instance and the instance
|
||||
* parsed from the {@link org.elasticsearch.common.xcontent.XContent} of the test instance.
|
||||
*
|
||||
* @return true if equivalence should be asserted, otherwise false
|
||||
*/
|
||||
protected boolean assertToXContentEquivalence() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Params that have to be provided when calling calling {@link ToXContent#toXContent(XContentBuilder, ToXContent.Params)}
|
||||
*/
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.elasticsearch.common.bytes.BytesReference;
|
|||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
@ -42,6 +43,7 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
private static final ParseField STEP_TIME_FIELD = new ParseField("step_time");
|
||||
private static final ParseField STEP_INFO_FIELD = new ParseField("step_info");
|
||||
private static final ParseField PHASE_EXECUTION_INFO = new ParseField("phase_execution");
|
||||
private static final ParseField AGE_FIELD = new ParseField("age");
|
||||
|
||||
public static final ConstructingObjectParser<IndexLifecycleExplainResponse, Void> PARSER = new ConstructingObjectParser<>(
|
||||
"index_lifecycle_explain_response",
|
||||
|
@ -58,7 +60,9 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
(Long) (a[9]),
|
||||
(Long) (a[10]),
|
||||
(BytesReference) a[11],
|
||||
(PhaseExecutionInfo) a[12]));
|
||||
(PhaseExecutionInfo) a[12]
|
||||
// a[13] == "age"
|
||||
));
|
||||
static {
|
||||
PARSER.declareString(ConstructingObjectParser.constructorArg(), INDEX_FIELD);
|
||||
PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), MANAGED_BY_ILM_FIELD);
|
||||
|
@ -78,6 +82,7 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
}, STEP_INFO_FIELD);
|
||||
PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> PhaseExecutionInfo.parse(p, ""),
|
||||
PHASE_EXECUTION_INFO);
|
||||
PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), AGE_FIELD);
|
||||
}
|
||||
|
||||
private final String index;
|
||||
|
@ -243,6 +248,14 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
return phaseExecutionInfo;
|
||||
}
|
||||
|
||||
public TimeValue getAge() {
|
||||
if (lifecycleDate == null) {
|
||||
return TimeValue.MINUS_ONE;
|
||||
} else {
|
||||
return TimeValue.timeValueMillis(System.currentTimeMillis() - lifecycleDate);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
|
@ -252,6 +265,7 @@ public class IndexLifecycleExplainResponse implements ToXContentObject, Writeabl
|
|||
builder.field(POLICY_NAME_FIELD.getPreferredName(), policyName);
|
||||
if (lifecycleDate != null) {
|
||||
builder.timeField(LIFECYCLE_DATE_MILLIS_FIELD.getPreferredName(), LIFECYCLE_DATE_FIELD.getPreferredName(), lifecycleDate);
|
||||
builder.field(AGE_FIELD.getPreferredName(), getAge().toHumanReadableString(2));
|
||||
}
|
||||
if (phase != null) {
|
||||
builder.field(PHASE_FIELD.getPreferredName(), phase);
|
||||
|
|
|
@ -56,6 +56,11 @@ public class ExplainLifecycleResponseTests extends AbstractSerializingTestCase<E
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean assertToXContentEquivalence() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected NamedWriteableRegistry getNamedWriteableRegistry() {
|
||||
return new NamedWriteableRegistry(Arrays
|
||||
.asList(new NamedWriteableRegistry.Entry(LifecycleAction.class, MockAction.NAME, MockAction::new)));
|
||||
|
|
|
@ -93,6 +93,11 @@ public class IndexLifecycleExplainResponseTests extends AbstractSerializingTestC
|
|||
return IndexLifecycleExplainResponse.PARSER.apply(parser, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean assertToXContentEquivalence() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IndexLifecycleExplainResponse mutateInstance(IndexLifecycleExplainResponse instance) throws IOException {
|
||||
String index = instance.getIndex();
|
||||
|
|
|
@ -104,6 +104,7 @@ teardown:
|
|||
- match: { indices.my_index.action: "complete" }
|
||||
- match: { indices.my_index.step: "complete" }
|
||||
- is_true: indices.my_index.phase_time_millis
|
||||
- is_true: indices.my_index.age
|
||||
- is_false: indices.my_index.failed_step
|
||||
- is_false: indices.my_index.step_info
|
||||
- is_false: indices.my_index.phase_execution
|
||||
|
@ -126,6 +127,7 @@ teardown:
|
|||
- match: { indices.my_index.action: "complete" }
|
||||
- match: { indices.my_index.step: "complete" }
|
||||
- is_true: indices.my_index.phase_time_millis
|
||||
- is_true: indices.my_index.age
|
||||
- is_false: indices.my_index.failed_step
|
||||
- is_false: indices.my_index.step_info
|
||||
- is_false: indices.my_index.phase_execution
|
||||
|
@ -137,6 +139,7 @@ teardown:
|
|||
- match: { indices.my_index2.action: "complete" }
|
||||
- match: { indices.my_index2.step: "complete" }
|
||||
- is_true: indices.my_index2.phase_time_millis
|
||||
- is_true: indices.my_index2.age
|
||||
- is_false: indices.my_index2.failed_step
|
||||
- is_false: indices.my_index2.step_info
|
||||
- is_false: indices.my_index2.phase_execution
|
||||
|
@ -159,6 +162,7 @@ teardown:
|
|||
- match: { indices.my_index.action: "complete" }
|
||||
- match: { indices.my_index.step: "complete" }
|
||||
- is_true: indices.my_index.phase_time_millis
|
||||
- is_true: indices.my_index.age
|
||||
- is_false: indices.my_index.failed_step
|
||||
- is_false: indices.my_index.step_info
|
||||
- is_false: indices.my_index.phase_execution
|
||||
|
@ -170,6 +174,7 @@ teardown:
|
|||
- match: { indices.my_index2.action: "complete" }
|
||||
- match: { indices.my_index2.step: "complete" }
|
||||
- is_true: indices.my_index2.phase_time_millis
|
||||
- is_true: indices.my_index2.age
|
||||
- is_false: indices.my_index2.failed_step
|
||||
- is_false: indices.my_index2.step_info
|
||||
- is_false: indices.my_index2.phase_execution
|
||||
|
@ -181,6 +186,7 @@ teardown:
|
|||
- match: { indices.another_index.action: "complete" }
|
||||
- match: { indices.another_index.step: "complete" }
|
||||
- is_true: indices.another_index.phase_time_millis
|
||||
- is_true: indices.another_index.age
|
||||
- is_false: indices.another_index.failed_step
|
||||
- is_false: indices.another_index.step_info
|
||||
- is_false: indices.another_index.phase_execution
|
||||
|
@ -191,6 +197,7 @@ teardown:
|
|||
- is_false: indices.unmanaged_index.phase
|
||||
- is_false: indices.unmanaged_index.action
|
||||
- is_false: indices.unmanaged_index.step
|
||||
- is_false: indices.unmanaged.age
|
||||
- is_false: indices.another_index.failed_step
|
||||
- is_false: indices.another_index.step_info
|
||||
|
||||
|
@ -208,6 +215,7 @@ teardown:
|
|||
- is_false: indices.unmanaged_index.action
|
||||
- is_false: indices.unmanaged_index.step
|
||||
- is_false: indices.unmanaged_index.phase_execution
|
||||
- is_false: indices.unmanaged.age
|
||||
- is_false: indices.another_index.failed_step
|
||||
- is_false: indices.another_index.step_info
|
||||
- is_false: indices.my_index
|
||||
|
|
Loading…
Reference in New Issue