/* * Copyright 2013 LinkedIn, Inc * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package voldemort.client.rebalance; import java.text.DecimalFormat; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; import voldemort.utils.Utils; public class RebalanceBatchPlanProgressBar { private static final Logger logger = Logger.getLogger(RebalanceBatchPlanProgressBar.class); private static final DecimalFormat decimalFormatter = new DecimalFormat("#.##"); private final int batchId; private final int totalTaskCount; private final int totalPartitionStoreCount; private final long startTimeMs; private Set<Integer> tasksInFlight; private int numTasksCompleted; private int numPartitionStoresMigrated; /** * Construct a progress bar object to track rebalance tasks completed and * partition-stores migrated. * * @param batchId * @param totalTaskCount * @param totalPartitionStoreCount */ RebalanceBatchPlanProgressBar(int batchId, int totalTaskCount, int totalPartitionStoreCount) { this.batchId = batchId; this.totalTaskCount = totalTaskCount; this.totalPartitionStoreCount = totalPartitionStoreCount; this.startTimeMs = System.currentTimeMillis(); this.tasksInFlight = new TreeSet<Integer>(); this.numTasksCompleted = 0; this.numPartitionStoresMigrated = 0; } /** * Called whenever a rebalance task starts. * * @param taskId */ synchronized public void beginTask(int taskId) { tasksInFlight.add(taskId); updateProgressBar(); } /** * Called whenever a rebalance task completes. This means one task is done * and some number of partition stores have been migrated. * * @param taskId * @param partitionStoresMigrated Number of partition stores moved by this * completed task. */ synchronized public void completeTask(int taskId, int partitionStoresMigrated) { tasksInFlight.remove(taskId); numTasksCompleted++; numPartitionStoresMigrated += partitionStoresMigrated; updateProgressBar(); } /** * Construct a pretty string documenting progress for this batch plan thus * far. * * @return pretty string documenting progress */ synchronized public String getPrettyProgressBar() { StringBuilder sb = new StringBuilder(); double taskRate = numTasksCompleted / (double) totalTaskCount; double partitionStoreRate = numPartitionStoresMigrated / (double) totalPartitionStoreCount; long deltaTimeMs = System.currentTimeMillis() - startTimeMs; long taskTimeRemainingMs = Long.MAX_VALUE; if(taskRate > 0) { taskTimeRemainingMs = (long) (deltaTimeMs * ((1.0 / taskRate) - 1.0)); } long partitionStoreTimeRemainingMs = Long.MAX_VALUE; if(partitionStoreRate > 0) { partitionStoreTimeRemainingMs = (long) (deltaTimeMs * ((1.0 / partitionStoreRate) - 1.0)); } // Title line sb.append("Progress update on rebalancing batch " + batchId).append(Utils.NEWLINE); // Tasks in flight update sb.append("There are currently " + tasksInFlight.size() + " rebalance tasks executing: ") .append(tasksInFlight) .append(".") .append(Utils.NEWLINE); // Tasks completed update sb.append("\t" + numTasksCompleted + " out of " + totalTaskCount + " rebalance tasks complete.") .append(Utils.NEWLINE) .append("\t") .append(decimalFormatter.format(taskRate * 100.0)) .append("% done, estimate ") .append(taskTimeRemainingMs) .append(" ms (") .append(TimeUnit.MILLISECONDS.toMinutes(taskTimeRemainingMs)) .append(" minutes) remaining.") .append(Utils.NEWLINE); // Partition-stores migrated update sb.append("\t" + numPartitionStoresMigrated + " out of " + totalPartitionStoreCount + " partition-stores migrated.") .append(Utils.NEWLINE) .append("\t") .append(decimalFormatter.format(partitionStoreRate * 100.0)) .append("% done, estimate ") .append(partitionStoreTimeRemainingMs) .append(" ms (") .append(TimeUnit.MILLISECONDS.toMinutes(partitionStoreTimeRemainingMs)) .append(" minutes) remaining.") .append(Utils.NEWLINE); return sb.toString(); } public void updateProgressBar() { if(logger.isInfoEnabled()) { String progressBar = getPrettyProgressBar(); logger.info(progressBar); } } }