|
@@ -22,6 +22,8 @@ import com.google.common.base.Preconditions;
|
|
|
import org.apache.commons.codec.digest.DigestUtils;
|
|
|
import org.apache.hadoop.classification.InterfaceAudience;
|
|
|
import org.apache.hadoop.conf.Configuration;
|
|
|
+import org.apache.hadoop.hdfs.server.datanode.fsdataset.FsDatasetSpi
|
|
|
+ .FsVolumeReferences;
|
|
|
import org.apache.hadoop.util.AutoCloseableLock;
|
|
|
import org.apache.hadoop.hdfs.DFSConfigKeys;
|
|
|
import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
|
|
@@ -41,6 +43,7 @@ import org.slf4j.LoggerFactory;
|
|
|
|
|
|
import java.io.IOException;
|
|
|
import java.nio.charset.Charset;
|
|
|
+import java.util.Arrays;
|
|
|
import java.util.HashMap;
|
|
|
import java.util.List;
|
|
|
import java.util.LinkedList;
|
|
@@ -192,7 +195,30 @@ public class DiskBalancer {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Returns the Current Work Status of a submitted Plan.
|
|
|
+ * Get FsVolume by volume UUID.
|
|
|
+ * @param fsDataset
|
|
|
+ * @param volUuid
|
|
|
+ * @return FsVolumeSpi
|
|
|
+ */
|
|
|
+ private static FsVolumeSpi getFsVolume(final FsDatasetSpi<?> fsDataset,
|
|
|
+ final String volUuid) {
|
|
|
+ FsVolumeSpi fsVolume = null;
|
|
|
+ try (FsVolumeReferences volumeReferences =
|
|
|
+ fsDataset.getFsVolumeReferences()) {
|
|
|
+ for (int i = 0; i < volumeReferences.size(); i++) {
|
|
|
+ if (volumeReferences.get(i).getStorageID().equals(volUuid)) {
|
|
|
+ fsVolume = volumeReferences.get(i);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (IOException e) {
|
|
|
+ LOG.warn("Disk Balancer - Error when closing volume references: ", e);
|
|
|
+ }
|
|
|
+ return fsVolume;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the current work status of a previously submitted Plan.
|
|
|
*
|
|
|
* @return DiskBalancerWorkStatus.
|
|
|
* @throws DiskBalancerException
|
|
@@ -214,8 +240,8 @@ public class DiskBalancer {
|
|
|
for (Map.Entry<VolumePair, DiskBalancerWorkItem> entry :
|
|
|
workMap.entrySet()) {
|
|
|
DiskBalancerWorkEntry workEntry = new DiskBalancerWorkEntry(
|
|
|
- entry.getKey().getSource().getBasePath(),
|
|
|
- entry.getKey().getDest().getBasePath(),
|
|
|
+ entry.getKey().getSourceVolBasePath(),
|
|
|
+ entry.getKey().getDestVolBasePath(),
|
|
|
entry.getValue());
|
|
|
status.addWorkEntry(workEntry);
|
|
|
}
|
|
@@ -269,12 +295,7 @@ public class DiskBalancer {
|
|
|
lock.lock();
|
|
|
try {
|
|
|
checkDiskBalancerEnabled();
|
|
|
- Map<String, String> pathMap = new HashMap<>();
|
|
|
- Map<String, FsVolumeSpi> volMap = getStorageIDToVolumeMap();
|
|
|
- for (Map.Entry<String, FsVolumeSpi> entry : volMap.entrySet()) {
|
|
|
- pathMap.put(entry.getKey(), entry.getValue().getBasePath());
|
|
|
- }
|
|
|
- return JsonUtil.toJsonString(pathMap);
|
|
|
+ return JsonUtil.toJsonString(getStorageIDToVolumeBasePathMap());
|
|
|
} catch (DiskBalancerException ex) {
|
|
|
throw ex;
|
|
|
} catch (IOException e) {
|
|
@@ -434,47 +455,52 @@ public class DiskBalancer {
|
|
|
|
|
|
// Cleanup any residual work in the map.
|
|
|
workMap.clear();
|
|
|
- Map<String, FsVolumeSpi> pathMap = getStorageIDToVolumeMap();
|
|
|
+ Map<String, String> storageIDToVolBasePathMap =
|
|
|
+ getStorageIDToVolumeBasePathMap();
|
|
|
|
|
|
for (Step step : plan.getVolumeSetPlans()) {
|
|
|
- String sourceuuid = step.getSourceVolume().getUuid();
|
|
|
- String destinationuuid = step.getDestinationVolume().getUuid();
|
|
|
-
|
|
|
- FsVolumeSpi sourceVol = pathMap.get(sourceuuid);
|
|
|
- if (sourceVol == null) {
|
|
|
- LOG.error("Disk Balancer - Unable to find source volume. submitPlan " +
|
|
|
- "failed.");
|
|
|
- throw new DiskBalancerException("Unable to find source volume.",
|
|
|
+ String sourceVolUuid = step.getSourceVolume().getUuid();
|
|
|
+ String destVolUuid = step.getDestinationVolume().getUuid();
|
|
|
+
|
|
|
+ String sourceVolBasePath = storageIDToVolBasePathMap.get(sourceVolUuid);
|
|
|
+ if (sourceVolBasePath == null) {
|
|
|
+ final String errMsg = "Disk Balancer - Unable to find volume: "
|
|
|
+ + step.getSourceVolume().getPath() + ". SubmitPlan failed.";
|
|
|
+ LOG.error(errMsg);
|
|
|
+ throw new DiskBalancerException(errMsg,
|
|
|
DiskBalancerException.Result.INVALID_VOLUME);
|
|
|
}
|
|
|
|
|
|
- FsVolumeSpi destVol = pathMap.get(destinationuuid);
|
|
|
- if (destVol == null) {
|
|
|
- LOG.error("Disk Balancer - Unable to find destination volume. " +
|
|
|
- "submitPlan failed.");
|
|
|
- throw new DiskBalancerException("Unable to find destination volume.",
|
|
|
+ String destVolBasePath = storageIDToVolBasePathMap.get(destVolUuid);
|
|
|
+ if (destVolBasePath == null) {
|
|
|
+ final String errMsg = "Disk Balancer - Unable to find volume: "
|
|
|
+ + step.getDestinationVolume().getPath() + ". SubmitPlan failed.";
|
|
|
+ LOG.error(errMsg);
|
|
|
+ throw new DiskBalancerException(errMsg,
|
|
|
DiskBalancerException.Result.INVALID_VOLUME);
|
|
|
}
|
|
|
- createWorkPlan(sourceVol, destVol, step);
|
|
|
+ VolumePair volumePair = new VolumePair(sourceVolUuid,
|
|
|
+ sourceVolBasePath, destVolUuid, destVolBasePath);
|
|
|
+ createWorkPlan(volumePair, step);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Returns a path to Volume Map.
|
|
|
+ * Returns volume UUID to volume base path map.
|
|
|
*
|
|
|
* @return Map
|
|
|
* @throws DiskBalancerException
|
|
|
*/
|
|
|
- private Map<String, FsVolumeSpi> getStorageIDToVolumeMap()
|
|
|
+ private Map<String, String> getStorageIDToVolumeBasePathMap()
|
|
|
throws DiskBalancerException {
|
|
|
- Map<String, FsVolumeSpi> pathMap = new HashMap<>();
|
|
|
+ Map<String, String> storageIDToVolBasePathMap = new HashMap<>();
|
|
|
FsDatasetSpi.FsVolumeReferences references;
|
|
|
try {
|
|
|
try(AutoCloseableLock lock = this.dataset.acquireDatasetLock()) {
|
|
|
references = this.dataset.getFsVolumeReferences();
|
|
|
for (int ndx = 0; ndx < references.size(); ndx++) {
|
|
|
FsVolumeSpi vol = references.get(ndx);
|
|
|
- pathMap.put(vol.getStorageID(), vol);
|
|
|
+ storageIDToVolBasePathMap.put(vol.getStorageID(), vol.getBasePath());
|
|
|
}
|
|
|
references.close();
|
|
|
}
|
|
@@ -483,7 +509,7 @@ public class DiskBalancer {
|
|
|
throw new DiskBalancerException("Internal error", ex,
|
|
|
DiskBalancerException.Result.INTERNAL_ERROR);
|
|
|
}
|
|
|
- return pathMap;
|
|
|
+ return storageIDToVolBasePathMap;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -513,26 +539,24 @@ public class DiskBalancer {
|
|
|
|
|
|
/**
|
|
|
* Insert work items to work map.
|
|
|
- *
|
|
|
- * @param source - Source vol
|
|
|
- * @param dest - destination volume
|
|
|
- * @param step - Move Step
|
|
|
+ * @param volumePair - VolumePair
|
|
|
+ * @param step - Move Step
|
|
|
*/
|
|
|
- private void createWorkPlan(FsVolumeSpi source, FsVolumeSpi dest,
|
|
|
- Step step) throws DiskBalancerException {
|
|
|
-
|
|
|
- if (source.getStorageID().equals(dest.getStorageID())) {
|
|
|
- LOG.info("Disk Balancer - source & destination volumes are same.");
|
|
|
- throw new DiskBalancerException("source and destination volumes are " +
|
|
|
- "same.", DiskBalancerException.Result.INVALID_MOVE);
|
|
|
+ private void createWorkPlan(final VolumePair volumePair, Step step)
|
|
|
+ throws DiskBalancerException {
|
|
|
+ if (volumePair.getSourceVolUuid().equals(volumePair.getDestVolUuid())) {
|
|
|
+ final String errMsg = "Disk Balancer - Source and destination volumes " +
|
|
|
+ "are same: " + volumePair.getSourceVolUuid();
|
|
|
+ LOG.warn(errMsg);
|
|
|
+ throw new DiskBalancerException(errMsg,
|
|
|
+ DiskBalancerException.Result.INVALID_MOVE);
|
|
|
}
|
|
|
- VolumePair pair = new VolumePair(source, dest);
|
|
|
long bytesToMove = step.getBytesToMove();
|
|
|
// In case we have a plan with more than
|
|
|
- // one line of same <source, dest>
|
|
|
+ // one line of same VolumePair
|
|
|
// we compress that into one work order.
|
|
|
- if (workMap.containsKey(pair)) {
|
|
|
- bytesToMove += workMap.get(pair).getBytesToCopy();
|
|
|
+ if (workMap.containsKey(volumePair)) {
|
|
|
+ bytesToMove += workMap.get(volumePair).getBytesToCopy();
|
|
|
}
|
|
|
|
|
|
DiskBalancerWorkItem work = new DiskBalancerWorkItem(bytesToMove, 0);
|
|
@@ -542,7 +566,7 @@ public class DiskBalancer {
|
|
|
work.setBandwidth(step.getBandwidth());
|
|
|
work.setTolerancePercent(step.getTolerancePercent());
|
|
|
work.setMaxDiskErrors(step.getMaxDiskErrors());
|
|
|
- workMap.put(pair, work);
|
|
|
+ workMap.put(volumePair, work);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -591,39 +615,63 @@ public class DiskBalancer {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Holds references to actual volumes that we will be operating against.
|
|
|
+ * Holds source and dest volumes UUIDs and their BasePaths
|
|
|
+ * that disk balancer will be operating against.
|
|
|
*/
|
|
|
public static class VolumePair {
|
|
|
- private final FsVolumeSpi source;
|
|
|
- private final FsVolumeSpi dest;
|
|
|
+ private final String sourceVolUuid;
|
|
|
+ private final String destVolUuid;
|
|
|
+ private final String sourceVolBasePath;
|
|
|
+ private final String destVolBasePath;
|
|
|
|
|
|
/**
|
|
|
* Constructs a volume pair.
|
|
|
+ * @param sourceVolUuid - Source Volume
|
|
|
+ * @param sourceVolBasePath - Source Volume Base Path
|
|
|
+ * @param destVolUuid - Destination Volume
|
|
|
+ * @param destVolBasePath - Destination Volume Base Path
|
|
|
+ */
|
|
|
+ public VolumePair(final String sourceVolUuid,
|
|
|
+ final String sourceVolBasePath, final String destVolUuid,
|
|
|
+ final String destVolBasePath) {
|
|
|
+ this.sourceVolUuid = sourceVolUuid;
|
|
|
+ this.sourceVolBasePath = sourceVolBasePath;
|
|
|
+ this.destVolUuid = destVolUuid;
|
|
|
+ this.destVolBasePath = destVolBasePath;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Gets source volume UUID.
|
|
|
*
|
|
|
- * @param source - Source Volume
|
|
|
- * @param dest - Destination Volume
|
|
|
+ * @return UUID String
|
|
|
*/
|
|
|
- public VolumePair(FsVolumeSpi source, FsVolumeSpi dest) {
|
|
|
- this.source = source;
|
|
|
- this.dest = dest;
|
|
|
+ public String getSourceVolUuid() {
|
|
|
+ return sourceVolUuid;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * gets source volume.
|
|
|
+ * Gets source volume base path.
|
|
|
+ * @return String
|
|
|
+ */
|
|
|
+ public String getSourceVolBasePath() {
|
|
|
+ return sourceVolBasePath;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * Gets destination volume UUID.
|
|
|
*
|
|
|
- * @return volume
|
|
|
+ * @return UUID String
|
|
|
*/
|
|
|
- public FsVolumeSpi getSource() {
|
|
|
- return source;
|
|
|
+ public String getDestVolUuid() {
|
|
|
+ return destVolUuid;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Gets Destination volume.
|
|
|
+ * Gets desitnation volume base path.
|
|
|
*
|
|
|
- * @return volume.
|
|
|
+ * @return String
|
|
|
*/
|
|
|
- public FsVolumeSpi getDest() {
|
|
|
- return dest;
|
|
|
+ public String getDestVolBasePath() {
|
|
|
+ return destVolBasePath;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -636,13 +684,21 @@ public class DiskBalancer {
|
|
|
}
|
|
|
|
|
|
VolumePair that = (VolumePair) o;
|
|
|
- return source.equals(that.source) && dest.equals(that.dest);
|
|
|
+ return sourceVolUuid.equals(that.sourceVolUuid)
|
|
|
+ && sourceVolBasePath.equals(that.sourceVolBasePath)
|
|
|
+ && destVolUuid.equals(that.destVolUuid)
|
|
|
+ && destVolBasePath.equals(that.destVolBasePath);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public int hashCode() {
|
|
|
- int result = source.getBasePath().hashCode();
|
|
|
- result = 31 * result + dest.getBasePath().hashCode();
|
|
|
+ final int primeNum = 31;
|
|
|
+ final List<String> volumeStrList = Arrays.asList(sourceVolUuid,
|
|
|
+ sourceVolBasePath, destVolUuid, destVolBasePath);
|
|
|
+ int result = 1;
|
|
|
+ for (String str : volumeStrList) {
|
|
|
+ result = (result * primeNum) + str.hashCode();
|
|
|
+ }
|
|
|
return result;
|
|
|
}
|
|
|
}
|
|
@@ -932,8 +988,28 @@ public class DiskBalancer {
|
|
|
*/
|
|
|
@Override
|
|
|
public void copyBlocks(VolumePair pair, DiskBalancerWorkItem item) {
|
|
|
- FsVolumeSpi source = pair.getSource();
|
|
|
- FsVolumeSpi dest = pair.getDest();
|
|
|
+ String sourceVolUuid = pair.getSourceVolUuid();
|
|
|
+ String destVolUuuid = pair.getDestVolUuid();
|
|
|
+
|
|
|
+ // When any of the DiskBalancerWorkItem volumes are not
|
|
|
+ // available, return after setting error in item.
|
|
|
+ FsVolumeSpi source = getFsVolume(this.dataset, sourceVolUuid);
|
|
|
+ if (source == null) {
|
|
|
+ final String errMsg = "Disk Balancer - Unable to find source volume: "
|
|
|
+ + pair.getDestVolBasePath();
|
|
|
+ LOG.error(errMsg);
|
|
|
+ item.setErrMsg(errMsg);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ FsVolumeSpi dest = getFsVolume(this.dataset, destVolUuuid);
|
|
|
+ if (dest == null) {
|
|
|
+ final String errMsg = "Disk Balancer - Unable to find dest volume: "
|
|
|
+ + pair.getDestVolBasePath();
|
|
|
+ LOG.error(errMsg);
|
|
|
+ item.setErrMsg(errMsg);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
List<FsVolumeSpi.BlockIterator> poolIters = new LinkedList<>();
|
|
|
startTime = Time.now();
|
|
|
item.setStartTime(startTime);
|
|
@@ -977,7 +1053,7 @@ public class DiskBalancer {
|
|
|
// we are not able to find any blocks to copy.
|
|
|
if (block == null) {
|
|
|
LOG.error("No source blocks, exiting the copy. Source: {}, " +
|
|
|
- "dest:{}", source.getBasePath(), dest.getBasePath());
|
|
|
+ "Dest:{}", source.getBasePath(), dest.getBasePath());
|
|
|
this.setExitFlag();
|
|
|
continue;
|
|
|
}
|