|
8 | 8 | */ |
9 | 9 | package org.elasticsearch.index.shard; |
10 | 10 |
|
| 11 | +import org.apache.logging.log4j.LogManager; |
| 12 | +import org.apache.logging.log4j.Logger; |
11 | 13 | import org.apache.lucene.index.DirectoryReader; |
12 | 14 | import org.apache.lucene.store.LockObtainFailedException; |
13 | 15 | import org.apache.lucene.util.SetOnce; |
|
22 | 24 | import org.elasticsearch.cluster.EstimatedHeapUsage; |
23 | 25 | import org.elasticsearch.cluster.EstimatedHeapUsageCollector; |
24 | 26 | import org.elasticsearch.cluster.InternalClusterInfoService; |
| 27 | +import org.elasticsearch.cluster.NodeWriteLoad; |
| 28 | +import org.elasticsearch.cluster.WriteLoadCollector; |
25 | 29 | import org.elasticsearch.cluster.metadata.IndexMetadata; |
26 | 30 | import org.elasticsearch.cluster.node.DiscoveryNode; |
27 | 31 | import org.elasticsearch.cluster.node.DiscoveryNodeUtils; |
28 | 32 | import org.elasticsearch.cluster.routing.RecoverySource; |
29 | 33 | import org.elasticsearch.cluster.routing.ShardRouting; |
30 | 34 | import org.elasticsearch.cluster.routing.ShardRoutingState; |
31 | 35 | import org.elasticsearch.cluster.routing.UnassignedInfo; |
| 36 | +import org.elasticsearch.cluster.routing.allocation.WriteLoadConstraintSettings; |
32 | 37 | import org.elasticsearch.cluster.service.ClusterService; |
33 | 38 | import org.elasticsearch.common.Strings; |
34 | 39 | import org.elasticsearch.common.UUIDs; |
|
117 | 122 | import static org.hamcrest.Matchers.either; |
118 | 123 | import static org.hamcrest.Matchers.equalTo; |
119 | 124 | import static org.hamcrest.Matchers.greaterThan; |
| 125 | +import static org.hamcrest.Matchers.greaterThanOrEqualTo; |
120 | 126 | import static org.hamcrest.Matchers.instanceOf; |
121 | 127 | import static org.hamcrest.Matchers.lessThanOrEqualTo; |
122 | 128 |
|
123 | 129 | public class IndexShardIT extends ESSingleNodeTestCase { |
| 130 | + private static final Logger logger = LogManager.getLogger(IndexShardIT.class); |
124 | 131 |
|
125 | 132 | @Override |
126 | 133 | protected Collection<Class<? extends Plugin>> getPlugins() { |
127 | | - return pluginList(InternalSettingsPlugin.class, BogusEstimatedHeapUsagePlugin.class); |
| 134 | + return pluginList(InternalSettingsPlugin.class, BogusEstimatedHeapUsagePlugin.class, BogusWriteLoadCollectorPlugin.class); |
128 | 135 | } |
129 | 136 |
|
130 | 137 | public void testLockTryingToDelete() throws Exception { |
@@ -295,6 +302,47 @@ public void testHeapUsageEstimateIsPresent() { |
295 | 302 | } |
296 | 303 | } |
297 | 304 |
|
| 305 | + public void testNodeWriteLoadsArePresent() { |
| 306 | + InternalClusterInfoService clusterInfoService = (InternalClusterInfoService) getInstanceFromNode(ClusterInfoService.class); |
| 307 | + ClusterInfoServiceUtils.refresh(clusterInfoService); |
| 308 | + Map<String, NodeWriteLoad> nodeWriteLoads = clusterInfoService.getClusterInfo().getNodeWriteLoads(); |
| 309 | + assertNotNull(nodeWriteLoads); |
| 310 | + /** Not collecting stats yet because allocation write load stats collection is disabled by default. |
| 311 | + * see {@link WriteLoadConstraintSettings.WRITE_LOAD_DECIDER_ENABLED_SETTING} */ |
| 312 | + assertTrue(nodeWriteLoads.isEmpty()); |
| 313 | + |
| 314 | + // Enable collection for node write loads. |
| 315 | + updateClusterSettings( |
| 316 | + Settings.builder() |
| 317 | + .put( |
| 318 | + WriteLoadConstraintSettings.WRITE_LOAD_DECIDER_ENABLED_SETTING.getKey(), |
| 319 | + WriteLoadConstraintSettings.WriteLoadDeciderStatus.ENABLED |
| 320 | + ) |
| 321 | + .build() |
| 322 | + ); |
| 323 | + try { |
| 324 | + // Force a ClusterInfo refresh to run collection of the node write loads. |
| 325 | + ClusterInfoServiceUtils.refresh(clusterInfoService); |
| 326 | + nodeWriteLoads = clusterInfoService.getClusterInfo().getNodeWriteLoads(); |
| 327 | + |
| 328 | + /** Verify that each node has a write load reported. The test {@link BogusWriteLoadCollector} generates random load values */ |
| 329 | + ClusterState state = getInstanceFromNode(ClusterService.class).state(); |
| 330 | + assertEquals(state.nodes().size(), nodeWriteLoads.size()); |
| 331 | + for (DiscoveryNode node : state.nodes()) { |
| 332 | + assertTrue(nodeWriteLoads.containsKey(node.getId())); |
| 333 | + NodeWriteLoad nodeWriteLoad = nodeWriteLoads.get(node.getId()); |
| 334 | + assertThat(nodeWriteLoad.nodeId(), equalTo(node.getId())); |
| 335 | + assertThat(nodeWriteLoad.totalWriteThreadPoolThreads(), greaterThanOrEqualTo(0)); |
| 336 | + assertThat(nodeWriteLoad.percentWriteThreadPoolUtilization(), greaterThanOrEqualTo(0)); |
| 337 | + assertThat(nodeWriteLoad.maxTaskTimeInWriteQueueMillis(), greaterThanOrEqualTo(0L)); |
| 338 | + } |
| 339 | + } finally { |
| 340 | + updateClusterSettings( |
| 341 | + Settings.builder().putNull(WriteLoadConstraintSettings.WRITE_LOAD_DECIDER_ENABLED_SETTING.getKey()).build() |
| 342 | + ); |
| 343 | + } |
| 344 | + } |
| 345 | + |
298 | 346 | public void testIndexCanChangeCustomDataPath() throws Exception { |
299 | 347 | final String index = "test-custom-data-path"; |
300 | 348 | final Path sharedDataPath = getInstanceFromNode(Environment.class).sharedDataDir().resolve(randomAsciiLettersOfLength(10)); |
@@ -875,4 +923,55 @@ public ClusterService getClusterService() { |
875 | 923 | return clusterService.get(); |
876 | 924 | } |
877 | 925 | } |
| 926 | + |
| 927 | + /** |
| 928 | + * A simple {@link WriteLoadCollector} implementation that creates and returns random {@link NodeWriteLoad} for each node in the |
| 929 | + * cluster. |
| 930 | + * <p> |
| 931 | + * Note: there's an 'org.elasticsearch.cluster.WriteLoadCollector' file that declares this implementation so that the plugin system can |
| 932 | + * pick it up and use it for the test set-up. |
| 933 | + */ |
| 934 | + public static class BogusWriteLoadCollector implements WriteLoadCollector { |
| 935 | + |
| 936 | + private final BogusWriteLoadCollectorPlugin plugin; |
| 937 | + |
| 938 | + public BogusWriteLoadCollector(BogusWriteLoadCollectorPlugin plugin) { |
| 939 | + this.plugin = plugin; |
| 940 | + } |
| 941 | + |
| 942 | + @Override |
| 943 | + public void collectWriteLoads(ActionListener<Map<String, NodeWriteLoad>> listener) { |
| 944 | + ActionListener.completeWith( |
| 945 | + listener, |
| 946 | + () -> plugin.getClusterService() |
| 947 | + .state() |
| 948 | + .nodes() |
| 949 | + .stream() |
| 950 | + .collect( |
| 951 | + Collectors.toUnmodifiableMap( |
| 952 | + DiscoveryNode::getId, |
| 953 | + node -> new NodeWriteLoad(node.getId(), randomNonNegativeInt(), randomNonNegativeInt(), randomNonNegativeLong()) |
| 954 | + ) |
| 955 | + ) |
| 956 | + ); |
| 957 | + } |
| 958 | + } |
| 959 | + |
| 960 | + /** |
| 961 | + * Make a plugin to gain access to the {@link ClusterService} instance. |
| 962 | + */ |
| 963 | + public static class BogusWriteLoadCollectorPlugin extends Plugin implements ClusterPlugin { |
| 964 | + |
| 965 | + private final SetOnce<ClusterService> clusterService = new SetOnce<>(); |
| 966 | + |
| 967 | + @Override |
| 968 | + public Collection<?> createComponents(PluginServices services) { |
| 969 | + clusterService.set(services.clusterService()); |
| 970 | + return List.of(); |
| 971 | + } |
| 972 | + |
| 973 | + public ClusterService getClusterService() { |
| 974 | + return clusterService.get(); |
| 975 | + } |
| 976 | + } |
878 | 977 | } |
0 commit comments