Skip to content

Commit 04bb6c3

Browse files
authored
Merge branch 'main' into rtv0
2 parents fb7db43 + 5c3d5ae commit 04bb6c3

File tree

5 files changed

+82
-106
lines changed

5 files changed

+82
-106
lines changed

docs/changelog/136060.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 136060
2+
summary: Limit number of allocation explanations in `shards_availability` health indicator
3+
area: Health
4+
type: bug
5+
issues: []

server/src/main/java/org/elasticsearch/TransportVersions.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ static TransportVersion def(int id) {
6060
public static final TransportVersion V_7_8_1 = def(7_08_01_99);
6161
public static final TransportVersion V_7_9_0 = def(7_09_00_99);
6262
public static final TransportVersion V_7_10_0 = def(7_10_00_99);
63-
public static final TransportVersion V_8_0_0 = def(8_00_00_99);
6463
public static final TransportVersion V_8_8_0 = def(8_08_00_99);
6564
public static final TransportVersion V_8_8_1 = def(8_08_01_99);
6665
/*

server/src/main/java/org/elasticsearch/action/admin/indices/flush/TransportShardFlushAction.java

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
package org.elasticsearch.action.admin.indices.flush;
1111

12-
import org.elasticsearch.TransportVersions;
1312
import org.elasticsearch.action.ActionListener;
1413
import org.elasticsearch.action.ActionType;
1514
import org.elasticsearch.action.support.ActionFilters;
@@ -18,17 +17,11 @@
1817
import org.elasticsearch.cluster.action.shard.ShardStateAction;
1918
import org.elasticsearch.cluster.service.ClusterService;
2019
import org.elasticsearch.common.io.stream.StreamInput;
21-
import org.elasticsearch.common.io.stream.StreamOutput;
2220
import org.elasticsearch.common.settings.Settings;
2321
import org.elasticsearch.index.shard.IndexShard;
24-
import org.elasticsearch.index.shard.ShardId;
2522
import org.elasticsearch.indices.IndicesService;
2623
import org.elasticsearch.injection.guice.Inject;
27-
import org.elasticsearch.tasks.Task;
2824
import org.elasticsearch.threadpool.ThreadPool;
29-
import org.elasticsearch.transport.AbstractTransportRequest;
30-
import org.elasticsearch.transport.TransportChannel;
31-
import org.elasticsearch.transport.TransportRequestHandler;
3225
import org.elasticsearch.transport.TransportService;
3326

3427
import java.io.IOException;
@@ -64,12 +57,6 @@ public TransportShardFlushAction(
6457
PrimaryActionExecution.RejectOnOverload,
6558
ReplicaActionExecution.SubjectToCircuitBreaker
6659
);
67-
transportService.registerRequestHandler(
68-
PRE_SYNCED_FLUSH_ACTION_NAME,
69-
threadPool.executor(ThreadPool.Names.FLUSH),
70-
PreShardSyncedFlushRequest::new,
71-
new PreSyncedFlushTransportHandler(indicesService)
72-
);
7360
}
7461

7562
@Override
@@ -96,43 +83,4 @@ protected void shardOperationOnReplica(ShardFlushRequest request, IndexShard rep
9683
return new ReplicaResult();
9784
}));
9885
}
99-
100-
// TODO: Remove this transition in 9.0
101-
private static final String PRE_SYNCED_FLUSH_ACTION_NAME = "internal:indices/flush/synced/pre";
102-
103-
private static class PreShardSyncedFlushRequest extends AbstractTransportRequest {
104-
private final ShardId shardId;
105-
106-
private PreShardSyncedFlushRequest(StreamInput in) throws IOException {
107-
super(in);
108-
assert in.getTransportVersion().before(TransportVersions.V_8_0_0) : "received pre_sync request from a new node";
109-
this.shardId = new ShardId(in);
110-
}
111-
112-
@Override
113-
public String toString() {
114-
return "PreShardSyncedFlushRequest{" + "shardId=" + shardId + '}';
115-
}
116-
117-
@Override
118-
public void writeTo(StreamOutput out) throws IOException {
119-
assert false : "must not send pre_sync request from a new node";
120-
throw new UnsupportedOperationException("");
121-
}
122-
}
123-
124-
private static final class PreSyncedFlushTransportHandler implements TransportRequestHandler<PreShardSyncedFlushRequest> {
125-
private final IndicesService indicesService;
126-
127-
PreSyncedFlushTransportHandler(IndicesService indicesService) {
128-
this.indicesService = indicesService;
129-
}
130-
131-
@Override
132-
public void messageReceived(PreShardSyncedFlushRequest request, TransportChannel channel, Task task) {
133-
IndexShard indexShard = indicesService.indexServiceSafe(request.shardId.getIndex()).getShard(request.shardId.id());
134-
indexShard.flush(new FlushRequest().force(false).waitIfOngoing(true));
135-
throw new UnsupportedOperationException("Synced flush was removed and a normal flush was performed instead.");
136-
}
137-
}
13886
}

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

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -166,17 +166,18 @@ public String name() {
166166
* primary and replica availability, providing the color, diagnosis, and
167167
* messages about the available or unavailable shards in the cluster.
168168
* @param metadata Metadata for the cluster
169+
* @param maxAffectedResourcesCount Max number of affect resources to return
169170
* @return A new ShardAllocationStatus that has not yet been filled.
170171
*/
171-
public ShardAllocationStatus createNewStatus(Metadata metadata) {
172-
return new ShardAllocationStatus(metadata);
172+
public ShardAllocationStatus createNewStatus(Metadata metadata, int maxAffectedResourcesCount) {
173+
return new ShardAllocationStatus(metadata, maxAffectedResourcesCount);
173174
}
174175

175176
@Override
176177
public HealthIndicatorResult calculate(boolean verbose, int maxAffectedResourcesCount, HealthInfo healthInfo) {
177178
var state = clusterService.state();
178179
var shutdown = state.getMetadata().custom(NodesShutdownMetadata.TYPE, NodesShutdownMetadata.EMPTY);
179-
var status = createNewStatus(state.getMetadata());
180+
var status = createNewStatus(state.getMetadata(), maxAffectedResourcesCount);
180181
updateShardAllocationStatus(status, state, shutdown, verbose, replicaUnassignedBufferTime);
181182
return createIndicator(
182183
status.getStatus(),
@@ -464,18 +465,33 @@ static void updateShardAllocationStatus(
464465
);
465466

466467
public class ShardAllocationCounts {
467-
int unassigned = 0;
468-
int unassigned_new = 0;
469-
int unassigned_restarting = 0;
470-
int initializing = 0;
471-
int started = 0;
472-
int relocating = 0;
473-
public final Set<ProjectIndexName> indicesWithUnavailableShards = new HashSet<>();
474-
public final Set<ProjectIndexName> indicesWithAllShardsUnavailable = new HashSet<>();
468+
final int maxAffectedResourcesCount;
469+
int unassigned;
470+
int unassigned_new;
471+
int unassigned_restarting;
472+
int initializing;
473+
int started;
474+
int relocating;
475+
public final Set<ProjectIndexName> indicesWithUnavailableShards;
476+
public final Set<ProjectIndexName> indicesWithAllShardsUnavailable;
475477
// We keep the searchable snapshots separately as long as the original index is still available
476478
// This is checked during the post-processing
477-
public SearchableSnapshotsState searchableSnapshotsState = new SearchableSnapshotsState();
478-
final Map<Diagnosis.Definition, Set<ProjectIndexName>> diagnosisDefinitions = new HashMap<>();
479+
public SearchableSnapshotsState searchableSnapshotsState;
480+
final Map<Diagnosis.Definition, Set<ProjectIndexName>> diagnosisDefinitions;
481+
482+
public ShardAllocationCounts(int maxAffectedResourcesCount) {
483+
this.maxAffectedResourcesCount = maxAffectedResourcesCount;
484+
unassigned = 0;
485+
unassigned_new = 0;
486+
unassigned_restarting = 0;
487+
initializing = 0;
488+
started = 0;
489+
relocating = 0;
490+
indicesWithUnavailableShards = new HashSet<>();
491+
indicesWithAllShardsUnavailable = new HashSet<>();
492+
searchableSnapshotsState = new SearchableSnapshotsState();
493+
diagnosisDefinitions = new HashMap<>();
494+
}
479495

480496
public void increment(
481497
ProjectId projectId,
@@ -512,7 +528,15 @@ public void increment(
512528
unassigned_restarting++;
513529
} else {
514530
unassigned++;
515-
if (verbose) {
531+
// Computing the diagnosis can be very expensive in large clusters, so we limit the number of
532+
// computations to the maxAffectedResourcesCount. The main negative side effect of this is that
533+
// we might miss some diagnoses. We are willing to take this risk, and users can always
534+
// use the allocation explain API for more details or increase the maxAffectedResourcesCount.
535+
// Since we have two ShardAllocationCounts instances (primaries and replicas), we technically
536+
// do 2 * maxAffectedResourcesCount computations, but the added complexity of accurately
537+
// limiting the number of calls doesn't outweigh the benefits, as the main goal is to limit
538+
// the number of computations to a constant rather than a number that grows with the cluster size.
539+
if (verbose && unassigned <= maxAffectedResourcesCount) {
516540
diagnoseUnassignedShardRouting(routing, state).forEach(definition -> addDefinition(definition, projectIndex));
517541
}
518542
}
@@ -957,12 +981,16 @@ public Diagnosis.Definition getIncreaseNodeWithRoleCapacityAction(String role) {
957981
}
958982

959983
public class ShardAllocationStatus {
960-
protected final ShardAllocationCounts primaries = new ShardAllocationCounts();
961-
protected final ShardAllocationCounts replicas = new ShardAllocationCounts();
984+
protected final ShardAllocationCounts primaries;
985+
protected final ShardAllocationCounts replicas;
962986
protected final Metadata clusterMetadata;
987+
protected final int maxAffectedResourcesCount;
963988

964-
public ShardAllocationStatus(Metadata clusterMetadata) {
989+
public ShardAllocationStatus(Metadata clusterMetadata, int maxAffectedResourcesCount) {
965990
this.clusterMetadata = clusterMetadata;
991+
this.maxAffectedResourcesCount = maxAffectedResourcesCount;
992+
primaries = new ShardAllocationCounts(maxAffectedResourcesCount);
993+
replicas = new ShardAllocationCounts(maxAffectedResourcesCount);
966994
}
967995

968996
void addPrimary(ProjectId projectId, ShardRouting routing, ClusterState state, NodesShutdownMetadata shutdowns, boolean verbose) {

server/src/test/java/org/elasticsearch/cluster/routing/allocation/shards/ShardsAvailabilityHealthIndicatorServiceTests.java

Lines changed: 32 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ public void testAllReplicasUnassigned() {
361361
List.of()
362362
);
363363
var service = createShardsAvailabilityIndicatorService(projectId, clusterState);
364-
ShardAllocationStatus status = service.createNewStatus(clusterState.metadata());
364+
ShardAllocationStatus status = service.createNewStatus(clusterState.metadata(), randomNonNegativeInt());
365365
ShardsAvailabilityHealthIndicatorService.updateShardAllocationStatus(
366366
status,
367367
clusterState,
@@ -386,7 +386,7 @@ public void testAllReplicasUnassigned() {
386386
List.of()
387387
);
388388
var service = createShardsAvailabilityIndicatorService(projectId, clusterState);
389-
ShardAllocationStatus status = service.createNewStatus(clusterState.metadata());
389+
ShardAllocationStatus status = service.createNewStatus(clusterState.metadata(), randomNonNegativeInt());
390390
ShardsAvailabilityHealthIndicatorService.updateShardAllocationStatus(
391391
status,
392392
clusterState,
@@ -411,7 +411,7 @@ public void testAllReplicasUnassigned() {
411411
List.of()
412412
);
413413
var service = createShardsAvailabilityIndicatorService(projectId, clusterState);
414-
ShardAllocationStatus status = service.createNewStatus(clusterState.metadata());
414+
ShardAllocationStatus status = service.createNewStatus(clusterState.metadata(), randomNonNegativeInt());
415415
ShardsAvailabilityHealthIndicatorService.updateShardAllocationStatus(
416416
status,
417417
clusterState,
@@ -438,7 +438,7 @@ public void testAllReplicasUnassigned() {
438438
);
439439

440440
var service = createShardsAvailabilityIndicatorService(projectId, clusterState);
441-
ShardAllocationStatus status = service.createNewStatus(clusterState.metadata());
441+
ShardAllocationStatus status = service.createNewStatus(clusterState.metadata(), randomNonNegativeInt());
442442
ShardsAvailabilityHealthIndicatorService.updateShardAllocationStatus(
443443
status,
444444
clusterState,
@@ -477,7 +477,7 @@ public void testAllReplicasUnassigned() {
477477
List.of()
478478
);
479479
var service = createShardsAvailabilityIndicatorService(projectId, clusterState);
480-
ShardAllocationStatus status = service.createNewStatus(clusterState.metadata());
480+
ShardAllocationStatus status = service.createNewStatus(clusterState.metadata(), randomNonNegativeInt());
481481
ShardsAvailabilityHealthIndicatorService.updateShardAllocationStatus(
482482
status,
483483
clusterState,
@@ -508,7 +508,7 @@ public void testAllReplicasUnassigned() {
508508
ProjectId projectId = randomProjectIdOrDefault();
509509
var clusterState = createClusterStateWith(projectId, List.of(routingTable), List.of());
510510
var service = createShardsAvailabilityIndicatorService(projectId, clusterState);
511-
ShardAllocationStatus status = service.createNewStatus(clusterState.metadata());
511+
ShardAllocationStatus status = service.createNewStatus(clusterState.metadata(), randomNonNegativeInt());
512512
ShardsAvailabilityHealthIndicatorService.updateShardAllocationStatus(
513513
status,
514514
clusterState,
@@ -534,7 +534,7 @@ public void testAllReplicasUnassigned() {
534534
List.of()
535535
);
536536
var service = createShardsAvailabilityIndicatorService(projectId, clusterState);
537-
ShardAllocationStatus status = service.createNewStatus(clusterState.metadata());
537+
ShardAllocationStatus status = service.createNewStatus(clusterState.metadata(), randomNonNegativeInt());
538538
ShardsAvailabilityHealthIndicatorService.updateShardAllocationStatus(
539539
status,
540540
clusterState,
@@ -1791,32 +1791,31 @@ public void testLimitNumberOfAffectedResources() {
17911791

17921792
{
17931793
// assert the full result to check that details, impacts, and symptoms use the correct count of affected indices (5)
1794-
assertThat(
1795-
service.calculate(true, 2, HealthInfo.EMPTY_HEALTH_INFO),
1796-
equalTo(
1797-
createExpectedResult(
1798-
RED,
1799-
"This cluster has 5 unavailable primary shards.",
1800-
Map.of("unassigned_primaries", 5),
1801-
List.of(
1802-
new HealthIndicatorImpact(
1803-
NAME,
1804-
ShardsAvailabilityHealthIndicatorService.PRIMARY_UNASSIGNED_IMPACT_ID,
1805-
1,
1806-
"Cannot add data to 5 indices [red-index1, red-index2, red-index3, red-index4, red-index5]. Searches might "
1807-
+ "return incomplete results.",
1808-
List.of(ImpactArea.INGEST, ImpactArea.SEARCH)
1809-
)
1810-
),
1811-
List.of(
1812-
new Diagnosis(
1813-
ACTION_CHECK_ALLOCATION_EXPLAIN_API,
1814-
List.of(new Diagnosis.Resource(INDEX, List.of("red-index1", "red-index2")))
1815-
)
1816-
)
1794+
// since we limit the number of allocation explanations while looping over the shards, we can't guarantee
1795+
// which indices end up in the affected resources list, but we can at least check that the size is correct
1796+
var calculatedResult = service.calculate(true, 2, HealthInfo.EMPTY_HEALTH_INFO);
1797+
assertEquals(RED, calculatedResult.status());
1798+
assertEquals("This cluster has 5 unavailable primary shards.", calculatedResult.symptom());
1799+
assertEquals(new SimpleHealthIndicatorDetails(addDefaults(Map.of("unassigned_primaries", 5))), calculatedResult.details());
1800+
assertEquals(
1801+
List.of(
1802+
new HealthIndicatorImpact(
1803+
NAME,
1804+
ShardsAvailabilityHealthIndicatorService.PRIMARY_UNASSIGNED_IMPACT_ID,
1805+
1,
1806+
"Cannot add data to 5 indices [red-index1, red-index2, red-index3, red-index4, red-index5]. Searches might "
1807+
+ "return incomplete results.",
1808+
List.of(ImpactArea.INGEST, ImpactArea.SEARCH)
18171809
)
1818-
)
1810+
),
1811+
calculatedResult.impacts()
18191812
);
1813+
assertEquals("Expected 1 diagnosis but got " + calculatedResult.diagnosisList(), 1, calculatedResult.diagnosisList().size());
1814+
var diagnosis = calculatedResult.diagnosisList().get(0);
1815+
assertEquals(ACTION_CHECK_ALLOCATION_EXPLAIN_API, diagnosis.definition());
1816+
assertEquals("Expected 1 affected resource but got " + diagnosis.affectedResources(), 1, diagnosis.affectedResources().size());
1817+
var affectedResource = diagnosis.affectedResources().get(0);
1818+
assertEquals("Expected 2 indices but got " + affectedResource.getValues(), 2, affectedResource.getValues().size());
18201819
}
18211820

18221821
{
@@ -1838,11 +1837,8 @@ public void testLimitNumberOfAffectedResources() {
18381837
}
18391838

18401839
{
1841-
// 0 affected resources
1842-
assertThat(
1843-
service.calculate(true, 0, HealthInfo.EMPTY_HEALTH_INFO).diagnosisList(),
1844-
equalTo(List.of(new Diagnosis(ACTION_CHECK_ALLOCATION_EXPLAIN_API, List.of(new Diagnosis.Resource(INDEX, List.of())))))
1845-
);
1840+
// 0 affected resources means we don't do any shard allocation explanation and thus do not report any diagnosis
1841+
assertThat(service.calculate(true, 0, HealthInfo.EMPTY_HEALTH_INFO).diagnosisList(), equalTo(List.of()));
18461842
}
18471843
}
18481844

0 commit comments

Comments
 (0)