use of com.linkedin.kafka.cruisecontrol.analyzer.OptimizationOptions in project cruise-control by linkedin.
the class TopicReplicaDistributionGoal method rebalanceByMovingReplicasOut.
private boolean rebalanceByMovingReplicasOut(Broker broker, String topic, ClusterModel clusterModel, Set<Goal> optimizedGoals, OptimizationOptions optimizationOptions) {
// Get the eligible brokers.
SortedSet<Broker> candidateBrokers = new TreeSet<>(Comparator.comparingInt((Broker b) -> b.numReplicasOfTopicInBroker(topic)).thenComparingInt(Broker::id));
candidateBrokers.addAll(_fixOfflineReplicasOnly ? clusterModel.aliveBrokers() : clusterModel.aliveBrokers().stream().filter(b -> b.numReplicasOfTopicInBroker(topic) < _balanceUpperLimitByTopic.get(topic)).collect(Collectors.toSet()));
Collection<Replica> replicasOfTopicInBroker = broker.replicasOfTopicInBroker(topic);
int numReplicasOfTopicInBroker = replicasOfTopicInBroker.size();
int numOfflineTopicReplicas = GoalUtils.retainCurrentOfflineBrokerReplicas(broker, replicasOfTopicInBroker).size();
// If the source broker is excluded for replica move, set its upper limit to 0.
int balanceUpperLimitForSourceBroker = isExcludedForReplicaMove(broker) ? 0 : _balanceUpperLimitByTopic.get(topic);
boolean wasUnableToMoveOfflineReplica = false;
for (Replica replica : replicasToMoveOut(broker, topic)) {
if (wasUnableToMoveOfflineReplica && !replica.isCurrentOffline() && numReplicasOfTopicInBroker <= balanceUpperLimitForSourceBroker) {
// Was unable to move offline replicas from the broker, and remaining replica count is under the balance limit.
return false;
}
boolean wasOffline = replica.isCurrentOffline();
Broker b = maybeApplyBalancingAction(clusterModel, replica, candidateBrokers, ActionType.INTER_BROKER_REPLICA_MOVEMENT, optimizedGoals, optimizationOptions);
// Only check if we successfully moved something.
if (b != null) {
if (wasOffline) {
numOfflineTopicReplicas--;
}
if (--numReplicasOfTopicInBroker <= (numOfflineTopicReplicas == 0 ? balanceUpperLimitForSourceBroker : 0)) {
return false;
}
// Remove and reinsert the broker so the order is correct.
candidateBrokers.remove(b);
if (b.numReplicasOfTopicInBroker(topic) < _balanceUpperLimitByTopic.get(topic) || _fixOfflineReplicasOnly) {
candidateBrokers.add(b);
}
} else if (wasOffline) {
wasUnableToMoveOfflineReplica = true;
}
}
// All the topic replicas has been moved away from the broker.
return !broker.replicasOfTopicInBroker(topic).isEmpty();
}
use of com.linkedin.kafka.cruisecontrol.analyzer.OptimizationOptions in project cruise-control by linkedin.
the class TopicReplicaDistributionGoal method rebalanceForBroker.
/**
* Rebalance the given broker without violating the constraints of the current goal and optimized goals.
*
* @param broker Broker to be balanced.
* @param clusterModel The state of the cluster.
* @param optimizedGoals Optimized goals.
* @param optimizationOptions Options to take into account during optimization.
*/
@Override
protected void rebalanceForBroker(Broker broker, ClusterModel clusterModel, Set<Goal> optimizedGoals, OptimizationOptions optimizationOptions) {
LOG.debug("Rebalancing broker {} [limits] lower: {} upper: {}.", broker.id(), _balanceLowerLimitByTopic, _balanceUpperLimitByTopic);
for (String topic : broker.topics()) {
if (isTopicExcludedFromRebalance(topic)) {
continue;
}
Collection<Replica> replicas = broker.replicasOfTopicInBroker(topic);
int numTopicReplicas = replicas.size();
int numOfflineTopicReplicas = GoalUtils.retainCurrentOfflineBrokerReplicas(broker, replicas).size();
boolean isExcludedForReplicaMove = isExcludedForReplicaMove(broker);
boolean requireLessReplicas = numOfflineTopicReplicas > 0 || numTopicReplicas > _balanceUpperLimitByTopic.get(topic) || isExcludedForReplicaMove;
boolean requireMoreReplicas = !isExcludedForReplicaMove && broker.isAlive() && numTopicReplicas - numOfflineTopicReplicas < _balanceLowerLimitByTopic.get(topic);
if (skipBrokerRebalance(broker, clusterModel, replicas, requireLessReplicas, requireMoreReplicas, numOfflineTopicReplicas > 0, optimizationOptions.onlyMoveImmigrantReplicas())) {
continue;
}
// Update broker ids over the balance limit for logging purposes.
if (requireLessReplicas && rebalanceByMovingReplicasOut(broker, topic, clusterModel, optimizedGoals, optimizationOptions)) {
_brokerIdsAboveBalanceUpperLimitByTopic.computeIfAbsent(topic, t -> new HashSet<>()).add(broker.id());
LOG.debug("Failed to sufficiently decrease replicas of topic {} in broker {} with replica movements. Replicas: {}.", topic, broker.id(), broker.numReplicasOfTopicInBroker(topic));
}
if (requireMoreReplicas && rebalanceByMovingReplicasIn(broker, topic, clusterModel, optimizedGoals, optimizationOptions)) {
_brokerIdsUnderBalanceLowerLimitByTopic.computeIfAbsent(topic, t -> new HashSet<>()).add(broker.id());
LOG.debug("Failed to sufficiently increase replicas of topic {} in broker {} with replica movements. Replicas: {}.", topic, broker.id(), broker.numReplicasOfTopicInBroker(topic));
}
if (!_brokerIdsAboveBalanceUpperLimitByTopic.getOrDefault(topic, Collections.emptySet()).contains(broker.id()) && !_brokerIdsUnderBalanceLowerLimitByTopic.getOrDefault(topic, Collections.emptySet()).contains(broker.id())) {
LOG.debug("Successfully balanced replicas of topic {} in broker {} by moving replicas. Replicas: {}", topic, broker.id(), broker.numReplicasOfTopicInBroker(topic));
}
}
}
use of com.linkedin.kafka.cruisecontrol.analyzer.OptimizationOptions in project cruise-control by linkedin.
the class ReplicaDistributionGoal method rebalanceByMovingReplicasOut.
private boolean rebalanceByMovingReplicasOut(Broker broker, ClusterModel clusterModel, Set<Goal> optimizedGoals, OptimizationOptions optimizationOptions) {
long moveStartTimeMs = System.currentTimeMillis();
// Get the eligible brokers.
SortedSet<Broker> candidateBrokers = new TreeSet<>(Comparator.comparingInt((Broker b) -> b.replicas().size()).thenComparingInt(Broker::id));
candidateBrokers.addAll(_fixOfflineReplicasOnly ? clusterModel.aliveBrokers() : clusterModel.aliveBrokers().stream().filter(b -> b.replicas().size() < _balanceUpperLimit).collect(Collectors.toSet()));
// If the source broker is excluded for replica move, set its upper limit to 0.
int balanceUpperLimitForSourceBroker = isExcludedForReplicaMove(broker) ? 0 : _balanceUpperLimit;
// Now let's move things around.
boolean wasUnableToMoveOfflineReplica = false;
boolean fastMode = optimizationOptions.fastMode();
for (Replica replica : broker.trackedSortedReplicas(replicaSortName(this, false, false)).sortedReplicas(true)) {
if (!replica.isCurrentOffline()) {
if (fastMode && remainingTimeMs(_balancingConstraint.fastModePerBrokerMoveTimeoutMs(), moveStartTimeMs) <= 0) {
LOG.debug("Move replicas out timeout in fast mode for broker {}.", broker.id());
break;
}
if (wasUnableToMoveOfflineReplica && broker.replicas().size() <= balanceUpperLimitForSourceBroker) {
// Was unable to move offline replicas from the broker, and remaining replica count is under the balance limit.
return false;
}
}
Broker b = maybeApplyBalancingAction(clusterModel, replica, candidateBrokers, ActionType.INTER_BROKER_REPLICA_MOVEMENT, optimizedGoals, optimizationOptions);
// Only check if we successfully moved something.
if (b != null) {
if (broker.replicas().size() <= (broker.currentOfflineReplicas().isEmpty() ? balanceUpperLimitForSourceBroker : 0)) {
return false;
}
// Remove and reinsert the broker so the order is correct.
candidateBrokers.remove(b);
if (b.replicas().size() < _balanceUpperLimit || _fixOfflineReplicasOnly) {
candidateBrokers.add(b);
}
} else if (replica.isCurrentOffline()) {
wasUnableToMoveOfflineReplica = true;
}
}
// All the replicas has been moved away from the broker.
return !broker.replicas().isEmpty();
}
use of com.linkedin.kafka.cruisecontrol.analyzer.OptimizationOptions in project cruise-control by linkedin.
the class MinTopicLeadersPerBrokerGoal method maybeMoveLeaderOfTopicToBroker.
private void maybeMoveLeaderOfTopicToBroker(String topicMustHaveLeaderPerBroker, Broker broker, ClusterModel clusterModel, Set<Goal> optimizedGoals, OptimizationOptions optimizationOptions) throws OptimizationFailureException {
int topicLeaderCountOnReceiverBroker = broker.numLeadersFor(topicMustHaveLeaderPerBroker);
if (topicLeaderCountOnReceiverBroker >= minTopicLeadersPerBroker(topicMustHaveLeaderPerBroker)) {
// This broker has enough leader replica(s) for the given topic
return;
}
// Try to elect follower replica(s) of the interested topic on this broker to be leader
List<Replica> followerReplicas = broker.trackedSortedReplicas(_replicaSortName).sortedReplicas(false).stream().filter(replica -> !replica.isLeader() && replica.topicPartition().topic().equals(topicMustHaveLeaderPerBroker)).collect(Collectors.toList());
for (Replica followerReplica : followerReplicas) {
Replica leader = clusterModel.partition(followerReplica.topicPartition()).leader();
if (leader.broker().numLeadersFor(topicMustHaveLeaderPerBroker) > minTopicLeadersPerBroker(topicMustHaveLeaderPerBroker)) {
if (maybeApplyBalancingAction(clusterModel, leader, Collections.singleton(broker), LEADERSHIP_MOVEMENT, optimizedGoals, optimizationOptions) != null) {
topicLeaderCountOnReceiverBroker++;
if (topicLeaderCountOnReceiverBroker >= minTopicLeadersPerBroker(topicMustHaveLeaderPerBroker)) {
// This broker satisfies this goal for the given topic
return;
}
}
}
}
// Try to move leader replica(s) of the interested topic from other brokers to this broker
PriorityQueue<Broker> brokersWithExcessiveLeaderToMove = getBrokersWithExcessiveLeaderToMove(topicMustHaveLeaderPerBroker, clusterModel, broker);
while (!brokersWithExcessiveLeaderToMove.isEmpty()) {
Broker brokerWithExcessiveLeaderToMove = brokersWithExcessiveLeaderToMove.poll();
List<Replica> leadersOfTopic = brokerWithExcessiveLeaderToMove.trackedSortedReplicas(_replicaSortName).sortedReplicas(false).stream().filter(replica -> replica.isLeader() && replica.topicPartition().topic().equals(topicMustHaveLeaderPerBroker)).collect(Collectors.toList());
boolean leaderMoved = false;
int topicLeaderCountOnGiverBroker = leadersOfTopic.size();
for (Replica leaderOfTopic : leadersOfTopic) {
Broker destinationBroker = maybeApplyBalancingAction(clusterModel, leaderOfTopic, Collections.singleton(broker), INTER_BROKER_REPLICA_MOVEMENT, optimizedGoals, optimizationOptions);
if (destinationBroker != null) {
leaderMoved = true;
// Successfully move one leader replica
break;
}
}
if (leaderMoved) {
topicLeaderCountOnReceiverBroker++;
if (topicLeaderCountOnReceiverBroker >= minTopicLeadersPerBroker(topicMustHaveLeaderPerBroker)) {
// This broker satisfies this goal for the given topic
return;
}
topicLeaderCountOnGiverBroker--;
if (topicLeaderCountOnGiverBroker > minTopicLeadersPerBroker(topicMustHaveLeaderPerBroker)) {
// Still have excessive topic leader to give
brokersWithExcessiveLeaderToMove.add(brokerWithExcessiveLeaderToMove);
}
}
}
throw new OptimizationFailureException(String.format("[%s] Cannot make broker %d have at least %d leaders from topic %s.", name(), broker.id(), minTopicLeadersPerBroker(topicMustHaveLeaderPerBroker), topicMustHaveLeaderPerBroker));
}
use of com.linkedin.kafka.cruisecontrol.analyzer.OptimizationOptions in project cruise-control by linkedin.
the class AbstractRackAwareGoal method rebalanceForBroker.
/**
* Rebalance the given broker without violating the constraints of this custom rack aware goal and optimized goals.
*
* @param broker Broker to be balanced.
* @param clusterModel The state of the cluster.
* @param optimizedGoals Optimized goals.
* @param optimizationOptions Options to take into account during optimization.
* @param throwExceptionIfCannotMove {@code true} to throw an {@link OptimizationFailureException} in case a required
* balancing action for a replica fails for all rack-aware eligible brokers, {@code false} to just log the failure and return.
* This parameter enables selected goals fail early in case the unsatisfiability of a goal can be determined early.
*/
protected void rebalanceForBroker(Broker broker, ClusterModel clusterModel, Set<Goal> optimizedGoals, OptimizationOptions optimizationOptions, boolean throwExceptionIfCannotMove) throws OptimizationFailureException {
for (Replica replica : broker.trackedSortedReplicas(replicaSortName(this, false, false)).sortedReplicas(true)) {
if (broker.isAlive() && !broker.currentOfflineReplicas().contains(replica) && shouldKeepInTheCurrentBroker(replica, clusterModel)) {
continue;
}
// The relevant rack awareness condition is violated. Move replica to an eligible broker
SortedSet<Broker> eligibleBrokers = rackAwareEligibleBrokers(replica, clusterModel);
if (maybeApplyBalancingAction(clusterModel, replica, eligibleBrokers, ActionType.INTER_BROKER_REPLICA_MOVEMENT, optimizedGoals, optimizationOptions) == null) {
if (throwExceptionIfCannotMove) {
Set<String> partitionRackIds = clusterModel.partition(replica.topicPartition()).partitionBrokers().stream().map(partitionBroker -> partitionBroker.rack().id()).collect(Collectors.toSet());
ProvisionRecommendation recommendation = new ProvisionRecommendation.Builder(ProvisionStatus.UNDER_PROVISIONED).numBrokers(1).excludedRackIds(partitionRackIds).build();
throw new OptimizationFailureException(String.format("[%s] Cannot move %s to %s.", name(), replica, eligibleBrokers), recommendation);
}
LOG.debug("Cannot move replica {} to any broker in {}", replica, eligibleBrokers);
}
}
}
Aggregations