Skip to content

Commit bddc14c

Browse files
authored
Add multi-project support for health indicator shards_availability (elastic#125512)
1 parent fd2492f commit bddc14c

File tree

11 files changed

+693
-199
lines changed

11 files changed

+693
-199
lines changed

benchmarks/src/main/java/org/elasticsearch/benchmark/routing/allocation/ShardsAvailabilityHealthIndicatorBenchmark.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.elasticsearch.cluster.metadata.IndexMetadata;
1515
import org.elasticsearch.cluster.metadata.Metadata;
1616
import org.elasticsearch.cluster.node.DiscoveryNodes;
17+
import org.elasticsearch.cluster.project.DefaultProjectResolver;
1718
import org.elasticsearch.cluster.routing.IndexRoutingTable;
1819
import org.elasticsearch.cluster.routing.IndexShardRoutingTable;
1920
import org.elasticsearch.cluster.routing.RecoverySource;
@@ -178,7 +179,12 @@ public void setUp() throws Exception {
178179
new TaskManager(Settings.EMPTY, threadPool, Collections.emptySet())
179180
);
180181
clusterService.getClusterApplierService().setInitialState(initialClusterState);
181-
indicatorService = new ShardsAvailabilityHealthIndicatorService(clusterService, allocationService, new SystemIndices(List.of()));
182+
indicatorService = new ShardsAvailabilityHealthIndicatorService(
183+
clusterService,
184+
allocationService,
185+
new SystemIndices(List.of()),
186+
DefaultProjectResolver.INSTANCE
187+
);
182188
}
183189

184190
private int toInt(String v) {

modules/health-shards-availability/src/main/java/org/elasticsearch/health/plugin/ShardsAvailabilityPlugin.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,12 @@ public ShardsAvailabilityPlugin() {}
2727
@Override
2828
public Collection<?> createComponents(PluginServices services) {
2929
this.shardHealthService.set(
30-
new ShardsAvailabilityHealthIndicatorService(services.clusterService(), services.allocationService(), services.systemIndices())
30+
new ShardsAvailabilityHealthIndicatorService(
31+
services.clusterService(),
32+
services.allocationService(),
33+
services.systemIndices(),
34+
services.projectResolver()
35+
)
3136
);
3237
return Set.of(this.shardHealthService.get());
3338
}

server/src/internalClusterTest/java/org/elasticsearch/cluster/routing/allocation/shards/ShardsAvailabilityHealthIndicatorServiceIT.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.elasticsearch.cluster.ClusterChangedEvent;
1414
import org.elasticsearch.cluster.ClusterStateListener;
1515
import org.elasticsearch.cluster.metadata.IndexMetadata;
16+
import org.elasticsearch.cluster.project.ProjectResolver;
1617
import org.elasticsearch.cluster.routing.RoutingNodes;
1718
import org.elasticsearch.cluster.routing.allocation.AllocationService;
1819
import org.elasticsearch.cluster.service.ClusterService;
@@ -133,8 +134,9 @@ private void assertHealthDuring(Matcher<HealthStatus> statusMatcher, Runnable ac
133134
var clusterService = internalCluster().getCurrentMasterNodeInstance(ClusterService.class);
134135
var allocationService = internalCluster().getCurrentMasterNodeInstance(AllocationService.class);
135136
var systemIndices = internalCluster().getCurrentMasterNodeInstance(SystemIndices.class);
137+
var projectResolver = internalCluster().getCurrentMasterNodeInstance(ProjectResolver.class);
136138

137-
var service = new ShardsAvailabilityHealthIndicatorService(clusterService, allocationService, systemIndices);
139+
var service = new ShardsAvailabilityHealthIndicatorService(clusterService, allocationService, systemIndices, projectResolver);
138140
var states = new ArrayList<RoutingNodesAndHealth>();
139141
var listener = new ClusterStateListener() {
140142
@Override

server/src/main/java/org/elasticsearch/cluster/routing/allocation/shards/ShardsAvailabilityHealthIndicatorService.java

Lines changed: 190 additions & 93 deletions
Large diffs are not rendered by default.

server/src/main/java/org/elasticsearch/health/RestGetHealthAction.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
package org.elasticsearch.health;
1111

1212
import org.elasticsearch.client.internal.node.NodeClient;
13+
import org.elasticsearch.common.util.set.Sets;
1314
import org.elasticsearch.rest.BaseRestHandler;
1415
import org.elasticsearch.rest.RestRequest;
1516
import org.elasticsearch.rest.Scope;
@@ -19,6 +20,7 @@
1920

2021
import java.io.IOException;
2122
import java.util.List;
23+
import java.util.Set;
2224

2325
import static org.elasticsearch.rest.RestRequest.Method.GET;
2426

@@ -29,6 +31,8 @@ public class RestGetHealthAction extends BaseRestHandler {
2931

3032
private static final String SIZE_PARAM = "size";
3133

34+
private static final String CAPABILITY_MULTI_PROJECT_SHARDS_AVAILABILITY = "multi_project_shards_availability";
35+
3236
@Override
3337
public String getName() {
3438
// TODO: Existing - "cluster_health_action", "cat_health_action"
@@ -57,4 +61,9 @@ protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient cli
5761
public boolean canTripCircuitBreaker() {
5862
return false;
5963
}
64+
65+
@Override
66+
public Set<String> supportedCapabilities() {
67+
return Sets.union(Set.of(CAPABILITY_MULTI_PROJECT_SHARDS_AVAILABILITY), super.supportedCapabilities());
68+
}
6069
}

server/src/main/java/org/elasticsearch/health/node/HealthIndicatorDisplayValues.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import org.elasticsearch.cluster.metadata.IndexMetadata;
1313
import org.elasticsearch.cluster.metadata.Metadata;
14+
import org.elasticsearch.cluster.metadata.ProjectMetadata;
1415
import org.elasticsearch.cluster.node.DiscoveryNode;
1516

1617
import java.util.Collection;
@@ -47,6 +48,7 @@ public static String getNodeName(DiscoveryNode node) {
4748
* logging or user messages. The indices are sorted by priority and then by name to ensure a
4849
* deterministic message. If there are more indices than 10, it adds the '...' suffix.
4950
*/
51+
@Deprecated
5052
public static String getTruncatedIndices(Set<String> indices, Metadata clusterMetadata) {
5153
final int maxIndices = 10;
5254
String truncatedIndicesString = indices.stream()
@@ -59,6 +61,28 @@ public static String getTruncatedIndices(Set<String> indices, Metadata clusterMe
5961
return truncatedIndicesString;
6062
}
6163

64+
/**
65+
* Creates a string that displays max 10 indices from the given set to be used as examples in
66+
* logging or user messages. The indices are sorted by priority and then by name to ensure a
67+
* deterministic message. If there are more indices than 10, it adds the '...' suffix.
68+
*/
69+
public static String getTruncatedProjectIndices(
70+
Set<ProjectIndexName> indices,
71+
Metadata clusterMetadata,
72+
boolean supportsMultipleProjects
73+
) {
74+
final int maxIndices = 10;
75+
String truncatedIndicesString = indices.stream()
76+
.sorted(indicesComparatorByPriorityAndProjectIndex(clusterMetadata, supportsMultipleProjects))
77+
.limit(maxIndices)
78+
.map(projectIndexName -> projectIndexName.toString(supportsMultipleProjects))
79+
.collect(joining(", "));
80+
if (maxIndices < indices.size()) {
81+
truncatedIndicesString = truncatedIndicesString + ", ...";
82+
}
83+
return truncatedIndicesString;
84+
}
85+
6286
/**
6387
* Creates a string that displays all the values that fulfilled the predicate sorted in the natural order.
6488
* @param values, the values to be displayed
@@ -119,11 +143,31 @@ public static String regularVerb(String verb, int count) {
119143
* @param clusterMetadata Used to look up index priority.
120144
* @return Comparator instance
121145
*/
146+
@Deprecated
122147
public static Comparator<String> indicesComparatorByPriorityAndName(Metadata clusterMetadata) {
123148
// We want to show indices with a numerically higher index.priority first (since lower priority ones might get truncated):
124149
return Comparator.comparingInt((String indexName) -> {
125150
IndexMetadata indexMetadata = clusterMetadata.getProject().index(indexName);
126151
return indexMetadata == null ? -1 : indexMetadata.priority();
127152
}).reversed().thenComparing(Comparator.naturalOrder());
128153
}
154+
155+
/**
156+
* Sorts index names by their priority first, then alphabetically by name. If the priority cannot be determined for an index then
157+
* a priority of -1 is used to sort it behind other index names.
158+
* @param clusterMetadata Used to look up index priority.
159+
* @param supportsMultipleProjects Whether cluster supports multi-project
160+
* @return Comparator instance
161+
*/
162+
public static Comparator<ProjectIndexName> indicesComparatorByPriorityAndProjectIndex(
163+
Metadata clusterMetadata,
164+
boolean supportsMultipleProjects
165+
) {
166+
// We want to show indices with a numerically higher index.priority first (since lower priority ones might get truncated):
167+
return Comparator.comparingInt((ProjectIndexName projectIndexName) -> {
168+
ProjectMetadata projectMetadata = clusterMetadata.getProject(projectIndexName.projectId());
169+
IndexMetadata indexMetadata = projectMetadata.index(projectIndexName.indexName());
170+
return indexMetadata == null ? -1 : indexMetadata.priority();
171+
}).reversed().thenComparing(projectIndex -> projectIndex.toString(supportsMultipleProjects));
172+
}
129173
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.health.node;
11+
12+
import org.elasticsearch.cluster.metadata.ProjectId;
13+
14+
public record ProjectIndexName(ProjectId projectId, String indexName) implements Comparable<ProjectIndexName> {
15+
// VisibleForTesting
16+
public static final String DELIMITER = "/";
17+
18+
@Override
19+
public String toString() {
20+
return toString(true);
21+
}
22+
23+
public String toString(boolean withProjectId) {
24+
if (withProjectId) {
25+
return projectId.id() + DELIMITER + indexName;
26+
} else {
27+
return indexName;
28+
}
29+
}
30+
31+
@Override
32+
public int compareTo(ProjectIndexName other) {
33+
return this.toString().compareTo(other.toString());
34+
}
35+
}

server/src/main/java/org/elasticsearch/indices/SystemDataStreamDescriptor.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.elasticsearch.cluster.metadata.ComposableIndexTemplate;
1414
import org.elasticsearch.cluster.metadata.DataStream;
1515
import org.elasticsearch.cluster.metadata.Metadata;
16+
import org.elasticsearch.cluster.metadata.ProjectMetadata;
1617
import org.elasticsearch.index.Index;
1718

1819
import java.util.Collections;
@@ -106,8 +107,18 @@ public String getDataStreamName() {
106107
* @param metadata Metadata in which to look for indices
107108
* @return List of names of backing indices
108109
*/
110+
@Deprecated
109111
public List<String> getBackingIndexNames(Metadata metadata) {
110-
DataStream dataStream = metadata.getProject().dataStreams().get(dataStreamName);
112+
return getBackingIndexNames(metadata.getProject());
113+
}
114+
115+
/**
116+
* Retrieve backing indices for this system data stream
117+
* @param projectMetadata Project metadata in which to look for indices
118+
* @return List of names of backing indices
119+
*/
120+
public List<String> getBackingIndexNames(ProjectMetadata projectMetadata) {
121+
DataStream dataStream = projectMetadata.dataStreams().get(dataStreamName);
111122
if (dataStream == null) {
112123
return Collections.emptyList();
113124
}

server/src/test/java/org/elasticsearch/cluster/routing/allocation/ShardsAvailabilityActionGuideTests.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
package org.elasticsearch.cluster.routing.allocation;
1111

12+
import org.elasticsearch.cluster.project.ProjectResolver;
1213
import org.elasticsearch.cluster.routing.allocation.shards.ShardsAvailabilityHealthIndicatorService;
1314
import org.elasticsearch.cluster.service.ClusterService;
1415
import org.elasticsearch.common.settings.ClusterSettings;
@@ -46,7 +47,12 @@ public ShardsAvailabilityActionGuideTests() {
4647
ClusterService clusterService = mock(ClusterService.class);
4748
when(clusterService.getClusterSettings()).thenReturn(ClusterSettings.createBuiltInClusterSettings());
4849
when(clusterService.getSettings()).thenReturn(Settings.EMPTY);
49-
service = new ShardsAvailabilityHealthIndicatorService(clusterService, mock(AllocationService.class), mock(SystemIndices.class));
50+
service = new ShardsAvailabilityHealthIndicatorService(
51+
clusterService,
52+
mock(AllocationService.class),
53+
mock(SystemIndices.class),
54+
mock(ProjectResolver.class)
55+
);
5056
}
5157

5258
public void testRestoreFromSnapshotAction() {

0 commit comments

Comments
 (0)