|
35 | 35 | import org.elasticsearch.cluster.routing.ShardRouting; |
36 | 36 | import org.elasticsearch.cluster.routing.ShardRoutingState; |
37 | 37 | import org.elasticsearch.cluster.routing.allocation.AllocateUnassignedDecision; |
| 38 | +import org.elasticsearch.cluster.routing.allocation.AllocationService; |
38 | 39 | import org.elasticsearch.cluster.routing.allocation.RoutingAllocation; |
39 | 40 | import org.elasticsearch.cluster.routing.allocation.allocator.BalancedShardsAllocator.Balancer.PrioritiseByShardWriteLoadComparator; |
40 | 41 | import org.elasticsearch.cluster.routing.allocation.decider.AllocationDecider; |
|
48 | 49 | import org.elasticsearch.common.settings.Settings; |
49 | 50 | import org.elasticsearch.common.unit.ByteSizeUnit; |
50 | 51 | import org.elasticsearch.common.unit.ByteSizeValue; |
| 52 | +import org.elasticsearch.core.Nullable; |
51 | 53 | import org.elasticsearch.core.Strings; |
52 | 54 | import org.elasticsearch.core.Tuple; |
53 | 55 | import org.elasticsearch.index.IndexVersion; |
@@ -1104,6 +1106,161 @@ public void testShardMovementPriorityComparator() { |
1104 | 1106 | } |
1105 | 1107 | } |
1106 | 1108 |
|
| 1109 | + public void testAssigmentPreferenceForUnassignedShards() { |
| 1110 | + final var notPreferredDecider = new AllocationDecider() { |
| 1111 | + @Override |
| 1112 | + public Decision canAllocate(ShardRouting shardRouting, RoutingNode node, RoutingAllocation allocation) { |
| 1113 | + final var nodeId = node.node().getId(); |
| 1114 | + if (nodeId.startsWith("not-preferred")) { |
| 1115 | + return Decision.NOT_PREFERRED; |
| 1116 | + } else if (nodeId.startsWith("yes")) { |
| 1117 | + return Decision.YES; |
| 1118 | + } else if (nodeId.startsWith("no")) { |
| 1119 | + return Decision.NO; |
| 1120 | + } else if (nodeId.startsWith("throttle")) { |
| 1121 | + return Decision.THROTTLE; |
| 1122 | + } else { |
| 1123 | + throw new AssertionError("unexpected node name: " + node.node().getName()); |
| 1124 | + } |
| 1125 | + } |
| 1126 | + }; |
| 1127 | + |
| 1128 | + final var allocationService = new MockAllocationService( |
| 1129 | + new AllocationDeciders(List.of(notPreferredDecider)), |
| 1130 | + new TestGatewayAllocator(), |
| 1131 | + new BalancedShardsAllocator(BalancerSettings.DEFAULT, TEST_WRITE_LOAD_FORECASTER, new NodeNameDrivenBalancingWeightsFactory()), |
| 1132 | + () -> ClusterInfo.EMPTY, |
| 1133 | + SNAPSHOT_INFO_SERVICE_WITH_NO_SHARD_SIZES |
| 1134 | + ); |
| 1135 | + |
| 1136 | + // No allocation when NO |
| 1137 | + assertUnassigned(allocationService, shuffledList("no")); |
| 1138 | + // No allocation when THROTTLE |
| 1139 | + assertUnassigned(allocationService, shuffledList("throttle")); |
| 1140 | + // NOT_PREFERRED when no other choice |
| 1141 | + assertAssignedTo(allocationService, "not-preferred", shuffledList("not-preferred")); |
| 1142 | + // NOT_PREFERRED over NO |
| 1143 | + assertAssignedTo(allocationService, "not-preferred", shuffledList("not-preferred", "no")); |
| 1144 | + // THROTTLE (No allocation) over NOT_PREFERRED/NO |
| 1145 | + assertUnassigned(allocationService, shuffledList("throttle", "not-preferred", "no")); |
| 1146 | + // THROTTLE (No allocation) over NOT_PREFERRED |
| 1147 | + assertUnassigned(allocationService, shuffledList("throttle", "not-preferred")); |
| 1148 | + // YES over THROTTLE/NO/NOT_PREFERRED |
| 1149 | + assertAssignedTo(allocationService, "yes", shuffledList("not-preferred", "yes", "throttle", "no")); |
| 1150 | + // prioritize YES/THROTTLE by weight |
| 1151 | + assertUnassigned(allocationService, shuffledList("throttle-low", "yes-high", "yes")); |
| 1152 | + assertAssignedTo(allocationService, "yes-low", shuffledList("yes-low", "throttle", "throttle-high")); |
| 1153 | + // prioritize YES over THROTTLE when weights equal |
| 1154 | + assertAssignedTo(allocationService, "yes-low", shuffledList("yes-low", "throttle-low")); |
| 1155 | + // prioritize YES by weight |
| 1156 | + assertAssignedTo(allocationService, "yes-low", shuffledList("yes-low", "yes", "yes-high")); |
| 1157 | + // prioritize NOT_PREFERRED by weight |
| 1158 | + assertAssignedTo(allocationService, "not-preferred-low", shuffledList("not-preferred-low", "not-preferred", "not-preferred-high")); |
| 1159 | + } |
| 1160 | + |
| 1161 | + private void assertUnassigned(AllocationService allocationService, List<String> allNodeIds) { |
| 1162 | + assertAssignedTo(allocationService, null, allNodeIds); |
| 1163 | + } |
| 1164 | + |
| 1165 | + private void assertAssignedTo(AllocationService allocationService, @Nullable String expectedNodeId, List<String> allNodeIds) { |
| 1166 | + final var discoveryNodesBuilder = DiscoveryNodes.builder(); |
| 1167 | + for (String nodeName : allNodeIds) { |
| 1168 | + discoveryNodesBuilder.add(newNode(nodeName)); |
| 1169 | + } |
| 1170 | + final var projectMetadataBuilder = ProjectMetadata.builder(ProjectId.DEFAULT); |
| 1171 | + final var routingTableBuilder = RoutingTable.builder(TestShardRoutingRoleStrategies.DEFAULT_ROLE_ONLY); |
| 1172 | + |
| 1173 | + final var indexMetadata = anIndex("index", indexSettings(IndexVersion.current(), 1, 0)).build(); |
| 1174 | + projectMetadataBuilder.put(indexMetadata, false); |
| 1175 | + routingTableBuilder.addAsNew(indexMetadata); |
| 1176 | + |
| 1177 | + var clusterState = ClusterState.builder(ClusterName.DEFAULT) |
| 1178 | + .nodes(discoveryNodesBuilder) |
| 1179 | + .putProjectMetadata(projectMetadataBuilder) |
| 1180 | + .putRoutingTable(ProjectId.DEFAULT, routingTableBuilder.build()) |
| 1181 | + .build(); |
| 1182 | + |
| 1183 | + clusterState = startInitializingShardsAndReroute(allocationService, clusterState); |
| 1184 | + |
| 1185 | + final RoutingTable routingTable = clusterState.routingTable(ProjectId.DEFAULT); |
| 1186 | + final ShardRouting primaryShard = routingTable.shardRoutingTable(indexMetadata.getIndex().getName(), 0).primaryShard(); |
| 1187 | + assertThat(primaryShard.currentNodeId(), equalTo(expectedNodeId)); |
| 1188 | + } |
| 1189 | + |
| 1190 | + /** |
| 1191 | + * Returns specific values for {@link WeightFunction#calculateNodeWeightWithIndex} depending on the |
| 1192 | + * suffix of the node name. |
| 1193 | + */ |
| 1194 | + private static class NodeNameDrivenBalancingWeightsFactory implements BalancingWeightsFactory { |
| 1195 | + |
| 1196 | + private static class NodeNameDrivenWeightFunction extends WeightFunction { |
| 1197 | + |
| 1198 | + NodeNameDrivenWeightFunction() { |
| 1199 | + super(1.0f, 1.0f, 1.0f, 1.0f); |
| 1200 | + } |
| 1201 | + |
| 1202 | + @Override |
| 1203 | + float calculateNodeWeightWithIndex( |
| 1204 | + BalancedShardsAllocator.Balancer balancer, |
| 1205 | + BalancedShardsAllocator.ModelNode node, |
| 1206 | + BalancedShardsAllocator.ProjectIndex index |
| 1207 | + ) { |
| 1208 | + final var nodeId = node.getNodeId(); |
| 1209 | + if (nodeId.endsWith("-high")) { |
| 1210 | + return 10.0f; |
| 1211 | + } else if (nodeId.endsWith("-low")) { |
| 1212 | + return 0.0f; |
| 1213 | + } else { |
| 1214 | + return 5.0f; |
| 1215 | + } |
| 1216 | + } |
| 1217 | + } |
| 1218 | + |
| 1219 | + private static class NodeNameDrivenBalancingWeights implements BalancingWeights { |
| 1220 | + |
| 1221 | + private final NodeNameDrivenWeightFunction weightFunction = new NodeNameDrivenWeightFunction(); |
| 1222 | + |
| 1223 | + @Override |
| 1224 | + public WeightFunction weightFunctionForShard(ShardRouting shard) { |
| 1225 | + return weightFunction; |
| 1226 | + } |
| 1227 | + |
| 1228 | + @Override |
| 1229 | + public WeightFunction weightFunctionForNode(RoutingNode node) { |
| 1230 | + return weightFunction; |
| 1231 | + } |
| 1232 | + |
| 1233 | + @Override |
| 1234 | + public NodeSorters createNodeSorters( |
| 1235 | + BalancedShardsAllocator.ModelNode[] modelNodes, |
| 1236 | + BalancedShardsAllocator.Balancer balancer |
| 1237 | + ) { |
| 1238 | + final var nodeSorter = new BalancedShardsAllocator.NodeSorter(modelNodes, weightFunction, balancer); |
| 1239 | + return new NodeSorters() { |
| 1240 | + @Override |
| 1241 | + public BalancedShardsAllocator.NodeSorter sorterForShard(ShardRouting shard) { |
| 1242 | + return nodeSorter; |
| 1243 | + } |
| 1244 | + |
| 1245 | + @Override |
| 1246 | + public Iterator<BalancedShardsAllocator.NodeSorter> iterator() { |
| 1247 | + return List.of(nodeSorter).iterator(); |
| 1248 | + } |
| 1249 | + }; |
| 1250 | + } |
| 1251 | + |
| 1252 | + @Override |
| 1253 | + public boolean diskUsageIgnored() { |
| 1254 | + return true; |
| 1255 | + } |
| 1256 | + } |
| 1257 | + |
| 1258 | + @Override |
| 1259 | + public BalancingWeights create() { |
| 1260 | + return new NodeNameDrivenBalancingWeights(); |
| 1261 | + } |
| 1262 | + } |
| 1263 | + |
1107 | 1264 | /** |
1108 | 1265 | * Randomly select a shard and add a random write-load for it |
1109 | 1266 | * |
|
0 commit comments