[ALLOCATION] Remove primary balance factor

The `cluster.routing.allocation.balance.primary` setting has caused
a lot of confusion in the past while it has very little benefit form a
shard allocatioon point of view. Users tend to modify this value to
evently distribute primaries across the nodes which is dangerous since
a prmiary flag on it's own can trigger relocations. The primary flag for a shard
is should not have any impact on cluster performance unless the high level feature
suffereing from primary hotspots is buggy. Yet, this setting was intended to be a
tie-breaker which is not necessary anymore since the algorithm is deterministic.

This commit removes this setting entriely.
This commit is contained in:
Simon Willnauer 2015-01-06 14:02:42 +01:00
parent 8948b489d6
commit 236e2491b4
4 changed files with 15 additions and 107 deletions

View File

@ -82,11 +82,6 @@ due to forced awareness or allocation filtering.
tendency to equalize the number of shards per index across all nodes in
the cluster.
`cluster.routing.allocation.balance.primary`::
Defines a weight factor for the number of primaries of a specific index
allocated on a node (float). `0.00f`. Raising this raises the tendency
to equalize the number of primary shards across all nodes in the cluster. deprecated[1.3.8]
`cluster.routing.allocation.balance.threshold`::
Minimal optimization value of operations that should be performed (non
negative float). Defaults to `1.0f`. Raising this will cause the cluster

View File

@ -55,8 +55,6 @@ import static org.elasticsearch.cluster.routing.ShardRoutingState.RELOCATING;
* for shards allocated on a {@link RoutingNode}</li>
* <li><code>cluster.routing.allocation.balance.index</code> - The <b>index balance</b> defines a factor to the number
* of {@link org.elasticsearch.cluster.routing.ShardRouting}s per index allocated on a specific node</li>
* <li><code>cluster.routing.allocation.balance.primary</code> - the <b>primary balance</b> defines a weight factor for
* the number of primaries of a specific index allocated on a node</li>
* <li><code>cluster.routing.allocation.balance.threshold</code> - A <b>threshold</b> to set the minimal optimization
* value of operations that should be performed</li>
* </ul>
@ -69,37 +67,25 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
public static final String SETTING_THRESHOLD = "cluster.routing.allocation.balance.threshold";
public static final String SETTING_INDEX_BALANCE_FACTOR = "cluster.routing.allocation.balance.index";
public static final String SETTING_SHARD_BALANCE_FACTOR = "cluster.routing.allocation.balance.shard";
public static final String SETTING_PRIMARY_BALANCE_FACTOR = "cluster.routing.allocation.balance.primary";
private static final float DEFAULT_INDEX_BALANCE_FACTOR = 0.55f;
private static final float DEFAULT_SHARD_BALANCE_FACTOR = 0.45f;
/**
* The primary balance factor was introduces as a tie-breaker to make the initial allocation
* more deterministic. Yet other mechanism have been added ensure that the algorithm is more deterministic such that this
* setting is not needed anymore. Additionally, this setting was abused to balance shards based on their primary flag which can lead
* to unexpected behavior when allocating or balancing the shards.
*
* @deprecated the threshold primary balance factor is deprecated and should not be used.
*/
@Deprecated
private static final float DEFAULT_PRIMARY_BALANCE_FACTOR = 0.0f;
class ApplySettings implements NodeSettingsService.Listener {
@Override
public void onRefreshSettings(Settings settings) {
final float indexBalance = settings.getAsFloat(SETTING_INDEX_BALANCE_FACTOR, weightFunction.indexBalance);
final float shardBalance = settings.getAsFloat(SETTING_SHARD_BALANCE_FACTOR, weightFunction.shardBalance);
final float primaryBalance = settings.getAsFloat(SETTING_PRIMARY_BALANCE_FACTOR, weightFunction.primaryBalance);
float threshold = settings.getAsFloat(SETTING_THRESHOLD, BalancedShardsAllocator.this.threshold);
if (threshold <= 0.0f) {
throw new ElasticsearchIllegalArgumentException("threshold must be greater than 0.0f but was: " + threshold);
}
BalancedShardsAllocator.this.threshold = threshold;
BalancedShardsAllocator.this.weightFunction = new WeightFunction(indexBalance, shardBalance, primaryBalance);
BalancedShardsAllocator.this.weightFunction = new WeightFunction(indexBalance, shardBalance);
}
}
private volatile WeightFunction weightFunction = new WeightFunction(DEFAULT_INDEX_BALANCE_FACTOR, DEFAULT_SHARD_BALANCE_FACTOR, DEFAULT_PRIMARY_BALANCE_FACTOR);
private volatile WeightFunction weightFunction = new WeightFunction(DEFAULT_INDEX_BALANCE_FACTOR, DEFAULT_SHARD_BALANCE_FACTOR);
private volatile float threshold = 1.0f;
@ -153,13 +139,6 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
return weightFunction.indexBalance;
}
/**
* Returns the primary related weight factor.
*/
public float getPrimaryBalance() {
return weightFunction.primaryBalance;
}
/**
* Returns the shard related weight factor.
*/
@ -174,11 +153,10 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
* <ul>
* <li><code>index balance</code> - balance property over shards per index</li>
* <li><code>shard balance</code> - balance property over shards per cluster</li>
* <li><code>primary balance</code> - balance property over primaries per cluster</li>
* </ul>
* <p>
* Each of these properties are expressed as factor such that the properties factor defines the relative importance of the property for the
* weight function. For example if the weight function should calculate the weights only based on a global (shard) balance the index and primary balance
* weight function. For example if the weight function should calculate the weights only based on a global (shard) balance the index balance
* can be set to <tt>0.0</tt> and will in turn have no effect on the distribution.
* </p>
* The weight per index is calculated based on the following formula:
@ -189,35 +167,31 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
* <li>
* <code>weight<sub>node</sub>(node, index) = shardBalance * (node.numShards() - avgShardsPerNode)</code>
* </li>
* <li>
* <code>weight<sub>primary</sub>(node, index) = primaryBalance * (node.numPrimaries() - avgPrimariesPerNode)</code>
* </li>
* </ul>
* <code>weight(node, index) = weight<sub>index</sub>(node, index) + weight<sub>node</sub>(node, index) + weight<sub>primary</sub>(node, index)</code>
* <code>weight(node, index) = weight<sub>index</sub>(node, index) + weight<sub>node</sub>(node, index)</code>
*/
public static class WeightFunction {
private final float indexBalance;
private final float shardBalance;
private final float primaryBalance;
private final float[] theta;
public WeightFunction(float indexBalance, float shardBalance, float primaryBalance) {
float sum = indexBalance + shardBalance + primaryBalance;
public WeightFunction(float indexBalance, float shardBalance) {
float sum = indexBalance + shardBalance;
if (sum <= 0.0f) {
throw new ElasticsearchIllegalArgumentException("Balance factors must sum to a value > 0 but was: " + sum);
}
theta = new float[]{shardBalance / sum, indexBalance / sum, primaryBalance / sum};
theta = new float[]{shardBalance / sum, indexBalance / sum};
this.indexBalance = indexBalance;
this.shardBalance = shardBalance;
this.primaryBalance = primaryBalance;
}
public float weight(Operation operation, Balancer balancer, ModelNode node, String index) {
final float weightShard = node.numShards() - balancer.avgShardsPerNode();
final float weightIndex = node.numShards(index) - balancer.avgShardsPerNode(index);
final float weightPrimary = node.numPrimaries() - balancer.avgPrimariesPerNode();
return theta[0] * weightShard + theta[1] * weightIndex + theta[2] * weightPrimary;
final float weightShard = (node.numShards() - balancer.avgShardsPerNode());
final float weightIndex = (node.numShards(index) - balancer.avgShardsPerNode(index));
assert theta != null;
return theta[0] * weightShard + theta[1] * weightIndex;
}
}
@ -245,7 +219,6 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
* A {@link Balancer}
*/
public static class Balancer {
private final ESLogger logger;
private final Map<String, ModelNode> nodes = new HashMap<>();
private final HashSet<String> indices = new HashSet<>();
@ -304,12 +277,6 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
return ((float) metaData.numberOfShards()) / nodes.size();
}
/**
* Returns the average of primaries per node for the given index
*/
public float avgPrimariesPerNode(String index) {
return ((float) metaData.index(index).numberOfShards()) / nodes.size();
}
/**
* Returns a new {@link NodeSorter} that sorts the nodes based on their
@ -380,7 +347,7 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
final ModelNode maxNode = modelNodes[highIdx];
advance_range:
if (maxNode.numShards(index) > 0) {
float delta = absDelta(weights[lowIdx], weights[highIdx]);
final float delta = absDelta(weights[lowIdx], weights[highIdx]);
if (lessThan(delta, threshold)) {
if (lowIdx > 0 && highIdx-1 > 0 // is there a chance for a higher delta?
&& (absDelta(weights[0], weights[highIdx-1]) > threshold) // check if we need to break at all
@ -845,7 +812,6 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
private final Map<String, ModelIndex> indices = new HashMap<>();
/* cached stats - invalidated on add/remove and lazily calculated */
private int numShards = -1;
private int numPrimaries = -1;
public ModelNode(String id) {
this.id = id;
@ -875,22 +841,6 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
return index == null ? 0 : index.numShards();
}
public int numPrimaries(String idx) {
ModelIndex index = indices.get(idx);
return index == null ? 0 : index.numPrimaries();
}
public int numPrimaries() {
if (numPrimaries == -1) {
int sum = 0;
for (ModelIndex index : indices.values()) {
sum += index.numPrimaries();
}
numPrimaries = sum;
}
return numPrimaries;
}
public Collection<MutableShardRouting> shards() {
Collection<MutableShardRouting> result = new ArrayList<>();
for (ModelIndex index : indices.values()) {
@ -908,7 +858,7 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
}
public void addShard(MutableShardRouting shard, Decision decision) {
numPrimaries = numShards = -1;
numShards = -1;
ModelIndex index = indices.get(shard.index());
if (index == null) {
index = new ModelIndex(shard.index());
@ -918,7 +868,7 @@ public class BalancedShardsAllocator extends AbstractComponent implements Shards
}
public Decision removeShard(MutableShardRouting shard) {
numPrimaries = numShards = -1;
numShards = -1;
ModelIndex index = indices.get(shard.index());
Decision removed = null;
if (index != null) {

View File

@ -47,7 +47,6 @@ public class ClusterDynamicSettingsModule extends AbstractModule {
clusterDynamicSettings.addDynamicSetting(AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTES);
clusterDynamicSettings.addDynamicSetting(AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_FORCE_GROUP + "*");
clusterDynamicSettings.addDynamicSetting(BalancedShardsAllocator.SETTING_INDEX_BALANCE_FACTOR, Validator.FLOAT);
clusterDynamicSettings.addDynamicSetting(BalancedShardsAllocator.SETTING_PRIMARY_BALANCE_FACTOR, Validator.FLOAT);
clusterDynamicSettings.addDynamicSetting(BalancedShardsAllocator.SETTING_SHARD_BALANCE_FACTOR, Validator.FLOAT);
clusterDynamicSettings.addDynamicSetting(BalancedShardsAllocator.SETTING_THRESHOLD, Validator.NON_NEGATIVE_FLOAT);
clusterDynamicSettings.addDynamicSetting(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE,

View File

@ -59,14 +59,12 @@ public class BalanceConfigurationTests extends ElasticsearchAllocationTestCase {
/* Tests balance over indices only */
final float indexBalance = 1.0f;
final float replicaBalance = 0.0f;
final float primaryBalance = 0.0f;
final float balanceTreshold = 1.0f;
ImmutableSettings.Builder settings = settingsBuilder();
settings.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE, ClusterRebalanceAllocationDecider.ClusterRebalanceType.ALWAYS.toString());
settings.put(BalancedShardsAllocator.SETTING_INDEX_BALANCE_FACTOR, indexBalance);
settings.put(BalancedShardsAllocator.SETTING_SHARD_BALANCE_FACTOR, replicaBalance);
settings.put(BalancedShardsAllocator.SETTING_PRIMARY_BALANCE_FACTOR, primaryBalance);
settings.put(BalancedShardsAllocator.SETTING_THRESHOLD, balanceTreshold);
AllocationService strategy = createAllocationService(settings.build());
@ -87,14 +85,12 @@ public class BalanceConfigurationTests extends ElasticsearchAllocationTestCase {
/* Tests balance over replicas only */
final float indexBalance = 0.0f;
final float replicaBalance = 1.0f;
final float primaryBalance = 0.0f;
final float balanceTreshold = 1.0f;
ImmutableSettings.Builder settings = settingsBuilder();
settings.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE, ClusterRebalanceAllocationDecider.ClusterRebalanceType.ALWAYS.toString());
settings.put(BalancedShardsAllocator.SETTING_INDEX_BALANCE_FACTOR, indexBalance);
settings.put(BalancedShardsAllocator.SETTING_SHARD_BALANCE_FACTOR, replicaBalance);
settings.put(BalancedShardsAllocator.SETTING_PRIMARY_BALANCE_FACTOR, primaryBalance);
settings.put(BalancedShardsAllocator.SETTING_THRESHOLD, balanceTreshold);
AllocationService strategy = createAllocationService(settings.build());
@ -110,33 +106,6 @@ public class BalanceConfigurationTests extends ElasticsearchAllocationTestCase {
}
@Test
public void testPrimaryBalance() {
/* Tests balance over primaries only */
final float indexBalance = 0.0f;
final float replicaBalance = 0.0f;
final float primaryBalance = 1.0f;
final float balanceTreshold = 1.0f;
ImmutableSettings.Builder settings = settingsBuilder();
settings.put(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE, ClusterRebalanceAllocationDecider.ClusterRebalanceType.ALWAYS.toString());
settings.put(BalancedShardsAllocator.SETTING_INDEX_BALANCE_FACTOR, indexBalance);
settings.put(BalancedShardsAllocator.SETTING_SHARD_BALANCE_FACTOR, replicaBalance);
settings.put(BalancedShardsAllocator.SETTING_PRIMARY_BALANCE_FACTOR, primaryBalance);
settings.put(BalancedShardsAllocator.SETTING_THRESHOLD, balanceTreshold);
AllocationService strategy = createAllocationService(settings.build());
ClusterState clusterstate = initCluster(strategy);
assertPrimaryBalance(logger, clusterstate.getRoutingNodes(), numberOfNodes, numberOfIndices, numberOfReplicas, numberOfShards, balanceTreshold);
clusterstate = addNode(clusterstate, strategy);
assertPrimaryBalance(logger, clusterstate.getRoutingNodes(), numberOfNodes + 1, numberOfIndices, numberOfReplicas, numberOfShards, balanceTreshold);
clusterstate = removeNodes(clusterstate, strategy);
assertPrimaryBalance(logger, clusterstate.getRoutingNodes(), numberOfNodes + 1 - (numberOfNodes + 1) / 2, numberOfIndices, numberOfReplicas, numberOfShards, balanceTreshold);
}
private ClusterState initCluster(AllocationService strategy) {
MetaData.Builder metaDataBuilder = MetaData.builder();
RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
@ -311,7 +280,6 @@ public class BalanceConfigurationTests extends ElasticsearchAllocationTestCase {
ImmutableSettings.Builder settings = settingsBuilder();
settings.put(BalancedShardsAllocator.SETTING_INDEX_BALANCE_FACTOR, 0.2);
settings.put(BalancedShardsAllocator.SETTING_SHARD_BALANCE_FACTOR, 0.3);
settings.put(BalancedShardsAllocator.SETTING_PRIMARY_BALANCE_FACTOR, 0.5);
settings.put(BalancedShardsAllocator.SETTING_THRESHOLD, 2.0);
final NodeSettingsService.Listener[] listeners = new NodeSettingsService.Listener[1];
NodeSettingsService service = new NodeSettingsService(settingsBuilder().build()) {
@ -326,7 +294,6 @@ public class BalanceConfigurationTests extends ElasticsearchAllocationTestCase {
BalancedShardsAllocator allocator = new BalancedShardsAllocator(settings.build(), service);
assertThat(allocator.getIndexBalance(), Matchers.equalTo(0.2f));
assertThat(allocator.getShardBalance(), Matchers.equalTo(0.3f));
assertThat(allocator.getPrimaryBalance(), Matchers.equalTo(0.5f));
assertThat(allocator.getThreshold(), Matchers.equalTo(2.0f));
settings = settingsBuilder();
@ -334,18 +301,15 @@ public class BalanceConfigurationTests extends ElasticsearchAllocationTestCase {
listeners[0].onRefreshSettings(settings.build());
assertThat(allocator.getIndexBalance(), Matchers.equalTo(0.2f));
assertThat(allocator.getShardBalance(), Matchers.equalTo(0.3f));
assertThat(allocator.getPrimaryBalance(), Matchers.equalTo(0.5f));
assertThat(allocator.getThreshold(), Matchers.equalTo(2.0f));
settings = settingsBuilder();
settings.put(BalancedShardsAllocator.SETTING_INDEX_BALANCE_FACTOR, 0.5);
settings.put(BalancedShardsAllocator.SETTING_SHARD_BALANCE_FACTOR, 0.1);
settings.put(BalancedShardsAllocator.SETTING_PRIMARY_BALANCE_FACTOR, 0.4);
settings.put(BalancedShardsAllocator.SETTING_THRESHOLD, 3.0);
listeners[0].onRefreshSettings(settings.build());
assertThat(allocator.getIndexBalance(), Matchers.equalTo(0.5f));
assertThat(allocator.getShardBalance(), Matchers.equalTo(0.1f));
assertThat(allocator.getPrimaryBalance(), Matchers.equalTo(0.4f));
assertThat(allocator.getThreshold(), Matchers.equalTo(3.0f));
}