Skip to content

Commit 9ebd6e6

Browse files
authored
Only skip deleting a downsampled index if downsampling is in progress as part of DSL retention. (#109054)
Backporting #109020 to 8.14 branch.
1 parent a78f070 commit 9ebd6e6

File tree

3 files changed

+91
-10
lines changed

3 files changed

+91
-10
lines changed

docs/changelog/109020.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 109020
2+
summary: Only skip deleting a downsampled index if downsampling is in progress as
3+
part of DSL retention
4+
area: Data streams
5+
type: bug
6+
issues: []

modules/data-streams/src/main/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleService.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393

9494
import static org.elasticsearch.cluster.metadata.IndexMetadata.APIBlock.WRITE;
9595
import static org.elasticsearch.cluster.metadata.IndexMetadata.DownsampleTaskStatus.STARTED;
96+
import static org.elasticsearch.cluster.metadata.IndexMetadata.DownsampleTaskStatus.SUCCESS;
9697
import static org.elasticsearch.cluster.metadata.IndexMetadata.DownsampleTaskStatus.UNKNOWN;
9798
import static org.elasticsearch.cluster.metadata.IndexMetadata.INDEX_DOWNSAMPLE_STATUS;
9899
import static org.elasticsearch.datastreams.DataStreamsPlugin.LIFECYCLE_CUSTOM_INDEX_METADATA_KEY;
@@ -826,7 +827,7 @@ private Set<Index> maybeExecuteRollover(ClusterState state, DataStream dataStrea
826827
* @param indicesToExcludeForRemainingRun Indices to exclude from retention even if it would be time for them to be deleted
827828
* @return The set of indices that delete requests have been sent for
828829
*/
829-
private Set<Index> maybeExecuteRetention(ClusterState state, DataStream dataStream, Set<Index> indicesToExcludeForRemainingRun) {
830+
Set<Index> maybeExecuteRetention(ClusterState state, DataStream dataStream, Set<Index> indicesToExcludeForRemainingRun) {
830831
Metadata metadata = state.metadata();
831832
DataStreamGlobalRetention globalRetention = DataStreamGlobalRetention.getFromClusterState(state);
832833
List<Index> backingIndicesOlderThanRetention = dataStream.getIndicesPastRetention(metadata::index, nowSupplier, globalRetention);
@@ -845,14 +846,7 @@ private Set<Index> maybeExecuteRetention(ClusterState state, DataStream dataStre
845846
IndexMetadata.DownsampleTaskStatus downsampleStatus = INDEX_DOWNSAMPLE_STATUS.get(backingIndex.getSettings());
846847
// we don't want to delete the source index if they have an in-progress downsampling operation because the
847848
// target downsample index will remain in the system as a standalone index
848-
if (downsampleStatus.equals(UNKNOWN)) {
849-
indicesToBeRemoved.add(index);
850-
851-
// there's an opportunity here to batch the delete requests (i.e. delete 100 indices / request)
852-
// let's start simple and reevaluate
853-
String indexName = backingIndex.getIndex().getName();
854-
deleteIndexOnce(indexName, "the lapsed [" + effectiveDataRetention + "] retention period");
855-
} else {
849+
if (downsampleStatus == STARTED) {
856850
// there's an opportunity here to cancel downsampling and delete the source index now
857851
logger.trace(
858852
"Data stream lifecycle skips deleting index [{}] even though its retention period [{}] has lapsed "
@@ -862,6 +856,15 @@ private Set<Index> maybeExecuteRetention(ClusterState state, DataStream dataStre
862856
effectiveDataRetention,
863857
downsampleStatus
864858
);
859+
} else {
860+
// UNKNOWN is the default value, and has no real use. So index should be deleted
861+
// SUCCESS meaning downsampling completed successfully and there is nothing in progress, so we can also delete
862+
indicesToBeRemoved.add(index);
863+
864+
// there's an opportunity here to batch the delete requests (i.e. delete 100 indices / request)
865+
// let's start simple and reevaluate
866+
String indexName = backingIndex.getIndex().getName();
867+
deleteIndexOnce(indexName, "the lapsed [" + effectiveDataRetention + "] retention period");
865868
}
866869
}
867870
}

modules/data-streams/src/test/java/org/elasticsearch/datastreams/lifecycle/DataStreamLifecycleServiceTests.java

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,10 @@
115115
import static org.elasticsearch.datastreams.lifecycle.DataStreamLifecycleService.TARGET_MERGE_FACTOR_VALUE;
116116
import static org.elasticsearch.test.ClusterServiceUtils.createClusterService;
117117
import static org.elasticsearch.test.ClusterServiceUtils.setState;
118+
import static org.hamcrest.Matchers.contains;
118119
import static org.hamcrest.Matchers.containsInAnyOrder;
119120
import static org.hamcrest.Matchers.containsString;
121+
import static org.hamcrest.Matchers.empty;
120122
import static org.hamcrest.Matchers.equalTo;
121123
import static org.hamcrest.Matchers.instanceOf;
122124
import static org.hamcrest.Matchers.is;
@@ -329,7 +331,7 @@ public void testRetentionSkippedWhilstDownsamplingInProgress() {
329331
.put(indexMetadata.getSettings())
330332
.put(
331333
IndexMetadata.INDEX_DOWNSAMPLE_STATUS_KEY,
332-
randomValueOtherThan(UNKNOWN, () -> randomFrom(IndexMetadata.DownsampleTaskStatus.values()))
334+
STARTED // See: See TransportDownsampleAction#createDownsampleIndex(...)
333335
)
334336
);
335337
indexMetaBuilder.putCustom(
@@ -1415,6 +1417,76 @@ public void testTrackingTimeStats() {
14151417
assertThat(service.getTimeBetweenStarts(), is(2 * delta));
14161418
}
14171419

1420+
public void testMaybeExecuteRetentionSuccessfulDownsampledIndex() {
1421+
String dataStreamName = randomAlphaOfLength(10).toLowerCase(Locale.ROOT);
1422+
ClusterState state = downsampleSetup(dataStreamName, SUCCESS);
1423+
DataStream dataStream = state.metadata().dataStreams().get(dataStreamName);
1424+
String firstGenIndexName = DataStream.getDefaultBackingIndexName(dataStreamName, 1);
1425+
1426+
// Executing the method to be tested:
1427+
Set<Index> indicesToBeRemoved = dataStreamLifecycleService.maybeExecuteRetention(clusterService.state(), dataStream, Set.of());
1428+
assertThat(indicesToBeRemoved, contains(state.getMetadata().index(firstGenIndexName).getIndex()));
1429+
}
1430+
1431+
public void testMaybeExecuteRetentionDownsampledIndexInProgress() {
1432+
String dataStreamName = randomAlphaOfLength(10).toLowerCase(Locale.ROOT);
1433+
ClusterState state = downsampleSetup(dataStreamName, STARTED);
1434+
DataStream dataStream = state.metadata().dataStreams().get(dataStreamName);
1435+
String firstGenIndexName = DataStream.getDefaultBackingIndexName(dataStreamName, 1);
1436+
1437+
// Executing the method to be tested:
1438+
Set<Index> indicesToBeRemoved = dataStreamLifecycleService.maybeExecuteRetention(clusterService.state(), dataStream, Set.of());
1439+
assertThat(indicesToBeRemoved, empty());
1440+
}
1441+
1442+
public void testMaybeExecuteRetentionDownsampledUnknown() {
1443+
String dataStreamName = randomAlphaOfLength(10).toLowerCase(Locale.ROOT);
1444+
ClusterState state = downsampleSetup(dataStreamName, UNKNOWN);
1445+
DataStream dataStream = state.metadata().dataStreams().get(dataStreamName);
1446+
String firstGenIndexName = DataStream.getDefaultBackingIndexName(dataStreamName, 1);
1447+
1448+
// Executing the method to be tested:
1449+
Set<Index> indicesToBeRemoved = dataStreamLifecycleService.maybeExecuteRetention(clusterService.state(), dataStream, Set.of());
1450+
assertThat(indicesToBeRemoved, contains(state.getMetadata().index(firstGenIndexName).getIndex()));
1451+
}
1452+
1453+
private ClusterState downsampleSetup(String dataStreamName, IndexMetadata.DownsampleTaskStatus status) {
1454+
// Base setup:
1455+
Metadata.Builder builder = Metadata.builder();
1456+
DataStream dataStream = createDataStream(
1457+
builder,
1458+
dataStreamName,
1459+
2,
1460+
settings(IndexVersion.current()).put(IndexSettings.MODE.getKey(), IndexMode.TIME_SERIES)
1461+
.put("index.routing_path", "@timestamp"),
1462+
DataStreamLifecycle.newBuilder()
1463+
.downsampling(
1464+
new Downsampling(
1465+
List.of(new Round(TimeValue.timeValueMillis(0), new DownsampleConfig(new DateHistogramInterval("5m"))))
1466+
)
1467+
)
1468+
.dataRetention(TimeValue.timeValueMillis(1))
1469+
.build(),
1470+
now
1471+
);
1472+
builder.put(dataStream);
1473+
1474+
// Update the first backing index so that is appears to have been downsampled:
1475+
String firstGenIndexName = DataStream.getDefaultBackingIndexName(dataStreamName, 1);
1476+
var imd = builder.get(firstGenIndexName);
1477+
var imdBuilder = new IndexMetadata.Builder(imd);
1478+
imdBuilder.settings(Settings.builder().put(imd.getSettings()).put(IndexMetadata.INDEX_DOWNSAMPLE_STATUS.getKey(), status).build());
1479+
builder.put(imdBuilder);
1480+
1481+
// Attaching state:
1482+
String nodeId = "localNode";
1483+
DiscoveryNodes.Builder nodesBuilder = buildNodes(nodeId);
1484+
nodesBuilder.masterNodeId(nodeId);
1485+
ClusterState state = ClusterState.builder(ClusterName.DEFAULT).metadata(builder).nodes(nodesBuilder).build();
1486+
setState(clusterService, state);
1487+
return state;
1488+
}
1489+
14181490
/*
14191491
* Creates a test cluster state with the given indexName. If customDataStreamLifecycleMetadata is not null, it is added as the value
14201492
* of the index's custom metadata named "data_stream_lifecycle".

0 commit comments

Comments
 (0)