HDFS-9715. Check storage ID uniqueness on datanode startup (Contributed by Lei (Eddy) Xu)

This commit is contained in:
Vinayakumar B 2016-02-03 07:35:01 +05:30
parent 414cf07650
commit 04375756a5
5 changed files with 95 additions and 23 deletions

View File

@ -1946,6 +1946,9 @@ Release 2.8.0 - UNRELEASED
HDFS-8999. Allow a file to be closed with COMMITTED but not yet COMPLETE HDFS-8999. Allow a file to be closed with COMMITTED but not yet COMPLETE
blocks. (szetszwo) blocks. (szetszwo)
HDFS-9715. Check storage ID uniqueness on datanode startup
(Lei (Eddy) Xu via vinayakumarb)
BUG FIXES BUG FIXES
HDFS-7501. TransactionsSinceLastCheckpoint can be negative on SBNs. HDFS-7501. TransactionsSinceLastCheckpoint can be negative on SBNs.

View File

@ -374,6 +374,31 @@ private static List<VolumeFailureInfo> getInitialVolumeFailureInfos(
return volumeFailureInfos; return volumeFailureInfos;
} }
/**
* Activate a volume to serve requests.
* @throws IOException if the storage UUID already exists.
*/
private synchronized void activateVolume(
ReplicaMap replicaMap,
Storage.StorageDirectory sd, StorageType storageType,
FsVolumeReference ref) throws IOException {
DatanodeStorage dnStorage = storageMap.get(sd.getStorageUuid());
if (dnStorage != null) {
final String errorMsg = String.format(
"Found duplicated storage UUID: %s in %s.",
sd.getStorageUuid(), sd.getVersionFile());
LOG.error(errorMsg);
throw new IOException(errorMsg);
}
volumeMap.addAll(replicaMap);
storageMap.put(sd.getStorageUuid(),
new DatanodeStorage(sd.getStorageUuid(),
DatanodeStorage.State.NORMAL,
storageType));
asyncDiskService.addVolume(sd.getCurrentDir());
volumes.addVolume(ref);
}
private void addVolume(Collection<StorageLocation> dataLocations, private void addVolume(Collection<StorageLocation> dataLocations,
Storage.StorageDirectory sd) throws IOException { Storage.StorageDirectory sd) throws IOException {
final File dir = sd.getCurrentDir(); final File dir = sd.getCurrentDir();
@ -389,16 +414,7 @@ private void addVolume(Collection<StorageLocation> dataLocations,
ReplicaMap tempVolumeMap = new ReplicaMap(this); ReplicaMap tempVolumeMap = new ReplicaMap(this);
fsVolume.getVolumeMap(tempVolumeMap, ramDiskReplicaTracker); fsVolume.getVolumeMap(tempVolumeMap, ramDiskReplicaTracker);
synchronized (this) { activateVolume(tempVolumeMap, sd, storageType, ref);
volumeMap.addAll(tempVolumeMap);
storageMap.put(sd.getStorageUuid(),
new DatanodeStorage(sd.getStorageUuid(),
DatanodeStorage.State.NORMAL,
storageType));
asyncDiskService.addVolume(sd.getCurrentDir());
volumes.addVolume(ref);
}
LOG.info("Added volume - " + dir + ", StorageType: " + storageType); LOG.info("Added volume - " + dir + ", StorageType: " + storageType);
} }
@ -456,15 +472,7 @@ public void addVolume(final StorageLocation location,
setupAsyncLazyPersistThread(fsVolume); setupAsyncLazyPersistThread(fsVolume);
builder.build(); builder.build();
synchronized (this) { activateVolume(tempVolumeMap, sd, storageType, ref);
volumeMap.addAll(tempVolumeMap);
storageMap.put(sd.getStorageUuid(),
new DatanodeStorage(sd.getStorageUuid(),
DatanodeStorage.State.NORMAL,
storageType));
asyncDiskService.addVolume(sd.getCurrentDir());
volumes.addVolume(ref);
}
LOG.info("Added volume - " + dir + ", StorageType: " + storageType); LOG.info("Added volume - " + dir + ", StorageType: " + storageType);
} }

View File

@ -30,6 +30,7 @@
import java.net.URI; import java.net.URI;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Properties;
import java.util.zip.CRC32; import java.util.zip.CRC32;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
@ -49,6 +50,7 @@
import org.apache.hadoop.hdfs.server.datanode.DataNodeLayoutVersion; import org.apache.hadoop.hdfs.server.datanode.DataNodeLayoutVersion;
import org.apache.hadoop.hdfs.server.datanode.DataStorage; import org.apache.hadoop.hdfs.server.datanode.DataStorage;
import org.apache.hadoop.hdfs.server.namenode.NNStorage; import org.apache.hadoop.hdfs.server.namenode.NNStorage;
import org.apache.hadoop.hdfs.server.protocol.DatanodeStorage;
import org.apache.hadoop.hdfs.server.protocol.NamenodeProtocols; import org.apache.hadoop.hdfs.server.protocol.NamenodeProtocols;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
@ -379,6 +381,14 @@ public static File[] createDataNodeStorageDirs(String[] parents,
localFS.copyToLocalFile(new Path(datanodeStorage.toString(), "current"), localFS.copyToLocalFile(new Path(datanodeStorage.toString(), "current"),
new Path(newDir.toString()), new Path(newDir.toString()),
false); false);
// Change the storage UUID to avoid conflicts when DN starts up.
StorageDirectory sd = new StorageDirectory(
new File(datanodeStorage.toString()));
sd.setStorageUuid(DatanodeStorage.generateUuid());
Properties properties = Storage.readPropertiesFile(sd.getVersionFile());
properties.setProperty("storageID", sd.getStorageUuid());
Storage.writeProperties(sd.getVersionFile(), properties);
retVal[i] = newDir; retVal[i] = newDir;
} }
return retVal; return retVal;

View File

@ -213,6 +213,32 @@ public void testAddVolumes() throws IOException {
assertTrue(actualVolumes.containsAll(expectedVolumes)); assertTrue(actualVolumes.containsAll(expectedVolumes));
} }
@Test
public void testAddVolumeWithSameStorageUuid() throws IOException {
HdfsConfiguration conf = new HdfsConfiguration();
MiniDFSCluster cluster = new MiniDFSCluster.Builder(conf)
.numDataNodes(1).build();
try {
cluster.waitActive();
assertTrue(cluster.getDataNodes().get(0).isConnectedToNN(
cluster.getNameNode().getServiceRpcAddress()));
MiniDFSCluster.DataNodeProperties dn = cluster.stopDataNode(0);
File vol0 = cluster.getStorageDir(0, 0);
File vol1 = cluster.getStorageDir(0, 1);
Storage.StorageDirectory sd0 = new Storage.StorageDirectory(vol0);
Storage.StorageDirectory sd1 = new Storage.StorageDirectory(vol1);
FileUtils.copyFile(sd0.getVersionFile(), sd1.getVersionFile());
cluster.restartDataNode(dn, true);
cluster.waitActive();
assertFalse(cluster.getDataNodes().get(0).isConnectedToNN(
cluster.getNameNode().getServiceRpcAddress()));
} finally {
cluster.shutdown();
}
}
@Test(timeout = 30000) @Test(timeout = 30000)
public void testRemoveVolumes() throws IOException { public void testRemoveVolumes() throws IOException {
// Feed FsDataset with block metadata. // Feed FsDataset with block metadata.

View File

@ -34,6 +34,8 @@
import java.util.Collections; import java.util.Collections;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -314,13 +316,16 @@ public static void assertParallelFilesAreIdentical(List<File> dirs,
} }
} }
Set<String> ignoredProperties = new HashSet<>();
ignoredProperties.add("storageID");
for (List<File> sameNameList : groupedByName.values()) { for (List<File> sameNameList : groupedByName.values()) {
if (sameNameList.get(0).isDirectory()) { if (sameNameList.get(0).isDirectory()) {
// recurse // recurse
assertParallelFilesAreIdentical(sameNameList, ignoredFileNames); assertParallelFilesAreIdentical(sameNameList, ignoredFileNames);
} else { } else {
if ("VERSION".equals(sameNameList.get(0).getName())) { if ("VERSION".equals(sameNameList.get(0).getName())) {
assertPropertiesFilesSame(sameNameList.toArray(new File[0])); assertPropertiesFilesSame(sameNameList.toArray(new File[0]),
ignoredProperties);
} else { } else {
assertFileContentsSame(sameNameList.toArray(new File[0])); assertFileContentsSame(sameNameList.toArray(new File[0]));
} }
@ -339,6 +344,20 @@ public static void assertParallelFilesAreIdentical(List<File> dirs,
*/ */
public static void assertPropertiesFilesSame(File[] propFiles) public static void assertPropertiesFilesSame(File[] propFiles)
throws IOException { throws IOException {
assertPropertiesFilesSame(propFiles, null);
}
/**
* Assert that a set of properties files all contain the same data.
*
* @param propFiles the files to compare.
* @param ignoredProperties the property names to be ignored during
* comparison.
* @throws IOException if the files cannot be opened or read
* @throws AssertionError if the files differ
*/
public static void assertPropertiesFilesSame(
File[] propFiles, Set<String> ignoredProperties) throws IOException {
Set<Map.Entry<Object, Object>> prevProps = null; Set<Map.Entry<Object, Object>> prevProps = null;
for (File f : propFiles) { for (File f : propFiles) {
@ -355,7 +374,13 @@ public static void assertPropertiesFilesSame(File[] propFiles)
} else { } else {
Set<Entry<Object,Object>> diff = Set<Entry<Object,Object>> diff =
Sets.symmetricDifference(prevProps, props.entrySet()); Sets.symmetricDifference(prevProps, props.entrySet());
if (!diff.isEmpty()) { Iterator<Entry<Object, Object>> it = diff.iterator();
while (it.hasNext()) {
Entry<Object, Object> entry = it.next();
if (ignoredProperties != null &&
ignoredProperties.contains(entry.getKey())) {
continue;
}
fail("Properties file " + f + " differs from " + propFiles[0]); fail("Properties file " + f + " differs from " + propFiles[0]);
} }
} }