Skip to content

Commit

Permalink
adopt canSearchShard
Browse files Browse the repository at this point in the history
  • Loading branch information
jakelandis committed Sep 27, 2023
1 parent 040c2a6 commit db5361a
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

package org.elasticsearch.cluster.routing;

import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.Diff;
import org.elasticsearch.cluster.SimpleDiffable;
import org.elasticsearch.cluster.metadata.IndexMetadata;
Expand Down Expand Up @@ -234,12 +235,12 @@ public boolean allPrimaryShardsActive() {
/**
* @return <code>true</code> if an index is available to service search queries.
*/
public boolean readyForSearch() {
public boolean readyForSearch(ClusterState clusterState) {
for (IndexShardRoutingTable shardRoutingTable : this.shards) {
boolean found = false;
for (int idx = 0; idx < shardRoutingTable.size(); idx++) {
ShardRouting shardRouting = shardRoutingTable.shard(idx);
if (shardRouting.active() && shardRouting.role().isSearchable()) {
if (shardRouting.active() && OperationRouting.canSearchShard(shardRouting, clusterState)) {
found = true;
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import org.elasticsearch.TransportVersion;
import org.elasticsearch.TransportVersions;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.routing.RecoverySource.ExistingStoreRecoverySource;
import org.elasticsearch.cluster.routing.RecoverySource.PeerRecoverySource;
Expand Down Expand Up @@ -930,6 +931,11 @@ public boolean isPromotableToPrimary() {
return role.isPromotableToPrimary();
}

/**
* Determine if role searchable. Consumers should prefer {@link OperationRouting#canSearchShard(ShardRouting, ClusterState)} to
* determine if a shard can be searched and {@link IndexRoutingTable#readyForSearch(ClusterState)} to determine if an index
* is ready to be searched.
*/
public boolean isSearchable() {
return role.isSearchable();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,34 @@

package org.elasticsearch.cluster.routing;

import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.common.UUIDs;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.test.ESTestCase;
import org.mockito.Mockito;

import java.util.List;

import static org.elasticsearch.index.IndexSettings.INDEX_FAST_REFRESH_SETTING;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class IndexRoutingTableTests extends ESTestCase {

public void testReadyForSearch() {
innerReadyForSearch(false);
innerReadyForSearch(true);
}

private void innerReadyForSearch(boolean fastRefresh) {
Index index = new Index(randomIdentifier(), UUIDs.randomBase64UUID());
ClusterState clusterState = mock(ClusterState.class, Mockito.RETURNS_DEEP_STUBS);
when(clusterState.metadata().index(any(Index.class)).getSettings()).thenReturn(
Settings.builder().put(INDEX_FAST_REFRESH_SETTING.getKey(), fastRefresh).build()
);
// 2 primaries that are search and index
ShardId p1 = new ShardId(index, 0);
IndexShardRoutingTable shardTable1 = new IndexShardRoutingTable(
Expand All @@ -31,79 +48,120 @@ public void testReadyForSearch() {
List.of(getShard(p2, true, ShardRoutingState.STARTED, ShardRouting.Role.DEFAULT))
);
IndexRoutingTable indexRoutingTable = new IndexRoutingTable(index, new IndexShardRoutingTable[] { shardTable1, shardTable2 });
assertTrue(indexRoutingTable.readyForSearch());
assertTrue(indexRoutingTable.readyForSearch(clusterState));

// 2 primaries that are index only
shardTable1 = new IndexShardRoutingTable(p1, List.of(getShard(p1, true, ShardRoutingState.STARTED, ShardRouting.Role.INDEX_ONLY)));
shardTable2 = new IndexShardRoutingTable(p2, List.of(getShard(p2, true, ShardRoutingState.STARTED, ShardRouting.Role.INDEX_ONLY)));
indexRoutingTable = new IndexRoutingTable(index, new IndexShardRoutingTable[] { shardTable1, shardTable2 });
if (fastRefresh) {
assertTrue(indexRoutingTable.readyForSearch(clusterState));
} else {
assertFalse(indexRoutingTable.readyForSearch(clusterState));
}

// 2 unassigned primaries that are index only
shardTable1 = new IndexShardRoutingTable(
p1,
List.of(getShard(p1, true, randomFrom(ShardRoutingState.values()), ShardRouting.Role.INDEX_ONLY))
List.of(getShard(p1, true, ShardRoutingState.UNASSIGNED, ShardRouting.Role.INDEX_ONLY))
);
shardTable2 = new IndexShardRoutingTable(
p2,
List.of(getShard(p2, true, randomFrom(ShardRoutingState.values()), ShardRouting.Role.INDEX_ONLY))
List.of(getShard(p2, true, ShardRoutingState.UNASSIGNED, ShardRouting.Role.INDEX_ONLY))
);
indexRoutingTable = new IndexRoutingTable(index, new IndexShardRoutingTable[] { shardTable1, shardTable2 });
assertFalse(indexRoutingTable.readyForSearch());
assertFalse(indexRoutingTable.readyForSearch(clusterState));

// 2 primaries that are index only with replicas that are not all available
shardTable1 = new IndexShardRoutingTable(
p1,
List.of(
getShard(p1, true, randomFrom(ShardRoutingState.values()), ShardRouting.Role.INDEX_ONLY),
getShard(p1, true, ShardRoutingState.STARTED, ShardRouting.Role.INDEX_ONLY),
getShard(p1, false, ShardRoutingState.STARTED, ShardRouting.Role.SEARCH_ONLY),
getShard(p1, false, ShardRoutingState.STARTED, ShardRouting.Role.SEARCH_ONLY)
)
);
shardTable2 = new IndexShardRoutingTable(
p2,
List.of(
getShard(p2, true, randomFrom(ShardRoutingState.values()), ShardRouting.Role.INDEX_ONLY),
getShard(p2, true, ShardRoutingState.STARTED, ShardRouting.Role.INDEX_ONLY),
getShard(p2, false, ShardRoutingState.UNASSIGNED, ShardRouting.Role.SEARCH_ONLY),
getShard(p2, false, ShardRoutingState.UNASSIGNED, ShardRouting.Role.SEARCH_ONLY)
)
);
indexRoutingTable = new IndexRoutingTable(index, new IndexShardRoutingTable[] { shardTable1, shardTable2 });
assertFalse(indexRoutingTable.readyForSearch());
if (fastRefresh) {
assertTrue(indexRoutingTable.readyForSearch(clusterState));
} else {
assertFalse(indexRoutingTable.readyForSearch(clusterState));
}

// 2 primaries that are index only with some replicas that are all available
shardTable1 = new IndexShardRoutingTable(
p1,
List.of(
getShard(p1, true, randomFrom(ShardRoutingState.values()), ShardRouting.Role.INDEX_ONLY),
getShard(p1, true, ShardRoutingState.STARTED, ShardRouting.Role.INDEX_ONLY),
getShard(p1, false, ShardRoutingState.STARTED, ShardRouting.Role.SEARCH_ONLY),
getShard(p1, false, ShardRoutingState.STARTED, ShardRouting.Role.SEARCH_ONLY)
)
);
shardTable2 = new IndexShardRoutingTable(
p2,
List.of(
getShard(p2, true, ShardRoutingState.STARTED, ShardRouting.Role.INDEX_ONLY),
getShard(p2, false, ShardRoutingState.STARTED, ShardRouting.Role.SEARCH_ONLY),
getShard(p2, false, ShardRoutingState.STARTED, ShardRouting.Role.SEARCH_ONLY)
)
);
indexRoutingTable = new IndexRoutingTable(index, new IndexShardRoutingTable[] { shardTable1, shardTable2 });
assertTrue(indexRoutingTable.readyForSearch(clusterState));

// 2 unassigned primaries that are index only with some replicas that are all available
// TODO: Is this correct behavior for fastRefresh ? Fast refresh indices do not support replicas so this can not practically happen.
// However, if we add support we will want to ensure that readyForSearch/canSearchShard allows for searching replicas for
// indices with fast refresh for when the index shard is not available.
shardTable1 = new IndexShardRoutingTable(
p1,
List.of(
getShard(p1, true, ShardRoutingState.UNASSIGNED, ShardRouting.Role.INDEX_ONLY),
getShard(p1, false, ShardRoutingState.STARTED, ShardRouting.Role.SEARCH_ONLY),
getShard(p1, false, ShardRoutingState.STARTED, ShardRouting.Role.SEARCH_ONLY)
)
);
shardTable2 = new IndexShardRoutingTable(
p2,
List.of(
getShard(p2, true, randomFrom(ShardRoutingState.values()), ShardRouting.Role.INDEX_ONLY),
getShard(p2, true, ShardRoutingState.UNASSIGNED, ShardRouting.Role.INDEX_ONLY),
getShard(p2, false, ShardRoutingState.STARTED, ShardRouting.Role.SEARCH_ONLY),
getShard(p2, false, ShardRoutingState.STARTED, ShardRouting.Role.SEARCH_ONLY)
)
);
indexRoutingTable = new IndexRoutingTable(index, new IndexShardRoutingTable[] { shardTable1, shardTable2 });
assertTrue(indexRoutingTable.readyForSearch());
if (fastRefresh) {
assertFalse(indexRoutingTable.readyForSearch(clusterState)); // this is wrong if ever support replicas for fast refreshes
} else {
assertTrue(indexRoutingTable.readyForSearch(clusterState));
}

// 2 primaries that are index only with at least 1 replica per primary that is available
shardTable1 = new IndexShardRoutingTable(
p1,
List.of(
getShard(p1, true, randomFrom(ShardRoutingState.values()), ShardRouting.Role.INDEX_ONLY),
getShard(p1, true, ShardRoutingState.STARTED, ShardRouting.Role.INDEX_ONLY),
getShard(p1, false, ShardRoutingState.STARTED, ShardRouting.Role.SEARCH_ONLY),
getShard(p1, false, ShardRoutingState.UNASSIGNED, ShardRouting.Role.SEARCH_ONLY)
)
);
shardTable2 = new IndexShardRoutingTable(
p2,
List.of(
getShard(p2, true, randomFrom(ShardRoutingState.values()), ShardRouting.Role.INDEX_ONLY),
getShard(p2, true, ShardRoutingState.STARTED, ShardRouting.Role.INDEX_ONLY),
getShard(p2, false, ShardRoutingState.UNASSIGNED, ShardRouting.Role.SEARCH_ONLY),
getShard(p2, false, ShardRoutingState.STARTED, ShardRouting.Role.SEARCH_ONLY)
)
);
indexRoutingTable = new IndexRoutingTable(index, new IndexShardRoutingTable[] { shardTable1, shardTable2 });
assertTrue(indexRoutingTable.readyForSearch());
assertTrue(indexRoutingTable.readyForSearch(clusterState));
}

private ShardRouting getShard(ShardId shardId, boolean isPrimary, ShardRoutingState state, ShardRouting.Role role) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ private boolean checkIndexAvailable(ClusterState state) {
return false;
}
final IndexRoutingTable routingTable = state.routingTable().index(metadata.getIndex());
if (routingTable == null || routingTable.readyForSearch() == false) {
if (routingTable == null || routingTable.readyForSearch(state) == false) {
logger.debug("Index [{}] is not yet active", aliasName);
return false;
} else {
Expand Down

0 comments on commit db5361a

Please sign in to comment.