use of com.linkedin.kafka.cruisecontrol.model.Replica in project cruise-control by linkedin.
the class ReplicaDistributionGoal method updateGoalState.
/**
* Update goal state after one round of self-healing / rebalance.
* @param clusterModel The state of the cluster.
* @param excludedTopics The topics that should be excluded from the optimization proposal.
*/
@Override
protected void updateGoalState(ClusterModel clusterModel, Set<String> excludedTopics) throws OptimizationFailureException {
// While proposals exclude the excludedTopics, the balance still considers utilization of the excludedTopic replicas.
if (!_brokerIdsAboveBalanceUpperLimit.isEmpty()) {
LOG.warn("Replicas count on broker ids:{} {} above the balance limit of {} after {}.", _brokerIdsAboveBalanceUpperLimit, (_brokerIdsAboveBalanceUpperLimit.size() > 1) ? "are" : "is", _balanceUpperLimit, (clusterModel.selfHealingEligibleReplicas().isEmpty()) ? "rebalance" : "self-healing");
_brokerIdsAboveBalanceUpperLimit.clear();
_succeeded = false;
}
if (!_brokerIdsUnderBalanceLowerLimit.isEmpty()) {
LOG.warn("Replica count on broker ids:{} {} under the balance limit of {} after {}.", _brokerIdsUnderBalanceLowerLimit, (_brokerIdsUnderBalanceLowerLimit.size() > 1) ? "are" : "is", _balanceLowerLimit, (clusterModel.selfHealingEligibleReplicas().isEmpty()) ? "rebalance" : "self-healing");
_brokerIdsUnderBalanceLowerLimit.clear();
_succeeded = false;
}
// Sanity check: No self-healing eligible replica should remain at a decommissioned broker.
for (Replica replica : clusterModel.selfHealingEligibleReplicas()) {
if (replica.broker().isAlive()) {
continue;
}
if (_selfHealingDeadBrokersOnly) {
throw new OptimizationFailureException("Self healing failed to move the replica away from decommissioned brokers.");
}
_selfHealingDeadBrokersOnly = true;
LOG.warn("Omitting resource balance limit to relocate remaining replicas from dead brokers to healthy ones.");
return;
}
// No dead broker contains replica.
_selfHealingDeadBrokersOnly = false;
// Sanity check: No self-healing eligible replica should remain at a decommissioned broker.
for (Replica replica : clusterModel.selfHealingEligibleReplicas()) {
if (!replica.broker().isAlive()) {
throw new OptimizationFailureException("Self healing failed to move the replica away from decommissioned broker.");
}
}
finish();
}
use of com.linkedin.kafka.cruisecontrol.model.Replica in project cruise-control by linkedin.
the class ReplicaDistributionTarget method moveReplicasInSourceBrokerToEligibleBrokers.
/**
* Move replicas residing in the given cluster and given healthy source broker having given set of topic partitions
* to eligible brokers. Replica movements are guaranteed not to violate the requirements of optimized goals.
*
* @param clusterModel The state of the cluster.
* @param replicasInBrokerToMove Replicas to move from the given source broker.
* @param optimizedGoals Goals that have already been optimized. The function ensures that their requirements won't
* be violated.
* @param excludedTopics The topics that should be excluded from the optimization action.
*/
boolean moveReplicasInSourceBrokerToEligibleBrokers(ClusterModel clusterModel, SortedSet<Replica> replicasInBrokerToMove, Set<Goal> optimizedGoals, Set<String> excludedTopics) {
// Get number of replicas to move from the local to a remote broker to achieve the distribution target.
int numReplicasToMove = numReplicasToMove(replicasInBrokerToMove.size());
if (numReplicasToMove == 0) {
return true;
}
for (Replica replicaToMove : replicasInBrokerToMove) {
if (excludedTopics.contains(replicaToMove.topicPartition().topic()) && replicaToMove.originalBroker().isAlive()) {
continue;
}
if (moveReplicaToEligibleBroker(clusterModel, replicaToMove, optimizedGoals)) {
numReplicasToMove--;
// Check if any more replicas need to move.
if (numReplicasToMove == 0) {
break;
}
}
}
// Update consumed warm broker credits. These credits are consumed because the broker was unable to move all
// replicas that it was supposed to move to reach a balanced state to eligible brokers.
_consumedWarmBrokerCredits += numReplicasToMove;
return numReplicasToMove == 0;
}
use of com.linkedin.kafka.cruisecontrol.model.Replica in project cruise-control by linkedin.
the class ResourceDistributionGoal method selfSatisfied.
/**
* Check if requirements of this goal are not violated if this action is applied to the given cluster state,
* false otherwise. An action is acceptable if: (1) destination broker utilization for the given resource is less
* than the source broker utilization. (2) movement is acceptable (i.e. under the broker balance limit for balanced
* resources) for already balanced resources. Already balanced resources are the ones that have gone through the
* "resource distribution" process specified in this goal.
*
* @param clusterModel The state of the cluster.
* @param action Action containing information about potential modification to the given cluster model.
* @return True if requirements of this goal are not violated if this action is applied to the given cluster state,
* false otherwise.
*/
@Override
protected boolean selfSatisfied(ClusterModel clusterModel, BalancingAction action) {
Broker destinationBroker = clusterModel.broker(action.destinationBrokerId());
Replica sourceReplica = clusterModel.broker(action.sourceBrokerId()).replica(action.topicPartition());
// must be executed.
if (!sourceReplica.broker().isAlive() && _selfHealingDeadBrokersOnly) {
return action.balancingAction() != REPLICA_SWAP;
}
switch(action.balancingAction()) {
case REPLICA_SWAP:
Replica destinationReplica = destinationBroker.replica(action.destinationTopicPartition());
double sourceUtilizationDelta = destinationReplica.load().expectedUtilizationFor(resource()) - sourceReplica.load().expectedUtilizationFor(resource());
return sourceUtilizationDelta != 0 && !isSwapViolatingLimit(sourceReplica, destinationReplica);
case REPLICA_MOVEMENT:
case LEADERSHIP_MOVEMENT:
// Check that current destination would not become more unbalanced.
return isLoadUnderBalanceUpperLimitAfterChange(sourceReplica.load(), destinationBroker, ADD) && isLoadAboveBalanceLowerLimitAfterChange(sourceReplica.load(), sourceReplica.broker(), REMOVE);
default:
throw new IllegalArgumentException("Unsupported balancing action " + action.balancingAction() + " is provided.");
}
}
use of com.linkedin.kafka.cruisecontrol.model.Replica in project cruise-control by linkedin.
the class ResourceDistributionGoal method sortedCandidateReplicas.
/**
* Get the sorted replicas in the given broker whose (1) topic is not an excluded topic AND (2) do not violate the given load
* limit in ascending or descending order. Load limit requires the replica load to be (1) above the given limit in
* descending order, (2) below the given limit in ascending order.
*
* @param broker Broker whose replicas will be considered.
* @param excludedTopics Excluded topics for which the replicas will be remove from the returned candidate replicas.
* @param loadLimit Load limit determining the lower cutoff in descending order, upper cutoff in ascending order.
* @param isAscending True if sort requested in ascending order, false otherwise.
* @return Sorted replicas in the given broker whose (1) topic is not an excluded topic AND (2) do not violate the
* given load limit in ascending or descending order.
*/
private SortedSet<Replica> sortedCandidateReplicas(Broker broker, Set<String> excludedTopics, double loadLimit, boolean isAscending) {
SortedSet<Replica> candidateReplicas = new TreeSet<>((r1, r2) -> {
int result = isAscending ? Double.compare(r1.load().expectedUtilizationFor(resource()), r2.load().expectedUtilizationFor(resource())) : Double.compare(r2.load().expectedUtilizationFor(resource()), r1.load().expectedUtilizationFor(resource()));
return result == 0 ? r1.topicPartition().toString().compareTo(r2.topicPartition().toString()) : result;
});
// If the resource is NW_OUT, candidate replicas consider only the leaders -- i.e. only the leaders have NW_OUT load,
// otherwise all replicas on broker are considered.
Set<Replica> coveredReplicas = resource() == Resource.NW_OUT ? broker.leaderReplicas() : broker.replicas();
// The given load limit determines the lower cutoff in descending order, upper cutoff in ascending order.
if (isAscending) {
candidateReplicas.addAll(coveredReplicas.stream().filter(r -> !shouldExclude(r, excludedTopics) && r.load().expectedUtilizationFor(resource()) < loadLimit).collect(Collectors.toSet()));
} else {
candidateReplicas.addAll(coveredReplicas.stream().filter(r -> !shouldExclude(r, excludedTopics) && r.load().expectedUtilizationFor(resource()) > loadLimit).collect(Collectors.toSet()));
}
return candidateReplicas;
}
use of com.linkedin.kafka.cruisecontrol.model.Replica in project cruise-control by linkedin.
the class ResourceDistributionGoal method rebalanceByMovingLoadOut.
private boolean rebalanceByMovingLoadOut(Broker broker, ClusterModel clusterModel, Set<Goal> optimizedGoals, ActionType actionType, Set<String> excludedTopics) {
// Get the eligible brokers.
SortedSet<Broker> candidateBrokers = new TreeSet<>(Comparator.comparingDouble(this::utilizationPercentage).thenComparingInt(Broker::id));
if (_selfHealingDeadBrokersOnly) {
candidateBrokers.addAll(clusterModel.healthyBrokers());
} else {
candidateBrokers.addAll(clusterModel.healthyBrokersUnderThreshold(resource(), _balanceUpperThreshold));
}
// Get the replicas to rebalance.
List<Replica> replicasToMove;
if (actionType == LEADERSHIP_MOVEMENT) {
// Only take leader replicas to move leaders.
replicasToMove = new ArrayList<>(broker.leaderReplicas());
replicasToMove.sort((r1, r2) -> Double.compare(r2.load().expectedUtilizationFor(resource()), r1.load().expectedUtilizationFor(resource())));
} else {
// Take all replicas for replica movements.
replicasToMove = broker.sortedReplicas(resource());
}
// Now let's move things around.
for (Replica replica : replicasToMove) {
if (shouldExclude(replica, excludedTopics)) {
continue;
}
// It does not make sense to move a replica without utilization from a live broker.
if (replica.load().expectedUtilizationFor(resource()) == 0.0 && broker.isAlive()) {
break;
}
// An optimization for leader movements.
SortedSet<Broker> eligibleBrokers;
if (actionType == LEADERSHIP_MOVEMENT) {
eligibleBrokers = new TreeSet<>(Comparator.comparingDouble(this::utilizationPercentage).thenComparingInt(Broker::id));
clusterModel.partition(replica.topicPartition()).followerBrokers().forEach(b -> {
if (candidateBrokers.contains(b)) {
eligibleBrokers.add(b);
}
});
} else {
eligibleBrokers = candidateBrokers;
}
Broker b = maybeApplyBalancingAction(clusterModel, replica, eligibleBrokers, actionType, optimizedGoals);
// Only check if we successfully moved something.
if (b != null) {
if (isLoadUnderBalanceUpperLimit(broker)) {
return false;
}
// Remove and reinsert the broker so the order is correct.
candidateBrokers.remove(b);
if (utilizationPercentage(b) < _balanceUpperThreshold) {
candidateBrokers.add(b);
}
}
}
// we consider it as not over limit.
return !broker.replicas().isEmpty();
}
Aggregations