示例#1
0
  /** Compute balance plan */
  public BalancePlan(Balancer balancer, List<DatanodeInfo> datanodes) {
    if (datanodes == null || datanodes.isEmpty()) {
      throw new IllegalArgumentException("cannot prepare plan for empty cluster");
    }

    avgRemaining = computeAvgRemaining(datanodes);
    lowerRemainingThreshold = Math.max(avgRemaining / 2, avgRemaining - balancer.threshold);
    upperRemainingThreshold = Math.min(PERCENTAGE_BASE, avgRemaining + balancer.threshold);
    if (lowerRemainingThreshold > upperRemainingThreshold) {
      throw new IllegalStateException("lowerThresh > upperThresh");
    }

    LOG.info(
        "balanced range: [ "
            + lowerRemainingThreshold
            + ", "
            + upperRemainingThreshold
            + " ], average remaining: "
            + avgRemaining);

    long overLoadedBytes = 0L, underLoadedBytes = 0L;
    Bucket clusterBucket = new Bucket();
    Map<Node, Bucket> rackBuckets = new HashMap<Node, Bucket>();
    for (DatanodeInfo datanode : datanodes) {
      // Update network topology
      cluster.add(datanode);
      // Create bucket if none
      assert datanode.getParent() != null : "node outside of any rack";
      Bucket bucket = rackBuckets.get(datanode.getParent());
      if (bucket == null) {
        bucket = new Bucket();
        rackBuckets.put(datanode.getParent(), bucket);
      }
      // Put DataNode into chosen bucket
      BalancerDatanode datanodeS;
      if (getRemaining(datanode) < avgRemaining) {
        // Above average utilized
        datanodeS = balancer.getSource(datanode, avgRemaining);
        bucket.addSource((Source) datanodeS);
        clusterBucket.addSource((Source) datanodeS);
        if (isOverUtilized(datanodeS)) {
          overLoadedBytes +=
              (long)
                  ((lowerRemainingThreshold - datanodeS.getCurrentRemaining())
                      * datanodeS.getDatanode().getCapacity()
                      / PERCENTAGE_BASE);
        }
      } else {
        // Below average utilized
        datanodeS = new Target(datanode, avgRemaining);
        bucket.addTarget((Target) datanodeS);
        clusterBucket.addTarget((Target) datanodeS);
        if (isUnderUtilized(datanodeS)) {
          underLoadedBytes +=
              (long)
                  ((datanodeS.getCurrentRemaining() - upperRemainingThreshold)
                      * datanodeS.getDatanode().getCapacity()
                      / PERCENTAGE_BASE);
        }
      }
      // Update all DataNodes list
      this.datanodes.put(datanode.getStorageID(), datanodeS);
    }
    bytesLeftToMove = Math.max(overLoadedBytes, underLoadedBytes);

    logImbalancedNodes();

    // Balance each rack bucket separately
    for (Bucket bucket : rackBuckets.values()) {
      double rackAverage = bucket.computeAvgRemaining();
      if (lowerRemainingThreshold <= rackAverage && rackAverage <= upperRemainingThreshold) {
        bucket.updatePlan();
      }
      // If perfectly balanced rack renders only over or underutilized DataNodes
      // we do not bother balancing it
    }
    // Balance cluster-wide afterwards
    clusterBucket.externalUpdate();
    clusterBucket.updatePlan();

    bytesToMove = 0L;
    for (Source src : sources) {
      bytesToMove += src.scheduledSize;
    }

    logPlanOutcome();
  }