Search in sources :

Example 16 with ReplicaPlacementInfo

use of com.linkedin.kafka.cruisecontrol.model.ReplicaPlacementInfo in project cruise-control by linkedin.

the class ExcludedBrokersForLeadershipTest method test.

@Test
public void test() throws Exception {
    // Before the optimization, goals are expected to be undecided wrt their provision status.
    assertEquals(ProvisionStatus.UNDECIDED, _goal.provisionResponse().status());
    if (_exceptionClass == null) {
        Map<TopicPartition, List<ReplicaPlacementInfo>> initReplicaDistribution = _clusterModel.getReplicaDistribution();
        Map<TopicPartition, ReplicaPlacementInfo> initLeaderDistribution = _clusterModel.getLeaderDistribution();
        Set<Integer> excludedBrokersForLeadership = _optimizationOptions.excludedBrokersForLeadership();
        if (_expectedToOptimize) {
            assertTrue("Failed to optimize " + _goal.name() + " with excluded brokers for leadership.", _goal.optimize(_clusterModel, Collections.emptySet(), _optimizationOptions));
        } else {
            assertFalse("Optimized " + _goal.name() + " with excluded brokers for leadership " + excludedBrokersForLeadership, _goal.optimize(_clusterModel, Collections.emptySet(), _optimizationOptions));
        }
        // The cluster cannot be underprovisioned, because _exceptionClass was null.
        assertNotEquals(ProvisionStatus.UNDER_PROVISIONED, _goal.provisionResponse().status());
        // Generated proposals cannot move leadership to the excluded brokers for leadership.
        if (!excludedBrokersForLeadership.isEmpty()) {
            Set<ExecutionProposal> goalProposals = AnalyzerUtils.getDiff(initReplicaDistribution, initLeaderDistribution, _clusterModel);
            for (ExecutionProposal proposal : goalProposals) {
                if (proposal.hasLeaderAction() && excludedBrokersForLeadership.contains(proposal.newLeader().brokerId()) && _clusterModel.broker(proposal.oldLeader().brokerId()).isAlive()) {
                    fail(String.format("Leadership move in %s from an online replica to an excluded broker for leadership %s.", proposal, excludedBrokersForLeadership));
                }
            }
        }
    } else {
        _expected.expect(_exceptionClass);
        assertTrue("Failed to optimize with excluded brokers for leadership.", _goal.optimize(_clusterModel, Collections.emptySet(), _optimizationOptions));
        assertEquals(ProvisionStatus.UNDER_PROVISIONED, _goal.provisionResponse().status());
    }
}
Also used : ExecutionProposal(com.linkedin.kafka.cruisecontrol.executor.ExecutionProposal) TopicPartition(org.apache.kafka.common.TopicPartition) List(java.util.List) ArrayList(java.util.ArrayList) ReplicaPlacementInfo(com.linkedin.kafka.cruisecontrol.model.ReplicaPlacementInfo) Test(org.junit.Test)

Example 17 with ReplicaPlacementInfo

use of com.linkedin.kafka.cruisecontrol.model.ReplicaPlacementInfo in project cruise-control by linkedin.

the class ExcludedTopicsTest method test.

@Test
public void test() throws Exception {
    // Before the optimization, goals are expected to be undecided wrt their provision status.
    assertEquals(ProvisionStatus.UNDECIDED, _goal.provisionResponse().status());
    if (_exceptionClass == null) {
        Map<TopicPartition, List<ReplicaPlacementInfo>> initReplicaDistribution = _clusterModel.getReplicaDistribution();
        Map<TopicPartition, ReplicaPlacementInfo> initLeaderDistribution = _clusterModel.getLeaderDistribution();
        Set<String> excludedTopics = _optimizationOptions.excludedTopics();
        if (_expectedToOptimize) {
            assertTrue("Excluded Topics Test failed to optimize " + _goal.name() + " with excluded topics " + excludedTopics, _goal.optimize(_clusterModel, Collections.emptySet(), _optimizationOptions));
        } else {
            assertFalse("Excluded Topics Test optimized " + _goal.name() + " with excluded topics " + excludedTopics, _goal.optimize(_clusterModel, Collections.emptySet(), _optimizationOptions));
        }
        // The cluster cannot be underprovisioned, because _exceptionClass was null.
        assertNotEquals(ProvisionStatus.UNDER_PROVISIONED, _goal.provisionResponse().status());
        // Generated proposals cannot have the excluded topic.
        if (!excludedTopics.isEmpty()) {
            Set<ExecutionProposal> goalProposals = AnalyzerUtils.getDiff(initReplicaDistribution, initLeaderDistribution, _clusterModel);
            assertEquals(_expectedToGenerateProposals, !goalProposals.isEmpty());
            for (ExecutionProposal proposal : goalProposals) {
                if (excludedTopics.contains(proposal.topic())) {
                    for (ReplicaPlacementInfo r : proposal.replicasToRemove()) {
                        if (_clusterModel.broker(r.brokerId()).isAlive()) {
                            fail(String.format("Proposal %s contains excluded topic %s, but the broker %d is still alive.", proposal, proposal.topic(), r.brokerId()));
                        }
                    }
                }
            }
        }
    } else {
        _expected.expect(_exceptionClass);
        assertTrue("Excluded Topics Test failed to optimize with excluded topics.", _goal.optimize(_clusterModel, Collections.emptySet(), _optimizationOptions));
        assertEquals(ProvisionStatus.UNDER_PROVISIONED, _goal.provisionResponse().status());
    }
}
Also used : ExecutionProposal(com.linkedin.kafka.cruisecontrol.executor.ExecutionProposal) TopicPartition(org.apache.kafka.common.TopicPartition) List(java.util.List) ArrayList(java.util.ArrayList) ReplicaPlacementInfo(com.linkedin.kafka.cruisecontrol.model.ReplicaPlacementInfo) Test(org.junit.Test)

Example 18 with ReplicaPlacementInfo

use of com.linkedin.kafka.cruisecontrol.model.ReplicaPlacementInfo in project cruise-control by linkedin.

the class AnalyzerUtils method hasDiff.

/**
 * Get whether there is any diff represented by a set of balancing proposals to move from the initial to final distribution.
 *
 * @param initialReplicaDistribution Initial distribution of replicas over the cluster.
 * @param initialLeaderDistribution Initial distribution of the leaders.
 * @param optimizedClusterModel The optimized cluster model.
 * @return The diff represented by the set of balancing proposals to move from initial to final distribution.
 */
public static boolean hasDiff(Map<TopicPartition, List<ReplicaPlacementInfo>> initialReplicaDistribution, Map<TopicPartition, ReplicaPlacementInfo> initialLeaderDistribution, ClusterModel optimizedClusterModel) {
    Map<TopicPartition, List<ReplicaPlacementInfo>> finalReplicaDistribution = optimizedClusterModel.getReplicaDistribution();
    sanityCheckReplicaDistribution(initialReplicaDistribution, finalReplicaDistribution, false);
    boolean hasDiff = false;
    for (Map.Entry<TopicPartition, List<ReplicaPlacementInfo>> entry : initialReplicaDistribution.entrySet()) {
        TopicPartition tp = entry.getKey();
        List<ReplicaPlacementInfo> initialReplicas = entry.getValue();
        List<ReplicaPlacementInfo> finalReplicas = finalReplicaDistribution.get(tp);
        if (!finalReplicas.equals(initialReplicas)) {
            hasDiff = true;
            break;
        } else {
            Replica finalLeader = optimizedClusterModel.partition(tp).leader();
            ReplicaPlacementInfo finalLeaderPlacementInfo = new ReplicaPlacementInfo(finalLeader.broker().id(), finalLeader.disk() == null ? null : finalLeader.disk().logDir());
            if (!initialLeaderDistribution.get(tp).equals(finalLeaderPlacementInfo)) {
                hasDiff = true;
                break;
            }
        // The partition has no change.
        }
    }
    return hasDiff;
}
Also used : TopicPartition(org.apache.kafka.common.TopicPartition) List(java.util.List) TreeMap(java.util.TreeMap) Map(java.util.Map) Replica(com.linkedin.kafka.cruisecontrol.model.Replica) ReplicaPlacementInfo(com.linkedin.kafka.cruisecontrol.model.ReplicaPlacementInfo)

Example 19 with ReplicaPlacementInfo

use of com.linkedin.kafka.cruisecontrol.model.ReplicaPlacementInfo in project cruise-control by linkedin.

the class AnalyzerUtils method getDiff.

/**
 * Get the diff represented by the set of balancing proposals to move from initial to final distribution.
 *
 * @param initialReplicaDistribution Initial distribution of replicas over the cluster.
 * @param initialLeaderDistribution Initial distribution of the leaders.
 * @param optimizedClusterModel The optimized cluster model.
 * @param skipReplicationFactorChangeCheck Whether skip sanity check of topic partition's replication factor change before
 *                                         and after optimization.
 * @return The diff represented by the set of balancing proposals to move from initial to final distribution.
 */
public static Set<ExecutionProposal> getDiff(Map<TopicPartition, List<ReplicaPlacementInfo>> initialReplicaDistribution, Map<TopicPartition, ReplicaPlacementInfo> initialLeaderDistribution, ClusterModel optimizedClusterModel, boolean skipReplicationFactorChangeCheck) {
    Map<TopicPartition, List<ReplicaPlacementInfo>> finalReplicaDistribution = optimizedClusterModel.getReplicaDistribution();
    sanityCheckReplicaDistribution(initialReplicaDistribution, finalReplicaDistribution, skipReplicationFactorChangeCheck);
    // Generate a set of execution proposals to represent the diff between initial and final distribution.
    Set<ExecutionProposal> diff = new HashSet<>();
    for (Map.Entry<TopicPartition, List<ReplicaPlacementInfo>> entry : initialReplicaDistribution.entrySet()) {
        TopicPartition tp = entry.getKey();
        List<ReplicaPlacementInfo> initialReplicas = entry.getValue();
        List<ReplicaPlacementInfo> finalReplicas = finalReplicaDistribution.get(tp);
        Replica finalLeader = optimizedClusterModel.partition(tp).leader();
        ReplicaPlacementInfo finalLeaderPlacementInfo = new ReplicaPlacementInfo(finalLeader.broker().id(), finalLeader.disk() == null ? null : finalLeader.disk().logDir());
        // The partition has no change.
        if (finalReplicas.equals(initialReplicas) && initialLeaderDistribution.get(tp).equals(finalLeaderPlacementInfo)) {
            continue;
        }
        // We need to adjust the final broker list order to ensure the final leader is the first replica.
        if (finalLeaderPlacementInfo != finalReplicas.get(0)) {
            int leaderPos = finalReplicas.indexOf(finalLeaderPlacementInfo);
            finalReplicas.set(leaderPos, finalReplicas.get(0));
            finalReplicas.set(0, finalLeaderPlacementInfo);
        }
        double partitionSize = optimizedClusterModel.partition(tp).leader().load().expectedUtilizationFor(Resource.DISK);
        diff.add(new ExecutionProposal(tp, (int) partitionSize, initialLeaderDistribution.get(tp), initialReplicas, finalReplicas));
    }
    return diff;
}
Also used : Replica(com.linkedin.kafka.cruisecontrol.model.Replica) ExecutionProposal(com.linkedin.kafka.cruisecontrol.executor.ExecutionProposal) TopicPartition(org.apache.kafka.common.TopicPartition) List(java.util.List) TreeMap(java.util.TreeMap) Map(java.util.Map) ReplicaPlacementInfo(com.linkedin.kafka.cruisecontrol.model.ReplicaPlacementInfo) HashSet(java.util.HashSet)

Example 20 with ReplicaPlacementInfo

use of com.linkedin.kafka.cruisecontrol.model.ReplicaPlacementInfo in project cruise-control by linkedin.

the class GoalOptimizer method optimizations.

/**
 * Depending the existence of dead/broken/decommissioned brokers in the given cluster:
 * (1) Re-balance: Generates proposals to update the state of the cluster to achieve a final balanced state.
 * (2) Self-healing: Generates proposals to move replicas away from decommissioned brokers and broken disks.
 * Returns a map from goal names to stats. Initial stats are returned under goal name "init".
 *
 * Assumptions:
 * <ul>
 *   <li>The cluster model cannot be null.</li>
 *   <li>At least one goal has been provided in goalsByPriority.</li>
 *   <li>There is at least one alive broker in the cluster.</li>
 * </ul>
 *
 * @param clusterModel The state of the cluster over which the balancing proposal will be applied. Function execution
 *                     updates the cluster state with balancing proposals. If the cluster model is specified, the
 *                     cached proposal will be ignored.
 * @param goalsByPriority the goals ordered by priority.
 * @param operationProgress to report the job progress.
 * @param initReplicaDistributionForProposalGeneration The initial replica distribution of the cluster. This is only
 *                                                     needed if the passed in clusterModel is not the original cluster
 *                                                     model so that initial replica distribution can not be deducted
 *                                                     from that cluster model, otherwise it is null. One case explicitly
 *                                                     specifying initial replica distribution needed is to increase/decrease
 *                                                     specific topic partition's replication factor, in this case some
 *                                                     replicas are tentatively deleted/added in cluster model before
 *                                                     passing it in to generate proposals.
 * @param optimizationOptions Optimization options.
 * @return Results of optimization containing the proposals and stats.
 */
public OptimizerResult optimizations(ClusterModel clusterModel, List<Goal> goalsByPriority, OperationProgress operationProgress, Map<TopicPartition, List<ReplicaPlacementInfo>> initReplicaDistributionForProposalGeneration, OptimizationOptions optimizationOptions) throws KafkaCruiseControlException {
    LOG.trace("Cluster before optimization is {}", clusterModel);
    BrokerStats brokerStatsBeforeOptimization = clusterModel.brokerStats(null);
    Map<TopicPartition, List<ReplicaPlacementInfo>> initReplicaDistribution = clusterModel.getReplicaDistribution();
    Map<TopicPartition, ReplicaPlacementInfo> initLeaderDistribution = clusterModel.getLeaderDistribution();
    boolean isSelfHealing = !clusterModel.selfHealingEligibleReplicas().isEmpty();
    // Set of balancing proposals that will be applied to the given cluster state to satisfy goals (leadership
    // transfer AFTER partition transfer.)
    Set<Goal> optimizedGoals = new HashSet<>();
    Set<String> violatedGoalNamesBeforeOptimization = new HashSet<>();
    Set<String> violatedGoalNamesAfterOptimization = new HashSet<>();
    LinkedHashMap<Goal, ClusterModelStats> statsByGoalPriority = new LinkedHashMap<>(goalsByPriority.size());
    Map<TopicPartition, List<ReplicaPlacementInfo>> preOptimizedReplicaDistribution = null;
    Map<TopicPartition, ReplicaPlacementInfo> preOptimizedLeaderDistribution = null;
    ProvisionResponse provisionResponse = new ProvisionResponse(ProvisionStatus.UNDECIDED);
    Map<String, Duration> optimizationDurationByGoal = new HashMap<>();
    for (Goal goal : goalsByPriority) {
        preOptimizedReplicaDistribution = preOptimizedReplicaDistribution == null ? initReplicaDistribution : clusterModel.getReplicaDistribution();
        preOptimizedLeaderDistribution = preOptimizedLeaderDistribution == null ? initLeaderDistribution : clusterModel.getLeaderDistribution();
        OptimizationForGoal step = new OptimizationForGoal(goal.name());
        operationProgress.addStep(step);
        LOG.debug("Optimizing goal {}", goal.name());
        long startTimeMs = _time.milliseconds();
        boolean succeeded = goal.optimize(clusterModel, optimizedGoals, optimizationOptions);
        optimizedGoals.add(goal);
        statsByGoalPriority.put(goal, clusterModel.getClusterStats(_balancingConstraint, optimizationOptions));
        optimizationDurationByGoal.put(goal.name(), Duration.ofMillis(_time.milliseconds() - startTimeMs));
        boolean hasDiff = AnalyzerUtils.hasDiff(preOptimizedReplicaDistribution, preOptimizedLeaderDistribution, clusterModel);
        if (hasDiff || !succeeded) {
            violatedGoalNamesBeforeOptimization.add(goal.name());
        }
        if (!succeeded) {
            violatedGoalNamesAfterOptimization.add(goal.name());
        }
        LOG.debug("[{}/{}] Generated {} proposals for {}{}.", optimizedGoals.size(), _goalsByPriority.size(), hasDiff ? "some" : "no", isSelfHealing ? "self-healing " : "", goal.name());
        step.done();
        if (LOG.isDebugEnabled()) {
            LOG.debug("Broker level stats after optimization: {}", clusterModel.brokerStats(null));
        }
        provisionResponse.aggregate(goal.provisionResponse());
    }
    // Broker level stats in the final cluster state.
    if (LOG.isTraceEnabled()) {
        LOG.trace("Broker level stats after optimization: {}%n", clusterModel.brokerStats(null));
    }
    // Skip replication factor change check here since in above iteration we already check for each goal it does not change
    // any partition's replication factor.
    Set<ExecutionProposal> proposals = AnalyzerUtils.getDiff(initReplicaDistributionForProposalGeneration != null ? initReplicaDistributionForProposalGeneration : initReplicaDistribution, initLeaderDistribution, clusterModel, true);
    return new OptimizerResult(statsByGoalPriority, violatedGoalNamesBeforeOptimization, violatedGoalNamesAfterOptimization, proposals, brokerStatsBeforeOptimization, clusterModel, optimizationOptions, balancednessCostByGoal(goalsByPriority, _priorityWeight, _strictnessWeight), optimizationDurationByGoal, provisionResponse);
}
Also used : OptimizationForGoal(com.linkedin.kafka.cruisecontrol.async.progress.OptimizationForGoal) ClusterModelStats(com.linkedin.kafka.cruisecontrol.model.ClusterModelStats) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) Duration(java.time.Duration) LinkedHashMap(java.util.LinkedHashMap) Goal(com.linkedin.kafka.cruisecontrol.analyzer.goals.Goal) OptimizationForGoal(com.linkedin.kafka.cruisecontrol.async.progress.OptimizationForGoal) KafkaCruiseControlUtils.balancednessCostByGoal(com.linkedin.kafka.cruisecontrol.KafkaCruiseControlUtils.balancednessCostByGoal) ExecutionProposal(com.linkedin.kafka.cruisecontrol.executor.ExecutionProposal) TopicPartition(org.apache.kafka.common.TopicPartition) List(java.util.List) BrokerStats(com.linkedin.kafka.cruisecontrol.servlet.response.stats.BrokerStats) ReplicaPlacementInfo(com.linkedin.kafka.cruisecontrol.model.ReplicaPlacementInfo) HashSet(java.util.HashSet)

Aggregations

ReplicaPlacementInfo (com.linkedin.kafka.cruisecontrol.model.ReplicaPlacementInfo)36 TopicPartition (org.apache.kafka.common.TopicPartition)26 Test (org.junit.Test)23 List (java.util.List)12 ArrayList (java.util.ArrayList)11 ExecutionProposal (com.linkedin.kafka.cruisecontrol.executor.ExecutionProposal)8 KafkaZkClient (kafka.zk.KafkaZkClient)8 HashMap (java.util.HashMap)6 AdminClient (org.apache.kafka.clients.admin.AdminClient)6 HashSet (java.util.HashSet)5 AlterPartitionReassignmentsResult (org.apache.kafka.clients.admin.AlterPartitionReassignmentsResult)4 Node (org.apache.kafka.common.Node)4 KafkaCruiseControlConfig (com.linkedin.kafka.cruisecontrol.config.KafkaCruiseControlConfig)3 ClusterModel (com.linkedin.kafka.cruisecontrol.model.ClusterModel)3 Properties (java.util.Properties)3 TopicDescription (org.apache.kafka.clients.admin.TopicDescription)3 Cluster (org.apache.kafka.common.Cluster)3 PartitionInfo (org.apache.kafka.common.PartitionInfo)3 MetricRegistry (com.codahale.metrics.MetricRegistry)2 Goal (com.linkedin.kafka.cruisecontrol.analyzer.goals.Goal)2