From b0f15f842ddb7592c86b6867559ab47c04343062 Mon Sep 17 00:00:00 2001 From: "ievgen.degtiarenko" Date: Mon, 6 Feb 2023 08:50:10 +0100 Subject: [PATCH 1/2] Expose forecasted and actual disk usage per tier and node --- .../cluster/get-desired-balance.asciidoc | 26 +++- .../30_desired_balance.yml | 7 + .../test/cluster.desired_balance/10_basic.yml | 7 + .../TransportGetDesiredBalanceAction.java | 6 +- .../allocator/ClusterBalanceStats.java | 60 ++++++--- .../DesiredBalanceResponseTests.java | 59 ++++++--- ...TransportGetDesiredBalanceActionTests.java | 6 + .../allocator/ClusterBalanceStatsTests.java | 121 ++++++++++++------ 8 files changed, 209 insertions(+), 83 deletions(-) diff --git a/docs/reference/cluster/get-desired-balance.asciidoc b/docs/reference/cluster/get-desired-balance.asciidoc index 03bb9727089fb..54380dc4fa490 100644 --- a/docs/reference/cluster/get-desired-balance.asciidoc +++ b/docs/reference/cluster/get-desired-balance.asciidoc @@ -19,6 +19,9 @@ GET /_internal/desired_balance The API returns the following result: +Where `actual_disk_usage` is computed using shard sizes from cluster info only +and `forecast_disk_usage` uses shard size forecast when available. + [source,console-result] -------------------------------------------------- { @@ -55,6 +58,13 @@ The API returns the following result: "max" : 16.0, "average" : 12.0, "std_dev" : 2.8284271247461903 + }, + "actual_disk_usage" : { + "total" : 36.0, + "min" : 10.0, + "max" : 16.0, + "average" : 12.0, + "std_dev" : 2.8284271247461903 } }, "data_warm" : { @@ -78,6 +88,13 @@ The API returns the following result: "max" : 18.0, "average" : 14.0, "std_dev" : 2.8284271247461903 + }, + "actual_disk_usage" : { + "total" : 42.0, + "min" : 12.0, + "max" : 18.0, + "average" : 14.0, + "std_dev" : 2.8284271247461903 } } }, @@ -85,17 +102,20 @@ The API returns the following result: "node-1": { "shard_count": 10, "forecast_write_load": 8.5, - "forecast_disk_usage_bytes": 498435 + "forecast_disk_usage_bytes": 498435, + "actual_disk_usage_bytes": 498435 }, "node-2": { "shard_count": 15, "forecast_write_load": 3.25, - "forecast_disk_usage_bytes": 384935 + "forecast_disk_usage_bytes": 384935, + "actual_disk_usage_bytes": 384935 }, "node-3": { "shard_count": 12, "forecast_write_load": 6.0, - "forecast_disk_usage_bytes": 648766 + "forecast_disk_usage_bytes": 648766, + "actual_disk_usage_bytes": 648766 } } }, diff --git a/qa/smoke-test-multinode/src/yamlRestTest/resources/rest-api-spec/test/smoke_test_multinode/30_desired_balance.yml b/qa/smoke-test-multinode/src/yamlRestTest/resources/rest-api-spec/test/smoke_test_multinode/30_desired_balance.yml index c7841a9ff099f..fc078adeda62a 100644 --- a/qa/smoke-test-multinode/src/yamlRestTest/resources/rest-api-spec/test/smoke_test_multinode/30_desired_balance.yml +++ b/qa/smoke-test-multinode/src/yamlRestTest/resources/rest-api-spec/test/smoke_test_multinode/30_desired_balance.yml @@ -94,8 +94,15 @@ setup: - is_true: 'cluster_balance_stats.tiers.data_content.forecast_disk_usage.max' - is_true: 'cluster_balance_stats.tiers.data_content.forecast_disk_usage.average' - is_true: 'cluster_balance_stats.tiers.data_content.forecast_disk_usage.std_dev' + - is_true: 'cluster_balance_stats.tiers.data_content.actual_disk_usage' + - is_true: 'cluster_balance_stats.tiers.data_content.actual_disk_usage.total' + - is_true: 'cluster_balance_stats.tiers.data_content.actual_disk_usage.min' + - is_true: 'cluster_balance_stats.tiers.data_content.actual_disk_usage.max' + - is_true: 'cluster_balance_stats.tiers.data_content.actual_disk_usage.average' + - is_true: 'cluster_balance_stats.tiers.data_content.actual_disk_usage.std_dev' - is_true: 'cluster_balance_stats.nodes' - is_true: 'cluster_balance_stats.nodes.test-cluster-0' - gte: { 'cluster_balance_stats.nodes.test-cluster-0.shard_count' : 0 } - gte: { 'cluster_balance_stats.nodes.test-cluster-0.forecast_write_load': 0.0 } - gte: { 'cluster_balance_stats.nodes.test-cluster-0.forecast_disk_usage_bytes' : 0 } + - gte: { 'cluster_balance_stats.nodes.test-cluster-0.actual_disk_usage_bytes' : 0 } diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/cluster.desired_balance/10_basic.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/cluster.desired_balance/10_basic.yml index 15dc9853ff50f..5969e946494cd 100644 --- a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/cluster.desired_balance/10_basic.yml +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/cluster.desired_balance/10_basic.yml @@ -49,11 +49,18 @@ setup: - is_true: 'cluster_balance_stats.tiers.data_content.forecast_disk_usage.max' - is_true: 'cluster_balance_stats.tiers.data_content.forecast_disk_usage.average' - is_true: 'cluster_balance_stats.tiers.data_content.forecast_disk_usage.std_dev' + - is_true: 'cluster_balance_stats.tiers.data_content.actual_disk_usage' + - is_true: 'cluster_balance_stats.tiers.data_content.actual_disk_usage.total' + - is_true: 'cluster_balance_stats.tiers.data_content.actual_disk_usage.min' + - is_true: 'cluster_balance_stats.tiers.data_content.actual_disk_usage.max' + - is_true: 'cluster_balance_stats.tiers.data_content.actual_disk_usage.average' + - is_true: 'cluster_balance_stats.tiers.data_content.actual_disk_usage.std_dev' - is_true: 'cluster_balance_stats.nodes' - is_true: 'cluster_balance_stats.nodes.test-cluster-0' - gte: { 'cluster_balance_stats.nodes.test-cluster-0.shard_count' : 0 } - gte: { 'cluster_balance_stats.nodes.test-cluster-0.forecast_write_load': 0.0 } - gte: { 'cluster_balance_stats.nodes.test-cluster-0.forecast_disk_usage_bytes' : 0 } + - gte: { 'cluster_balance_stats.nodes.test-cluster-0.actual_disk_usage_bytes' : 0 } --- "Test get desired balance for single shard": diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportGetDesiredBalanceAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportGetDesiredBalanceAction.java index c0331bb94f6b3..0ad58e9b987c3 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportGetDesiredBalanceAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/allocation/TransportGetDesiredBalanceAction.java @@ -11,6 +11,7 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.master.TransportMasterNodeReadAction; +import org.elasticsearch.cluster.ClusterInfoService; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.block.ClusterBlockException; import org.elasticsearch.cluster.block.ClusterBlockLevel; @@ -43,6 +44,7 @@ public class TransportGetDesiredBalanceAction extends TransportMasterNodeReadAct @Nullable private final DesiredBalanceShardsAllocator desiredBalanceShardsAllocator; + private final ClusterInfoService clusterInfoService; private final WriteLoadForecaster writeLoadForecaster; @Inject @@ -53,6 +55,7 @@ public TransportGetDesiredBalanceAction( ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver, ShardsAllocator shardsAllocator, + ClusterInfoService clusterInfoService, WriteLoadForecaster writeLoadForecaster ) { super( @@ -67,6 +70,7 @@ public TransportGetDesiredBalanceAction( ThreadPool.Names.MANAGEMENT ); this.desiredBalanceShardsAllocator = shardsAllocator instanceof DesiredBalanceShardsAllocator allocator ? allocator : null; + this.clusterInfoService = clusterInfoService; this.writeLoadForecaster = writeLoadForecaster; } @@ -90,7 +94,7 @@ protected void masterOperation( listener.onResponse( new DesiredBalanceResponse( desiredBalanceShardsAllocator.getStats(), - ClusterBalanceStats.createFrom(state, writeLoadForecaster), + ClusterBalanceStats.createFrom(state, clusterInfoService.getClusterInfo(), writeLoadForecaster), createRoutingTable(state, latestDesiredBalance) ) ); diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/ClusterBalanceStats.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/ClusterBalanceStats.java index 9bebebcdfe542..7021bd6554b4d 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/ClusterBalanceStats.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/ClusterBalanceStats.java @@ -8,6 +8,7 @@ package org.elasticsearch.cluster.routing.allocation.allocator; +import org.elasticsearch.cluster.ClusterInfo; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.node.DiscoveryNodeRole; @@ -36,7 +37,11 @@ public record ClusterBalanceStats(Map tiers, Map>(); var nodes = new HashMap(); for (RoutingNode routingNode : clusterState.getRoutingNodes()) { @@ -44,7 +49,7 @@ public static ClusterBalanceStats createFrom(ClusterState clusterState, WriteLoa if (dataRoles.isEmpty()) { continue; } - var nodeStats = NodeBalanceStats.createFrom(routingNode, clusterState.metadata(), writeLoadForecaster); + var nodeStats = NodeBalanceStats.createFrom(routingNode, clusterState.metadata(), clusterInfo, writeLoadForecaster); nodes.put(routingNode.node().getName(), nodeStats); for (DiscoveryNodeRole role : dataRoles) { tierToNodeStats.computeIfAbsent(role.roleName(), ignored -> new ArrayList<>()).add(nodeStats); @@ -71,21 +76,29 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder.startObject().field("tiers").map(tiers).field("nodes").map(nodes).endObject(); } - public record TierBalanceStats(MetricStats shardCount, MetricStats forecastWriteLoad, MetricStats forecastShardSize) - implements - Writeable, - ToXContentObject { + public record TierBalanceStats( + MetricStats shardCount, + MetricStats forecastWriteLoad, + MetricStats forecastShardSize, + MetricStats actualShardSize + ) implements Writeable, ToXContentObject { private static TierBalanceStats createFrom(List nodes) { return new TierBalanceStats( MetricStats.createFrom(nodes, it -> it.shards), MetricStats.createFrom(nodes, it -> it.forecastWriteLoad), - MetricStats.createFrom(nodes, it -> it.forecastShardSize) + MetricStats.createFrom(nodes, it -> it.forecastShardSize), + MetricStats.createFrom(nodes, it -> it.actualShardSize) ); } public static TierBalanceStats readFrom(StreamInput in) throws IOException { - return new TierBalanceStats(MetricStats.readFrom(in), MetricStats.readFrom(in), MetricStats.readFrom(in)); + return new TierBalanceStats( + MetricStats.readFrom(in), + MetricStats.readFrom(in), + MetricStats.readFrom(in), + MetricStats.readFrom(in) + ); } @Override @@ -93,6 +106,7 @@ public void writeTo(StreamOutput out) throws IOException { shardCount.writeTo(out); forecastWriteLoad.writeTo(out); forecastShardSize.writeTo(out); + actualShardSize.writeTo(out); } @Override @@ -101,6 +115,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws .field("shard_count", shardCount) .field("forecast_write_load", forecastWriteLoad) .field("forecast_disk_usage", forecastShardSize) + .field("actual_disk_usage", actualShardSize) .endObject(); } } @@ -155,24 +170,35 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } } - public record NodeBalanceStats(int shards, double forecastWriteLoad, long forecastShardSize) implements Writeable, ToXContentObject { + public record NodeBalanceStats(int shards, double forecastWriteLoad, long forecastShardSize, long actualShardSize) + implements + Writeable, + ToXContentObject { - private static NodeBalanceStats createFrom(RoutingNode routingNode, Metadata metadata, WriteLoadForecaster writeLoadForecaster) { - double totalWriteLoad = 0.0; - long totalShardSize = 0L; + private static NodeBalanceStats createFrom( + RoutingNode routingNode, + Metadata metadata, + ClusterInfo clusterInfo, + WriteLoadForecaster writeLoadForecaster + ) { + double forecastWriteLoad = 0.0; + long forecastShardSize = 0L; + long actualShardSize = 0L; for (ShardRouting shardRouting : routingNode) { var indexMetadata = metadata.index(shardRouting.index()); + var shardSize = clusterInfo.getShardSize(shardRouting, 0L); assert indexMetadata != null; - totalWriteLoad += writeLoadForecaster.getForecastedWriteLoad(indexMetadata).orElse(0.0); - totalShardSize += indexMetadata.getForecastedShardSizeInBytes().orElse(0); + forecastWriteLoad += writeLoadForecaster.getForecastedWriteLoad(indexMetadata).orElse(0.0); + forecastShardSize += indexMetadata.getForecastedShardSizeInBytes().orElse(shardSize); + actualShardSize += shardSize; } - return new NodeBalanceStats(routingNode.size(), totalWriteLoad, totalShardSize); + return new NodeBalanceStats(routingNode.size(), forecastWriteLoad, forecastShardSize, actualShardSize); } public static NodeBalanceStats readFrom(StreamInput in) throws IOException { - return new NodeBalanceStats(in.readInt(), in.readDouble(), in.readLong()); + return new NodeBalanceStats(in.readInt(), in.readDouble(), in.readLong(), in.readLong()); } @Override @@ -180,6 +206,7 @@ public void writeTo(StreamOutput out) throws IOException { out.writeInt(shards); out.writeDouble(forecastWriteLoad); out.writeLong(forecastShardSize); + out.writeLong(actualShardSize); } @Override @@ -188,6 +215,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws .field("shard_count", shards) .field("forecast_write_load", forecastWriteLoad) .humanReadableField("forecast_disk_usage_bytes", "forecast_disk_usage", ByteSizeValue.ofBytes(forecastShardSize)) + .humanReadableField("actual_disk_usage_bytes", "actual_disk_usage", ByteSizeValue.ofBytes(actualShardSize)) .endObject(); } } diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/allocation/DesiredBalanceResponseTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/allocation/DesiredBalanceResponseTests.java index 338c949674df8..9d5dcca152db8 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/allocation/DesiredBalanceResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/allocation/DesiredBalanceResponseTests.java @@ -23,12 +23,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; import static java.util.function.Function.identity; import static java.util.stream.Collectors.toMap; +import static org.hamcrest.Matchers.containsInAnyOrder; public class DesiredBalanceResponseTests extends AbstractWireSerializingTestCase { @@ -73,6 +73,7 @@ private ClusterBalanceStats randomClusterBalanceStats() { private ClusterBalanceStats.TierBalanceStats randomTierBalanceStats() { return new ClusterBalanceStats.TierBalanceStats( + new ClusterBalanceStats.MetricStats(randomDouble(), randomDouble(), randomDouble(), randomDouble(), randomDouble()), new ClusterBalanceStats.MetricStats(randomDouble(), randomDouble(), randomDouble(), randomDouble(), randomDouble()), new ClusterBalanceStats.MetricStats(randomDouble(), randomDouble(), randomDouble(), randomDouble(), randomDouble()), new ClusterBalanceStats.MetricStats(randomDouble(), randomDouble(), randomDouble(), randomDouble(), randomDouble()) @@ -83,6 +84,7 @@ private ClusterBalanceStats.NodeBalanceStats randomNodeBalanceStats() { return new ClusterBalanceStats.NodeBalanceStats( randomIntBetween(0, Integer.MAX_VALUE), randomDouble(), + randomLongBetween(0, Long.MAX_VALUE), randomLongBetween(0, Long.MAX_VALUE) ); } @@ -160,7 +162,7 @@ public void testToXContent() throws IOException { Map json = createParser( ChunkedToXContent.wrapAsToXContent(response).toXContent(XContentFactory.jsonBuilder(), ToXContent.EMPTY_PARAMS) ).map(); - assertEquals(Set.of("stats", "cluster_balance_stats", "routing_table"), json.keySet()); + assertThat(json.keySet(), containsInAnyOrder("stats", "cluster_balance_stats", "routing_table")); // stats Map stats = (Map) json.get("stats"); @@ -175,49 +177,64 @@ public void testToXContent() throws IOException { // cluster balance stats Map clusterBalanceStats = (Map) json.get("cluster_balance_stats"); - assertEquals(Set.of("tiers", "nodes"), clusterBalanceStats.keySet()); + assertThat(clusterBalanceStats.keySet(), containsInAnyOrder("tiers", "nodes")); // tier balance stats Map tiers = (Map) clusterBalanceStats.get("tiers"); assertEquals(tiers.keySet(), response.getClusterBalanceStats().tiers().keySet()); for (var entry : response.getClusterBalanceStats().tiers().entrySet()) { Map tierStats = (Map) tiers.get(entry.getKey()); - assertEquals(Set.of("shard_count", "forecast_write_load", "forecast_disk_usage"), tierStats.keySet()); + assertThat( + tierStats.keySet(), + containsInAnyOrder("shard_count", "forecast_write_load", "forecast_disk_usage", "actual_disk_usage") + ); Map shardCountStats = (Map) tierStats.get("shard_count"); - assertEquals(Set.of("total", "average", "min", "max", "std_dev"), shardCountStats.keySet()); + assertThat(shardCountStats.keySet(), containsInAnyOrder("total", "average", "min", "max", "std_dev")); assertEquals(shardCountStats.get("total"), entry.getValue().shardCount().total()); assertEquals(shardCountStats.get("average"), entry.getValue().shardCount().average()); assertEquals(shardCountStats.get("min"), entry.getValue().shardCount().min()); assertEquals(shardCountStats.get("max"), entry.getValue().shardCount().max()); assertEquals(shardCountStats.get("std_dev"), entry.getValue().shardCount().stdDev()); - Map totalWriteLoadStats = (Map) tierStats.get("forecast_write_load"); - assertEquals(Set.of("total", "average", "min", "max", "std_dev"), totalWriteLoadStats.keySet()); - assertEquals(totalWriteLoadStats.get("total"), entry.getValue().forecastWriteLoad().total()); - assertEquals(totalWriteLoadStats.get("average"), entry.getValue().forecastWriteLoad().average()); - assertEquals(totalWriteLoadStats.get("min"), entry.getValue().forecastWriteLoad().min()); - assertEquals(totalWriteLoadStats.get("max"), entry.getValue().forecastWriteLoad().max()); - assertEquals(totalWriteLoadStats.get("std_dev"), entry.getValue().forecastWriteLoad().stdDev()); + Map forecastWriteLoadStats = (Map) tierStats.get("forecast_write_load"); + assertThat(forecastWriteLoadStats.keySet(), containsInAnyOrder("total", "average", "min", "max", "std_dev")); + assertEquals(forecastWriteLoadStats.get("total"), entry.getValue().forecastWriteLoad().total()); + assertEquals(forecastWriteLoadStats.get("average"), entry.getValue().forecastWriteLoad().average()); + assertEquals(forecastWriteLoadStats.get("min"), entry.getValue().forecastWriteLoad().min()); + assertEquals(forecastWriteLoadStats.get("max"), entry.getValue().forecastWriteLoad().max()); + assertEquals(forecastWriteLoadStats.get("std_dev"), entry.getValue().forecastWriteLoad().stdDev()); - Map totalShardStats = (Map) tierStats.get("forecast_disk_usage"); - assertEquals(Set.of("total", "average", "min", "max", "std_dev"), totalShardStats.keySet()); - assertEquals(totalShardStats.get("total"), entry.getValue().forecastShardSize().total()); - assertEquals(totalShardStats.get("average"), entry.getValue().forecastShardSize().average()); - assertEquals(totalShardStats.get("min"), entry.getValue().forecastShardSize().min()); - assertEquals(totalShardStats.get("max"), entry.getValue().forecastShardSize().max()); - assertEquals(totalShardStats.get("std_dev"), entry.getValue().forecastShardSize().stdDev()); + Map forecastDiskUsageStats = (Map) tierStats.get("forecast_disk_usage"); + assertThat(forecastDiskUsageStats.keySet(), containsInAnyOrder("total", "average", "min", "max", "std_dev")); + assertEquals(forecastDiskUsageStats.get("total"), entry.getValue().forecastShardSize().total()); + assertEquals(forecastDiskUsageStats.get("average"), entry.getValue().forecastShardSize().average()); + assertEquals(forecastDiskUsageStats.get("min"), entry.getValue().forecastShardSize().min()); + assertEquals(forecastDiskUsageStats.get("max"), entry.getValue().forecastShardSize().max()); + assertEquals(forecastDiskUsageStats.get("std_dev"), entry.getValue().forecastShardSize().stdDev()); + + Map actualDiskUsageStats = (Map) tierStats.get("actual_disk_usage"); + assertThat(actualDiskUsageStats.keySet(), containsInAnyOrder("total", "average", "min", "max", "std_dev")); + assertEquals(actualDiskUsageStats.get("total"), entry.getValue().actualShardSize().total()); + assertEquals(actualDiskUsageStats.get("average"), entry.getValue().actualShardSize().average()); + assertEquals(actualDiskUsageStats.get("min"), entry.getValue().actualShardSize().min()); + assertEquals(actualDiskUsageStats.get("max"), entry.getValue().actualShardSize().max()); + assertEquals(actualDiskUsageStats.get("std_dev"), entry.getValue().actualShardSize().stdDev()); } // node balance stats Map nodes = (Map) clusterBalanceStats.get("nodes"); assertEquals(nodes.keySet(), response.getClusterBalanceStats().nodes().keySet()); for (var entry : response.getClusterBalanceStats().nodes().entrySet()) { Map nodesStats = (Map) nodes.get(entry.getKey()); - assertEquals(Set.of("shard_count", "forecast_write_load", "forecast_disk_usage_bytes"), nodesStats.keySet()); + assertThat( + nodesStats.keySet(), + containsInAnyOrder("shard_count", "forecast_write_load", "forecast_disk_usage_bytes", "actual_disk_usage_bytes") + ); assertEquals(nodesStats.get("shard_count"), entry.getValue().shards()); assertEquals(nodesStats.get("forecast_write_load"), entry.getValue().forecastWriteLoad()); assertEquals(nodesStats.get("forecast_disk_usage_bytes"), entry.getValue().forecastShardSize()); + assertEquals(nodesStats.get("actual_disk_usage_bytes"), entry.getValue().actualShardSize()); } // routing table @@ -232,7 +249,7 @@ public void testToXContent() throws IOException { for (var shardEntry : indexEntry.getValue().entrySet()) { DesiredBalanceResponse.DesiredShards desiredShards = shardEntry.getValue(); Map jsonDesiredShard = (Map) jsonIndexShards.get(String.valueOf(shardEntry.getKey())); - assertEquals(Set.of("current", "desired"), jsonDesiredShard.keySet()); + assertThat(jsonDesiredShard.keySet(), containsInAnyOrder("current", "desired")); List> jsonCurrent = (List>) jsonDesiredShard.get("current"); for (int i = 0; i < jsonCurrent.size(); i++) { Map jsonShard = jsonCurrent.get(i); diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/allocation/TransportGetDesiredBalanceActionTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/allocation/TransportGetDesiredBalanceActionTests.java index 2a7dc273c2100..7505d4c4e8f0e 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/allocation/TransportGetDesiredBalanceActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/allocation/TransportGetDesiredBalanceActionTests.java @@ -11,6 +11,8 @@ import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.cluster.ClusterInfo; +import org.elasticsearch.cluster.ClusterInfoService; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ESAllocationTestCase; @@ -59,6 +61,7 @@ public class TransportGetDesiredBalanceActionTests extends ESAllocationTestCase { private final DesiredBalanceShardsAllocator desiredBalanceShardsAllocator = mock(DesiredBalanceShardsAllocator.class); + private final ClusterInfoService clusterInfoService = mock(ClusterInfoService.class); private final TransportGetDesiredBalanceAction transportGetDesiredBalanceAction = new TransportGetDesiredBalanceAction( mock(TransportService.class), mock(ClusterService.class), @@ -66,6 +69,7 @@ public class TransportGetDesiredBalanceActionTests extends ESAllocationTestCase mock(ActionFilters.class), mock(IndexNameExpressionResolver.class), desiredBalanceShardsAllocator, + clusterInfoService, TEST_WRITE_LOAD_FORECASTER ); @SuppressWarnings("unchecked") @@ -81,6 +85,7 @@ public void testReturnsErrorIfAllocatorIsNotDesiredBalanced() throws Exception { mock(ActionFilters.class), mock(IndexNameExpressionResolver.class), mock(ShardsAllocator.class), + mock(ClusterInfoService.class), mock(WriteLoadForecaster.class) ).masterOperation(mock(Task.class), mock(DesiredBalanceRequest.class), clusterState, listener); @@ -202,6 +207,7 @@ public void testGetDesiredBalance() throws Exception { randomInt(Integer.MAX_VALUE) ); when(desiredBalanceShardsAllocator.getStats()).thenReturn(desiredBalanceStats); + when(clusterInfoService.getClusterInfo()).thenReturn(ClusterInfo.EMPTY); var clusterState = ClusterState.builder(ClusterName.DEFAULT) .metadata(metadataBuilder.build()) diff --git a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/allocator/ClusterBalanceStatsTests.java b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/allocator/ClusterBalanceStatsTests.java index 922b778ecc061..db0e187d217fd 100644 --- a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/allocator/ClusterBalanceStatsTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/allocator/ClusterBalanceStatsTests.java @@ -9,6 +9,7 @@ package org.elasticsearch.cluster.routing.allocation.allocator; import org.elasticsearch.Version; +import org.elasticsearch.cluster.ClusterInfo; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ESAllocationTestCase; @@ -25,6 +26,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import static org.elasticsearch.cluster.node.DiscoveryNodeRole.DATA_CONTENT_NODE_ROLE; import static org.elasticsearch.cluster.node.DiscoveryNodeRole.DATA_HOT_NODE_ROLE; @@ -49,8 +52,11 @@ public void testStatsForSingleTierClusterWithNoForecasts() { startedIndex("index-3", null, null, "node-3", "node-1") ) ); + var clusterInfo = createClusterInfo( + List.of(indexSizes("index-1", 1L, 1L), indexSizes("index-2", 2L, 2L), indexSizes("index-3", 3L, 3L)) + ); - var stats = ClusterBalanceStats.createFrom(clusterState, TEST_WRITE_LOAD_FORECASTER); + var stats = ClusterBalanceStats.createFrom(clusterState, clusterInfo, TEST_WRITE_LOAD_FORECASTER); assertThat( stats, @@ -61,16 +67,14 @@ public void testStatsForSingleTierClusterWithNoForecasts() { new ClusterBalanceStats.TierBalanceStats( new ClusterBalanceStats.MetricStats(6.0, 2.0, 2.0, 2.0, 0.0), new ClusterBalanceStats.MetricStats(0.0, 0.0, 0.0, 0.0, 0.0), - new ClusterBalanceStats.MetricStats(0.0, 0.0, 0.0, 0.0, 0.0) + new ClusterBalanceStats.MetricStats(12.0, 3.0, 5.0, 4.0, stdDev(3.0, 5.0, 4.0)), + new ClusterBalanceStats.MetricStats(12.0, 3.0, 5.0, 4.0, stdDev(3.0, 5.0, 4.0)) ) ), - Map.of( - "node-1", - new ClusterBalanceStats.NodeBalanceStats(2, 0.0, 0L), - "node-2", - new ClusterBalanceStats.NodeBalanceStats(2, 0.0, 0L), - "node-3", - new ClusterBalanceStats.NodeBalanceStats(2, 0.0, 0L) + Map.ofEntries( + Map.entry("node-1", new ClusterBalanceStats.NodeBalanceStats(2, 0.0, 4L, 4L)), + Map.entry("node-2", new ClusterBalanceStats.NodeBalanceStats(2, 0.0, 3L, 3L)), + Map.entry("node-3", new ClusterBalanceStats.NodeBalanceStats(2, 0.0, 5L, 5L)) ) ) ) @@ -91,8 +95,12 @@ public void testStatsForSingleTierClusterWithForecasts() { startedIndex("index-3", 2.0, 6L, "node-3", "node-1") ) ); + // intentionally different from forecast + var clusterInfo = createClusterInfo( + List.of(indexSizes("index-1", 1L, 1L), indexSizes("index-2", 2L, 2L), indexSizes("index-3", 3L, 3L)) + ); - var stats = ClusterBalanceStats.createFrom(clusterState, TEST_WRITE_LOAD_FORECASTER); + var stats = ClusterBalanceStats.createFrom(clusterState, clusterInfo, TEST_WRITE_LOAD_FORECASTER); assertThat( stats, @@ -103,16 +111,14 @@ public void testStatsForSingleTierClusterWithForecasts() { new ClusterBalanceStats.TierBalanceStats( new ClusterBalanceStats.MetricStats(6.0, 2.0, 2.0, 2.0, 0.0), new ClusterBalanceStats.MetricStats(12.0, 3.5, 4.5, 4.0, stdDev(3.5, 4.0, 4.5)), - new ClusterBalanceStats.MetricStats(36.0, 10.0, 14.0, 12.0, stdDev(10.0, 12.0, 14.0)) + new ClusterBalanceStats.MetricStats(36.0, 10.0, 14.0, 12.0, stdDev(10.0, 12.0, 14.0)), + new ClusterBalanceStats.MetricStats(12.0, 3.0, 5.0, 4.0, stdDev(3.0, 5.0, 4.0)) ) ), - Map.of( - "node-1", - new ClusterBalanceStats.NodeBalanceStats(2, 3.5, 14L), - "node-2", - new ClusterBalanceStats.NodeBalanceStats(2, 4.0, 12L), - "node-3", - new ClusterBalanceStats.NodeBalanceStats(2, 4.5, 10L) + Map.ofEntries( + Map.entry("node-1", new ClusterBalanceStats.NodeBalanceStats(2, 3.5, 14L, 4L)), + Map.entry("node-2", new ClusterBalanceStats.NodeBalanceStats(2, 4.0, 12L, 3L)), + Map.entry("node-3", new ClusterBalanceStats.NodeBalanceStats(2, 4.5, 10L, 5L)) ) ) ) @@ -138,8 +144,18 @@ public void testStatsForHotWarmClusterWithForecasts() { startedIndex("index-warm-2", 0.0, 18L, "node-warm-3") ) ); + // intentionally different from forecast + var clusterInfo = createClusterInfo( + List.of( + indexSizes("index-hot-1", 4L, 4L, 4L), + indexSizes("index-hot-2", 5L, 5L), + indexSizes("index-hot-3", 6L, 6L), + indexSizes("index-warm-1", 12L, 12L), + indexSizes("index-warm-2", 18L) + ) + ); - var stats = ClusterBalanceStats.createFrom(clusterState, TEST_WRITE_LOAD_FORECASTER); + var stats = ClusterBalanceStats.createFrom(clusterState, clusterInfo, TEST_WRITE_LOAD_FORECASTER); assertThat( stats, @@ -150,34 +166,31 @@ public void testStatsForHotWarmClusterWithForecasts() { new ClusterBalanceStats.TierBalanceStats( new ClusterBalanceStats.MetricStats(7.0, 2.0, 3.0, 7.0 / 3, stdDev(3.0, 2.0, 2.0)), new ClusterBalanceStats.MetricStats(21.0, 6.0, 8.5, 7.0, stdDev(6.0, 8.5, 6.5)), - new ClusterBalanceStats.MetricStats(36.0, 10.0, 16.0, 12.0, stdDev(10.0, 10.0, 16.0)) + new ClusterBalanceStats.MetricStats(36.0, 10.0, 16.0, 12.0, stdDev(10.0, 10.0, 16.0)), + new ClusterBalanceStats.MetricStats(34.0, 9.0, 15.0, 34.0 / 3, stdDev(9.0, 10.0, 15.0)) ), DATA_HOT_NODE_ROLE.roleName(), new ClusterBalanceStats.TierBalanceStats( new ClusterBalanceStats.MetricStats(7.0, 2.0, 3.0, 7.0 / 3, stdDev(3.0, 2.0, 2.0)), new ClusterBalanceStats.MetricStats(21.0, 6.0, 8.5, 7.0, stdDev(6.0, 8.5, 6.5)), - new ClusterBalanceStats.MetricStats(36.0, 10.0, 16.0, 12.0, stdDev(10.0, 10.0, 16.0)) + new ClusterBalanceStats.MetricStats(36.0, 10.0, 16.0, 12.0, stdDev(10.0, 10.0, 16.0)), + new ClusterBalanceStats.MetricStats(34.0, 9.0, 15.0, 34.0 / 3, stdDev(9.0, 10.0, 15.0)) ), DATA_WARM_NODE_ROLE.roleName(), new ClusterBalanceStats.TierBalanceStats( new ClusterBalanceStats.MetricStats(3.0, 1.0, 1.0, 1.0, 0.0), new ClusterBalanceStats.MetricStats(0.0, 0.0, 0.0, 0.0, 0.0), + new ClusterBalanceStats.MetricStats(42.0, 12.0, 18.0, 14.0, stdDev(12.0, 12.0, 18.0)), new ClusterBalanceStats.MetricStats(42.0, 12.0, 18.0, 14.0, stdDev(12.0, 12.0, 18.0)) ) ), - Map.of( - "node-hot-1", - new ClusterBalanceStats.NodeBalanceStats(3, 8.5, 16L), - "node-hot-2", - new ClusterBalanceStats.NodeBalanceStats(2, 6.0, 10L), - "node-hot-3", - new ClusterBalanceStats.NodeBalanceStats(2, 6.5, 10L), - "node-warm-1", - new ClusterBalanceStats.NodeBalanceStats(1, 0.0, 12L), - "node-warm-2", - new ClusterBalanceStats.NodeBalanceStats(1, 0.0, 12L), - "node-warm-3", - new ClusterBalanceStats.NodeBalanceStats(1, 0.0, 18L) + Map.ofEntries( + Map.entry("node-hot-1", new ClusterBalanceStats.NodeBalanceStats(3, 8.5, 16L, 15L)), + Map.entry("node-hot-2", new ClusterBalanceStats.NodeBalanceStats(2, 6.0, 10L, 9L)), + Map.entry("node-hot-3", new ClusterBalanceStats.NodeBalanceStats(2, 6.5, 10L, 10L)), + Map.entry("node-warm-1", new ClusterBalanceStats.NodeBalanceStats(1, 0.0, 12L, 12L)), + Map.entry("node-warm-2", new ClusterBalanceStats.NodeBalanceStats(1, 0.0, 12L, 12L)), + Map.entry("node-warm-3", new ClusterBalanceStats.NodeBalanceStats(1, 0.0, 18L, 18L)) ) ) ) @@ -194,8 +207,9 @@ public void testStatsForNoIndicesInTier() { ), List.of() ); + var clusterInfo = createClusterInfo(List.of()); - var stats = ClusterBalanceStats.createFrom(clusterState, TEST_WRITE_LOAD_FORECASTER); + var stats = ClusterBalanceStats.createFrom(clusterState, clusterInfo, TEST_WRITE_LOAD_FORECASTER); assertThat( stats, @@ -204,18 +218,16 @@ public void testStatsForNoIndicesInTier() { Map.of( DATA_CONTENT_NODE_ROLE.roleName(), new ClusterBalanceStats.TierBalanceStats( + new ClusterBalanceStats.MetricStats(0.0, 0.0, 0.0, 0.0, 0.0), new ClusterBalanceStats.MetricStats(0.0, 0.0, 0.0, 0.0, 0.0), new ClusterBalanceStats.MetricStats(0.0, 0.0, 0.0, 0.0, 0.0), new ClusterBalanceStats.MetricStats(0.0, 0.0, 0.0, 0.0, 0.0) ) ), - Map.of( - "node-1", - new ClusterBalanceStats.NodeBalanceStats(0, 0.0, 0L), - "node-2", - new ClusterBalanceStats.NodeBalanceStats(0, 0.0, 0L), - "node-3", - new ClusterBalanceStats.NodeBalanceStats(0, 0.0, 0L) + Map.ofEntries( + Map.entry("node-1", new ClusterBalanceStats.NodeBalanceStats(0, 0.0, 0L, 0L)), + Map.entry("node-2", new ClusterBalanceStats.NodeBalanceStats(0, 0.0, 0L, 0L)), + Map.entry("node-3", new ClusterBalanceStats.NodeBalanceStats(0, 0.0, 0L, 0L)) ) ) ) @@ -267,6 +279,31 @@ private static Tuple startedIndex( ); } + private ClusterInfo createClusterInfo(List> shardSizes) { + return new ClusterInfo( + Map.of(), + Map.of(), + shardSizes.stream() + .flatMap( + entry -> IntStream.range(0, entry.v2().length) + .mapToObj( + index -> Map.entry( + ClusterInfo.shardIdentifierFromRouting(new ShardId(entry.v1(), "_na_", index), true), + entry.v2()[index] + ) + ) + ) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)), + Map.of(), + Map.of(), + Map.of() + ); + } + + private static Tuple indexSizes(String name, long... sizes) { + return Tuple.tuple(name, sizes); + } + private static double stdDev(double... data) { double total = 0.0; double total2 = 0.0; From 36077149776b577bd5fbe472f265765b418fb92e Mon Sep 17 00:00:00 2001 From: "ievgen.degtiarenko" Date: Mon, 6 Feb 2023 10:00:31 +0100 Subject: [PATCH 2/2] revert explanations --- docs/reference/cluster/get-desired-balance.asciidoc | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/reference/cluster/get-desired-balance.asciidoc b/docs/reference/cluster/get-desired-balance.asciidoc index 54380dc4fa490..a9db8838ee27f 100644 --- a/docs/reference/cluster/get-desired-balance.asciidoc +++ b/docs/reference/cluster/get-desired-balance.asciidoc @@ -19,9 +19,6 @@ GET /_internal/desired_balance The API returns the following result: -Where `actual_disk_usage` is computed using shard sizes from cluster info only -and `forecast_disk_usage` uses shard size forecast when available. - [source,console-result] -------------------------------------------------- {